component_loader.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/component_loader.h" 6 7#include "base/command_line.h" 8#include "base/file_util.h" 9#include "base/json/json_string_value_serializer.h" 10#include "base/path_service.h" 11#include "base/prefs/pref_notifier.h" 12#include "base/prefs/public/pref_change_registrar.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/extensions/extension_service.h" 15#include "chrome/browser/prefs/pref_service.h" 16#include "chrome/browser/profiles/profile.h" 17#include "chrome/common/chrome_notification_types.h" 18#include "chrome/common/chrome_paths.h" 19#include "chrome/common/chrome_switches.h" 20#include "chrome/common/extensions/extension.h" 21#include "chrome/common/extensions/extension_file_util.h" 22#include "chrome/common/extensions/extension_manifest_constants.h" 23#include "chrome/common/extensions/feature_switch.h" 24#include "chrome/common/pref_names.h" 25#include "content/public/browser/notification_details.h" 26#include "content/public/browser/notification_source.h" 27#include "grit/browser_resources.h" 28#include "ui/base/resource/resource_bundle.h" 29 30#if defined(OFFICIAL_BUILD) 31#include "chrome/browser/defaults.h" 32#endif 33 34#if defined(OS_CHROMEOS) 35#include "chrome/browser/chromeos/login/user_manager.h" 36#endif 37 38#if defined(USE_ASH) 39#include "grit/chromium_strings.h" 40#include "ui/base/l10n/l10n_util.h" 41#endif 42 43namespace extensions { 44 45namespace { 46 47std::string GenerateId(const DictionaryValue* manifest, const FilePath& path) { 48 std::string raw_key; 49 std::string id_input; 50 std::string id; 51 CHECK(manifest->GetString(extension_manifest_keys::kPublicKey, &raw_key)); 52 CHECK(Extension::ParsePEMKeyBytes(raw_key, &id_input)); 53 CHECK(Extension::GenerateId(id_input, &id)); 54 return id; 55} 56 57} // namespace 58 59ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo( 60 const DictionaryValue* manifest, const FilePath& directory) 61 : manifest(manifest), 62 root_directory(directory) { 63 if (!root_directory.IsAbsolute()) { 64 CHECK(PathService::Get(chrome::DIR_RESOURCES, &root_directory)); 65 root_directory = root_directory.Append(directory); 66 } 67 extension_id = GenerateId(manifest, root_directory); 68} 69 70ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service, 71 PrefService* prefs, 72 PrefService* local_state) 73 : prefs_(prefs), 74 local_state_(local_state), 75 extension_service_(extension_service) { 76 pref_change_registrar_.Init(prefs); 77 78 // This pref is set by policy. We have to watch it for change because on 79 // ChromeOS, policy isn't loaded until after the browser process is started. 80 pref_change_registrar_.Add(prefs::kEnterpriseWebStoreURL, this); 81} 82 83ComponentLoader::~ComponentLoader() { 84 ClearAllRegistered(); 85} 86 87const Extension* ComponentLoader::GetScriptBubble() const { 88 if (script_bubble_id_.empty()) 89 return NULL; 90 91 return extension_service_->extensions()->GetByID(script_bubble_id_); 92} 93 94void ComponentLoader::LoadAll() { 95 for (RegisteredComponentExtensions::iterator it = 96 component_extensions_.begin(); 97 it != component_extensions_.end(); ++it) { 98 Load(*it); 99 } 100} 101 102DictionaryValue* ComponentLoader::ParseManifest( 103 const std::string& manifest_contents) const { 104 JSONStringValueSerializer serializer(manifest_contents); 105 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); 106 107 if (!manifest.get() || !manifest->IsType(Value::TYPE_DICTIONARY)) { 108 LOG(ERROR) << "Failed to parse extension manifest."; 109 return NULL; 110 } 111 // Transfer ownership to the caller. 112 return static_cast<DictionaryValue*>(manifest.release()); 113} 114 115void ComponentLoader::ClearAllRegistered() { 116 for (RegisteredComponentExtensions::iterator it = 117 component_extensions_.begin(); 118 it != component_extensions_.end(); ++it) { 119 delete it->manifest; 120 } 121 122 component_extensions_.clear(); 123} 124 125std::string ComponentLoader::Add(int manifest_resource_id, 126 const FilePath& root_directory) { 127 std::string manifest_contents = 128 ResourceBundle::GetSharedInstance().GetRawDataResource( 129 manifest_resource_id).as_string(); 130 return Add(manifest_contents, root_directory); 131} 132 133std::string ComponentLoader::Add(const std::string& manifest_contents, 134 const FilePath& root_directory) { 135 // The Value is kept for the lifetime of the ComponentLoader. This is 136 // required in case LoadAll() is called again. 137 DictionaryValue* manifest = ParseManifest(manifest_contents); 138 if (manifest) 139 return Add(manifest, root_directory); 140 return ""; 141} 142 143std::string ComponentLoader::Add(const DictionaryValue* parsed_manifest, 144 const FilePath& root_directory) { 145 ComponentExtensionInfo info(parsed_manifest, root_directory); 146 component_extensions_.push_back(info); 147 if (extension_service_->is_ready()) 148 Load(info); 149 return info.extension_id; 150} 151 152std::string ComponentLoader::AddOrReplace(const FilePath& path) { 153 FilePath absolute_path = path; 154 file_util::AbsolutePath(&absolute_path); 155 std::string error; 156 scoped_ptr<DictionaryValue> manifest( 157 extension_file_util::LoadManifest(absolute_path, &error)); 158 if (!manifest.get()) { 159 LOG(ERROR) << "Could not load extension from '" << 160 absolute_path.value() << "'. " << error; 161 return NULL; 162 } 163 Remove(GenerateId(manifest.get(), absolute_path)); 164 165 return Add(manifest.release(), absolute_path); 166} 167 168void ComponentLoader::Reload(const std::string& extension_id) { 169 for (RegisteredComponentExtensions::iterator it = 170 component_extensions_.begin(); it != component_extensions_.end(); 171 ++it) { 172 if (it->extension_id == extension_id) { 173 Load(*it); 174 break; 175 } 176 } 177} 178 179const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) { 180 // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated 181 // our component extensions to the new manifest version. 182 int flags = Extension::REQUIRE_KEY; 183 184 std::string error; 185 186 scoped_refptr<const Extension> extension(Extension::Create( 187 info.root_directory, 188 Extension::COMPONENT, 189 *info.manifest, 190 flags, 191 &error)); 192 if (!extension.get()) { 193 LOG(ERROR) << error; 194 return NULL; 195 } 196 CHECK_EQ(info.extension_id, extension->id()) << extension->name(); 197 extension_service_->AddExtension(extension); 198 return extension; 199} 200 201void ComponentLoader::Remove(const FilePath& root_directory) { 202 // Find the ComponentExtensionInfo for the extension. 203 RegisteredComponentExtensions::iterator it = component_extensions_.begin(); 204 for (; it != component_extensions_.end(); ++it) { 205 if (it->root_directory == root_directory) { 206 Remove(GenerateId(it->manifest, root_directory)); 207 break; 208 } 209 } 210} 211 212void ComponentLoader::Remove(const std::string& id) { 213 RegisteredComponentExtensions::iterator it = component_extensions_.begin(); 214 for (; it != component_extensions_.end(); ++it) { 215 if (it->extension_id == id) { 216 delete it->manifest; 217 it = component_extensions_.erase(it); 218 if (extension_service_->is_ready()) 219 extension_service_-> 220 UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE); 221 break; 222 } 223 } 224} 225 226bool ComponentLoader::Exists(const std::string& id) const { 227 RegisteredComponentExtensions::const_iterator it = 228 component_extensions_.begin(); 229 for (; it != component_extensions_.end(); ++it) 230 if (it->extension_id == id) 231 return true; 232 return false; 233} 234 235void ComponentLoader::AddFileManagerExtension() { 236#if defined(FILE_MANAGER_EXTENSION) 237 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 238 int manifest_id = command_line->HasSwitch(switches::kFileManagerPackaged) ? 239 IDR_FILEMANAGER_MANIFEST : 240 IDR_FILEMANAGER_MANIFEST_V1; 241#ifndef NDEBUG 242 if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) { 243 FilePath filemgr_extension_path( 244 command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath)); 245 Add(manifest_id, filemgr_extension_path); 246 return; 247 } 248#endif // NDEBUG 249 Add(manifest_id, FilePath(FILE_PATH_LITERAL("file_manager"))); 250#endif // defined(FILE_MANAGER_EXTENSION) 251} 252 253#if defined(OS_CHROMEOS) 254void ComponentLoader::AddGaiaAuthExtension() { 255 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 256 if (command_line->HasSwitch(switches::kAuthExtensionPath)) { 257 FilePath auth_extension_path = 258 command_line->GetSwitchValuePath(switches::kAuthExtensionPath); 259 Add(IDR_GAIA_TEST_AUTH_MANIFEST, auth_extension_path); 260 return; 261 } 262 Add(IDR_GAIA_AUTH_MANIFEST, FilePath(FILE_PATH_LITERAL("gaia_auth"))); 263} 264#endif // NDEBUG 265 266void ComponentLoader::AddOrReloadEnterpriseWebStore() { 267 FilePath path(FILE_PATH_LITERAL("enterprise_web_store")); 268 269 // Remove the extension if it was already loaded. 270 Remove(path); 271 272 std::string enterprise_webstore_url = 273 prefs_->GetString(prefs::kEnterpriseWebStoreURL); 274 275 // Load the extension only if the URL preference is set. 276 if (!enterprise_webstore_url.empty()) { 277 std::string manifest_contents = 278 ResourceBundle::GetSharedInstance().GetRawDataResource( 279 IDR_ENTERPRISE_WEBSTORE_MANIFEST).as_string(); 280 281 // The manifest is missing some values that are provided by policy. 282 DictionaryValue* manifest = ParseManifest(manifest_contents); 283 if (manifest) { 284 std::string name = prefs_->GetString(prefs::kEnterpriseWebStoreName); 285 manifest->SetString("app.launch.web_url", enterprise_webstore_url); 286 manifest->SetString("name", name); 287 Add(manifest, path); 288 } 289 } 290} 291 292void ComponentLoader::AddChromeApp() { 293#if defined(USE_ASH) 294 std::string manifest_contents = 295 ResourceBundle::GetSharedInstance().GetRawDataResource( 296 IDR_CHROME_APP_MANIFEST).as_string(); 297 298 // The Value is kept for the lifetime of the ComponentLoader. This is 299 // required in case LoadAll() is called again. 300 DictionaryValue* manifest = ParseManifest(manifest_contents); 301 302 // Update manifest to use a proper name. 303 manifest->SetString(extension_manifest_keys::kName, 304 l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME)); 305 306 if (manifest) 307 Add(manifest, FilePath(FILE_PATH_LITERAL("chrome_app"))); 308#endif 309} 310 311void ComponentLoader::AddScriptBubble() { 312 if (FeatureSwitch::script_bubble()->IsEnabled()) { 313 script_bubble_id_ = 314 Add(IDR_SCRIPT_BUBBLE_MANIFEST, 315 FilePath(FILE_PATH_LITERAL("script_bubble"))); 316 } 317} 318 319void ComponentLoader::AddDefaultComponentExtensions() { 320#if defined(OS_CHROMEOS) 321 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession)) 322 Add(IDR_BOOKMARKS_MANIFEST, 323 FilePath(FILE_PATH_LITERAL("bookmark_manager"))); 324#else 325 Add(IDR_BOOKMARKS_MANIFEST, FilePath(FILE_PATH_LITERAL("bookmark_manager"))); 326#endif 327 328#if defined(OS_CHROMEOS) 329 Add(IDR_WALLPAPERMANAGER_MANIFEST, 330 FilePath(FILE_PATH_LITERAL("chromeos/wallpaper_manager"))); 331#endif 332 333#if defined(FILE_MANAGER_EXTENSION) 334 AddFileManagerExtension(); 335#endif 336 337#if defined(OS_CHROMEOS) 338 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 339 if (command_line->HasSwitch(switches::kEnableBackgroundLoader)) { 340 Add(IDR_BACKLOADER_MANIFEST, 341 FilePath(FILE_PATH_LITERAL("backloader"))); 342 } 343 344 Add(IDR_MOBILE_MANIFEST, 345 FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile"))); 346 347 Add(IDR_CROSH_BUILTIN_MANIFEST, FilePath(FILE_PATH_LITERAL( 348 "/usr/share/chromeos-assets/crosh_builtin"))); 349 350 AddGaiaAuthExtension(); 351 352 // TODO(gauravsh): Only include echo extension on official builds. 353 FilePath echo_extension_path(FILE_PATH_LITERAL( 354 "/usr/share/chromeos-assets/echo")); 355 if (command_line->HasSwitch(switches::kEchoExtensionPath)) { 356 echo_extension_path = 357 command_line->GetSwitchValuePath(switches::kEchoExtensionPath); 358 } 359 Add(IDR_ECHO_MANIFEST, echo_extension_path); 360 361#if defined(OFFICIAL_BUILD) 362 if (browser_defaults::enable_help_app) { 363 Add(IDR_HELP_MANIFEST, 364 FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp"))); 365 } 366#endif 367#endif // !defined(OS_CHROMEOS) 368 369 Add(IDR_WEBSTORE_MANIFEST, FilePath(FILE_PATH_LITERAL("web_store"))); 370 371#if defined(OS_WIN) 372 if (CommandLine::ForCurrentProcess()->HasSwitch( 373 switches::kEnableSettingsApp)) { 374 Add(IDR_SETTINGS_APP_MANIFEST, 375 FilePath(FILE_PATH_LITERAL("settings_app"))); 376 } 377#endif 378 379#if !defined(OS_CHROMEOS) 380 // Cloud Print component app. Not required on Chrome OS. 381 Add(IDR_CLOUDPRINT_MANIFEST, FilePath(FILE_PATH_LITERAL("cloud_print"))); 382#endif 383 384#if defined(OS_CHROMEOS) 385 // Register access extensions only if accessibility is enabled. 386 if (local_state_->GetBoolean(prefs::kSpokenFeedbackEnabled)) { 387 FilePath path = FilePath(extension_misc::kAccessExtensionPath) 388 .AppendASCII(extension_misc::kChromeVoxDirectoryName); 389 Add(IDR_CHROMEVOX_MANIFEST, path); 390 } 391#endif 392 393 // If a URL for the enterprise webstore has been specified, load the 394 // component extension. This extension might also be loaded later, because 395 // it is specified by policy, and on ChromeOS policies are loaded after 396 // the browser process has started. 397 AddOrReloadEnterpriseWebStore(); 398 399#if defined(USE_ASH) 400 AddChromeApp(); 401#endif 402 403 AddScriptBubble(); 404} 405 406void ComponentLoader::OnPreferenceChanged(PrefServiceBase* service, 407 const std::string& pref_name) { 408 DCHECK_EQ(std::string(prefs::kEnterpriseWebStoreURL), pref_name); 409 AddOrReloadEnterpriseWebStore(); 410} 411 412// static 413void ComponentLoader::RegisterUserPrefs(PrefService* prefs) { 414 prefs->RegisterStringPref(prefs::kEnterpriseWebStoreURL, 415 std::string() /* default_value */, 416 PrefService::UNSYNCABLE_PREF); 417 prefs->RegisterStringPref(prefs::kEnterpriseWebStoreName, 418 std::string() /* default_value */, 419 PrefService::UNSYNCABLE_PREF); 420} 421 422} // namespace extensions 423