| Index: chrome/browser/extensions/extensions_service.cc
|
| diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
|
| index a675f5b8e0c38039385b8114923e817f2432eb2c..8284defa8beba74c927f4ac02c46eb5339534173 100644
|
| --- a/chrome/browser/extensions/extensions_service.cc
|
| +++ b/chrome/browser/extensions/extensions_service.cc
|
| @@ -347,8 +347,9 @@ void ExtensionsServiceBackend::LoadSingleExtension(
|
| // prefs.
|
| BrowserThread::PostTask(
|
| BrowserThread::UI, FROM_HERE,
|
| - NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
|
| - extension, true));
|
| + NewRunnableMethod(frontend_,
|
| + &ExtensionsService::OnExtensionInstalled,
|
| + extension));
|
| }
|
|
|
| void ExtensionsServiceBackend::ReportExtensionLoadError(
|
| @@ -644,7 +645,6 @@ void ExtensionsService::InstallExtension(const FilePath& extension_path) {
|
| scoped_refptr<CrxInstaller> installer(
|
| new CrxInstaller(this, // frontend
|
| NULL)); // no client (silent install)
|
| - installer->set_allow_privilege_increase(true);
|
| installer->InstallCrx(extension_path);
|
| }
|
|
|
| @@ -943,6 +943,27 @@ void ExtensionsService::DisableExtension(const std::string& extension_id) {
|
| UpdateActiveExtensionsInCrashReporter();
|
| }
|
|
|
| +void ExtensionsService::GrantPermissions(const Extension* extension) {
|
| + CHECK(extension);
|
| +
|
| + // We only maintain the granted permissions prefs for INTERNAL extensions.
|
| + CHECK(extension->location() == Extension::INTERNAL);
|
| +
|
| + ExtensionExtent effective_hosts = extension->GetEffectiveHostPermissions();
|
| + extension_prefs_->AddGrantedPermissions(extension->id(),
|
| + extension->HasFullPermissions(),
|
| + extension->api_permissions(),
|
| + effective_hosts);
|
| +}
|
| +
|
| +void ExtensionsService::GrantPermissionsAndEnableExtension(
|
| + const Extension* extension) {
|
| + CHECK(extension);
|
| + GrantPermissions(extension);
|
| + extension_prefs_->SetDidExtensionEscalatePermissions(extension, false);
|
| + EnableExtension(extension->id());
|
| +}
|
| +
|
| void ExtensionsService::LoadExtension(const FilePath& extension_path) {
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| @@ -975,7 +996,7 @@ void ExtensionsService::LoadComponentExtensions() {
|
| return;
|
| }
|
|
|
| - OnExtensionLoaded(extension, false); // Don't allow privilege increase.
|
| + OnExtensionLoaded(extension);
|
| }
|
| }
|
|
|
| @@ -1120,7 +1141,6 @@ void ExtensionsService::LoadAllExtensions() {
|
| browser_action_count);
|
| }
|
|
|
| -
|
| void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
|
| bool write_to_prefs) {
|
| std::string error;
|
| @@ -1147,7 +1167,7 @@ void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
|
| if (write_to_prefs)
|
| extension_prefs_->UpdateManifest(extension);
|
|
|
| - OnExtensionLoaded(extension, true);
|
| + OnExtensionLoaded(extension);
|
|
|
| if (Extension::IsExternalLocation(info.extension_location)) {
|
| BrowserThread::PostTask(
|
| @@ -1523,8 +1543,7 @@ void ExtensionsService::OnLoadedInstalledExtensions() {
|
| NotificationService::NoDetails());
|
| }
|
|
|
| -void ExtensionsService::OnExtensionLoaded(const Extension* extension,
|
| - bool allow_privilege_increase) {
|
| +void ExtensionsService::OnExtensionLoaded(const Extension* extension) {
|
| // Ensure extension is deleted unless we transfer ownership.
|
| scoped_refptr<const Extension> scoped_extension(extension);
|
|
|
| @@ -1535,63 +1554,29 @@ void ExtensionsService::OnExtensionLoaded(const Extension* extension,
|
| if (disabled_extension_paths_.erase(extension->id()) > 0)
|
| EnableExtension(extension->id());
|
|
|
| - // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
|
| - // that extensions are enabled by default?
|
| - if (extensions_enabled() ||
|
| - extension->is_theme() ||
|
| - extension->location() == Extension::LOAD ||
|
| - extension->location() == Extension::COMPONENT ||
|
| - Extension::IsExternalLocation(extension->location())) {
|
| - const Extension* old = GetExtensionByIdInternal(extension->id(),
|
| - true, true);
|
| - if (old) {
|
| - // CrxInstaller should have guaranteed that we aren't downgrading.
|
| - CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
|
| -
|
| - bool allow_silent_upgrade =
|
| - allow_privilege_increase || !Extension::IsPrivilegeIncrease(
|
| - old, extension);
|
| -
|
| - // Extensions get upgraded if silent upgrades are allowed, otherwise
|
| - // they get disabled.
|
| - if (allow_silent_upgrade) {
|
| - SetBeingUpgraded(old, true);
|
| - SetBeingUpgraded(extension, true);
|
| - }
|
| -
|
| - // To upgrade an extension in place, unload the old one and
|
| - // then load the new one.
|
| - UnloadExtension(old->id());
|
| - old = NULL;
|
| + // Check if the extension's privileges have changed and disable the extension
|
| + // if necessary.
|
| + DisableIfPrivilegeIncrease(extension);
|
|
|
| - if (!allow_silent_upgrade) {
|
| - // Extension has changed permissions significantly. Disable it. We
|
| - // send a notification below.
|
| - extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
|
| - extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
|
| - }
|
| - }
|
| -
|
| - switch (extension_prefs_->GetExtensionState(extension->id())) {
|
| - case Extension::ENABLED:
|
| - extensions_.push_back(scoped_extension);
|
| + switch (extension_prefs_->GetExtensionState(extension->id())) {
|
| + case Extension::ENABLED:
|
| + extensions_.push_back(scoped_extension);
|
|
|
| - NotifyExtensionLoaded(extension);
|
| + NotifyExtensionLoaded(extension);
|
|
|
| - ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
|
| - extension->GetChromeURLOverrides());
|
| - break;
|
| - case Extension::DISABLED:
|
| - disabled_extensions_.push_back(scoped_extension);
|
| - NotificationService::current()->Notify(
|
| - NotificationType::EXTENSION_UPDATE_DISABLED,
|
| - Source<Profile>(profile_),
|
| - Details<const Extension>(extension));
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| + ExtensionDOMUI::RegisterChromeURLOverrides(
|
| + profile_, extension->GetChromeURLOverrides());
|
| + break;
|
| + case Extension::DISABLED:
|
| + disabled_extensions_.push_back(scoped_extension);
|
| + NotificationService::current()->Notify(
|
| + NotificationType::EXTENSION_UPDATE_DISABLED,
|
| + Source<Profile>(profile_),
|
| + Details<const Extension>(extension));
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| }
|
|
|
| SetBeingUpgraded(extension, false);
|
| @@ -1609,6 +1594,86 @@ void ExtensionsService::OnExtensionLoaded(const Extension* extension,
|
| }
|
| }
|
|
|
| +void ExtensionsService::DisableIfPrivilegeIncrease(const Extension* extension) {
|
| + // We keep track of all permissions the user has granted each extension.
|
| + // This allows extensions to gracefully support backwards compatibility
|
| + // by including unknown permissions in their manifests. When the user
|
| + // installs the extension, only the recognized permissions are recorded.
|
| + // When the unknown permissions become recognized (e.g., through browser
|
| + // upgrade), we can prompt the user to accept these new permissions.
|
| + // Extensions can also silently upgrade to less permissions, and then
|
| + // silently upgrade to a version that adds these permissions back.
|
| + //
|
| + // For example, pretend that Chrome 10 includes a permission "omnibox"
|
| + // for an API that adds suggestions to the omnibox. An extension can
|
| + // maintain backwards compatibility while still having "omnibox" in the
|
| + // manifest. If a user installs the extension on Chrome 9, the browser
|
| + // will record the permissions it recognized, not including "omnibox."
|
| + // When upgrading to Chrome 10, "omnibox" will be recognized and Chrome
|
| + // will disable the extension and prompt the user to approve the increase
|
| + // in privileges. The extension could then release a new version that
|
| + // removes the "omnibox" permission. When the user upgrades, Chrome will
|
| + // still remember that "omnibox" had been granted, so that if the
|
| + // extension once again includes "omnibox" in an upgrade, the extension
|
| + // can upgrade without requiring this user's approval.
|
| + const Extension* old = GetExtensionByIdInternal(extension->id(),
|
| + true, true);
|
| + bool granted_full_access;
|
| + std::set<std::string> granted_apis;
|
| + ExtensionExtent granted_extent;
|
| +
|
| + bool is_extension_upgrade = old != NULL;
|
| + bool is_privilege_increase = false;
|
| +
|
| + // We only record the granted permissions for INTERNAL extensions, since
|
| + // they can't silently increase privileges.
|
| + if (extension->location() == Extension::INTERNAL) {
|
| + // Add all the recognized permissions if the granted permissions list
|
| + // hasn't been initialized yet.
|
| + if (!extension_prefs_->GetGrantedPermissions(extension->id(),
|
| + &granted_full_access,
|
| + &granted_apis,
|
| + &granted_extent)) {
|
| + GrantPermissions(extension);
|
| + CHECK(extension_prefs_->GetGrantedPermissions(extension->id(),
|
| + &granted_full_access,
|
| + &granted_apis,
|
| + &granted_extent));
|
| + }
|
| +
|
| + // Here, we check if an extension's privileges have increased in a manner
|
| + // that requires the user's approval. This could occur because the browser
|
| + // upgraded and recognized additional privileges, or an extension upgrades
|
| + // to a version that requires additional privileges.
|
| + is_privilege_increase = Extension::IsPrivilegeIncrease(
|
| + granted_full_access, granted_apis, granted_extent, extension);
|
| + }
|
| +
|
| + if (is_extension_upgrade) {
|
| + // CrxInstaller should have guaranteed that we aren't downgrading.
|
| + CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
|
| +
|
| + // Extensions get upgraded if the privileges are allowed to increase or
|
| + // the privileges haven't increased.
|
| + if (!is_privilege_increase) {
|
| + SetBeingUpgraded(old, true);
|
| + SetBeingUpgraded(extension, true);
|
| + }
|
| +
|
| + // To upgrade an extension in place, unload the old one and
|
| + // then load the new one.
|
| + UnloadExtension(old->id());
|
| + old = NULL;
|
| + }
|
| +
|
| + // Extension has changed permissions significantly. Disable it. A
|
| + // notification should be sent by the caller.
|
| + if (is_privilege_increase) {
|
| + extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
|
| + extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
|
| + }
|
| +}
|
| +
|
| void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
|
| std::set<std::string> extension_ids;
|
| for (size_t i = 0; i < extensions_.size(); ++i) {
|
| @@ -1620,8 +1685,7 @@ void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
|
| child_process_logging::SetActiveExtensions(extension_ids);
|
| }
|
|
|
| -void ExtensionsService::OnExtensionInstalled(const Extension* extension,
|
| - bool allow_privilege_increase) {
|
| +void ExtensionsService::OnExtensionInstalled(const Extension* extension) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
|
|
| // Ensure extension is deleted unless we transfer ownership.
|
| @@ -1763,7 +1827,7 @@ void ExtensionsService::OnExtensionInstalled(const Extension* extension,
|
| }
|
|
|
| // Transfer ownership of |extension| to OnExtensionLoaded.
|
| - OnExtensionLoaded(scoped_extension, allow_privilege_increase);
|
| + OnExtensionLoaded(scoped_extension);
|
| }
|
|
|
| const Extension* ExtensionsService::GetExtensionByIdInternal(
|
| @@ -1881,7 +1945,6 @@ void ExtensionsService::OnExternalExtensionFileFound(
|
| NULL)); // no client (silent install)
|
| installer->set_install_source(location);
|
| installer->set_expected_id(id);
|
| - installer->set_allow_privilege_increase(true);
|
| installer->InstallCrx(path);
|
| }
|
|
|
|
|