Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/component_loader.h" | 5 #include "chrome/browser/extensions/component_loader.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/path_service.h" | 8 #include "base/path_service.h" |
| 9 #include "base/json/json_value_serializer.h" | 9 #include "base/json/json_value_serializer.h" |
| 10 #include "chrome/browser/browser_process.h" | 10 #include "chrome/browser/browser_process.h" |
| 11 #include "chrome/browser/extensions/extension_service.h" | 11 #include "chrome/browser/extensions/extension_service.h" |
| 12 #include "chrome/browser/prefs/pref_change_registrar.h" | |
| 13 #include "chrome/browser/prefs/pref_notifier.h" | |
| 14 #include "chrome/browser/profiles/profile.h" | |
| 15 #include "chrome/common/chrome_notification_types.h" | |
| 12 #include "chrome/common/chrome_paths.h" | 16 #include "chrome/common/chrome_paths.h" |
| 13 #include "chrome/common/chrome_switches.h" | 17 #include "chrome/common/chrome_switches.h" |
| 14 #include "chrome/common/extensions/extension.h" | 18 #include "chrome/common/extensions/extension.h" |
| 15 #include "chrome/common/pref_names.h" | 19 #include "chrome/common/pref_names.h" |
| 20 #include "content/public/browser/notification_details.h" | |
| 21 #include "content/public/browser/notification_source.h" | |
| 16 #include "grit/browser_resources.h" | 22 #include "grit/browser_resources.h" |
| 17 #include "ui/base/resource/resource_bundle.h" | 23 #include "ui/base/resource/resource_bundle.h" |
| 18 | 24 |
| 19 #if defined(OFFICIAL_BUILD) | 25 #if defined(OFFICIAL_BUILD) |
| 20 #include "chrome/browser/defaults.h" | 26 #include "chrome/browser/defaults.h" |
| 21 #endif | 27 #endif |
| 22 | 28 |
| 23 namespace { | |
| 24 | |
| 25 typedef std::list<std::pair<FilePath::StringType, int> > | |
| 26 ComponentExtensionList; | |
| 27 | |
| 28 #if defined(FILE_MANAGER_EXTENSION) | |
| 29 void AddFileManagerExtension(ComponentExtensionList* component_extensions) { | |
| 30 #ifndef NDEBUG | |
| 31 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 32 if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) { | |
| 33 FilePath filemgr_extension_path = | |
| 34 command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath); | |
| 35 component_extensions->push_back(std::make_pair( | |
| 36 filemgr_extension_path.value(), | |
| 37 IDR_FILEMANAGER_MANIFEST)); | |
| 38 return; | |
| 39 } | |
| 40 #endif // NDEBUG | |
| 41 component_extensions->push_back(std::make_pair( | |
| 42 FILE_PATH_LITERAL("file_manager"), | |
| 43 IDR_FILEMANAGER_MANIFEST)); | |
| 44 } | |
| 45 #endif // defined(FILE_MANAGER_EXTENSION) | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 namespace extensions { | 29 namespace extensions { |
| 50 | 30 |
| 51 bool ComponentLoader::ComponentExtensionInfo::Equals( | 31 ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service, |
| 52 const ComponentExtensionInfo& other) const { | 32 PrefService* prefs, |
| 53 return other.manifest == manifest && other.root_directory == root_directory; | 33 PrefService* local_state) |
| 54 } | 34 : prefs_(prefs), |
| 35 local_state_(local_state), | |
| 36 extension_service_(extension_service) { | |
| 37 pref_change_registrar_.Init(prefs); | |
| 55 | 38 |
| 56 ComponentLoader::ComponentLoader(ExtensionService* extension_service) | 39 // This pref is set by policy. We have to watch it for change because on |
| 57 : extension_service_(extension_service) { | 40 // ChromeOS, policy isn't loaded until after the browser process is started. |
| 41 pref_change_registrar_.Add(prefs::kEnterpriseWebStoreURL, this); | |
| 58 } | 42 } |
| 59 | 43 |
| 60 ComponentLoader::~ComponentLoader() { | 44 ComponentLoader::~ComponentLoader() { |
| 61 } | 45 } |
| 62 | 46 |
| 63 void ComponentLoader::LoadAll() { | 47 void ComponentLoader::LoadAll() { |
| 64 for (RegisteredComponentExtensions::iterator it = | 48 for (RegisteredComponentExtensions::iterator it = |
| 65 component_extensions_.begin(); | 49 component_extensions_.begin(); |
| 66 it != component_extensions_.end(); ++it) { | 50 it != component_extensions_.end(); ++it) { |
| 67 Load(*it); | 51 Load(*it); |
| 68 } | 52 } |
| 69 } | 53 } |
| 70 | 54 |
| 55 DictionaryValue* ComponentLoader::ParseManifest( | |
| 56 const std::string& manifest_contents) const { | |
| 57 JSONStringValueSerializer serializer(manifest_contents); | |
| 58 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); | |
| 59 | |
| 60 if (!manifest.get() || !manifest->IsType(Value::TYPE_DICTIONARY)) { | |
| 61 LOG(ERROR) << "Failed to parse extension manifest."; | |
| 62 return NULL; | |
| 63 } | |
| 64 // Transfer ownership to the caller. | |
| 65 return static_cast<DictionaryValue*>(manifest.release()); | |
| 66 } | |
| 67 | |
| 71 const Extension* ComponentLoader::Add( | 68 const Extension* ComponentLoader::Add( |
| 72 const std::string& manifest, const FilePath& root_directory) { | 69 int manifest_resource_id, |
| 73 ComponentExtensionInfo info(manifest, root_directory); | 70 const FilePath& root_directory) { |
| 74 Register(info); | 71 std::string manifest_contents = |
| 72 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 73 manifest_resource_id).as_string(); | |
| 74 return Add(manifest_contents, root_directory); | |
| 75 } | |
| 76 | |
| 77 const Extension* ComponentLoader::Add( | |
| 78 std::string& manifest_contents, | |
| 79 const FilePath& root_directory) { | |
| 80 // The Value is kept for the lifetime of the ComponentLoader. This is | |
| 81 // required in case LoadAll() is called again. | |
| 82 DictionaryValue* manifest = ParseManifest(manifest_contents); | |
| 83 if (manifest) | |
| 84 return Add(manifest, root_directory); | |
| 85 return NULL; | |
| 86 } | |
| 87 | |
| 88 const Extension* ComponentLoader::Add( | |
| 89 const DictionaryValue* parsed_manifest, | |
| 90 const FilePath& root_directory) { | |
| 91 // Get the absolute path to the extension. | |
| 92 FilePath absolute_path(root_directory); | |
| 93 if (!absolute_path.IsAbsolute()) { | |
| 94 if (PathService::Get(chrome::DIR_RESOURCES, &absolute_path)) { | |
| 95 absolute_path = absolute_path.Append(root_directory); | |
| 96 } else { | |
| 97 NOTREACHED(); | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 ComponentExtensionInfo info(parsed_manifest, absolute_path); | |
| 102 component_extensions_.push_back(info); | |
| 75 if (extension_service_->is_ready()) | 103 if (extension_service_->is_ready()) |
| 76 return Load(info); | 104 return Load(info); |
| 77 return NULL; | 105 return NULL; |
| 78 } | 106 } |
| 79 | 107 |
| 80 const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) { | 108 const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) { |
| 81 JSONStringValueSerializer serializer(info.manifest); | |
| 82 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); | |
| 83 if (!manifest.get()) { | |
| 84 LOG(ERROR) << "Failed to parse manifest for extension"; | |
| 85 return NULL; | |
| 86 } | |
| 87 | |
| 88 int flags = Extension::REQUIRE_KEY; | 109 int flags = Extension::REQUIRE_KEY; |
| 89 if (Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT)) | 110 if (Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT)) |
| 90 flags |= Extension::STRICT_ERROR_CHECKS; | 111 flags |= Extension::STRICT_ERROR_CHECKS; |
| 91 std::string error; | 112 std::string error; |
| 92 scoped_refptr<const Extension> extension(Extension::Create( | 113 scoped_refptr<const Extension> extension(Extension::Create( |
| 93 info.root_directory, | 114 info.root_directory, |
| 94 Extension::COMPONENT, | 115 Extension::COMPONENT, |
| 95 *static_cast<DictionaryValue*>(manifest.get()), | 116 *info.manifest, |
| 96 flags, | 117 flags, |
| 97 &error)); | 118 &error)); |
| 98 if (!extension.get()) { | 119 if (!extension.get()) { |
| 99 LOG(ERROR) << error; | 120 LOG(ERROR) << error; |
| 100 return NULL; | 121 return NULL; |
| 101 } | 122 } |
| 102 extension_service_->AddExtension(extension); | 123 extension_service_->AddExtension(extension); |
| 103 return extension; | 124 return extension; |
| 104 } | 125 } |
| 105 | 126 |
| 106 void ComponentLoader::Remove(const std::string& manifest_str) { | 127 void ComponentLoader::Remove(const FilePath& root_directory) { |
| 107 // Unload the extension. | 128 // Find the ComponentExtensionInfo for the extension. |
| 108 JSONStringValueSerializer serializer(manifest_str); | 129 RegisteredComponentExtensions::iterator it = component_extensions_.begin(); |
| 109 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); | 130 for (; it != component_extensions_.end(); ++it) { |
| 110 if (!manifest.get()) { | 131 if (it->root_directory == root_directory) |
| 111 LOG(ERROR) << "Failed to parse manifest for extension"; | 132 break; |
| 133 } | |
| 134 // If the extension is not in the list, there's nothing to do. | |
| 135 if (it == component_extensions_.end()) | |
| 112 return; | 136 return; |
| 113 } | 137 |
| 138 const DictionaryValue* manifest = it->manifest; | |
| 139 | |
| 140 // Remove the extension from the list of registered extensions. | |
| 141 *it = component_extensions_.back(); | |
| 142 component_extensions_.pop_back(); | |
| 143 | |
| 144 // Determine the extension id and unload the extension. | |
| 114 std::string public_key; | 145 std::string public_key; |
| 115 std::string public_key_bytes; | 146 std::string public_key_bytes; |
| 116 std::string id; | 147 std::string id; |
| 117 if (!static_cast<DictionaryValue*>(manifest.get())-> | 148 if (!manifest->GetString( |
| 118 GetString(extension_manifest_keys::kPublicKey, &public_key) || | 149 extension_manifest_keys::kPublicKey, &public_key) || |
| 119 !Extension::ParsePEMKeyBytes(public_key, &public_key_bytes) || | 150 !Extension::ParsePEMKeyBytes(public_key, &public_key_bytes) || |
| 120 !Extension::GenerateId(public_key_bytes, &id)) { | 151 !Extension::GenerateId(public_key_bytes, &id)) { |
| 121 LOG(ERROR) << "Failed to get extension id"; | 152 LOG(ERROR) << "Failed to get extension id"; |
| 122 return; | 153 return; |
| 123 } | 154 } |
| 124 extension_service_-> | 155 extension_service_-> |
| 125 UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE); | 156 UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE); |
| 126 | |
| 127 // Unregister the extension. | |
| 128 RegisteredComponentExtensions new_component_extensions; | |
| 129 for (RegisteredComponentExtensions::iterator it = | |
| 130 component_extensions_.begin(); | |
| 131 it != component_extensions_.end(); ++it) { | |
| 132 if (it->manifest != manifest_str) | |
| 133 new_component_extensions.push_back(*it); | |
| 134 } | |
| 135 component_extensions_.swap(new_component_extensions); | |
| 136 } | 157 } |
| 137 | 158 |
| 138 // We take ComponentExtensionList: | 159 void ComponentLoader::AddFileManagerExtension() { |
| 139 // path, manifest ID => full manifest, absolute path | 160 #if defined(FILE_MANAGER_EXTENSION) |
| 161 #ifndef NDEBUG | |
| 162 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 163 if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) { | |
| 164 FilePath filemgr_extension_path( | |
| 165 command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath)); | |
| 166 Add(IDR_FILEMANAGER_MANIFEST, filemgr_extension_path); | |
| 167 return; | |
| 168 } | |
| 169 #endif // NDEBUG | |
| 170 Add(IDR_FILEMANAGER_MANIFEST, FilePath(FILE_PATH_LITERAL("file_manager"))); | |
| 171 #endif // defined(FILE_MANAGER_EXTENSION) | |
| 172 } | |
| 173 | |
| 174 void ComponentLoader::AddOrReloadEnterpriseWebStore() { | |
| 175 FilePath path(FILE_PATH_LITERAL("enterprise_web_store")); | |
| 176 | |
| 177 // Remove the extension if it was already loaded. | |
| 178 Remove(path); | |
|
Jeffrey Yasskin
2013/05/14 02:26:32
This Remove() call isn't working anymore, at least
| |
| 179 | |
| 180 std::string enterprise_webstore_url = | |
| 181 prefs_->GetString(prefs::kEnterpriseWebStoreURL); | |
| 182 | |
| 183 // Load the extension only if the URL preference is set. | |
| 184 if (!enterprise_webstore_url.empty()) { | |
| 185 std::string manifest_contents = | |
| 186 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 187 IDR_ENTERPRISE_WEBSTORE_MANIFEST).as_string(); | |
| 188 | |
| 189 // The manifest is missing some values that are provided by policy. | |
| 190 DictionaryValue* manifest = ParseManifest(manifest_contents); | |
| 191 if (manifest) { | |
| 192 std::string name = prefs_->GetString(prefs::kEnterpriseWebStoreName); | |
| 193 manifest->SetString("app.launch.web_url", enterprise_webstore_url); | |
| 194 manifest->SetString("name", name); | |
| 195 Add(manifest, path); | |
| 196 } | |
| 197 } | |
| 198 } | |
| 199 | |
| 140 void ComponentLoader::AddDefaultComponentExtensions() { | 200 void ComponentLoader::AddDefaultComponentExtensions() { |
| 141 ComponentExtensionList component_extensions; | 201 Add(IDR_BOOKMARKS_MANIFEST, FilePath(FILE_PATH_LITERAL("bookmark_manager"))); |
| 142 | |
| 143 // Bookmark manager. | |
| 144 component_extensions.push_back(std::make_pair( | |
| 145 FILE_PATH_LITERAL("bookmark_manager"), | |
| 146 IDR_BOOKMARKS_MANIFEST)); | |
| 147 | 202 |
| 148 #if defined(FILE_MANAGER_EXTENSION) | 203 #if defined(FILE_MANAGER_EXTENSION) |
| 149 AddFileManagerExtension(&component_extensions); | 204 AddFileManagerExtension(); |
| 150 #endif | 205 #endif |
| 151 | 206 |
| 152 #if defined(USE_VIRTUAL_KEYBOARD) | 207 #if defined(USE_VIRTUAL_KEYBOARD) |
| 153 component_extensions.push_back(std::make_pair( | 208 Add(IDR_KEYBOARD_MANIFEST, FilePath(FILE_PATH_LITERAL("keyboard"))); |
| 154 FILE_PATH_LITERAL("keyboard"), | |
| 155 IDR_KEYBOARD_MANIFEST)); | |
| 156 #endif | 209 #endif |
| 157 | 210 |
| 158 #if defined(OS_CHROMEOS) | 211 #if defined(OS_CHROMEOS) |
| 159 component_extensions.push_back(std::make_pair( | 212 Add(IDR_MOBILE_MANIFEST, |
| 160 FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile"), | 213 FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile"))); |
| 161 IDR_MOBILE_MANIFEST)); | |
| 162 | 214 |
| 163 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | 215 const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| 164 if (command_line->HasSwitch(switches::kAuthExtensionPath)) { | 216 if (command_line->HasSwitch(switches::kAuthExtensionPath)) { |
| 165 FilePath auth_extension_path = | 217 FilePath auth_extension_path = |
| 166 command_line->GetSwitchValuePath(switches::kAuthExtensionPath); | 218 command_line->GetSwitchValuePath(switches::kAuthExtensionPath); |
| 167 component_extensions.push_back(std::make_pair( | 219 Add(IDR_GAIA_TEST_AUTH_MANIFEST, auth_extension_path); |
| 168 auth_extension_path.value(), | |
| 169 IDR_GAIA_TEST_AUTH_MANIFEST)); | |
| 170 } else { | 220 } else { |
| 171 component_extensions.push_back(std::make_pair( | 221 Add(IDR_GAIA_AUTH_MANIFEST, |
| 172 FILE_PATH_LITERAL("/usr/share/chromeos-assets/gaia_auth"), | 222 FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/gaia_auth"))); |
| 173 IDR_GAIA_AUTH_MANIFEST)); | |
| 174 } | 223 } |
| 175 | 224 |
| 176 #if defined(OFFICIAL_BUILD) | 225 #if defined(OFFICIAL_BUILD) |
| 177 if (browser_defaults::enable_help_app) { | 226 if (browser_defaults::enable_help_app) { |
| 178 component_extensions.push_back(std::make_pair( | 227 Add(IDR_HELP_MANIFEST, |
| 179 FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp"), | 228 FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp"))); |
| 180 IDR_HELP_MANIFEST)); | |
| 181 } | 229 } |
| 182 #endif | 230 #endif |
| 183 #endif | 231 #endif // !defined(OS_CHROMEOS) |
| 184 | 232 |
| 185 // Web Store. | 233 Add(IDR_WEBSTORE_MANIFEST, FilePath(FILE_PATH_LITERAL("web_store"))); |
| 186 component_extensions.push_back(std::make_pair( | |
| 187 FILE_PATH_LITERAL("web_store"), | |
| 188 IDR_WEBSTORE_MANIFEST)); | |
| 189 | 234 |
| 190 #if !defined(OS_CHROMEOS) | 235 #if !defined(OS_CHROMEOS) |
| 191 // Cloud Print component app. Not required on Chrome OS. | 236 // Cloud Print component app. Not required on Chrome OS. |
| 192 component_extensions.push_back(std::make_pair( | 237 Add(IDR_CLOUDPRINT_MANIFEST, FilePath(FILE_PATH_LITERAL("cloud_print"))); |
| 193 FILE_PATH_LITERAL("cloud_print"), | 238 #endif |
| 194 IDR_CLOUDPRINT_MANIFEST)); | |
| 195 #endif // !defined(OS_CHROMEOS) | |
| 196 | |
| 197 for (ComponentExtensionList::iterator iter = component_extensions.begin(); | |
| 198 iter != component_extensions.end(); ++iter) { | |
| 199 FilePath path(iter->first); | |
| 200 if (!path.IsAbsolute()) { | |
| 201 if (PathService::Get(chrome::DIR_RESOURCES, &path)) { | |
| 202 path = path.Append(iter->first); | |
| 203 } else { | |
| 204 NOTREACHED(); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 std::string manifest = | |
| 209 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 210 iter->second).as_string(); | |
| 211 Add(manifest, path); | |
| 212 } | |
| 213 | 239 |
| 214 #if defined(OS_CHROMEOS) | 240 #if defined(OS_CHROMEOS) |
| 215 // Register access extensions only if accessibility is enabled. | 241 // Register access extensions only if accessibility is enabled. |
| 216 if (g_browser_process->local_state()-> | 242 if (local_state_->GetBoolean(prefs::kAccessibilityEnabled)) { |
| 217 GetBoolean(prefs::kAccessibilityEnabled)) { | |
| 218 FilePath path = FilePath(extension_misc::kAccessExtensionPath) | 243 FilePath path = FilePath(extension_misc::kAccessExtensionPath) |
| 219 .AppendASCII(extension_misc::kChromeVoxDirectoryName); | 244 .AppendASCII(extension_misc::kChromeVoxDirectoryName); |
| 220 std::string manifest = | 245 Add(IDR_CHROMEVOX_MANIFEST, path); |
| 221 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
| 222 IDR_CHROMEVOX_MANIFEST).as_string(); | |
| 223 Add(manifest, path); | |
| 224 } | 246 } |
| 225 #endif | 247 #endif |
| 248 | |
| 249 // If a URL for the enterprise webstore has been specified, load the | |
| 250 // component extension. This extension might also be loaded later, because | |
| 251 // it is specified by policy, and on ChromeOS policies are loaded after | |
| 252 // the browser process has started. | |
| 253 AddOrReloadEnterpriseWebStore(); | |
| 254 } | |
| 255 | |
| 256 void ComponentLoader::Observe( | |
| 257 int type, | |
| 258 const content::NotificationSource& source, | |
| 259 const content::NotificationDetails& details) { | |
| 260 if (type == chrome::NOTIFICATION_PREF_CHANGED) { | |
| 261 const std::string* name = | |
| 262 content::Details<const std::string>(details).ptr(); | |
| 263 if (*name == prefs::kEnterpriseWebStoreURL) | |
| 264 AddOrReloadEnterpriseWebStore(); | |
| 265 else | |
| 266 NOTREACHED(); | |
| 267 } else { | |
| 268 NOTREACHED(); | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 // static | |
| 273 void ComponentLoader::RegisterUserPrefs(PrefService* prefs) { | |
| 274 prefs->RegisterStringPref(prefs::kEnterpriseWebStoreURL, | |
| 275 std::string() /* default_value */, | |
| 276 PrefService::UNSYNCABLE_PREF); | |
| 277 prefs->RegisterStringPref(prefs::kEnterpriseWebStoreName, | |
| 278 std::string() /* default_value */, | |
| 279 PrefService::UNSYNCABLE_PREF); | |
| 226 } | 280 } |
| 227 | 281 |
| 228 } // namespace extensions | 282 } // namespace extensions |
| OLD | NEW |