diff options
author | akuegel@chromium.org <akuegel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-06 17:30:45 +0000 |
---|---|---|
committer | akuegel@chromium.org <akuegel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-06 17:30:45 +0000 |
commit | 849749dd183cb18744d12f31735dd51b330018f0 (patch) | |
tree | 99b16543460fd38478ba03315202da79ffea5fee /chrome | |
parent | f8c68bba65422497f8efc690a8f3564e9035cd25 (diff) | |
download | chromium_src-849749dd183cb18744d12f31735dd51b330018f0.zip chromium_src-849749dd183cb18744d12f31735dd51b330018f0.tar.gz chromium_src-849749dd183cb18744d12f31735dd51b330018f0.tar.bz2 |
Currently the unpacked_installer does not check if there is
a policy which might disallow allowing an extension. This CL
changes this and also adds request authorization calls for
managed users. Moreover some common logic of
unpacked_installer and crx_installer is combined.
The managed_user_service_unittest is adjusted to these changes.
BUG=171370
TEST=Manual
Review URL: https://chromiumcodereview.appspot.com/12965012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198476 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/extensions/crx_installer.cc | 199 | ||||
-rw-r--r-- | chrome/browser/extensions/crx_installer.h | 37 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_installer.cc | 77 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_installer.h | 80 | ||||
-rw-r--r-- | chrome/browser/extensions/unpacked_installer.cc | 171 | ||||
-rw-r--r-- | chrome/browser/extensions/unpacked_installer.h | 16 | ||||
-rw-r--r-- | chrome/browser/managed_mode/managed_user_service.cc | 10 | ||||
-rw-r--r-- | chrome/browser/managed_mode/managed_user_service.h | 7 | ||||
-rw-r--r-- | chrome/browser/managed_mode/managed_user_service_unittest.cc | 29 | ||||
-rw-r--r-- | chrome/chrome_browser_extensions.gypi | 2 |
10 files changed, 401 insertions, 227 deletions
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 2efea97..8929506 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -28,9 +28,7 @@ #include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/management_policy.h" #include "chrome/browser/extensions/permissions_updater.h" -#include "chrome/browser/extensions/requirements_checker.h" #include "chrome/browser/extensions/webstore_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/web_app.h" @@ -54,11 +52,6 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#if defined(ENABLE_MANAGED_USERS) -#include "chrome/browser/managed_mode/managed_user_service.h" -#include "chrome/browser/managed_mode/managed_user_service_factory.h" -#endif - using content::BrowserThread; using content::UserMetricsAction; using extensions::SharedModuleInfo; @@ -86,24 +79,23 @@ scoped_refptr<CrxInstaller> CrxInstaller::Create( // static scoped_refptr<CrxInstaller> CrxInstaller::Create( - ExtensionService* frontend, + ExtensionService* service, ExtensionInstallPrompt* client, const WebstoreInstaller::Approval* approval) { - return new CrxInstaller(frontend->AsWeakPtr(), client, approval); + return new CrxInstaller(service->AsWeakPtr(), client, approval); } CrxInstaller::CrxInstaller( - base::WeakPtr<ExtensionService> frontend_weak, + base::WeakPtr<ExtensionService> service_weak, ExtensionInstallPrompt* client, const WebstoreInstaller::Approval* approval) - : install_directory_(frontend_weak->install_directory()), + : install_directory_(service_weak->install_directory()), install_source_(Manifest::INTERNAL), approved_(false), - extensions_enabled_(frontend_weak->extensions_enabled()), + extensions_enabled_(service_weak->extensions_enabled()), delete_source_(false), create_app_shortcut_(false), - frontend_weak_(frontend_weak), - profile_(frontend_weak->profile()), + service_weak_(service_weak), client_(client), apps_require_extension_mime_type_(false), allow_silent_install_(false), @@ -113,15 +105,15 @@ CrxInstaller::CrxInstaller( off_store_install_allow_reason_(OffStoreInstallDisallowed), did_handle_successfully_(true), error_on_unsupported_requirements_(false), - requirements_checker_(new RequirementsChecker()), has_requirement_errors_(false), install_wait_for_idle_(true), - update_from_settings_page_(false) { - installer_task_runner_ = frontend_weak->GetFileTaskRunner(); + update_from_settings_page_(false), + installer_(service_weak->profile()) { + installer_task_runner_ = service_weak->GetFileTaskRunner(); if (!approval) return; - CHECK(profile_->IsSameProfile(approval->profile)); + CHECK(profile()->IsSameProfile(approval->profile)); if (client_) { client_->install_ui()->SetUseAppInstalledBubble( approval->use_app_installed_bubble); @@ -297,7 +289,7 @@ CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) { } } - if (extension_->is_app()) { + if (installer_.extension()->is_app()) { // If the app was downloaded, apps_require_extension_mime_type_ // will be set. In this case, check that it was served with the // right mime type. Make an exception for file URLs, which come @@ -332,7 +324,7 @@ CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) { pattern.SetHost(download_url_.host()); pattern.SetMatchSubdomains(true); - URLPatternSet patterns = extension_->web_extent(); + URLPatternSet patterns = installer_.extension()->web_extent(); for (URLPatternSet::const_iterator i = patterns.begin(); i != patterns.end(); ++i) { if (!pattern.MatchesHost(i->host())) { @@ -374,8 +366,7 @@ void CrxInstaller::OnUnpackSuccess(const base::FilePath& temp_dir, install_cause(), extension_misc::NUM_INSTALL_CAUSES); - // Note: We take ownership of |extension| and |temp_dir|. - extension_ = extension; + installer_.set_extension(extension); temp_dir_ = temp_dir; if (original_manifest) @@ -394,7 +385,7 @@ void CrxInstaller::OnUnpackSuccess(const base::FilePath& temp_dir, } if (client_) { - IconsInfo::DecodeIcon(extension_.get(), + IconsInfo::DecodeIcon(installer_.extension(), extension_misc::EXTENSION_ICON_LARGE, ExtensionIconSet::MATCH_BIGGER, &install_icon_); @@ -408,17 +399,18 @@ void CrxInstaller::OnUnpackSuccess(const base::FilePath& temp_dir, void CrxInstaller::CheckImportsAndRequirements() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) + ExtensionService* service = service_weak_; + if (!service || service->browser_terminating()) return; - if (SharedModuleInfo::ImportsModules(extension_)) { + if (SharedModuleInfo::ImportsModules(extension())) { const std::vector<SharedModuleInfo::ImportInfo>& imports = - SharedModuleInfo::GetImports(extension_); + SharedModuleInfo::GetImports(extension()); std::vector<SharedModuleInfo::ImportInfo>::const_iterator i; for (i = imports.begin(); i != imports.end(); ++i) { Version version_required(i->minimum_version); const Extension* imported_module = - frontend_weak_->GetExtensionById(i->extension_id, true); + service->GetExtensionById(i->extension_id, true); if (!imported_module || (version_required.IsValid() && imported_module->version()->CompareTo(version_required) < 0)) { @@ -438,16 +430,13 @@ void CrxInstaller::CheckImportsAndRequirements() { } } } - AddRef(); // Balanced in OnRequirementsChecked(). - requirements_checker_->Check(extension_, - base::Bind(&CrxInstaller::OnRequirementsChecked, + installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked, this)); } void CrxInstaller::OnRequirementsChecked( std::vector<std::string> requirement_errors) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - Release(); // Balanced in CheckImportsAndRequirements(). if (!requirement_errors.empty()) { if (error_on_unsupported_requirements_) { ReportFailureFromUIThread(CrxInstallerError( @@ -458,77 +447,48 @@ void CrxInstaller::OnRequirementsChecked( } #if defined(ENABLE_MANAGED_USERS) && !defined(OS_CHROMEOS) - // Check whether the profile is managed. - ManagedUserService* service = - ManagedUserServiceFactory::GetForProfile(profile_); - if (service->ProfileIsManaged()) { - // Extensions which should be installed by policy are installed without - // using the ExtensionInstallPrompt. In that case, |client_| is NULL. - if (client_ == NULL) { - // Automatically set authorization - OnAuthorizationResult(true); - return; - } - // parent_web_contents could be NULL when the client is instantiated from - // ExtensionEnableFlow, but that code path does not lead to here. - CHECK(client_->parent_web_contents()); - service->RequestAuthorization( - client_->parent_web_contents(), - base::Bind(&CrxInstaller::OnAuthorizationResult, - this)); + // Extensions which should be installed by policy are installed without + // using the ExtensionInstallPrompt. In that case, |client_| is NULL. + if (!client_) { + // Automatically set authorization + installer_.OnAuthorizationResult( + base::Bind(&CrxInstaller::ConfirmInstall, this), + true); return; } -#endif + // |parent_web_contents| could be NULL when the client is instantiated from + // ExtensionEnableFlow, but that code path does not lead to here. Also they + // are NULL in some tests. + installer_.ShowPassphraseDialog( + client_->parent_web_contents(), + base::Bind(&CrxInstaller::ConfirmInstall, this)); +#else ConfirmInstall(); -} - -#if defined(ENABLE_MANAGED_USERS) -void CrxInstaller::OnAuthorizationResult(bool success) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (success) { - ManagedUserService* service = - ManagedUserServiceFactory::GetForProfile(profile_); - DCHECK(service); - service->AddElevationForExtension(extension_->id()); - } - // In case the authorization was not successful, ConfirmInstall will give an - // appropriate error to the user. - ConfirmInstall(); -} #endif +} void CrxInstaller::ConfirmInstall() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) + ExtensionService* service = service_weak_; + if (!service || service->browser_terminating()) return; - // Check whether this install is initiated from the settings page to - // update an existing extension or app. - CheckUpdateFromSettingsPage(); - - // For managed users the call UserMayLoad returns false if the profile is not - // elevated. - string16 error; - if (!ExtensionSystem::Get(profile_)->management_policy()-> - UserMayLoad(extension_, &error)) { + string16 error = installer_.CheckManagementPolicy(); + if (!error.empty()) { ReportFailureFromUIThread(CrxInstallerError(error)); return; } -#if defined(ENABLE_MANAGED_USERS) - // Reset the elevation of managed users. - ManagedUserService* service = - ManagedUserServiceFactory::GetForProfile(profile_); - if (service->ProfileIsManaged()) - service->RemoveElevationForExtension(extension_->id()); -#endif + // Check whether this install is initiated from the settings page to + // update an existing extension or app. + CheckUpdateFromSettingsPage(); GURL overlapping_url; const Extension* overlapping_extension = - frontend_weak_->extensions()-> - GetHostedAppByOverlappingWebExtent(extension_->web_extent()); + service->extensions()->GetHostedAppByOverlappingWebExtent( + extension()->web_extent()); if (overlapping_extension && - overlapping_extension->id() != extension_->id()) { + overlapping_extension->id() != extension()->id()) { ReportFailureFromUIThread( CrxInstallerError( l10n_util::GetStringFUTF16( @@ -538,13 +498,13 @@ void CrxInstaller::ConfirmInstall() { } current_version_ = - frontend_weak_->extension_prefs()->GetVersionString(extension_->id()); + service->extension_prefs()->GetVersionString(extension()->id()); if (client_ && (!allow_silent_install_ || !approved_) && !update_from_settings_page_) { AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort(). - client_->ConfirmInstall(this, extension_.get(), show_dialog_callback_); + client_->ConfirmInstall(this, extension(), show_dialog_callback_); } else { if (!installer_task_runner_->PostTask( FROM_HERE, @@ -557,7 +517,8 @@ void CrxInstaller::ConfirmInstall() { void CrxInstaller::InstallUIProceed() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) + ExtensionService* service = service_weak_; + if (!service || service->browser_terminating()) return; // If update_from_settings_page_ boolean is true, this functions is @@ -565,7 +526,7 @@ void CrxInstaller::InstallUIProceed() { // and if it is false, this function is called in response to // ExtensionInstallPrompt::ConfirmInstall(). if (update_from_settings_page_) { - frontend_weak_->GrantPermissionsAndEnableExtension(extension_.get()); + service->GrantPermissionsAndEnableExtension(extension()); } else { if (!installer_task_runner_->PostTask( FROM_HERE, @@ -586,7 +547,7 @@ void CrxInstaller::InstallUIAbort(bool user_initiated) { "Extensions.Permissions_InstallCancel" : "Extensions.Permissions_InstallAbort"; ExtensionService::RecordPermissionMessagesHistogram( - extension_, histogram_name.c_str()); + extension(), histogram_name.c_str()); // Kill the theme loading bubble. content::NotificationService* service = @@ -609,10 +570,10 @@ void CrxInstaller::CompleteInstall() { if (!current_version_.empty()) { Version current_version(current_version_); - if (current_version.CompareTo(*(extension_->version())) > 0) { + if (current_version.CompareTo(*(extension()->version())) > 0) { ReportFailureFromFileThread( CrxInstallerError( - l10n_util::GetStringUTF16(extension_->is_app() ? + l10n_util::GetStringUTF16(extension()->is_app() ? IDS_APP_CANT_DOWNGRADE_VERSION : IDS_EXTENSION_CANT_DOWNGRADE_VERSION))); return; @@ -628,8 +589,8 @@ void CrxInstaller::CompleteInstall() { base::FilePath version_dir = extension_file_util::InstallExtension( unpacked_extension_root_, - extension_->id(), - extension_->VersionString(), + extension()->id(), + extension()->VersionString(), install_directory_); if (version_dir.empty()) { ReportFailureFromFileThread( @@ -646,15 +607,15 @@ void CrxInstaller::CompleteInstall() { // lazily and based on the Extension's root path at that moment. // TODO(rdevlin.cronin): Continue removing std::string errors and replacing // with string16 - std::string extension_id = extension_->id(); + std::string extension_id = extension()->id(); std::string error; - extension_ = extension_file_util::LoadExtension( + installer_.set_extension(extension_file_util::LoadExtension( version_dir, install_source_, - extension_->creation_flags() | Extension::REQUIRE_KEY, - &error); + extension()->creation_flags() | Extension::REQUIRE_KEY, + &error)); - if (extension_) { + if (extension()) { ReportSuccessFromFileThread(); } else { LOG(ERROR) << error << " " << extension_id << " " << download_url_; @@ -717,36 +678,36 @@ void CrxInstaller::ReportSuccessFromFileThread() { void CrxInstaller::ReportSuccessFromUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) + if (!service_weak_ || service_weak_->browser_terminating()) return; if (!update_from_settings_page_) { // If there is a client, tell the client about installation. if (client_) - client_->OnInstallSuccess(extension_.get(), install_icon_.get()); + client_->OnInstallSuccess(extension(), install_icon_.get()); // We update the extension's granted permissions if the user already // approved the install (client_ is non NULL), or we are allowed to install // this silently. if (client_ || allow_silent_install_) { PermissionsUpdater perms_updater(profile()); - perms_updater.GrantActivePermissions(extension_); + perms_updater.GrantActivePermissions(extension()); } } // Install the extension if it's not blacklisted, but notify either way. base::Closure on_success = base::Bind(&ExtensionService::OnExtensionInstalled, - frontend_weak_, - extension_, + service_weak_, + extension(), page_ordinal_, has_requirement_errors_, install_wait_for_idle_); if (bypass_blacklist_for_test_) { HandleIsBlacklistedResponse(on_success, false); } else { - ExtensionSystem::Get(profile_)->blacklist()->IsBlacklisted( - extension_->id(), + ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted( + extension()->id(), base::Bind(&CrxInstaller::HandleIsBlacklistedResponse, this, on_success)); @@ -757,15 +718,16 @@ void CrxInstaller::HandleIsBlacklistedResponse( const base::Closure& on_success, bool is_blacklisted) { if (is_blacklisted) { - string16 error = - l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED, - UTF8ToUTF16(extension_->name())); + string16 error = l10n_util::GetStringFUTF16( + IDS_EXTENSION_IS_BLACKLISTED, + UTF8ToUTF16(extension()->name())); make_scoped_ptr(ExtensionInstallUI::Create(profile()))->OnInstallFailure( extensions::CrxInstallerError(error)); // Show error via reporter to make tests happy. ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX", - extension_->location(), Manifest::NUM_LOCATIONS); + extension()->location(), + Manifest::NUM_LOCATIONS); } else { on_success.Run(); } @@ -781,7 +743,8 @@ void CrxInstaller::NotifyCrxInstallComplete(bool success) { content::NotificationService::current()->Notify( chrome::NOTIFICATION_CRX_INSTALLER_DONE, content::Source<CrxInstaller>(this), - content::Details<const Extension>(success ? extension_.get() : NULL)); + content::Details<const Extension>( + success ? extension() : NULL)); if (success) ConfirmReEnable(); @@ -813,14 +776,15 @@ void CrxInstaller::CleanupTempFiles() { void CrxInstaller::CheckUpdateFromSettingsPage() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) + ExtensionService* service = service_weak_; + if (!service || service->browser_terminating()) return; if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage) return; const Extension* installed_extension = - frontend_weak_->GetInstalledExtension(extension_->id()); + service->GetInstalledExtension(extension()->id()); if (installed_extension) { // Previous version of the extension exists. update_from_settings_page_ = true; @@ -833,19 +797,20 @@ void CrxInstaller::CheckUpdateFromSettingsPage() { void CrxInstaller::ConfirmReEnable() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!frontend_weak_.get() || frontend_weak_->browser_terminating()) + ExtensionService* service = service_weak_; + if (!service || service->browser_terminating()) return; if (!update_from_settings_page_) return; - extensions::ExtensionPrefs* prefs = frontend_weak_->extension_prefs(); - if (!prefs->DidExtensionEscalatePermissions(extension_->id())) + ExtensionPrefs* prefs = service->extension_prefs(); + if (!prefs->DidExtensionEscalatePermissions(extension()->id())) return; if (client_) { AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort(). - client_->ConfirmReEnable(this, extension_.get()); + client_->ConfirmReEnable(this, extension()); } } diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index e394c33..a66b4a1 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -13,6 +13,7 @@ #include "base/memory/weak_ptr.h" #include "base/version.h" #include "chrome/browser/extensions/extension_install_prompt.h" +#include "chrome/browser/extensions/extension_installer.h" #include "chrome/browser/extensions/sandboxed_unpacker.h" #include "chrome/browser/extensions/webstore_installer.h" #include "chrome/common/extensions/extension.h" @@ -74,17 +75,17 @@ class CrxInstaller NumOffStoreInstallAllowReasons }; - // Extensions will be installed into frontend->install_directory(), - // then registered with |frontend|. Any install UI will be displayed + // Extensions will be installed into service->install_directory(), + // then registered with |service|. Any install UI will be displayed // using |client|. Pass NULL for |client| for silent install static scoped_refptr<CrxInstaller> Create( - ExtensionService* frontend, + ExtensionService* service, ExtensionInstallPrompt* client); // Same as the previous method, except use the |approval| to bypass the // prompt. Note that the caller retains ownership of |approval|. static scoped_refptr<CrxInstaller> Create( - ExtensionService* frontend, + ExtensionService* service, ExtensionInstallPrompt* client, const WebstoreInstaller::Approval* approval); @@ -191,14 +192,16 @@ class CrxInstaller bool did_handle_successfully() const { return did_handle_successfully_; } - Profile* profile() { return profile_; } + Profile* profile() { return installer_.profile(); } + + const Extension* extension() { return installer_.extension(); } private: friend class ::ExtensionServiceTest; friend class ExtensionUpdaterTest; friend class ExtensionCrxInstallerTest; - CrxInstaller(base::WeakPtr<ExtensionService> frontend_weak, + CrxInstaller(base::WeakPtr<ExtensionService> service_weak, ExtensionInstallPrompt* client, const WebstoreInstaller::Approval* approval); virtual ~CrxInstaller(); @@ -227,13 +230,7 @@ class CrxInstaller // Runs on the UI thread. Callback from RequirementsChecker. void OnRequirementsChecked(std::vector<std::string> requirement_errors); -#if defined(ENABLE_MANAGED_USERS) - // Runs on the UI thread. Callback from the managed user passphrase dialog. - void OnAuthorizationResult(bool success); -#endif - - // Runs on the UI thread. Confirms with the user (via ExtensionInstallPrompt) - // that it is OK to install this extension. + // Runs on the UI thread. Confirms the installation to the ExtensionService. void ConfirmInstall(); // Runs on File thread. Install the unpacked extension into the profile and @@ -311,10 +308,6 @@ class CrxInstaller // apps. bool create_app_shortcut_; - // The extension we're installing. We own this and either pass it off to - // ExtensionService on success, or delete it on failure. - scoped_refptr<const Extension> extension_; - // The ordinal of the NTP apps page |extension_| will be shown on. syncer::StringOrdinal page_ordinal_; @@ -334,10 +327,7 @@ class CrxInstaller base::FilePath temp_dir_; // The frontend we will report results back to. - base::WeakPtr<ExtensionService> frontend_weak_; - - // The Profile where the extension is being installed in. - Profile* profile_; + base::WeakPtr<ExtensionService> service_weak_; // The client we will work with to do the installation. This can be NULL, in // which case the install is silent. @@ -388,8 +378,6 @@ class CrxInstaller // will continue but the extension will be distabled. bool error_on_unsupported_requirements_; - scoped_ptr<RequirementsChecker> requirements_checker_; - bool has_requirement_errors_; bool install_wait_for_idle_; @@ -404,6 +392,9 @@ class CrxInstaller // page. bool update_from_settings_page_; + // Gives access to common methods and data of an extension installer. + ExtensionInstaller installer_; + DISALLOW_COPY_AND_ASSIGN(CrxInstaller); }; diff --git a/chrome/browser/extensions/extension_installer.cc b/chrome/browser/extensions/extension_installer.cc new file mode 100644 index 0000000..e49a756 --- /dev/null +++ b/chrome/browser/extensions/extension_installer.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2013 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/extension_installer.h" + +#include "base/bind.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/management_policy.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/browser_thread.h" + +#if defined(ENABLE_MANAGED_USERS) +#include "chrome/browser/managed_mode/managed_user_service.h" +#include "chrome/browser/managed_mode/managed_user_service_factory.h" +#endif + +namespace extensions { + +ExtensionInstaller::ExtensionInstaller(Profile* profile) + : requirements_checker_(new RequirementsChecker()), + profile_(profile), + weak_ptr_factory_(this) { +} + +ExtensionInstaller::~ExtensionInstaller() { +} + +void ExtensionInstaller::CheckRequirements( + const RequirementsCallback& callback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + requirements_checker_->Check(extension_, callback); +} + +#if defined(ENABLE_MANAGED_USERS) +void ExtensionInstaller::ShowPassphraseDialog( + content::WebContents* web_contents, + const base::Closure& authorization_callback) { + + ManagedUserService* service = + ManagedUserServiceFactory::GetForProfile(profile()); + // Check whether the profile is managed. + if (service->ProfileIsManaged()) { + service->RequestAuthorization( + web_contents, + base::Bind(&ExtensionInstaller::OnAuthorizationResult, + weak_ptr_factory_.GetWeakPtr(), + authorization_callback)); + return; + } + authorization_callback.Run(); +} + +void ExtensionInstaller::OnAuthorizationResult( + const base::Closure& authorization_callback, + bool success) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (success) { + ManagedUserService* service = + ManagedUserServiceFactory::GetForProfile(profile_); + if (service->ProfileIsManaged()) + service->AddElevationForExtension(extension_->id()); + } + authorization_callback.Run(); +} +#endif + +string16 ExtensionInstaller::CheckManagementPolicy() { + string16 error; + bool allowed = + ExtensionSystem::Get(profile_)->management_policy()->UserMayLoad( + extension_, &error); + DCHECK(allowed || !error.empty()); + return error; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/extension_installer.h b/chrome/browser/extensions/extension_installer.h new file mode 100644 index 0000000..99aca3e --- /dev/null +++ b/chrome/browser/extensions/extension_installer.h @@ -0,0 +1,80 @@ +// Copyright 2013 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_EXTENSION_INSTALLER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALLER_H_ + +#include <string> +#include <vector> + +#include "base/memory/weak_ptr.h" +#include "base/string16.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/requirements_checker.h" +#include "chrome/common/extensions/extension.h" + +class Profile; + +namespace content { +class WebContents; +} + +namespace extensions { + + +// Holds common methods and data of extension installers. +class ExtensionInstaller { + public: + typedef base::Callback<void(std::vector<std::string>)> RequirementsCallback; + + explicit ExtensionInstaller(Profile* profile); + ~ExtensionInstaller(); + + // Called on the UI thread to start the requirements check on the extension + void CheckRequirements(const RequirementsCallback& callback); + +#if defined(ENABLE_MANAGED_USERS) + // Shows the managed user passphrase dialog if the managed user is not in + // elevated state yet. + void ShowPassphraseDialog(content::WebContents* web_contents, + const base::Closure& authorization_callback); + + // Runs on the UI thread. Callback from the managed user passphrase dialog. + void OnAuthorizationResult(const base::Closure& authorization_callback, + bool success); +#endif + + // Checks the management policy if the extension can be installed. + string16 CheckManagementPolicy(); + + Profile* profile() const { + return profile_; + } + + void set_extension(const Extension* extension) { + extension_ = extension; + } + + scoped_refptr<const Extension> extension() { + return extension_; + } + + private: + scoped_ptr<RequirementsChecker> requirements_checker_; + + // The Profile where the extension is being installed in. + Profile* profile_; + + // The extension we're installing. We either pass it off to ExtensionService + // on success, or drop the reference on failure. + scoped_refptr<const Extension> extension_; + + base::WeakPtrFactory<ExtensionInstaller> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionInstaller); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_INSTALLER_H_ diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc index 61211e4..587b590 100644 --- a/chrome/browser/extensions/unpacked_installer.cc +++ b/chrome/browser/extensions/unpacked_installer.cc @@ -14,7 +14,8 @@ #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/permissions_updater.h" -#include "chrome/browser/extensions/requirements_checker.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/extensions/api/plugins/plugins_handler.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_file_util.h" @@ -23,6 +24,11 @@ #include "extensions/common/id_util.h" #include "sync/api/string_ordinal.h" +#if defined(ENABLE_MANAGED_USERS) +#include "chrome/browser/managed_mode/managed_user_service.h" +#include "chrome/browser/managed_mode/managed_user_service_factory.h" +#endif + using content::BrowserThread; using extensions::Extension; @@ -34,9 +40,9 @@ const char kUnpackedExtensionsBlacklistedError[] = // Manages an ExtensionInstallPrompt for a particular extension. class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate { public: - SimpleExtensionLoadPrompt(Profile* profile, - base::WeakPtr<ExtensionService> extension_service, - const Extension* extension); + SimpleExtensionLoadPrompt(const Extension* extension, + Profile* profile, + const base::Closure& callback); virtual ~SimpleExtensionLoadPrompt(); void ShowPrompt(); @@ -46,19 +52,19 @@ class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate { virtual void InstallUIAbort(bool user_initiated) OVERRIDE; private: - base::WeakPtr<ExtensionService> service_weak_; scoped_ptr<ExtensionInstallPrompt> install_ui_; scoped_refptr<const Extension> extension_; + base::Closure callback_; }; SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt( + const Extension* extension, Profile* profile, - base::WeakPtr<ExtensionService> extension_service, - const Extension* extension) - : service_weak_(extension_service), - extension_(extension) { - install_ui_.reset( - ExtensionInstallUI::CreateInstallPromptWithProfile(profile)); + const base::Closure& callback) + : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile( + profile)), + extension_(extension), + callback_(callback) { } SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() { @@ -66,19 +72,13 @@ SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() { void SimpleExtensionLoadPrompt::ShowPrompt() { install_ui_->ConfirmInstall( - this, extension_, ExtensionInstallPrompt::GetDefaultShowDialogCallback()); + this, + extension_, + ExtensionInstallPrompt::GetDefaultShowDialogCallback()); } void SimpleExtensionLoadPrompt::InstallUIProceed() { - if (service_weak_) { - extensions::PermissionsUpdater perms_updater(service_weak_->profile()); - perms_updater.GrantActivePermissions(extension_); - service_weak_->OnExtensionInstalled( - extension_, - syncer::StringOrdinal(), - false /* no requirement errors */, - false /* don't wait for idle */); - } + callback_.Run(); delete this; } @@ -100,9 +100,9 @@ scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create( UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service) : service_weak_(extension_service->AsWeakPtr()), prompt_for_plugins_(true), - requirements_checker_(new RequirementsChecker()), require_modern_manifest_version_(true), - launch_on_load_(false) { + launch_on_load_(false), + installer_(extension_service->profile()) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } @@ -114,7 +114,9 @@ UnpackedInstaller::~UnpackedInstaller() { void UnpackedInstaller::Load(const base::FilePath& path_in) { DCHECK(extension_path_.empty()); extension_path_ = path_in; - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, base::Bind(&UnpackedInstaller::GetAbsolutePath, this)); } @@ -137,26 +139,45 @@ void UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in, } std::string error; - extension_ = extension_file_util::LoadExtension( + installer_.set_extension(extension_file_util::LoadExtension( extension_path_, Manifest::COMMAND_LINE, GetFlags(), - &error); + &error)); - if (!extension_) { + if (!installer_.extension()) { ReportExtensionLoadError(error); return; } launch_on_load_ = launch_on_load; - CheckRequirements(); + ShowInstallPrompt(); } -void UnpackedInstaller::CheckRequirements() { +void UnpackedInstaller::ShowInstallPrompt() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - requirements_checker_->Check( - extension_, + if (!service_weak_) + return; + + const ExtensionSet* disabled_extensions = + service_weak_->disabled_extensions(); + if (service_weak_->show_extensions_prompts() && + prompt_for_plugins_ && + PluginInfo::HasPlugins(installer_.extension()) && + !disabled_extensions->Contains(installer_.extension()->id())) { + SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt( + installer_.extension(), + installer_.profile(), + base::Bind(&UnpackedInstaller::CallCheckRequirements, this)); + prompt->ShowPrompt(); + return; + } + CallCheckRequirements(); +} + +void UnpackedInstaller::CallCheckRequirements() { + installer_.CheckRequirements( base::Bind(&UnpackedInstaller::OnRequirementsChecked, this)); } @@ -169,15 +190,31 @@ void UnpackedInstaller::OnRequirementsChecked( return; } - OnLoaded(); +#if defined(ENABLE_MANAGED_USERS) + ManagedUserService* service = + ManagedUserServiceFactory::GetForProfile(installer_.profile()); + if (service->ProfileIsManaged()) { + Browser* browser = chrome::FindLastActiveWithProfile( + service_weak_->profile(), + chrome::GetActiveDesktop()); + DCHECK(service->CanSkipPassphraseDialog( + browser->tab_strip_model()->GetActiveWebContents())); + installer_.OnAuthorizationResult( + base::Bind(&UnpackedInstaller::ConfirmInstall, this), + true); + return; + } +#endif + ConfirmInstall(); } int UnpackedInstaller::GetFlags() { std::string id = id_util::GenerateIdForPath(extension_path_); bool allow_file_access = Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED); - if (service_weak_->extension_prefs()->HasAllowFileAccessSetting(id)) - allow_file_access = service_weak_->extension_prefs()->AllowFileAccess(id); + ExtensionPrefs* prefs = service_weak_->extension_prefs(); + if (prefs->HasAllowFileAccessSetting(id)) + allow_file_access = prefs->AllowFileAccess(id); int result = Extension::FOLLOW_SYMLINKS_ANYWHERE; if (allow_file_access) @@ -193,7 +230,8 @@ bool UnpackedInstaller::IsLoadingUnpackedAllowed() const { return true; // If there is a "*" in the extension blacklist, then no extensions should be // allowed at all (except explicitly whitelisted extensions). - return !service_weak_->extension_prefs()->ExtensionsBlacklistedByDefault(); + ExtensionPrefs* prefs = service_weak_->extension_prefs(); + return !prefs->ExtensionsBlacklistedByDefault(); } void UnpackedInstaller::GetAbsolutePath() { @@ -201,7 +239,8 @@ void UnpackedInstaller::GetAbsolutePath() { extension_path_ = base::MakeAbsoluteFilePath(extension_path_); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this)); } @@ -215,31 +254,34 @@ void UnpackedInstaller::CheckExtensionFileAccess() { return; } - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind( - &UnpackedInstaller::LoadWithFileAccess, this, GetFlags())); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags())); } void UnpackedInstaller::LoadWithFileAccess(int flags) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); std::string error; - extension_ = extension_file_util::LoadExtension( + installer_.set_extension(extension_file_util::LoadExtension( extension_path_, Manifest::UNPACKED, flags, - &error); + &error)); - if (!extension_) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind( - &UnpackedInstaller::ReportExtensionLoadError, - this, error)); + if (!installer_.extension()) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error)); return; } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&UnpackedInstaller::CheckRequirements, this)); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&UnpackedInstaller::ShowInstallPrompt, this)); } void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) { @@ -249,34 +291,25 @@ void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) { service_weak_->ReportExtensionLoadError(extension_path_, error, true); } -void UnpackedInstaller::OnLoaded() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!service_weak_) +void UnpackedInstaller::ConfirmInstall() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + string16 error = installer_.CheckManagementPolicy(); + if (!error.empty()) { + ReportExtensionLoadError(UTF16ToASCII(error)); return; - const ExtensionSet* disabled_extensions = - service_weak_->disabled_extensions(); - if (service_weak_->show_extensions_prompts() && - prompt_for_plugins_ && - PluginInfo::HasPlugins(extension_) && - !disabled_extensions->Contains(extension_->id())) { - SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt( - service_weak_->profile(), - service_weak_, - extension_); - prompt->ShowPrompt(); - return; // continues in SimpleExtensionLoadPrompt::InstallPrompt* } PermissionsUpdater perms_updater(service_weak_->profile()); - perms_updater.GrantActivePermissions(extension_); + perms_updater.GrantActivePermissions(installer_.extension()); if (launch_on_load_) - service_weak_->ScheduleLaunchOnLoad(extension_->id()); + service_weak_->ScheduleLaunchOnLoad(installer_.extension()->id()); - service_weak_->OnExtensionInstalled(extension_, - syncer::StringOrdinal(), - false /* no requirement errors */, - false /* don't wait for idle */); + service_weak_->OnExtensionInstalled( + installer_.extension(), + syncer::StringOrdinal(), + false /* no requirement errors */, + false /* don't wait for idle */); } } // namespace extensions diff --git a/chrome/browser/extensions/unpacked_installer.h b/chrome/browser/extensions/unpacked_installer.h index 6f78492..d5964c5 100644 --- a/chrome/browser/extensions/unpacked_installer.h +++ b/chrome/browser/extensions/unpacked_installer.h @@ -12,6 +12,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/extension_installer.h" class ExtensionService; @@ -64,7 +65,10 @@ class UnpackedInstaller virtual ~UnpackedInstaller(); // Must be called from the UI thread. - void CheckRequirements(); + void ShowInstallPrompt(); + + // Calls CheckRequirements. + void CallCheckRequirements(); // Callback from RequirementsChecker. void OnRequirementsChecked(std::vector<std::string> requirement_errors); @@ -87,11 +91,12 @@ class UnpackedInstaller void ReportExtensionLoadError(const std::string& error); // Called when an unpacked extension has been loaded and installed. - void OnLoaded(); + void ConfirmInstall(); // Helper to get the Extension::CreateFlags for the installing extension. int GetFlags(); + // The service we will report results back to. base::WeakPtr<ExtensionService> service_weak_; // The pathname of the directory to load from, which is an absolute path @@ -102,10 +107,6 @@ class UnpackedInstaller // loading. bool prompt_for_plugins_; - scoped_ptr<RequirementsChecker> requirements_checker_; - - scoped_refptr<const Extension> extension_; - // Whether to require the extension installed to have a modern manifest // version. bool require_modern_manifest_version_; @@ -113,6 +114,9 @@ class UnpackedInstaller // Whether to launch the extension once it's loaded. bool launch_on_load_; + // Gives access to common methods and data of an extension installer. + ExtensionInstaller installer_; + DISALLOW_COPY_AND_ASSIGN(UnpackedInstaller); }; diff --git a/chrome/browser/managed_mode/managed_user_service.cc b/chrome/browser/managed_mode/managed_user_service.cc index 97683c4..f97dd31 100644 --- a/chrome/browser/managed_mode/managed_user_service.cc +++ b/chrome/browser/managed_mode/managed_user_service.cc @@ -111,8 +111,9 @@ void ManagedUserService::URLFilterContext::SetManualURLs( } ManagedUserService::ManagedUserService(Profile* profile) - : profile_(profile), startup_elevation_(false) { -} + : profile_(profile), + startup_elevation_(false), + skip_dialog_for_testing_(false) {} ManagedUserService::~ManagedUserService() { } @@ -138,7 +139,8 @@ bool ManagedUserService::CanSkipPassphraseDialog( #if defined(OS_CHROMEOS) NOTREACHED(); #endif - return IsElevatedForWebContents(web_contents) || + return skip_dialog_for_testing_ || + IsElevatedForWebContents(web_contents) || IsPassphraseEmpty(); } @@ -149,7 +151,7 @@ void ManagedUserService::RequestAuthorization( NOTREACHED(); #endif - if (CanSkipPassphraseDialog(web_contents)) { + if (skip_dialog_for_testing_ || CanSkipPassphraseDialog(web_contents)) { callback.Run(true); return; } diff --git a/chrome/browser/managed_mode/managed_user_service.h b/chrome/browser/managed_mode/managed_user_service.h index 11c1859..5da4881 100644 --- a/chrome/browser/managed_mode/managed_user_service.h +++ b/chrome/browser/managed_mode/managed_user_service.h @@ -121,6 +121,10 @@ class ManagedUserService : public ProfileKeyedService, return startup_elevation_; } + void set_skip_dialog_for_testing(bool skip) { + skip_dialog_for_testing_ = skip; + } + // extensions::ManagementPolicy::Provider implementation: virtual std::string GetDebugPolicyProviderName() const OVERRIDE; virtual bool UserMayLoad(const extensions::Extension* extension, @@ -204,6 +208,9 @@ class ManagedUserService : public ProfileKeyedService, // by the managed user. std::set<std::string> elevated_for_extensions_; + // Skips the passphrase dialog in tests if set to true. + bool skip_dialog_for_testing_; + URLFilterContext url_filter_context_; }; diff --git a/chrome/browser/managed_mode/managed_user_service_unittest.cc b/chrome/browser/managed_mode/managed_user_service_unittest.cc index 1529f1c..cc89fb9 100644 --- a/chrome/browser/managed_mode/managed_user_service_unittest.cc +++ b/chrome/browser/managed_mode/managed_user_service_unittest.cc @@ -8,11 +8,14 @@ #include "chrome/browser/extensions/extension_service_unittest.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/managed_mode/managed_user_service.h" +#include "chrome/browser/managed_mode/managed_user_service_factory.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_list.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" +#include "chrome/test/base/test_browser_window.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -187,10 +190,11 @@ TEST_F(ManagedUserServiceExtensionTest, NoContentPacks) { #if !defined(OS_CHROMEOS) TEST_F(ManagedUserServiceExtensionTest, InstallContentPacks) { - ManagedUserService managed_user_service(profile_.get()); - managed_user_service.InitForTesting(); + ManagedUserService* managed_user_service = + ManagedUserServiceFactory::GetForProfile(profile_.get()); + managed_user_service->InitForTesting(); ManagedModeURLFilter* url_filter = - managed_user_service.GetURLFilterForUIThread(); + managed_user_service->GetURLFilterForUIThread(); ManagedModeURLFilterObserver observer(url_filter); observer.Wait(); @@ -209,6 +213,16 @@ TEST_F(ManagedUserServiceExtensionTest, InstallContentPacks) { EXPECT_EQ(ManagedModeURLFilter::WARN, url_filter->GetFilteringBehaviorForURL(example_url)); + managed_user_service->set_skip_dialog_for_testing(true); + + // Create a testing browser in order to make it possible to use the unpacked + // installer. + Browser::CreateParams profile_params(profile_.get(), + chrome::HOST_DESKTOP_TYPE_NATIVE); + scoped_ptr<Browser> browser( + chrome::CreateBrowserWithTestWindowForParams(&profile_params)); + BrowserList::SetLastActive(browser.get()); + // Load a content pack. scoped_refptr<extensions::UnpackedInstaller> installer( extensions::UnpackedInstaller::Create(service_)); @@ -230,7 +244,7 @@ TEST_F(ManagedUserServiceExtensionTest, InstallContentPacks) { ASSERT_TRUE(extension); ScopedVector<ManagedModeSiteList> site_lists = - GetActiveSiteLists(&managed_user_service); + GetActiveSiteLists(managed_user_service); ASSERT_EQ(1u, site_lists.size()); std::vector<ManagedModeSiteList::Site> sites; site_lists[0]->GetSites(&sites); @@ -253,7 +267,7 @@ TEST_F(ManagedUserServiceExtensionTest, InstallContentPacks) { installer->Load(extension_path); observer.Wait(); - site_lists = GetActiveSiteLists(&managed_user_service); + site_lists = GetActiveSiteLists(managed_user_service); ASSERT_EQ(2u, site_lists.size()); sites.clear(); site_lists[0]->GetSites(&sites); @@ -278,13 +292,12 @@ TEST_F(ManagedUserServiceExtensionTest, InstallContentPacks) { #endif // Disable the first content pack. - managed_user_service.AddElevationForExtension(extension->id()); + managed_user_service->AddElevationForExtension(extension->id()); service_->DisableExtension(extension->id(), extensions::Extension::DISABLE_USER_ACTION); - managed_user_service.RemoveElevationForExtension(extension->id()); observer.Wait(); - site_lists = GetActiveSiteLists(&managed_user_service); + site_lists = GetActiveSiteLists(managed_user_service); ASSERT_EQ(1u, site_lists.size()); sites.clear(); site_lists[0]->GetSites(&sites); diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index eab9fe1..95cd7f6 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -570,6 +570,8 @@ 'browser/extensions/extension_install_prompt.h', 'browser/extensions/extension_install_ui.cc', 'browser/extensions/extension_install_ui.h', + 'browser/extensions/extension_installer.cc', + 'browser/extensions/extension_installer.h', 'browser/extensions/extension_keybinding_registry.cc', 'browser/extensions/extension_keybinding_registry.h', 'browser/extensions/extension_pref_store.cc', |