diff options
author | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-02 18:18:01 +0000 |
---|---|---|
committer | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-02 18:18:01 +0000 |
commit | d8c8f25f706a2f565ced667ddd90ff3f1c831a6d (patch) | |
tree | 7e4b28786c5613136de1f713fde269bd5195e65e /chrome | |
parent | 8d93603116c5be19665c84340c5c12b61b62b5f1 (diff) | |
download | chromium_src-d8c8f25f706a2f565ced667ddd90ff3f1c831a6d.zip chromium_src-d8c8f25f706a2f565ced667ddd90ff3f1c831a6d.tar.gz chromium_src-d8c8f25f706a2f565ced667ddd90ff3f1c831a6d.tar.bz2 |
Refactor loading out of ExtensionService.
- Make ExtensionServiceBackend go away, as it's only used for installing unpacked extensions. Tear this out into UnpackedInstaller task.
- Pull out LoadAllExtensions / LoadInstalledExtension into InstalledExtensionLoader task.
- Break out component extensions to be handled by ComponentLoader.
Coming soon: a unit test for InstalledExtensionLoader.
BUG=97883
TEST=existing tests + InstalledExtensionLoaderUnittest
Review URL: http://codereview.chromium.org/8417012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108319 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
28 files changed, 1065 insertions, 867 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc index 6348a82..a52aadc 100644 --- a/chrome/browser/autocomplete/autocomplete_browsertest.cc +++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc @@ -12,6 +12,7 @@ #include "chrome/browser/autocomplete/autocomplete_popup_model.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/history/history.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tabs/tab_strip_model.h" @@ -264,12 +265,12 @@ IN_PROC_BROWSER_TEST_F(AutocompleteBrowserTest, ExtensionAppProvider) { FilePath test_dir; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); // Load a packaged app. - service->LoadExtension(test_dir.AppendASCII("extensions") - .AppendASCII("packaged_app")); + extensions::UnpackedInstaller::Create(service)->Load( + test_dir.AppendASCII("extensions").AppendASCII("packaged_app")); WaitForExtensionLoad(); // Load a hosted app. - service->LoadExtension(test_dir.AppendASCII("extensions") - .AppendASCII("app")); + extensions::UnpackedInstaller::Create(service)->Load( + test_dir.AppendASCII("extensions").AppendASCII("app")); WaitForExtensionLoad(); ASSERT_EQ(extension_count + 2U, service->extensions()->size()); diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index ce93a20..01b4377 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -50,6 +50,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/extension_toolbar_model.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/net/url_request_mock_util.h" #include "chrome/browser/prefs/pref_service.h" @@ -858,13 +859,17 @@ void AutomationProvider::InstallExtension( if (extension_path.MatchesExtension(FILE_PATH_LITERAL(".crx"))) { ExtensionInstallUI* client = (with_ui ? new ExtensionInstallUI(profile_) : NULL); - scoped_refptr<CrxInstaller> installer(service->MakeCrxInstaller(client)); + scoped_refptr<CrxInstaller> installer( + CrxInstaller::Create(service, client)); if (!with_ui) installer->set_allow_silent_install(true); installer->set_install_cause(extension_misc::INSTALL_CAUSE_AUTOMATION); installer->InstallCrx(extension_path); } else { - service->LoadExtension(extension_path, with_ui); + scoped_refptr<extensions::UnpackedInstaller> installer( + extensions::UnpackedInstaller::Create(service)); + installer->set_prompt_for_plugins(with_ui); + installer->Load(extension_path); } } else { AutomationMsg_InstallExtension::WriteReplyParams(reply_message, 0); diff --git a/chrome/browser/chromeos/accessibility_util.cc b/chrome/browser/chromeos/accessibility_util.cc index 22d13e3..cee420c 100644 --- a/chrome/browser/chromeos/accessibility_util.cc +++ b/chrome/browser/chromeos/accessibility_util.cc @@ -10,6 +10,7 @@ #include "chrome/browser/chromeos/dbus/dbus_thread_manager.h" #include "chrome/browser/chromeos/dbus/speech_synthesizer_client.h" #include "chrome/browser/extensions/extension_accessibility_api.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/file_reader.h" #include "chrome/browser/profiles/profile.h" @@ -110,11 +111,9 @@ void EnableAccessibility(bool enabled, WebUI* login_web_ui) { GetRawDataResource(IDR_CHROMEVOX_MANIFEST).as_string(); FilePath path = FilePath(extension_misc::kAccessExtensionPath) .AppendASCII(extension_misc::kChromeVoxDirectoryName); - ExtensionService::ComponentExtensionInfo info(manifest, path); if (enabled) { // Load ChromeVox - extension_service->register_component_extension(info); const Extension* extension = - extension_service->LoadComponentExtension(info); + extension_service->component_loader()->Add(manifest, path); if (login_web_ui) { RenderViewHost* render_view_host = @@ -136,8 +135,7 @@ void EnableAccessibility(bool enabled, WebUI* login_web_ui) { LOG(INFO) << "ChromeVox was Loaded."; } else { // Unload ChromeVox - extension_service->UnloadComponentExtension(info); - extension_service->UnregisterComponentExtension(info); + extension_service->component_loader()->Remove(manifest); LOG(INFO) << "ChromeVox was Unloaded."; } } diff --git a/chrome/browser/debugger/devtools_sanity_unittest.cc b/chrome/browser/debugger/devtools_sanity_unittest.cc index 87dca28..9bf6ade 100644 --- a/chrome/browser/debugger/devtools_sanity_unittest.cc +++ b/chrome/browser/debugger/devtools_sanity_unittest.cc @@ -12,6 +12,7 @@ #include "chrome/browser/debugger/devtools_window.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_notification_types.h" @@ -211,7 +212,7 @@ class DevToolsExtensionDebugTest : public DevToolsSanityTest, new CancelableQuitTask("Extension load timed out."); MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit, 4*1000); - service->LoadExtension(path); + extensions::UnpackedInstaller::Create(service)->Load(path); ui_test_utils::RunMessageLoop(); delayed_quit->cancel(); } diff --git a/chrome/browser/download/download_crx_util.cc b/chrome/browser/download/download_crx_util.cc index 23f54ca..f709f9c 100644 --- a/chrome/browser/download/download_crx_util.cc +++ b/chrome/browser/download/download_crx_util.cc @@ -57,7 +57,7 @@ scoped_refptr<CrxInstaller> OpenChromeExtension( CHECK(service); scoped_refptr<CrxInstaller> installer( - service->MakeCrxInstaller(CreateExtensionInstallUI(profile))); + CrxInstaller::Create(service, CreateExtensionInstallUI(profile))); installer->set_delete_source(true); if (UserScript::IsURLUserScript(download_item.GetURL(), diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc new file mode 100644 index 0000000..6cf7f55 --- /dev/null +++ b/chrome/browser/extensions/component_loader.cc @@ -0,0 +1,224 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/component_loader.h" + +#include "base/command_line.h" +#include "base/path_service.h" +#include "base/json/json_value_serializer.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_names.h" +#include "grit/browser_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +typedef std::list<std::pair<FilePath::StringType, int> > + ComponentExtensionList; + +#if defined(FILE_MANAGER_EXTENSION) +void AddFileManagerExtension(ComponentExtensionList* component_extensions) { +#ifndef NDEBUG + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) { + FilePath filemgr_extension_path = + command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath); + component_extensions->push_back(std::make_pair( + filemgr_extension_path.value(), + IDR_FILEMANAGER_MANIFEST)); + return; + } +#endif // NDEBUG + component_extensions->push_back(std::make_pair( + FILE_PATH_LITERAL("file_manager"), + IDR_FILEMANAGER_MANIFEST)); +} +#endif // defined(FILE_MANAGER_EXTENSION) + +} // namespace + +namespace extensions { + +bool ComponentLoader::ComponentExtensionInfo::Equals( + const ComponentExtensionInfo& other) const { + return other.manifest == manifest && other.root_directory == root_directory; +} + +ComponentLoader::ComponentLoader(ExtensionService* extension_service) + : extension_service_(extension_service) { +} + +ComponentLoader::~ComponentLoader() { +} + +void ComponentLoader::LoadAll() { + for (RegisteredComponentExtensions::iterator it = + component_extensions_.begin(); + it != component_extensions_.end(); ++it) { + Load(*it); + } +} + +const Extension* ComponentLoader::Add( + const std::string& manifest, const FilePath& root_directory) { + ComponentExtensionInfo info(manifest, root_directory); + Register(info); + if (extension_service_->is_ready()) + return Load(info); + return NULL; +} + +const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) { + JSONStringValueSerializer serializer(info.manifest); + scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); + if (!manifest.get()) { + LOG(ERROR) << "Failed to parse manifest for extension"; + return NULL; + } + + int flags = Extension::REQUIRE_KEY; + if (Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT)) + flags |= Extension::STRICT_ERROR_CHECKS; + std::string error; + scoped_refptr<const Extension> extension(Extension::Create( + info.root_directory, + Extension::COMPONENT, + *static_cast<DictionaryValue*>(manifest.get()), + flags, + &error)); + if (!extension.get()) { + LOG(ERROR) << error; + return NULL; + } + extension_service_->AddExtension(extension); + return extension; +} + +void ComponentLoader::Remove(const std::string& manifest_str) { + // Unload the extension. + JSONStringValueSerializer serializer(manifest_str); + scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); + if (!manifest.get()) { + LOG(ERROR) << "Failed to parse manifest for extension"; + return; + } + std::string public_key; + std::string public_key_bytes; + std::string id; + if (!static_cast<DictionaryValue*>(manifest.get())-> + GetString(extension_manifest_keys::kPublicKey, &public_key) || + !Extension::ParsePEMKeyBytes(public_key, &public_key_bytes) || + !Extension::GenerateId(public_key_bytes, &id)) { + LOG(ERROR) << "Failed to get extension id"; + return; + } + extension_service_-> + UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE); + + // Unregister the extension. + RegisteredComponentExtensions new_component_extensions; + for (RegisteredComponentExtensions::iterator it = + component_extensions_.begin(); + it != component_extensions_.end(); ++it) { + if (it->manifest != manifest_str) + new_component_extensions.push_back(*it); + } + component_extensions_.swap(new_component_extensions); +} + +// We take ComponentExtensionList: +// path, manifest ID => full manifest, absolute path +void ComponentLoader::AddDefaultComponentExtensions() { + ComponentExtensionList component_extensions; + + // Bookmark manager. + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("bookmark_manager"), + IDR_BOOKMARKS_MANIFEST)); + +#if defined(FILE_MANAGER_EXTENSION) + AddFileManagerExtension(&component_extensions); +#endif + +#if defined(TOUCH_UI) + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("keyboard"), + IDR_KEYBOARD_MANIFEST)); +#endif + +#if defined(OS_CHROMEOS) + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile"), + IDR_MOBILE_MANIFEST)); + + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kAuthExtensionPath)) { + FilePath auth_extension_path = + command_line->GetSwitchValuePath(switches::kAuthExtensionPath); + component_extensions.push_back(std::make_pair( + auth_extension_path.value(), + IDR_GAIA_TEST_AUTH_MANIFEST)); + } else { + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("/usr/share/chromeos-assets/gaia_auth"), + IDR_GAIA_AUTH_MANIFEST)); + } + +#if defined(OFFICIAL_BUILD) + if (browser_defaults::enable_help_app) { + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp"), + IDR_HELP_MANIFEST)); + } +#endif +#endif + + // Web Store. + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("web_store"), + IDR_WEBSTORE_MANIFEST)); + +#if !defined(OS_CHROMEOS) + // Cloud Print component app. Not required on Chrome OS. + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("cloud_print"), + IDR_CLOUDPRINT_MANIFEST)); +#endif // !defined(OS_CHROMEOS) + + for (ComponentExtensionList::iterator iter = component_extensions.begin(); + iter != component_extensions.end(); ++iter) { + FilePath path(iter->first); + if (!path.IsAbsolute()) { + if (PathService::Get(chrome::DIR_RESOURCES, &path)) { + path = path.Append(iter->first); + } else { + NOTREACHED(); + } + } + + std::string manifest = + ResourceBundle::GetSharedInstance().GetRawDataResource( + iter->second).as_string(); + Add(manifest, path); + } + +#if defined(OS_CHROMEOS) + // Register access extensions only if accessibility is enabled. + if (g_browser_process->local_state()-> + GetBoolean(prefs::kAccessibilityEnabled)) { + FilePath path = FilePath(extension_misc::kAccessExtensionPath) + .AppendASCII(extension_misc::kChromeVoxDirectoryName); + std::string manifest = + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_CHROMEVOX_MANIFEST).as_string(); + Add(manifest, path); + } +#endif +} + +} // namespace extensions diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h new file mode 100644 index 0000000..6961098 --- /dev/null +++ b/chrome/browser/extensions/component_loader.h @@ -0,0 +1,83 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_COMPONENT_LOADER_H_ +#define CHROME_BROWSER_EXTENSIONS_COMPONENT_LOADER_H_ +#pragma once + +#include <string> + +#include "base/file_path.h" + +class Extension; +class ExtensionService; + +namespace extensions { + +// For registering, loading, and unloading component extensions. +class ComponentLoader { + public: + explicit ComponentLoader(ExtensionService* extension_service); + virtual ~ComponentLoader(); + + // Loads any registered component extensions. + void LoadAll(); + + // Loads and registers a component extension. If ExtensionService has been + // initialized, the extension is loaded; otherwise, the load is deferred + // until LoadAll is called. + const Extension* Add(const std::string& manifest, + const FilePath& root_directory); + + // Unloads a component extension and removes it from the list of component + // extensions to be loaded. + void Remove(const std::string& manifest_str); + + // Adds the default component extensions. + // + // Component extension manifests must contain a 'key' property with a unique + // public key, serialized in base64. You can create a suitable value with the + // following commands on a unixy system: + // + // ssh-keygen -t rsa -b 1024 -N '' -f /tmp/key.pem + // openssl rsa -pubout -outform DER < /tmp/key.pem 2>/dev/null | base64 -w 0 + void AddDefaultComponentExtensions(); + + private: + // Information about a registered component extension. + struct ComponentExtensionInfo { + ComponentExtensionInfo(const std::string& manifest, + const FilePath& root_directory) + : manifest(manifest), + root_directory(root_directory) { + } + + bool Equals(const ComponentExtensionInfo& other) const; + + // The extension's manifest. This is required for component extensions so + // that ExtensionService doesn't need to go to disk to load them. + std::string manifest; + + // Directory where the extension is stored. + FilePath root_directory; + }; + + // Loads a registered component extension. + const Extension* Load(const ComponentExtensionInfo& info); + + // Registers an extension to be loaded as a component extension. + void Register(const ComponentExtensionInfo& info) { + component_extensions_.push_back(info); + } + + // List of registered component extensions (see Extension::Location). + typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions; + RegisteredComponentExtensions component_extensions_; + + ExtensionService* extension_service_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_COMPONENT_LOADER_H_ diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 2700002..a8b39ca 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -62,6 +62,13 @@ CrxInstaller::WhitelistEntry::WhitelistEntry() CrxInstaller::WhitelistEntry::~WhitelistEntry() {} // static +scoped_refptr<CrxInstaller> CrxInstaller::Create( + ExtensionService* frontend, + ExtensionInstallUI* client) { + return new CrxInstaller(frontend->AsWeakPtr(), client); +} + +// static void CrxInstaller::SetWhitelistedInstallId(const std::string& id) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); g_whitelisted_install_data.Get().ids.insert(id); diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index 4e0474e..d2955ae 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -48,6 +48,13 @@ class CrxInstaller : public SandboxedExtensionUnpackerClient, public ExtensionInstallUI::Delegate { public: + // Extensions will be installed into frontend->install_directory(), + // then registered with |frontend|. Any install UI will be displayed + // using |client|. Pass NULL for |client| for silent install. + static scoped_refptr<CrxInstaller> Create( + ExtensionService* frontend, + ExtensionInstallUI* client); + // This is pretty lame, but given the difficulty of connecting a particular // ExtensionFunction to a resulting download in the download manager, it's // currently necessary. This is the |id| of an extension to be installed @@ -92,13 +99,6 @@ class CrxInstaller // only be called on the UI thread. static bool ClearWhitelistedInstallId(const std::string& id); - // Constructor. Extensions will be installed into - // frontend_weak->install_directory() then registered with - // |frontend_weak|. Any install UI will be displayed using - // |client|. Pass NULL for |client| for silent install. - CrxInstaller(base::WeakPtr<ExtensionService> frontend_weak, - ExtensionInstallUI* client); - // Install the crx in |source_file|. void InstallCrx(const FilePath& source_file); @@ -141,8 +141,10 @@ class CrxInstaller return (creation_flags_ & Extension::FROM_WEBSTORE) > 0; } void set_is_gallery_install(bool val) { - if (val) creation_flags_ |= Extension::FROM_WEBSTORE; - else creation_flags_ &= ~Extension::FROM_WEBSTORE; + if (val) + creation_flags_ |= Extension::FROM_WEBSTORE; + else + creation_flags_ &= ~Extension::FROM_WEBSTORE; } // The original download URL should be set when the WebstoreInstaller is @@ -181,6 +183,8 @@ class CrxInstaller private: friend class ExtensionUpdaterTest; + CrxInstaller(base::WeakPtr<ExtensionService> frontend_weak, + ExtensionInstallUI* client); virtual ~CrxInstaller(); // Converts the source user script to an extension. diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc index d108c56..14153b1 100644 --- a/chrome/browser/extensions/crx_installer_browsertest.cc +++ b/chrome/browser/extensions/crx_installer_browsertest.cc @@ -52,7 +52,8 @@ class ExtensionCrxInstallerTest : public ExtensionBrowserTest { MockInstallUI* mock_install_ui = new MockInstallUI(browser()->profile()); scoped_refptr<CrxInstaller> installer( - service->MakeCrxInstaller(mock_install_ui /* ownership transferred */)); + CrxInstaller::Create(service, + mock_install_ui /* ownership transferred */)); installer->set_allow_silent_install(true); installer->set_is_gallery_install(true); diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 3c8a214..771b1ad 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -12,12 +12,14 @@ #include "base/path_service.h" #include "base/scoped_temp_dir.h" #include "base/string_number_conversions.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_creator.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" @@ -66,7 +68,10 @@ const Extension* ExtensionBrowserTest::LoadExtensionWithOptions( content::NotificationRegistrar registrar; registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, content::NotificationService::AllSources()); - service->LoadExtension(path, false); + scoped_refptr<extensions::UnpackedInstaller> installer( + extensions::UnpackedInstaller::Create(service)); + installer->set_prompt_for_plugins(false); + installer->Load(path); ui_test_utils::RunMessageLoop(); } @@ -147,8 +152,7 @@ bool ExtensionBrowserTest::LoadExtensionAsComponent(const FilePath& path) { &manifest)) return false; - service->LoadComponentExtension( - ExtensionService::ComponentExtensionInfo(manifest, path)); + service->component_loader()->Add(manifest, path); return true; } @@ -269,7 +273,7 @@ bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id, return false; scoped_refptr<CrxInstaller> installer( - service->MakeCrxInstaller(install_ui)); + CrxInstaller::Create(service, install_ui)); installer->set_expected_id(id); installer->set_is_gallery_install(from_webstore); installer->InstallCrx(crx_path); diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 49b33e7..14275d1 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -12,11 +12,9 @@ #include "base/callback.h" #include "base/command_line.h" #include "base/file_util.h" -#include "base/json/json_value_serializer.h" #include "base/logging.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" -#include "base/path_service.h" #include "base/stl_util.h" #include "base/string16.h" #include "base/string_number_conversions.h" @@ -32,6 +30,7 @@ #include "chrome/browser/chrome_plugin_service_filter.h" #include "chrome/browser/extensions/app_notification_manager.h" #include "chrome/browser/extensions/apps_promo.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/default_apps_trial.h" #include "chrome/browser/extensions/extension_accessibility_api.h" @@ -43,7 +42,6 @@ #include "chrome/browser/extensions/extension_global_error.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_input_ime_api.h" -#include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_management_api.h" #include "chrome/browser/extensions/extension_preference_api.h" #include "chrome/browser/extensions/extension_process_manager.h" @@ -55,7 +53,9 @@ #include "chrome/browser/extensions/extension_webnavigation_api.h" #include "chrome/browser/extensions/external_extension_provider_impl.h" #include "chrome/browser/extensions/external_extension_provider_interface.h" +#include "chrome/browser/extensions/installed_loader.h" #include "chrome/browser/extensions/pending_extension_manager.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/history/history_extension_api.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/prefs/pref_service.h" @@ -75,13 +75,11 @@ #include "chrome/browser/ui/webui/ntp/thumbnail_source.h" #include "chrome/common/child_process_logging.h" #include "chrome/common/chrome_notification_types.h" -#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/extension_file_util.h" -#include "chrome/common/extensions/extension_l10n_util.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_resource.h" #include "chrome/common/pref_names.h" @@ -137,29 +135,6 @@ static const int kOmniboxIconPaddingRight = 0; const char* kNaClPluginMimeType = "application/x-nacl"; -// The following enumeration is used in histograms matching -// Extensions.ManifestReload* . Values may be added, as long -// as existing values are not changed. -enum ManifestReloadReason { - NOT_NEEDED = 0, // Reload not needed. - UNPACKED_DIR, // Unpacked directory - NEEDS_RELOCALIZATION, // The local has changed since we read this extension. - NUM_MANIFEST_RELOAD_REASONS -}; - -ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) { - // Always reload manifests of unpacked extensions, because they can change - // on disk independent of the manifest in our prefs. - if (info.extension_location == Extension::LOAD) - return UNPACKED_DIR; - - // Reload the manifest if it needs to be relocalized. - if (extension_l10n_util::ShouldRelocalizeManifest(info)) - return NEEDS_RELOCALIZATION; - - return NOT_NEEDED; -} - static void ForceShutdownPlugin(const FilePath& plugin_path) { PluginProcessHost* plugin = PluginService::GetInstance()->FindNpapiPluginProcess(plugin_path); @@ -175,60 +150,8 @@ static bool IsSyncableApp(const Extension& extension) { return extension.GetSyncType() == Extension::SYNC_TYPE_APP; } -// Manages an ExtensionInstallUI for a particular extension. -class SimpleExtensionLoadPrompt : public ExtensionInstallUI::Delegate { - public: - SimpleExtensionLoadPrompt(Profile* profile, - base::WeakPtr<ExtensionService> extension_service, - const Extension* extension); - ~SimpleExtensionLoadPrompt(); - - void ShowPrompt(); - - // ExtensionInstallUI::Delegate - virtual void InstallUIProceed(); - virtual void InstallUIAbort(bool user_initiated); - - private: - base::WeakPtr<ExtensionService> extension_service_; - scoped_ptr<ExtensionInstallUI> install_ui_; - scoped_refptr<const Extension> extension_; -}; - -SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt( - Profile* profile, - base::WeakPtr<ExtensionService> extension_service, - const Extension* extension) - : extension_service_(extension_service), - install_ui_(new ExtensionInstallUI(profile)), - extension_(extension) { -} - -SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() { -} - -void SimpleExtensionLoadPrompt::ShowPrompt() { - install_ui_->ConfirmInstall(this, extension_); -} - -void SimpleExtensionLoadPrompt::InstallUIProceed() { - if (extension_service_.get()) - extension_service_->OnExtensionInstalled( - extension_, false, -1); // Not from web store. - delete this; -} - -void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) { - delete this; -} - } // namespace -bool ExtensionService::ComponentExtensionInfo::Equals( - const ComponentExtensionInfo& other) const { - return other.manifest == manifest && other.root_directory == root_directory; -} - ExtensionService::ExtensionRuntimeData::ExtensionRuntimeData() : background_page_ready(false), being_upgraded(false), @@ -251,146 +174,6 @@ const char* ExtensionService::kExtensionSettingsDirectoryName = "Extension Settings"; const char* ExtensionService::kAppSettingsDirectoryName = "App Settings"; -// Implements IO for the ExtensionService. - -class ExtensionServiceBackend - : public base::RefCountedThreadSafe<ExtensionServiceBackend> { - public: - // |install_directory| is a path where to look for extensions to load. - ExtensionServiceBackend( - base::WeakPtr<ExtensionService> frontend, - const FilePath& install_directory); - - // Loads a single extension from |path| where |path| is the top directory of - // a specific extension where its manifest file lives. If |prompt_for_plugins| - // is true and the extension contains plugins, we prompt the user before - // loading. - // Errors are reported through ExtensionErrorReporter. On success, - // AddExtension() is called. - // TODO(erikkay): It might be useful to be able to load a packed extension - // (presumably into memory) without installing it. - void LoadSingleExtension(const FilePath &path, bool prompt_for_plugins); - - private: - friend class base::RefCountedThreadSafe<ExtensionServiceBackend>; - - virtual ~ExtensionServiceBackend(); - - // LoadSingleExtension needs to check the file access preference, which needs - // to happen back on the UI thread, so it posts CheckExtensionFileAccess on - // the UI thread. In turn, once that gets the pref, it goes back to the - // file thread with LoadSingleExtensionWithFileAccess. - void CheckExtensionFileAccess(const FilePath& extension_path, - bool prompt_for_plugins); - void LoadSingleExtensionWithFileAccess( - const FilePath &path, bool allow_file_access, bool prompt_for_plugins); - - // Notify the frontend that there was an error loading an extension. - void ReportExtensionLoadError(const FilePath& extension_path, - const std::string& error); - - // Notify the frontend that an extension was installed. - void OnLoadSingleExtension(const scoped_refptr<const Extension>& extension, - bool prompt_for_plugins); - - base::WeakPtr<ExtensionService> frontend_; - - // The top-level extensions directory being installed to. - FilePath install_directory_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionServiceBackend); -}; - -ExtensionServiceBackend::ExtensionServiceBackend( - base::WeakPtr<ExtensionService> frontend, - const FilePath& install_directory) - : frontend_(frontend), - install_directory_(install_directory) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); -} - -ExtensionServiceBackend::~ExtensionServiceBackend() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || - BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} - -void ExtensionServiceBackend::LoadSingleExtension(const FilePath& path_in, - bool prompt_for_plugins) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - - FilePath extension_path = path_in; - file_util::AbsolutePath(&extension_path); - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&ExtensionServiceBackend::CheckExtensionFileAccess, - this, extension_path, prompt_for_plugins)); -} - -void ExtensionServiceBackend::CheckExtensionFileAccess( - const FilePath& extension_path, bool prompt_for_plugins) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - std::string id = Extension::GenerateIdForPath(extension_path); - // Unpacked extensions default to allowing file access, but if that has been - // overridden, don't reset the value. - bool allow_file_access = - Extension::ShouldAlwaysAllowFileAccess(Extension::LOAD); - if (frontend_->extension_prefs()->HasAllowFileAccessSetting(id)) - allow_file_access = frontend_->extension_prefs()->AllowFileAccess(id); - - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind( - &ExtensionServiceBackend::LoadSingleExtensionWithFileAccess, - this, extension_path, allow_file_access, prompt_for_plugins)); -} - -void ExtensionServiceBackend::LoadSingleExtensionWithFileAccess( - const FilePath& extension_path, - bool allow_file_access, - bool prompt_for_plugins) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - int flags = allow_file_access ? - Extension::ALLOW_FILE_ACCESS : Extension::NO_FLAGS; - if (Extension::ShouldDoStrictErrorChecking(Extension::LOAD)) - flags |= Extension::STRICT_ERROR_CHECKS; - std::string error; - scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( - extension_path, - Extension::LOAD, - flags, - &error)); - - if (!extension) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind( - &ExtensionServiceBackend::ReportExtensionLoadError, - this, - extension_path, error)); - return; - } - - // Report this as an installed extension so that it gets remembered in the - // prefs. - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind( - &ExtensionServiceBackend::OnLoadSingleExtension, - this, extension, prompt_for_plugins)); -} - -void ExtensionServiceBackend::ReportExtensionLoadError( - const FilePath& extension_path, const std::string &error) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (frontend_.get()) - frontend_->ReportExtensionLoadError( - extension_path, error, true /* alert_on_error */); -} - -void ExtensionServiceBackend::OnLoadSingleExtension( - const scoped_refptr<const Extension>& extension, bool prompt_for_plugins) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (frontend_.get()) - frontend_->OnLoadSingleExtension(extension, prompt_for_plugins); -} - void ExtensionService::CheckExternalUninstall(const std::string& id) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -579,8 +362,7 @@ ExtensionService::ExtensionService(Profile* profile, ExtensionPrefs* extension_prefs, bool autoupdate_enabled, bool extensions_enabled) - : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), - profile_(profile), + : profile_(profile), extension_prefs_(extension_prefs), extension_settings_frontend_(profile), pending_extension_manager_(*ALLOW_THIS_IN_INITIALIZER_LIST(this)), @@ -629,9 +411,7 @@ ExtensionService::ExtensionService(Profile* profile, update_frequency)); } - backend_ = - new ExtensionServiceBackend(weak_ptr_factory_.GetWeakPtr(), - install_directory_); + component_loader_.reset(new extensions::ComponentLoader(this)); app_notification_manager_->Init(); @@ -667,18 +447,6 @@ PendingExtensionManager* ExtensionService::pending_extension_manager() { return &pending_extension_manager_; } -void ExtensionService::UnregisterComponentExtension( - const ComponentExtensionInfo& info) { - RegisteredComponentExtensions new_component_extension_manifests; - for (RegisteredComponentExtensions::iterator it = - component_extension_manifests_.begin(); - it != component_extension_manifests_.end(); ++it) { - if (!it->Equals(info)) - new_component_extension_manifests.push_back(*it); - } - component_extension_manifests_.swap(new_component_extension_manifests); -} - ExtensionService::~ExtensionService() { // No need to unload extensions here because they are profile-scoped, and the // profile is in the process of being deleted. @@ -752,7 +520,8 @@ void ExtensionService::Init() { // the first extension, because its members listen for loaded notifications. g_browser_process->resource_dispatcher_host(); - LoadAllExtensions(); + component_loader_->LoadAll(); + extensions::InstalledLoader(this).LoadAllExtensions(); // TODO(erikkay) this should probably be deferred to a future point // rather than running immediately at startup. @@ -795,7 +564,7 @@ bool ExtensionService::UpdateExtension( (!is_pending_extension || pending_extension_info.install_silently()) ? NULL : new ExtensionInstallUI(profile_); - scoped_refptr<CrxInstaller> installer(MakeCrxInstaller(client)); + scoped_refptr<CrxInstaller> installer(CrxInstaller::Create(this, client)); installer->set_expected_id(id); if (is_pending_extension) installer->set_install_source(pending_extension_info.install_source()); @@ -849,12 +618,13 @@ void ExtensionService::ReloadExtension(const std::string& extension_id) { extension_prefs_->GetInstalledExtensionInfo(extension_id)); if (installed_extension.get() && installed_extension->extension_manifest.get()) { - LoadInstalledExtension(*installed_extension, false); + extensions::InstalledLoader(this).Load(*installed_extension, false); } else { + // Otherwise, the extension is unpacked (location LOAD). // We should always be able to remember the extension's path. If it's not in // the map, someone failed to update |unloaded_extension_paths_|. CHECK(!path.empty()); - LoadExtension(path); + extensions::UnpackedInstaller::Create(this)->Load(path); } } @@ -1108,275 +878,6 @@ void ExtensionService::UpdateActivePermissions( extension->SetActivePermissions(permissions); } -void ExtensionService::LoadExtension(const FilePath& extension_path) { - LoadExtension(extension_path, true); -} - -void ExtensionService::LoadExtension(const FilePath& extension_path, - bool prompt_for_plugins) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&ExtensionServiceBackend::LoadSingleExtension, backend_.get(), - extension_path, prompt_for_plugins)); -} - -void ExtensionService::LoadExtensionFromCommandLine( - const FilePath& path_in) { - - // Load extensions from the command line synchronously to avoid a race - // between extension loading and loading an URL from the command line. - base::ThreadRestrictions::ScopedAllowIO allow_io; - - FilePath extension_path = path_in; - file_util::AbsolutePath(&extension_path); - - std::string id = Extension::GenerateIdForPath(extension_path); - bool allow_file_access = - Extension::ShouldAlwaysAllowFileAccess(Extension::LOAD); - if (extension_prefs()->HasAllowFileAccessSetting(id)) - allow_file_access = extension_prefs()->AllowFileAccess(id); - - int flags = Extension::NO_FLAGS; - if (allow_file_access) - flags |= Extension::ALLOW_FILE_ACCESS; - if (Extension::ShouldDoStrictErrorChecking(Extension::LOAD)) - flags |= Extension::STRICT_ERROR_CHECKS; - - std::string error; - scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( - extension_path, - Extension::LOAD, - flags, - &error)); - - if (!extension) { - ReportExtensionLoadError(extension_path, error, true); - return; - } - - OnLoadSingleExtension(extension, false); -} - -void ExtensionService::LoadComponentExtensions() { - for (RegisteredComponentExtensions::iterator it = - component_extension_manifests_.begin(); - it != component_extension_manifests_.end(); ++it) { - LoadComponentExtension(*it); - } -} - -const Extension* ExtensionService::LoadComponentExtension( - const ComponentExtensionInfo &info) { - JSONStringValueSerializer serializer(info.manifest); - scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); - if (!manifest.get()) { - LOG(ERROR) << "Failed to parse manifest for extension"; - return NULL; - } - - int flags = Extension::REQUIRE_KEY; - if (Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT)) - flags |= Extension::STRICT_ERROR_CHECKS; - std::string error; - scoped_refptr<const Extension> extension(Extension::Create( - info.root_directory, - Extension::COMPONENT, - *static_cast<DictionaryValue*>(manifest.get()), - flags, - &error)); - if (!extension.get()) { - LOG(ERROR) << error; - return NULL; - } - AddExtension(extension); - return extension; -} - -void ExtensionService::UnloadComponentExtension( - const ComponentExtensionInfo& info) { - JSONStringValueSerializer serializer(info.manifest); - scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL)); - if (!manifest.get()) { - LOG(ERROR) << "Failed to parse manifest for extension"; - return; - } - std::string public_key; - std::string public_key_bytes; - std::string id; - if (!static_cast<DictionaryValue*>(manifest.get())-> - GetString(extension_manifest_keys::kPublicKey, &public_key) || - !Extension::ParsePEMKeyBytes(public_key, &public_key_bytes) || - !Extension::GenerateId(public_key_bytes, &id)) { - LOG(ERROR) << "Failed to get extension id"; - return; - } - UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE); -} - -void ExtensionService::LoadAllExtensions() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - base::TimeTicks start_time = base::TimeTicks::Now(); - - // Load any component extensions. - LoadComponentExtensions(); - - // Load the previously installed extensions. - scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info( - extension_prefs_->GetInstalledExtensionsInfo()); - - std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0); - bool should_write_prefs = false; - - for (size_t i = 0; i < extensions_info->size(); ++i) { - ExtensionInfo* info = extensions_info->at(i).get(); - - ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info); - ++reload_reason_counts[reload_reason]; - UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue", - reload_reason, 100); - - if (reload_reason != NOT_NEEDED) { - // Reloading and extension reads files from disk. We do this on the - // UI thread because reloads should be very rare, and the complexity - // added by delaying the time when the extensions service knows about - // all extensions is significant. See crbug.com/37548 for details. - // |allow_io| disables tests that file operations run on the file - // thread. - base::ThreadRestrictions::ScopedAllowIO allow_io; - - std::string error; - scoped_refptr<const Extension> extension( - extension_file_util::LoadExtension( - info->extension_path, - info->extension_location, - GetExtensionCreateFlagsForInstalledExtension(info), - &error)); - - if (extension.get()) { - extensions_info->at(i)->extension_manifest.reset( - static_cast<DictionaryValue*>( - extension->manifest_value()->DeepCopy())); - should_write_prefs = true; - } - } - } - - for (size_t i = 0; i < extensions_info->size(); ++i) { - LoadInstalledExtension(*extensions_info->at(i), should_write_prefs); - } - - OnLoadedInstalledExtensions(); - - // The histograms Extensions.ManifestReload* allow us to validate - // the assumption that reloading manifest is a rare event. - UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded", - reload_reason_counts[NOT_NEEDED]); - UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir", - reload_reason_counts[UNPACKED_DIR]); - UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization", - reload_reason_counts[NEEDS_RELOCALIZATION]); - - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size()); - UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size()); - - UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime", - base::TimeTicks::Now() - start_time); - - int app_user_count = 0; - int app_external_count = 0; - int hosted_app_count = 0; - int packaged_app_count = 0; - int user_script_count = 0; - int extension_user_count = 0; - int extension_external_count = 0; - int theme_count = 0; - int page_action_count = 0; - int browser_action_count = 0; - ExtensionList::iterator ex; - for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) { - Extension::Location location = (*ex)->location(); - Extension::Type type = (*ex)->GetType(); - if ((*ex)->is_app()) { - UMA_HISTOGRAM_ENUMERATION("Extensions.AppLocation", - location, 100); - } else if (type == Extension::TYPE_EXTENSION) { - UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionLocation", - location, 100); - } - - // Don't count component extensions, since they are only extensions as an - // implementation detail. - if (location == Extension::COMPONENT) - continue; - - // Don't count unpacked extensions, since they're a developer-specific - // feature. - if (location == Extension::LOAD) - continue; - - // Using an enumeration shows us the total installed ratio across all users. - // Using the totals per user at each startup tells us the distribution of - // usage for each user (e.g. 40% of users have at least one app installed). - UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type, 100); - switch (type) { - case Extension::TYPE_THEME: - ++theme_count; - break; - case Extension::TYPE_USER_SCRIPT: - ++user_script_count; - break; - case Extension::TYPE_HOSTED_APP: - ++hosted_app_count; - if (Extension::IsExternalLocation(location)) { - ++app_external_count; - } else { - ++app_user_count; - } - break; - case Extension::TYPE_PACKAGED_APP: - ++packaged_app_count; - if (Extension::IsExternalLocation(location)) { - ++app_external_count; - } else { - ++app_user_count; - } - break; - case Extension::TYPE_EXTENSION: - default: - if (Extension::IsExternalLocation(location)) { - ++extension_external_count; - } else { - ++extension_user_count; - } - break; - } - if ((*ex)->page_action() != NULL) - ++page_action_count; - if ((*ex)->browser_action() != NULL) - ++browser_action_count; - - RecordPermissionMessagesHistogram( - ex->get(), "Extensions.Permissions_Load"); - } - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp", - app_user_count + app_external_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp", packaged_app_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", - extension_user_count + extension_external_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser", - extension_user_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal", - extension_external_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count); - UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction", - browser_action_count); -} - // static void ExtensionService::RecordPermissionMessagesHistogram( const Extension* e, const char* histogram) { @@ -1399,65 +900,6 @@ void ExtensionService::RecordPermissionMessagesHistogram( } } -void ExtensionService::LoadInstalledExtension(const ExtensionInfo& info, - bool write_to_prefs) { - std::string error; - scoped_refptr<const Extension> extension(NULL); - - // An explicit check against policy is required to behave correctly during - // startup. This is because extensions that were previously OK might have - // been blacklisted in policy while Chrome was not running. - if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id, - info.extension_location)) { - error = errors::kDisabledByPolicy; - } else if (info.extension_manifest.get()) { - extension = Extension::Create( - info.extension_path, - info.extension_location, - *info.extension_manifest, - GetExtensionCreateFlagsForInstalledExtension(&info), - &error); - } else { - error = errors::kManifestUnreadable; - } - - // Once installed, non-unpacked extensions cannot change their IDs (e.g., by - // updating the 'key' field in their manifest). - if (extension && - extension->location() != Extension::LOAD && - info.extension_id != extension->id()) { - error = errors::kCannotChangeExtensionID; - extension = NULL; - UserMetrics::RecordAction(UserMetricsAction("Extensions.IDChangedError")); - } - - if (!extension) { - ReportExtensionLoadError(info.extension_path, error, false); - return; - } - - if (write_to_prefs) - extension_prefs_->UpdateManifest(extension); - - AddExtension(extension); -} - -int ExtensionService::GetExtensionCreateFlagsForInstalledExtension( - const ExtensionInfo* info) { - int flags = Extension::NO_FLAGS; - if (info->extension_location != Extension::LOAD) - flags |= Extension::REQUIRE_KEY; - if (Extension::ShouldDoStrictErrorChecking(info->extension_location)) - flags |= Extension::STRICT_ERROR_CHECKS; - if (extension_prefs_->AllowFileAccess(info->extension_id)) - flags |= Extension::ALLOW_FILE_ACCESS; - if (extension_prefs_->IsFromWebStore(info->extension_id)) - flags |= Extension::FROM_WEBSTORE; - if (extension_prefs_->IsFromBookmark(info->extension_id)) - flags |= Extension::FROM_BOOKMARK; - return flags; -} - void ExtensionService::NotifyExtensionLoaded(const Extension* extension) { // The ChromeURLRequestContexts need to be first to know that the extension // was loaded, otherwise a race can arise where a renderer that is created @@ -2206,7 +1648,7 @@ void ExtensionService::IdentifyAlertableExtensions() { // If this is the first time, grandfather extensions that would have // caused notification. scoped_ptr<ExtensionGlobalError> global_error( - new ExtensionGlobalError(weak_ptr_factory_.GetWeakPtr())); + new ExtensionGlobalError(AsWeakPtr())); bool needs_alert = false; for (ExtensionList::const_iterator iter = extensions_.begin(); iter != extensions_.end(); ++iter) { @@ -2356,7 +1798,8 @@ void ExtensionService::UnloadAllExtensions() { void ExtensionService::ReloadExtensions() { UnloadAllExtensions(); - LoadAllExtensions(); + component_loader_->LoadAll(); + extensions::InstalledLoader(this).LoadAllExtensions(); } void ExtensionService::GarbageCollectExtensions() { @@ -2562,22 +2005,6 @@ void ExtensionService::UpdateActiveExtensionsInCrashReporter() { child_process_logging::SetActiveExtensions(extension_ids); } -void ExtensionService::OnLoadSingleExtension(const Extension* extension, - bool prompt_for_plugins) { - // If this is a new install of an extension with plugins, prompt the user - // first. - if (show_extensions_prompts_ && prompt_for_plugins && - !extension->plugins().empty() && - disabled_extension_paths_.find(extension->id()) == - disabled_extension_paths_.end()) { - SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt( - profile_, weak_ptr_factory_.GetWeakPtr(), extension); - prompt->ShowPrompt(); - return; // continues in SimpleExtensionLoadPrompt::InstallUI* - } - OnExtensionInstalled(extension, false, -1); // Not from web store. -} - void ExtensionService::OnExtensionInstalled( const Extension* extension, bool from_webstore, int page_index) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -2815,7 +2242,7 @@ void ExtensionService::OnExternalExtensionFileFound( pending_extension_manager()->AddFromExternalFile(id, location); // no client (silent install) - scoped_refptr<CrxInstaller> installer(MakeCrxInstaller(NULL)); + scoped_refptr<CrxInstaller> installer(CrxInstaller::Create(this, NULL)); installer->set_install_source(location); installer->set_expected_id(id); installer->set_expected_version(*version); @@ -2828,11 +2255,10 @@ void ExtensionService::ReportExtensionLoadError( const FilePath& extension_path, const std::string &error, bool be_noisy) { - content::NotificationService* service = - content::NotificationService::current(); - service->Notify(chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, - content::Source<Profile>(profile_), - content::Details<const std::string>(&error)); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, + content::Source<Profile>(profile_), + content::Details<const std::string>(&error)); std::string path_str = UTF16ToUTF8(extension_path.LossyDisplayName()); std::string message = base::StringPrintf( @@ -2874,7 +2300,7 @@ void ExtensionService::Observe(int type, FROM_HERE, base::Bind( &ExtensionService::TrackTerminatedExtension, - weak_ptr_factory_.GetWeakPtr(), + AsWeakPtr(), host->extension())); break; } @@ -2951,11 +2377,6 @@ ExtensionIdSet ExtensionService::GetAppIds() const { return result; } -scoped_refptr<CrxInstaller> ExtensionService::MakeCrxInstaller( - ExtensionInstallUI* client) { - return new CrxInstaller(weak_ptr_factory_.GetWeakPtr(), client); -} - bool ExtensionService::IsBackgroundPageReady(const Extension* extension) { return (extension->background_url().is_empty() || extension_runtime_data_[extension->id()].background_page_ready); diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index 52cfa2f..47cc52d 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -53,15 +53,14 @@ class ExtensionCookiesEventRouter; class ExtensionDownloadsEventRouter; class ExtensionFileBrowserEventRouter; class ExtensionGlobalError; -class HistoryExtensionEventRouter; class ExtensionInstallUI; class ExtensionManagementEventRouter; class ExtensionPreferenceEventRouter; -class ExtensionServiceBackend; class ExtensionSyncData; class ExtensionToolbarModel; class ExtensionUpdater; class ExtensionWebNavigationEventRouter; +class HistoryExtensionEventRouter; class GURL; class PendingExtensionManager; class Profile; @@ -70,7 +69,11 @@ class Version; namespace chromeos { class ExtensionInputMethodEventRouter; -} // namespace chromeos +} + +namespace extensions { +class ComponentLoader; +} // This is an interface class to encapsulate the dependencies that // various classes have on ExtensionService. This allows easy mocking. @@ -116,25 +119,10 @@ class ExtensionServiceInterface : public SyncableService { class ExtensionService : public ExtensionServiceInterface, public ExternalExtensionProviderInterface::VisitorInterface, + public base::SupportsWeakPtr<ExtensionService>, public content::NotificationObserver { public: - // Information about a registered component extension. - struct ComponentExtensionInfo { - ComponentExtensionInfo(const std::string& manifest, - const FilePath& root_directory) - : manifest(manifest), - root_directory(root_directory) { - } - - bool Equals(const ComponentExtensionInfo& other) const; - - // The extension's manifest. This is required for component extensions so - // that ExtensionService doesn't need to go to disk to load them. - std::string manifest; - - // Directory where the extension is stored. - FilePath root_directory; - }; + using base::SupportsWeakPtr<ExtensionService>::AsWeakPtr; // The name of the directory inside the profile where extensions are // installed to. @@ -194,14 +182,6 @@ class ExtensionService // Gets the object managing the set of pending extensions. virtual PendingExtensionManager* pending_extension_manager() OVERRIDE; - // Registers an extension to be loaded as a component extension. - void register_component_extension(const ComponentExtensionInfo& info) { - component_extension_manifests_.push_back(info); - } - - // Unregisters a component extension from the list of extensions to be loaded - void UnregisterComponentExtension(const ComponentExtensionInfo& info); - const FilePath& install_directory() const { return install_directory_; } AppsPromo* apps_promo() { return &apps_promo_; } @@ -325,28 +305,6 @@ class ExtensionService void UpdateActivePermissions(const Extension* extension, const ExtensionPermissionSet* permissions); - // Loads the extension from the directory |extension_path|. - void LoadExtension(const FilePath& extension_path); - - // Loads the extension from the directory |extension_path|. - // This version of this method is intended for testing only. - void LoadExtension(const FilePath& extension_path, bool prompt_for_plugins); - - // Same as above, but for use with command line switch --load-extension=path. - void LoadExtensionFromCommandLine(const FilePath& extension_path); - - // Loads any component extensions. - void LoadComponentExtensions(); - - // Loads particular component extension. - const Extension* LoadComponentExtension(const ComponentExtensionInfo& info); - - // Unloads particular component extension. - void UnloadComponentExtension(const ComponentExtensionInfo& info); - - // Loads all known extensions (used by startup and testing code). - void LoadAllExtensions(); - // Check for updates (or potentially new extensions from external providers) void CheckForExternalUpdates(); @@ -401,10 +359,6 @@ class ExtensionService // been loaded from a file and installed. void AddExtension(const Extension* extension); - // Called by the backend when an unpacked extension has been loaded. - void OnLoadSingleExtension(const Extension* extension, - bool prompt_for_plugins); - // Called by the backend when an extension has been installed. void OnExtensionInstalled( const Extension* extension, bool from_webstore, int page_index); @@ -458,9 +412,12 @@ class ExtensionService ExtensionContentSettingsStore* GetExtensionContentSettingsStore(); // Whether the extension service is ready. - // TODO(skerner): Get rid of this method. crbug.com/63756 bool is_ready() { return ready_; } + extensions::ComponentLoader* component_loader() { + return component_loader_.get(); + } + // Note that this may return NULL if autoupdate is not turned on. ExtensionUpdater* updater(); @@ -492,7 +449,10 @@ class ExtensionService #endif // Notify the frontend that there was an error loading an extension. - // This method is public because ExtensionServiceBackend can post to here. + // This method is public because UnpackedInstaller and InstalledLoader + // can post to here. + // TODO(aa): Remove this. It doesn't do enough to be worth the dependency + // of these classes on ExtensionService. void ReportExtensionLoadError(const FilePath& extension_path, const std::string& error, bool be_noisy); @@ -565,9 +525,6 @@ class ExtensionService static void RecordPermissionMessagesHistogram( const Extension* e, const char* histogram); - // |client| can be NULL for a silent install. - scoped_refptr<CrxInstaller> MakeCrxInstaller(ExtensionInstallUI* client); - #if defined(UNIT_TEST) void TrackTerminatedExtensionForTest(const Extension* extension) { TrackTerminatedExtension(extension); @@ -679,9 +636,6 @@ class ExtensionService // Helper that updates the active extension list used for crash reporting. void UpdateActiveExtensionsInCrashReporter(); - // Helper method. Loads extension from prefs. - void LoadInstalledExtension(const ExtensionInfo& info, bool write_to_prefs); - // We implement some Pepper plug-ins using NaCl to take advantage of NaCl's // strong sandbox. Typically, these NaCl modules are stored in extensions // and registered here. Not all NaCl modules need to register for a MIME @@ -697,13 +651,6 @@ class ExtensionService NaClModuleInfoList::iterator FindNaClModule(const GURL& url); - // Returns the flags that should be used with Extension::Create() for an - // extension that is already installed. - int GetExtensionCreateFlagsForInstalledExtension( - const ExtensionInfo* info); - - base::WeakPtrFactory<ExtensionService> weak_ptr_factory_; - // The profile this ExtensionService is part of. Profile* profile_; @@ -746,9 +693,6 @@ class ExtensionService // Whether to notify users when they attempt to install an extension. bool show_extensions_prompts_; - // The backend that will do IO on behalf of this instance. - scoped_refptr<ExtensionServiceBackend> backend_; - // Used by dispatchers to limit API quota for individual extensions. ExtensionsQuotaService quota_service_; @@ -782,6 +726,9 @@ class ExtensionService content::NotificationRegistrar registrar_; PrefChangeRegistrar pref_change_registrar_; + // Keeps track of loading and unloading component extensions. + scoped_ptr<extensions::ComponentLoader> component_loader_; + // Keeps track of menu items added by extensions. ExtensionMenuManager menu_manager_; @@ -795,10 +742,6 @@ class ExtensionService ExtensionIconManager omnibox_icon_manager_; ExtensionIconManager omnibox_popup_icon_manager_; - // List of registered component extensions (see Extension::Location). - typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions; - RegisteredComponentExtensions component_extension_manifests_; - // Manages the promotion of the web store. AppsPromo apps_promo_; diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index f7c66dc..70bddf2 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -26,6 +26,7 @@ #include "base/version.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_creator.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_service.h" @@ -35,9 +36,11 @@ #include "chrome/browser/extensions/external_extension_provider_impl.h" #include "chrome/browser/extensions/external_extension_provider_interface.h" #include "chrome/browser/extensions/external_pref_extension_loader.h" +#include "chrome/browser/extensions/installed_loader.h" #include "chrome/browser/extensions/pack_extension_job.cc" #include "chrome/browser/extensions/pending_extension_info.h" #include "chrome/browser/extensions/pending_extension_manager.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/prefs/pref_service_mock_builder.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" @@ -590,8 +593,7 @@ class ExtensionServiceTest void StartCrxInstall(const FilePath& crx_path, bool from_webstore) { ASSERT_TRUE(file_util::PathExists(crx_path)) << "Path does not exist: "<< crx_path.value().c_str(); - scoped_refptr<CrxInstaller> installer( - service_->MakeCrxInstaller(NULL)); + scoped_refptr<CrxInstaller> installer(CrxInstaller::Create(service_, NULL)); installer->set_allow_silent_install(true); installer->set_is_gallery_install(from_webstore); installer->InstallCrx(crx_path); @@ -609,7 +611,7 @@ class ExtensionServiceTest ASSERT_TRUE(file_util::PathExists(crx_path)) << "Path does not exist: "<< crx_path.value().c_str(); // no client (silent install) - scoped_refptr<CrxInstaller> installer(service_->MakeCrxInstaller(NULL)); + scoped_refptr<CrxInstaller> installer(CrxInstaller::Create(service_, NULL)); installer->set_install_source(install_location); installer->InstallCrx(crx_path); @@ -1374,8 +1376,7 @@ TEST_F(ExtensionServiceTest, InstallUserScript) { .AppendASCII("user_script_basic.user.js"); ASSERT_TRUE(file_util::PathExists(path)); - scoped_refptr<CrxInstaller> installer( - service_->MakeCrxInstaller(NULL)); + scoped_refptr<CrxInstaller> installer(CrxInstaller::Create(service_, NULL)); installer->set_allow_silent_install(true); installer->InstallUserScript( path, @@ -1763,7 +1764,7 @@ TEST_F(ExtensionServiceTest, LoadLocalizedTheme) { FilePath extension_path = data_dir_ .AppendASCII("theme_i18n"); - service_->LoadExtension(extension_path); + extensions::UnpackedInstaller::Create(service_)->Load(extension_path); loop_.RunAllPending(); EXPECT_EQ(0u, GetErrors().size()); ASSERT_EQ(1u, loaded_.size()); @@ -2179,7 +2180,7 @@ TEST_F(ExtensionServiceTest, LoadExtensionsCanDowngrade) { JSONFileValueSerializer serializer(manifest_path); ASSERT_TRUE(serializer.Serialize(manifest)); - service_->LoadExtension(extension_path); + extensions::UnpackedInstaller::Create(service_)->Load(extension_path); loop_.RunAllPending(); EXPECT_EQ(0u, GetErrors().size()); @@ -2193,7 +2194,7 @@ TEST_F(ExtensionServiceTest, LoadExtensionsCanDowngrade) { manifest.SetString("version", "1.0"); ASSERT_TRUE(serializer.Serialize(manifest)); - service_->LoadExtension(extension_path); + extensions::UnpackedInstaller::Create(service_)->Load(extension_path); loop_.RunAllPending(); EXPECT_EQ(0u, GetErrors().size()); @@ -2622,8 +2623,7 @@ TEST_F(ExtensionServiceTest, ComponentExtensionWhitelisted) { std::string manifest; ASSERT_TRUE(file_util::ReadFileToString( path.Append(Extension::kManifestFilename), &manifest)); - service_->register_component_extension( - ExtensionService::ComponentExtensionInfo(manifest, path)); + service_->component_loader()->Add(manifest, path); service_->Init(); // Extension should be installed despite blacklist. @@ -3074,7 +3074,7 @@ TEST_F(ExtensionServiceTest, LoadExtension) { .AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"); - service_->LoadExtension(ext1); + extensions::UnpackedInstaller::Create(service_)->Load(ext1); loop_.RunAllPending(); EXPECT_EQ(0u, GetErrors().size()); ASSERT_EQ(1u, loaded_.size()); @@ -3088,7 +3088,7 @@ TEST_F(ExtensionServiceTest, LoadExtension) { // .AppendASCII("Extensions") .AppendASCII("cccccccccccccccccccccccccccccccc") .AppendASCII("1"); - service_->LoadExtension(no_manifest); + extensions::UnpackedInstaller::Create(service_)->Load(no_manifest); loop_.RunAllPending(); EXPECT_EQ(1u, GetErrors().size()); ASSERT_EQ(1u, loaded_.size()); @@ -3111,7 +3111,7 @@ TEST_F(ExtensionServiceTest, GenerateID) { FilePath no_id_ext = data_dir_.AppendASCII("no_id"); - service_->LoadExtension(no_id_ext); + extensions::UnpackedInstaller::Create(service_)->Load(no_id_ext); loop_.RunAllPending(); EXPECT_EQ(0u, GetErrors().size()); ASSERT_EQ(1u, loaded_.size()); @@ -3123,7 +3123,7 @@ TEST_F(ExtensionServiceTest, GenerateID) { std::string previous_id = loaded_[0]->id(); // If we reload the same path, we should get the same extension ID. - service_->LoadExtension(no_id_ext); + extensions::UnpackedInstaller::Create(service_)->Load(no_id_ext); loop_.RunAllPending(); ASSERT_EQ(1u, loaded_.size()); ASSERT_EQ(previous_id, loaded_[0]->id()); @@ -3252,7 +3252,7 @@ void ExtensionServiceTest::TestExternalProvider( // Should still be at 0. loaded_.clear(); - service_->LoadAllExtensions(); + extensions::InstalledLoader(service_).LoadAllExtensions(); loop_.RunAllPending(); ASSERT_EQ(0u, loaded_.size()); ValidatePrefKeyCount(1); @@ -3703,9 +3703,9 @@ TEST_F(ExtensionServiceTest, StorageQuota) { FilePath unlimited_quota_ext2 = extensions_path.AppendASCII("unlimited_quota") .AppendASCII("2.0"); - service_->LoadExtension(limited_quota_ext); - service_->LoadExtension(unlimited_quota_ext); - service_->LoadExtension(unlimited_quota_ext2); + extensions::UnpackedInstaller::Create(service_)->Load(limited_quota_ext); + extensions::UnpackedInstaller::Create(service_)->Load(unlimited_quota_ext); + extensions::UnpackedInstaller::Create(service_)->Load(unlimited_quota_ext2); loop_.RunAllPending(); ASSERT_EQ(3u, loaded_.size()); @@ -3719,7 +3719,7 @@ TEST_F(ExtensionServiceTest, StorageQuota) { loaded_[2]->url())); } -// Tests ExtensionService::register_component_extension(). +// Tests ComponentLoader::Add(). TEST_F(ExtensionServiceTest, ComponentExtensions) { InitializeEmptyExtensionService(); @@ -3736,8 +3736,7 @@ TEST_F(ExtensionServiceTest, ComponentExtensions) { ASSERT_TRUE(file_util::ReadFileToString( path.Append(Extension::kManifestFilename), &manifest)); - service_->register_component_extension( - ExtensionService::ComponentExtensionInfo(manifest, path)); + service_->component_loader()->Add(manifest, path); service_->Init(); // Note that we do not pump messages -- the extension should be loaded @@ -3748,7 +3747,7 @@ TEST_F(ExtensionServiceTest, ComponentExtensions) { EXPECT_EQ(Extension::COMPONENT, loaded_[0]->location()); EXPECT_EQ(1u, service_->extensions()->size()); - // Component extensions shouldn't get recourded in the prefs. + // Component extensions shouldn't get recorded in the prefs. ValidatePrefKeyCount(0); // Reload all extensions, and make sure it comes back. diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/extension_updater_unittest.cc index 7f916d6..79134fd 100644 --- a/chrome/browser/extensions/extension_updater_unittest.cc +++ b/chrome/browser/extensions/extension_updater_unittest.cc @@ -818,9 +818,9 @@ class ExtensionUpdaterTest : public testing::Test { profile.GetExtensionService()->set_show_extensions_prompts(false); scoped_refptr<CrxInstaller> fake_crx1( - profile.GetExtensionService()->MakeCrxInstaller(NULL)); + CrxInstaller::Create(profile.GetExtensionService(), NULL)); scoped_refptr<CrxInstaller> fake_crx2( - profile.GetExtensionService()->MakeCrxInstaller(NULL)); + CrxInstaller::Create(profile.GetExtensionService(), NULL)); if (updates_start_running) { // Add fake CrxInstaller to be returned by service.UpdateExtension(). diff --git a/chrome/browser/extensions/installed_loader.cc b/chrome/browser/extensions/installed_loader.cc new file mode 100644 index 0000000..20bd824 --- /dev/null +++ b/chrome/browser/extensions/installed_loader.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/installed_loader.h" + +#include "base/file_path.h" +#include "base/metrics/histogram.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_file_util.h" +#include "chrome/common/extensions/extension_l10n_util.h" +#include "chrome/common/pref_names.h" +#include "content/public/browser/notification_service.h" +#include "content/browser/user_metrics.h" + +namespace errors = extension_manifest_errors; + +namespace { + +// The following enumeration is used in histograms matching +// Extensions.ManifestReload* . Values may be added, as long as existing +// values are not changed. +enum ManifestReloadReason { + NOT_NEEDED = 0, // Reload not needed. + UNPACKED_DIR, // Unpacked directory. + NEEDS_RELOCALIZATION, // The locale has changed since we read this extension. + NUM_MANIFEST_RELOAD_REASONS +}; + +ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) { + // Always reload manifests of unpacked extensions, because they can change + // on disk independent of the manifest in our prefs. + if (info.extension_location == Extension::LOAD) + return UNPACKED_DIR; + + // Reload the manifest if it needs to be relocalized. + if (extension_l10n_util::ShouldRelocalizeManifest(info)) + return NEEDS_RELOCALIZATION; + + return NOT_NEEDED; +} + +} // namespace + +namespace extensions { + +InstalledLoader::InstalledLoader(ExtensionService* extension_service) + : extension_service_(extension_service), + extension_prefs_(extension_service->extension_prefs()) { +} + +InstalledLoader::~InstalledLoader() { +} + +void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) { + std::string error; + scoped_refptr<const Extension> extension(NULL); + // An explicit check against policy is required to behave correctly during + // startup. This is because extensions that were previously OK might have + // been blacklisted in policy while Chrome was not running. + if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id, + info.extension_location)) { + error = errors::kDisabledByPolicy; + } else if (info.extension_manifest.get()) { + extension = Extension::Create( + info.extension_path, + info.extension_location, + *info.extension_manifest, + GetCreationFlags(&info), + &error); + } else { + error = errors::kManifestUnreadable; + } + + // Once installed, non-unpacked extensions cannot change their IDs (e.g., by + // updating the 'key' field in their manifest). + if (extension && + extension->location() != Extension::LOAD && + info.extension_id != extension->id()) { + error = errors::kCannotChangeExtensionID; + extension = NULL; + UserMetrics::RecordAction(UserMetricsAction("Extensions.IDChangedError")); + } + + if (!extension) { + extension_service_-> + ReportExtensionLoadError(info.extension_path, error, false); + return; + } + + if (write_to_prefs) + extension_prefs_->UpdateManifest(extension); + + extension_service_->AddExtension(extension); +} + +void InstalledLoader::LoadAllExtensions() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + base::TimeTicks start_time = base::TimeTicks::Now(); + + scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info( + extension_prefs_->GetInstalledExtensionsInfo()); + + std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0); + bool should_write_prefs = false; + + for (size_t i = 0; i < extensions_info->size(); ++i) { + ExtensionInfo* info = extensions_info->at(i).get(); + + ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info); + ++reload_reason_counts[reload_reason]; + UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue", + reload_reason, 100); + + if (reload_reason != NOT_NEEDED) { + // Reloading an extension reads files from disk. We do this on the + // UI thread because reloads should be very rare, and the complexity + // added by delaying the time when the extensions service knows about + // all extensions is significant. See crbug.com/37548 for details. + // |allow_io| disables tests that file operations run on the file + // thread. + base::ThreadRestrictions::ScopedAllowIO allow_io; + + std::string error; + scoped_refptr<const Extension> extension( + extension_file_util::LoadExtension( + info->extension_path, + info->extension_location, + GetCreationFlags(info), + &error)); + + if (extension.get()) { + extensions_info->at(i)->extension_manifest.reset( + static_cast<DictionaryValue*>( + extension->manifest_value()->DeepCopy())); + should_write_prefs = true; + } + } + } + + for (size_t i = 0; i < extensions_info->size(); ++i) { + Load(*extensions_info->at(i), should_write_prefs); + } + + extension_service_->OnLoadedInstalledExtensions(); + + // The histograms Extensions.ManifestReload* allow us to validate + // the assumption that reloading manifest is a rare event. + UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded", + reload_reason_counts[NOT_NEEDED]); + UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir", + reload_reason_counts[UNPACKED_DIR]); + UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization", + reload_reason_counts[NEEDS_RELOCALIZATION]); + + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", + extension_service_->extensions()->size()); + UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", + extension_service_->disabled_extensions()->size()); + + UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime", + base::TimeTicks::Now() - start_time); + + int app_user_count = 0; + int app_external_count = 0; + int hosted_app_count = 0; + int packaged_app_count = 0; + int user_script_count = 0; + int extension_user_count = 0; + int extension_external_count = 0; + int theme_count = 0; + int page_action_count = 0; + int browser_action_count = 0; + const ExtensionList* extensions = extension_service_->extensions(); + ExtensionList::const_iterator ex; + for (ex = extensions->begin(); ex != extensions->end(); ++ex) { + Extension::Location location = (*ex)->location(); + Extension::Type type = (*ex)->GetType(); + if ((*ex)->is_app()) { + UMA_HISTOGRAM_ENUMERATION("Extensions.AppLocation", + location, 100); + } else if (type == Extension::TYPE_EXTENSION) { + UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionLocation", + location, 100); + } + + // Don't count component extensions, since they are only extensions as an + // implementation detail. + if (location == Extension::COMPONENT) + continue; + + // Don't count unpacked extensions, since they're a developer-specific + // feature. + if (location == Extension::LOAD) + continue; + + // Using an enumeration shows us the total installed ratio across all users. + // Using the totals per user at each startup tells us the distribution of + // usage for each user (e.g. 40% of users have at least one app installed). + UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type, 100); + switch (type) { + case Extension::TYPE_THEME: + ++theme_count; + break; + case Extension::TYPE_USER_SCRIPT: + ++user_script_count; + break; + case Extension::TYPE_HOSTED_APP: + ++hosted_app_count; + if (Extension::IsExternalLocation(location)) { + ++app_external_count; + } else { + ++app_user_count; + } + break; + case Extension::TYPE_PACKAGED_APP: + ++packaged_app_count; + if (Extension::IsExternalLocation(location)) { + ++app_external_count; + } else { + ++app_user_count; + } + break; + case Extension::TYPE_EXTENSION: + default: + if (Extension::IsExternalLocation(location)) { + ++extension_external_count; + } else { + ++extension_user_count; + } + break; + } + if ((*ex)->page_action() != NULL) + ++page_action_count; + if ((*ex)->browser_action() != NULL) + ++browser_action_count; + + extension_service_->RecordPermissionMessagesHistogram( + ex->get(), "Extensions.Permissions_Load"); + } + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp", + app_user_count + app_external_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp", packaged_app_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", + extension_user_count + extension_external_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser", + extension_user_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal", + extension_external_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count); + UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction", + browser_action_count); +} + +int InstalledLoader::GetCreationFlags(const ExtensionInfo* info) { + int flags = Extension::NO_FLAGS; + if (info->extension_location != Extension::LOAD) + flags |= Extension::REQUIRE_KEY; + if (Extension::ShouldDoStrictErrorChecking(info->extension_location)) + flags |= Extension::STRICT_ERROR_CHECKS; + if (extension_prefs_->AllowFileAccess(info->extension_id)) + flags |= Extension::ALLOW_FILE_ACCESS; + if (extension_prefs_->IsFromWebStore(info->extension_id)) + flags |= Extension::FROM_WEBSTORE; + if (extension_prefs_->IsFromBookmark(info->extension_id)) + flags |= Extension::FROM_BOOKMARK; + return flags; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/installed_loader.h b/chrome/browser/extensions/installed_loader.h new file mode 100644 index 0000000..774f10c --- /dev/null +++ b/chrome/browser/extensions/installed_loader.h @@ -0,0 +1,40 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_INSTALLED_LOADER_H_ +#define CHROME_BROWSER_EXTENSIONS_INSTALLED_LOADER_H_ +#pragma once + +class Extension; +class ExtensionPrefs; +class ExtensionService; +struct ExtensionInfo; + +namespace extensions { + +// Loads installed extensions from the prefs. +class InstalledLoader { + public: + explicit InstalledLoader(ExtensionService* extension_service); + virtual ~InstalledLoader(); + + // Loads extension from prefs. + void Load(const ExtensionInfo& info, bool write_to_prefs); + + // Loads all installed extensions (used by startup and testing code). + void LoadAllExtensions(); + + private: + // Returns the flags that should be used with Extension::Create() for an + // extension that is already installed. + int GetCreationFlags(const ExtensionInfo* info); + + ExtensionService* extension_service_; + + ExtensionPrefs* extension_prefs_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_INSTALLED_LOADER_H_ diff --git a/chrome/browser/extensions/network_delay_listener_unittest.cc b/chrome/browser/extensions/network_delay_listener_unittest.cc index 1223804..e550851 100644 --- a/chrome/browser/extensions/network_delay_listener_unittest.cc +++ b/chrome/browser/extensions/network_delay_listener_unittest.cc @@ -5,6 +5,7 @@ #include "base/message_loop.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service_unittest.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/network_delay_listener.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths.h" @@ -119,7 +120,7 @@ class NetworkDelayListenerTest .AppendASCII(id) .AppendASCII("1.0"); - service_->LoadExtension(extension_path); + extensions::UnpackedInstaller::Create(service_)->Load(extension_path); MessageLoop::current()->RunAllPending(); } diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc new file mode 100644 index 0000000..b747035 --- /dev/null +++ b/chrome/browser/extensions/unpacked_installer.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/unpacked_installer.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/file_util.h" +#include "chrome/browser/extensions/extension_install_ui.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_file_util.h" + +namespace { + +// Manages an ExtensionInstallUI for a particular extension. +class SimpleExtensionLoadPrompt : public ExtensionInstallUI::Delegate { + public: + SimpleExtensionLoadPrompt(Profile* profile, + base::WeakPtr<ExtensionService> extension_service, + const Extension* extension); + ~SimpleExtensionLoadPrompt(); + + void ShowPrompt(); + + // ExtensionInstallUI::Delegate + virtual void InstallUIProceed(); + virtual void InstallUIAbort(bool user_initiated); + + private: + base::WeakPtr<ExtensionService> service_weak_; + scoped_ptr<ExtensionInstallUI> install_ui_; + scoped_refptr<const Extension> extension_; +}; + +SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt( + Profile* profile, + base::WeakPtr<ExtensionService> extension_service, + const Extension* extension) + : service_weak_(extension_service), + install_ui_(new ExtensionInstallUI(profile)), + extension_(extension) { +} + +SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() { +} + +void SimpleExtensionLoadPrompt::ShowPrompt() { + install_ui_->ConfirmInstall(this, extension_); +} + +void SimpleExtensionLoadPrompt::InstallUIProceed() { + if (service_weak_.get()) + service_weak_->OnExtensionInstalled( + extension_, false, -1); // Not from web store. + delete this; +} + +void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) { + delete this; +} + +} // namespace + +namespace extensions { + +// static +scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create( + ExtensionService* extension_service) { + return scoped_refptr<UnpackedInstaller>( + new UnpackedInstaller(extension_service)); +} + +UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service) + : service_weak_(extension_service->AsWeakPtr()), + prompt_for_plugins_(true) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +UnpackedInstaller::~UnpackedInstaller() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || + BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} + +void UnpackedInstaller::Load(const FilePath& path_in) { + extension_path_ = path_in; + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind(&UnpackedInstaller::GetAbsolutePath, this)); +} + +void UnpackedInstaller::LoadFromCommandLine(const FilePath& path_in) { + if (!service_weak_.get()) + return; + // Load extensions from the command line synchronously to avoid a race + // between extension loading and loading an URL from the command line. + base::ThreadRestrictions::ScopedAllowIO allow_io; + + extension_path_ = path_in; + file_util::AbsolutePath(&extension_path_); + + std::string id = Extension::GenerateIdForPath(extension_path_); + bool allow_file_access = + Extension::ShouldAlwaysAllowFileAccess(Extension::LOAD); + if (service_weak_->extension_prefs()->HasAllowFileAccessSetting(id)) + allow_file_access = service_weak_->extension_prefs()->AllowFileAccess(id); + + int flags = Extension::NO_FLAGS; + if (allow_file_access) + flags |= Extension::ALLOW_FILE_ACCESS; + if (Extension::ShouldDoStrictErrorChecking(Extension::LOAD)) + flags |= Extension::STRICT_ERROR_CHECKS; + + std::string error; + scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( + extension_path_, + Extension::LOAD, + flags, + &error)); + + if (!extension) { + service_weak_->ReportExtensionLoadError(extension_path_, error, true); + return; + } + + OnLoaded(extension); +} + +void UnpackedInstaller::GetAbsolutePath() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + file_util::AbsolutePath(&extension_path_); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this)); +} + +void UnpackedInstaller::CheckExtensionFileAccess() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::string id = Extension::GenerateIdForPath(extension_path_); + // Unpacked extensions default to allowing file access, but if that has been + // overridden, don't reset the value. + bool allow_file_access = + Extension::ShouldAlwaysAllowFileAccess(Extension::LOAD); + if (service_weak_->extension_prefs()->HasAllowFileAccessSetting(id)) + allow_file_access = service_weak_->extension_prefs()->AllowFileAccess(id); + + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind( + &UnpackedInstaller::LoadWithFileAccess, + this, allow_file_access)); +} + +void UnpackedInstaller::LoadWithFileAccess(bool allow_file_access) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + int flags = allow_file_access ? + Extension::ALLOW_FILE_ACCESS : Extension::NO_FLAGS; + if (Extension::ShouldDoStrictErrorChecking(Extension::LOAD)) + flags |= Extension::STRICT_ERROR_CHECKS; + std::string error; + scoped_refptr<const Extension> extension(extension_file_util::LoadExtension( + extension_path_, + Extension::LOAD, + flags, + &error)); + + if (!extension) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind( + &UnpackedInstaller::ReportExtensionLoadError, + this, error)); + return; + } + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind( + &UnpackedInstaller::OnLoaded, + this, extension)); +} + +void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!service_weak_.get()) + return; + service_weak_->ReportExtensionLoadError(extension_path_, error, true); +} + +void UnpackedInstaller::OnLoaded( + const scoped_refptr<const Extension>& extension) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!service_weak_.get()) + return; + const ExtensionList* disabled_extensions = + service_weak_->disabled_extensions(); + if (service_weak_->show_extensions_prompts() && + prompt_for_plugins_ && + !extension->plugins().empty() && + std::find(disabled_extensions->begin(), + disabled_extensions->end(), + extension) != + disabled_extensions->end()) { + SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt( + service_weak_->profile(), + service_weak_, + extension); + prompt->ShowPrompt(); + return; // continues in SimpleExtensionLoadPrompt::InstallUI* + } + service_weak_->OnExtensionInstalled(extension, + false, // Not from web store. + -1); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/unpacked_installer.h b/chrome/browser/extensions/unpacked_installer.h new file mode 100644 index 0000000..f73d57a --- /dev/null +++ b/chrome/browser/extensions/unpacked_installer.h @@ -0,0 +1,82 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_UNPACKED_INSTALLER_H_ +#define CHROME_BROWSER_EXTENSIONS_UNPACKED_INSTALLER_H_ +#pragma once + +#include <string> + +#include "base/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" + +class Extension; +class ExtensionService; + +namespace extensions { + +// Installs and loads an unpacked extension. +// TODO(erikkay): It might be useful to be able to load a packed extension +// (presumably into memory) without installing it. +class UnpackedInstaller + : public base::RefCountedThreadSafe<UnpackedInstaller> { + public: + static scoped_refptr<UnpackedInstaller> Create( + ExtensionService* extension_service); + + // Loads the extension from the directory |extension_path|, which is + // the top directory of a specific extension where its manifest file lives. + // Errors are reported through ExtensionErrorReporter. On success, + // ExtensionService::AddExtension() is called. + void Load(const FilePath& extension_path); + + // Loads the extension from the directory |extension_path|; + // for use with command line switch --load-extension=path. + // This is equivalent to Load, except that it runs synchronously. + void LoadFromCommandLine(const FilePath& extension_path); + + // Allows prompting for plugins to be disabled; intended for testing only. + bool prompt_for_plugins() { return prompt_for_plugins_; } + void set_prompt_for_plugins(bool val) { prompt_for_plugins_ = val; } + + private: + friend class base::RefCountedThreadSafe<UnpackedInstaller>; + + explicit UnpackedInstaller(ExtensionService* extension_service); + virtual ~UnpackedInstaller(); + + // We change the input extension path to an absolute path, on the file thread. + // Then we need to check the file access preference, which needs + // to happen back on the UI thread, so it posts CheckExtensionFileAccess on + // the UI thread. In turn, once that gets the pref, it goes back to the + // file thread with LoadWithFileAccess. + // TODO(yoz): It would be nice to remove this ping-pong, but we need to know + // what file access flags to pass to extension_file_util::LoadExtension. + void GetAbsolutePath(); + void CheckExtensionFileAccess(); + void LoadWithFileAccess(bool allow_file_access); + + // Notify the frontend that there was an error loading an extension. + void ReportExtensionLoadError(const std::string& error); + + // Called when an unpacked extension has been loaded and installed. + void OnLoaded(const scoped_refptr<const Extension>& extension); + + base::WeakPtr<ExtensionService> service_weak_; + + // The pathname of the directory to load from, which is an absolute path + // after GetAbsolutePath has been called. + FilePath extension_path_; + + // If true and the extension contains plugins, we prompt the user before + // loading. + bool prompt_for_plugins_; + + DISALLOW_COPY_AND_ASSIGN(UnpackedInstaller); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_UNPACKED_INSTALLER_H_ diff --git a/chrome/browser/extensions/user_script_listener_unittest.cc b/chrome/browser/extensions/user_script_listener_unittest.cc index 3d88cc6..d046bdf 100644 --- a/chrome/browser/extensions/user_script_listener_unittest.cc +++ b/chrome/browser/extensions/user_script_listener_unittest.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/file_util.h" #include "base/message_loop.h" #include "base/threading/thread.h" #include "base/json/json_value_serializer.h" -#include "base/file_util.h" #include "chrome/browser/extensions/extension_service_unittest.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/user_script_listener.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths.h" @@ -132,7 +133,7 @@ class UserScriptListenerTest .AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"); - service_->LoadExtension(extension_path); + extensions::UnpackedInstaller::Create(service_)->Load(extension_path); } void UnloadTestExtension() { diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 8c886cf..aa2d69a 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -29,6 +29,7 @@ #include "chrome/browser/defaults.h" #include "chrome/browser/download/download_service.h" #include "chrome/browser/download/download_service_factory.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_devtools_manager.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_event_router.h" @@ -40,6 +41,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_settings_backend.h" #include "chrome/browser/extensions/extension_special_storage_policy.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/favicon/favicon_service.h" #include "chrome/browser/geolocation/chrome_geolocation_permission_context.h" @@ -86,7 +88,6 @@ #include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_notification_types.h" -#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_permission_set.h" @@ -106,11 +107,9 @@ #include "content/browser/user_metrics.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" -#include "grit/browser_resources.h" #include "grit/locale_settings.h" #include "net/base/transport_security_state.h" #include "net/http/http_server_properties.h" -#include "ui/base/resource/resource_bundle.h" #include "webkit/database/database_tracker.h" #include "webkit/quota/quota_manager.h" @@ -158,9 +157,6 @@ enum ContextType { kMediaContext }; -typedef std::list<std::pair<FilePath::StringType, int> > - ComponentExtensionList; - // Helper method needed because PostTask cannot currently take a Callback // function with non-void return type. // TODO(jhawkins): Remove once IgnoreReturn is fixed. @@ -168,25 +164,6 @@ void CreateDirectoryNoResult(const FilePath& path) { file_util::CreateDirectory(path); } -#if defined(FILE_MANAGER_EXTENSION) -void AddFileManagerExtension(ComponentExtensionList* component_extensions) { -#ifndef NDEBUG - const CommandLine* command_line = CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) { - FilePath filemgr_extension_path = - command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath); - component_extensions->push_back(std::make_pair( - filemgr_extension_path.value(), - IDR_FILEMANAGER_MANIFEST)); - return; - } -#endif // NDEBUG - component_extensions->push_back(std::make_pair( - FILE_PATH_LITERAL("file_manager"), - IDR_FILEMANAGER_MANIFEST)); -} -#endif // defined(FILE_MANAGER_EXTENSION) - // Gets the cache parameters from the command line. |type| is the type of // request context that we need, |cache_path| will be set to the user provided // path, or will not be touched if there is not an argument. |max_size| will @@ -479,15 +456,17 @@ void ProfileImpl::InitExtensions(bool extensions_enabled) { autoupdate_enabled, extensions_enabled)); - RegisterComponentExtensions(); + extension_service_->component_loader()->AddDefaultComponentExtensions(); extension_service_->Init(); if (extensions_enabled) { // Load any extensions specified with --load-extension. + // TODO(yoz): Seems like this should move into ExtensionService::Init. if (command_line->HasSwitch(switches::kLoadExtension)) { FilePath path = command_line->GetSwitchValuePath( switches::kLoadExtension); - extension_service_->LoadExtensionFromCommandLine(path); + extensions::UnpackedInstaller::Create(extension_service_.get())-> + LoadFromCommandLine(path); } } @@ -516,106 +495,6 @@ void ProfileImpl::InitExtensions(bool extensions_enabled) { } } -void ProfileImpl::RegisterComponentExtensions() { - // Register the component extensions. - // - // Component extension manifest must contain a 'key' property with a unique - // public key, serialized in base64. You can create a suitable value with the - // following commands on a unixy system: - // - // ssh-keygen -t rsa -b 1024 -N '' -f /tmp/key.pem - // openssl rsa -pubout -outform DER < /tmp/key.pem 2>/dev/null | base64 -w 0 - typedef std::list<std::pair<FilePath::StringType, int> > - ComponentExtensionList; - ComponentExtensionList component_extensions; - - // Bookmark manager. - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("bookmark_manager"), - IDR_BOOKMARKS_MANIFEST)); - -#if defined(FILE_MANAGER_EXTENSION) - AddFileManagerExtension(&component_extensions); -#endif - -#if defined(USE_VIRTUAL_KEYBOARD) - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("keyboard"), - IDR_KEYBOARD_MANIFEST)); -#endif - -#if defined(OS_CHROMEOS) - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile"), - IDR_MOBILE_MANIFEST)); - - const CommandLine* command_line = CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kAuthExtensionPath)) { - FilePath auth_extension_path = - command_line->GetSwitchValuePath(switches::kAuthExtensionPath); - component_extensions.push_back(std::make_pair( - auth_extension_path.value(), - IDR_GAIA_TEST_AUTH_MANIFEST)); - } else { - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("/usr/share/chromeos-assets/gaia_auth"), - IDR_GAIA_AUTH_MANIFEST)); - } - -#if defined(OFFICIAL_BUILD) - if (browser_defaults::enable_help_app) { - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp"), - IDR_HELP_MANIFEST)); - } -#endif -#endif - - // Web Store. - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("web_store"), - IDR_WEBSTORE_MANIFEST)); - -#if !defined(OS_CHROMEOS) - // Cloud Print component app. Not required on Chrome OS. - component_extensions.push_back(std::make_pair( - FILE_PATH_LITERAL("cloud_print"), - IDR_CLOUDPRINT_MANIFEST)); -#endif // !defined(OS_CHROMEOS) - - for (ComponentExtensionList::iterator iter = component_extensions.begin(); - iter != component_extensions.end(); ++iter) { - FilePath path(iter->first); - if (!path.IsAbsolute()) { - if (PathService::Get(chrome::DIR_RESOURCES, &path)) { - path = path.Append(iter->first); - } else { - NOTREACHED(); - } - } - - std::string manifest = - ResourceBundle::GetSharedInstance().GetRawDataResource( - iter->second).as_string(); - extension_service_->register_component_extension( - ExtensionService::ComponentExtensionInfo(manifest, path)); - } - -#if defined(OS_CHROMEOS) - // Register access extensions only if accessibility is enabled. - if (g_browser_process->local_state()-> - GetBoolean(prefs::kAccessibilityEnabled)) { - FilePath path = FilePath(extension_misc::kAccessExtensionPath) - .AppendASCII(extension_misc::kChromeVoxDirectoryName); - std::string manifest = - ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_CHROMEVOX_MANIFEST).as_string(); - extension_service_->register_component_extension( - ExtensionService::ComponentExtensionInfo(manifest, path)); - } -#endif -} - void ProfileImpl::InitPromoResources() { if (promo_resource_service_) return; diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index d7929e8..10ec531d 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -175,8 +175,6 @@ class ProfileImpl : public Profile, void EnsureSessionServiceCreated(); - void RegisterComponentExtensions(); - ExtensionPrefValueMap* GetExtensionPrefValueMap(); void CreateQuotaManagerAndClients(); diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index e5aa2dc..9a8948c 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -4148,7 +4148,8 @@ void Browser::OnInstallApplication(TabContentsWrapper* source, if (!extension_service) return; - scoped_refptr<CrxInstaller> installer(extension_service->MakeCrxInstaller( + scoped_refptr<CrxInstaller> installer(CrxInstaller::Create( + extension_service, extension_service->show_extensions_prompts() ? new ExtensionInstallUI(profile()) : NULL)); installer->InstallWebApp(web_app); diff --git a/chrome/browser/ui/panels/base_panel_browser_test.cc b/chrome/browser/ui/panels/base_panel_browser_test.cc index 8dc0809..651d5c6 100644 --- a/chrome/browser/ui/panels/base_panel_browser_test.cc +++ b/chrome/browser/ui/panels/base_panel_browser_test.cc @@ -330,7 +330,7 @@ scoped_refptr<Extension> BasePanelBrowserTest::CreateExtension( Extension::STRICT_ERROR_CHECKS, &error); EXPECT_TRUE(extension.get()); EXPECT_STREQ("", error.c_str()); - browser()->GetProfile()->GetExtensionService()->OnLoadSingleExtension( - extension.get(), false); + browser()->GetProfile()->GetExtensionService()-> + OnExtensionInstalled(extension.get(), false, -1); return extension; } diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc index 36efd10..466911e 100644 --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc @@ -877,7 +877,7 @@ void AppLauncherHandler::OnFaviconForApp(FaviconService::Handle handle, } scoped_refptr<CrxInstaller> installer( - extension_service_->MakeCrxInstaller(NULL)); + CrxInstaller::Create(extension_service_, NULL)); installer->set_page_index(install_info->page_index); installer->InstallWebApp(*web_app); attempted_bookmark_app_install_ = true; diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.cc b/chrome/browser/ui/webui/options/extension_settings_handler.cc index f8c3fc5..cc8607c 100644 --- a/chrome/browser/ui/webui/options/extension_settings_handler.cc +++ b/chrome/browser/ui/webui/options/extension_settings_handler.cc @@ -20,6 +20,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/extensions/extension_warning_set.h" +#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/google/google_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_contents/background_contents.h" @@ -427,7 +428,8 @@ void ExtensionSettingsHandler::HandleLoadMessage(const ListValue* args) { FilePath::StringType string_path; CHECK_EQ(1U, args->GetSize()) << args->GetSize(); CHECK(args->GetString(0, &string_path)); - extension_service_->LoadExtension(FilePath(string_path)); + extensions::UnpackedInstaller::Create(extension_service_)-> + Load(FilePath(string_path)); } void ExtensionSettingsHandler::ShowAlert(const std::string& message) { diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 28c03f9..d29cee5 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -952,16 +952,18 @@ 'browser/extensions/app_notification_storage.h', 'browser/extensions/apps_promo.cc', 'browser/extensions/apps_promo.h', - 'browser/extensions/default_apps.cc', - 'browser/extensions/default_apps.h', - 'browser/extensions/default_apps_trial.cc', - 'browser/extensions/default_apps_trial.h', + 'browser/extensions/component_loader.cc', + 'browser/extensions/component_loader.h', 'browser/extensions/convert_user_script.cc', 'browser/extensions/convert_user_script.h', 'browser/extensions/convert_web_app.cc', 'browser/extensions/convert_web_app.h', 'browser/extensions/crx_installer.cc', 'browser/extensions/crx_installer.h', + 'browser/extensions/default_apps.cc', + 'browser/extensions/default_apps.h', + 'browser/extensions/default_apps_trial.cc', + 'browser/extensions/default_apps_trial.h', 'browser/extensions/execute_code_in_tab_function.cc', 'browser/extensions/execute_code_in_tab_function.h', 'browser/extensions/extension_accessibility_api.cc', @@ -1230,6 +1232,8 @@ 'browser/extensions/image_loading_tracker.h', 'browser/extensions/in_memory_extension_settings_storage.cc', 'browser/extensions/in_memory_extension_settings_storage.h', + 'browser/extensions/installed_loader.cc', + 'browser/extensions/installed_loader.h', 'browser/extensions/key_identifier_conversion_views.cc', 'browser/extensions/key_identifier_conversion_views.h', 'browser/extensions/network_delay_listener.cc', @@ -1252,6 +1256,8 @@ 'browser/extensions/syncable_extension_settings_storage.h', 'browser/extensions/theme_installed_infobar_delegate.cc', 'browser/extensions/theme_installed_infobar_delegate.h', + 'browser/extensions/unpacked_installer.cc', + 'browser/extensions/unpacked_installer.h', 'browser/extensions/user_script_listener.cc', 'browser/extensions/user_script_listener.h', 'browser/extensions/user_script_master.cc', |