| Index: chrome/browser/extensions/extension_service.cc
|
| diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
|
| index d4f0e9a51096108c4dbe0aa4f7ad198b33e30823..d0cd7a3c19dfb207d74f74ff8a9ccac592fcc697 100644
|
| --- a/chrome/browser/extensions/extension_service.cc
|
| +++ b/chrome/browser/extensions/extension_service.cc
|
| @@ -76,6 +76,7 @@
|
| #include "chrome/common/chrome_version_info.h"
|
| #include "chrome/common/extensions/background_info.h"
|
| #include "chrome/common/extensions/extension.h"
|
| +#include "chrome/common/extensions/extension_constants.h"
|
| #include "chrome/common/extensions/extension_file_util.h"
|
| #include "chrome/common/extensions/extension_manifest_constants.h"
|
| #include "chrome/common/extensions/extension_messages.h"
|
| @@ -85,6 +86,7 @@
|
| #include "chrome/common/extensions/manifest.h"
|
| #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
|
| #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
|
| +#include "chrome/common/extensions/manifest_handlers/shared_module_info.h"
|
| #include "chrome/common/extensions/manifest_url_handler.h"
|
| #include "chrome/common/extensions/permissions/permissions_data.h"
|
| #include "chrome/common/extensions/sync_helper.h"
|
| @@ -128,6 +130,7 @@ using extensions::Manifest;
|
| using extensions::PermissionMessage;
|
| using extensions::PermissionMessages;
|
| using extensions::PermissionSet;
|
| +using extensions::SharedModuleInfo;
|
| using extensions::UnloadedExtensionInfo;
|
|
|
| namespace errors = extension_manifest_errors;
|
| @@ -157,6 +160,10 @@ static const int kGarbageCollectRetryDelay = 30;
|
| // which can be garbage collected.
|
| static const int kGarbageCollectStartupDelay = 30;
|
|
|
| +static bool IsSharedModule(const Extension* extension) {
|
| + return SharedModuleInfo::IsSharedModule(extension);
|
| +}
|
| +
|
| } // namespace
|
|
|
| ExtensionService::ExtensionRuntimeData::ExtensionRuntimeData()
|
| @@ -340,7 +347,7 @@ ExtensionService::ExtensionService(Profile* profile,
|
| event_routers_initialized_(false),
|
| update_once_all_providers_are_ready_(false),
|
| browser_terminating_(false),
|
| - installs_delayed_(false),
|
| + installs_delayed_for_gc_(false),
|
| is_first_run_(false),
|
| app_sync_bundle_(this),
|
| extension_sync_bundle_(this) {
|
| @@ -530,12 +537,38 @@ void ExtensionService::Init() {
|
| // extension listens to onStartup and opens a window).
|
| SetReadyAndNotifyListeners();
|
| } else {
|
| - // TODO(mek): It might be cleaner to do the FinishDelayedInstallInfo stuff
|
| - // here instead of in installedloader.
|
| -
|
| // LoadAllExtensions() calls OnLoadedInstalledExtensions().
|
| component_loader_->LoadAll();
|
| extensions::InstalledLoader(this).LoadAllExtensions();
|
| +
|
| + // Finish install (if possible) of extensions that were still delayed while
|
| + // the browser was shut down.
|
| + scoped_ptr<extensions::ExtensionPrefs::ExtensionsInfo> delayed_info(
|
| + extension_prefs_->GetAllDelayedInstallInfo());
|
| + for (size_t i = 0; i < delayed_info->size(); ++i) {
|
| + ExtensionInfo* info = delayed_info->at(i).get();
|
| + scoped_refptr<const Extension> extension(NULL);
|
| + if (info->extension_manifest) {
|
| + std::string error;
|
| + extension = Extension::Create(
|
| + info->extension_path,
|
| + info->extension_location,
|
| + *info->extension_manifest,
|
| + extension_prefs_->GetDelayedInstallCreationFlags(
|
| + info->extension_id),
|
| + info->extension_id,
|
| + &error);
|
| + if (extension.get())
|
| + delayed_installs_.Insert(extension);
|
| + }
|
| + }
|
| + MaybeFinishDelayedInstallations();
|
| +
|
| + scoped_ptr<extensions::ExtensionPrefs::ExtensionsInfo> delayed_info2(
|
| + extension_prefs_->GetAllDelayedInstallInfo());
|
| + UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateOnLoad",
|
| + delayed_info2->size() - delayed_info->size());
|
| +
|
| SetReadyAndNotifyListeners();
|
|
|
| // TODO(erikkay) this should probably be deferred to a future point
|
| @@ -672,7 +705,7 @@ void ExtensionService::ReloadExtension(const std::string& extension_id) {
|
| path = unloaded_extension_paths_[extension_id];
|
| }
|
|
|
| - if (delayed_updates_for_idle_.Contains(extension_id)) {
|
| + if (delayed_installs_.Contains(extension_id)) {
|
| FinishDelayedInstallation(extension_id);
|
| return;
|
| }
|
| @@ -800,9 +833,10 @@ bool ExtensionService::UninstallExtension(
|
| extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
|
| }
|
|
|
| - delayed_updates_for_idle_.Remove(extension_id);
|
| delayed_installs_.Remove(extension_id);
|
|
|
| + PruneSharedModulesOnUninstall(extension);
|
| +
|
| // Track the uninstallation.
|
| UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionUninstalled", 1, 2);
|
|
|
| @@ -2169,6 +2203,91 @@ void ExtensionService::UpdateActiveExtensionsInCrashReporter() {
|
| child_process_logging::SetActiveExtensions(extension_ids);
|
| }
|
|
|
| +ExtensionService::ImportStatus ExtensionService::SatisfyImports(
|
| + const Extension* extension) {
|
| + ImportStatus status = IMPORT_STATUS_OK;
|
| + std::vector<std::string> pending;
|
| + // TODO(elijahtaylor): Message the user if there is a failure that is
|
| + // unrecoverable.
|
| + if (SharedModuleInfo::ImportsModules(extension)) {
|
| + const std::vector<SharedModuleInfo::ImportInfo>& imports =
|
| + 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 =
|
| + GetExtensionById(i->extension_id, true);
|
| + if (!imported_module) {
|
| + if (extension->from_webstore()) {
|
| + status = IMPORT_STATUS_UNSATISFIED;
|
| + pending.push_back(i->extension_id);
|
| + } else {
|
| + return IMPORT_STATUS_UNRECOVERABLE;
|
| + }
|
| + } else if (!SharedModuleInfo::IsSharedModule(imported_module)) {
|
| + return IMPORT_STATUS_UNRECOVERABLE;
|
| + } else if (version_required.IsValid() &&
|
| + imported_module->version()->CompareTo(version_required) < 0) {
|
| + if (imported_module->from_webstore()) {
|
| + status = IMPORT_STATUS_UNSATISFIED;
|
| + } else {
|
| + return IMPORT_STATUS_UNRECOVERABLE;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (status == IMPORT_STATUS_UNSATISFIED) {
|
| + for (std::vector<std::string>::const_iterator iter = pending.begin();
|
| + iter != pending.end();
|
| + ++iter) {
|
| + pending_extension_manager()->AddFromExtensionImport(
|
| + *iter,
|
| + extension_urls::GetWebstoreUpdateUrl(),
|
| + IsSharedModule);
|
| + }
|
| + CheckForUpdatesSoon();
|
| + }
|
| + return status;
|
| +}
|
| +
|
| +scoped_ptr<const ExtensionSet>
|
| + ExtensionService::GetDependentExtensions(const Extension* extension) {
|
| + scoped_ptr<ExtensionSet> dependents(new ExtensionSet());
|
| + scoped_ptr<ExtensionSet> set_to_check(new ExtensionSet());
|
| + if (SharedModuleInfo::IsSharedModule(extension)) {
|
| + set_to_check->InsertAll(disabled_extensions_);
|
| + set_to_check->InsertAll(delayed_installs_);
|
| + set_to_check->InsertAll(extensions_);
|
| + for (ExtensionSet::const_iterator iter = set_to_check->begin();
|
| + iter != set_to_check->end(); ++iter) {
|
| + if (SharedModuleInfo::ImportsExtensionById(*iter, extension->id())) {
|
| + dependents->Insert(*iter);
|
| + }
|
| + }
|
| + }
|
| + return dependents.PassAs<const ExtensionSet>();
|
| +}
|
| +
|
| +void ExtensionService::PruneSharedModulesOnUninstall(
|
| + const Extension* extension) {
|
| + if (SharedModuleInfo::ImportsModules(extension)) {
|
| + const std::vector<SharedModuleInfo::ImportInfo>& imports =
|
| + SharedModuleInfo::GetImports(extension);
|
| + std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
|
| + for (i = imports.begin(); i != imports.end(); ++i) {
|
| + const Extension* imported_module =
|
| + GetExtensionById(i->extension_id, true);
|
| + if (imported_module && imported_module->from_webstore()) {
|
| + scoped_ptr<const ExtensionSet> dependents =
|
| + GetDependentExtensions(imported_module);
|
| + if (dependents->size() == 0) {
|
| + UninstallExtension(i->extension_id, false, NULL);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| void ExtensionService::OnExtensionInstalled(
|
| const Extension* extension,
|
| const syncer::StringOrdinal& page_ordinal,
|
| @@ -2251,10 +2370,10 @@ void ExtensionService::OnExtensionInstalled(
|
| initial_enable ? Extension::ENABLED : Extension::DISABLED;
|
| if (ShouldDelayExtensionUpdate(id, wait_for_idle)) {
|
| extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
|
| - page_ordinal);
|
| + extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE, page_ordinal);
|
|
|
| // Transfer ownership of |extension|.
|
| - delayed_updates_for_idle_.Insert(extension);
|
| + delayed_installs_.Insert(extension);
|
|
|
| // Notify extension of available update.
|
| extensions::RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
|
| @@ -2266,10 +2385,18 @@ void ExtensionService::OnExtensionInstalled(
|
| return;
|
| }
|
|
|
| - if (installs_delayed()) {
|
| + ImportStatus status = SatisfyImports(extension);
|
| + if (installs_delayed_for_gc()) {
|
| extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
|
| - page_ordinal);
|
| + extensions::ExtensionPrefs::DELAY_REASON_GC, page_ordinal);
|
| delayed_installs_.Insert(extension);
|
| + } else if (status != IMPORT_STATUS_OK) {
|
| + if (status == IMPORT_STATUS_UNSATISFIED) {
|
| + extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
|
| + extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS,
|
| + page_ordinal);
|
| + delayed_installs_.Insert(extension);
|
| + }
|
| } else {
|
| AddNewOrUpdatedExtension(extension, initial_state, page_ordinal);
|
| }
|
| @@ -2291,13 +2418,35 @@ void ExtensionService::AddNewOrUpdatedExtension(
|
|
|
| void ExtensionService::MaybeFinishDelayedInstallation(
|
| const std::string& extension_id) {
|
| - // Check if the extension already got updated.
|
| - if (!delayed_updates_for_idle_.Contains(extension_id))
|
| + // Check if the extension already got installed.
|
| + if (!delayed_installs_.Contains(extension_id))
|
| return;
|
| - // Check if the extension is idle.
|
| - if (!IsExtensionIdle(extension_id))
|
| + extensions::ExtensionPrefs::DelayReason reason =
|
| + extension_prefs_->GetDelayedInstallReason(extension_id);
|
| +
|
| + // Check if the extension is idle. DELAY_REASON_NONE is used for older
|
| + // preferences files that will not have set this field but it was previously
|
| + // only used for idle updates.
|
| + if ((reason == extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE ||
|
| + reason == extensions::ExtensionPrefs::DELAY_REASON_NONE) &&
|
| + is_ready() && !IsExtensionIdle(extension_id))
|
| return;
|
|
|
| + const Extension* extension = delayed_installs_.GetByID(extension_id);
|
| + if (reason == extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS) {
|
| + ImportStatus status = SatisfyImports(extension);
|
| + if (status != IMPORT_STATUS_OK) {
|
| + if (status == IMPORT_STATUS_UNRECOVERABLE) {
|
| + delayed_installs_.Remove(extension_id);
|
| + // Make sure no version of the extension is actually installed, (i.e.,
|
| + // that this delayed install was not an update).
|
| + CHECK(!extension_prefs_->GetInstalledExtensionInfo(extension_id).get());
|
| + extension_prefs_->DeleteExtensionPrefs(extension_id);
|
| + }
|
| + return;
|
| + }
|
| + }
|
| +
|
| FinishDelayedInstallation(extension_id);
|
| }
|
|
|
| @@ -2306,7 +2455,7 @@ void ExtensionService::FinishDelayedInstallation(
|
| scoped_refptr<const Extension> extension(
|
| GetPendingExtensionUpdate(extension_id));
|
| CHECK(extension.get());
|
| - delayed_updates_for_idle_.Remove(extension_id);
|
| + delayed_installs_.Remove(extension_id);
|
|
|
| if (!extension_prefs_->FinishDelayedInstallInfo(extension_id))
|
| NOTREACHED();
|
| @@ -2363,11 +2512,17 @@ void ExtensionService::FinishInstallation(const Extension* extension) {
|
| EXTERNAL_EXTENSION_INSTALLED,
|
| EXTERNAL_EXTENSION_BUCKET_BOUNDARY);
|
| }
|
| +
|
| + // Check extensions that may have been delayed only because this shared module
|
| + // was not available.
|
| + if (SharedModuleInfo::IsSharedModule(extension)) {
|
| + MaybeFinishDelayedInstallations();
|
| + }
|
| }
|
|
|
| const Extension* ExtensionService::GetPendingExtensionUpdate(
|
| const std::string& id) const {
|
| - return delayed_updates_for_idle_.GetByID(id);
|
| + return delayed_installs_.GetByID(id);
|
| }
|
|
|
| void ExtensionService::TrackTerminatedExtension(const Extension* extension) {
|
| @@ -2610,7 +2765,7 @@ void ExtensionService::Observe(int type,
|
| extensions::ExtensionHost* host =
|
| content::Details<extensions::ExtensionHost>(details).ptr();
|
| std::string extension_id = host->extension_id();
|
| - if (delayed_updates_for_idle_.Contains(extension_id)) {
|
| + if (delayed_installs_.Contains(extension_id)) {
|
| // We were waiting for this extension to become idle, it now might have,
|
| // so maybe finish installation.
|
| base::MessageLoop::current()->PostDelayedTask(
|
| @@ -2794,8 +2949,8 @@ void ExtensionService::GarbageCollectIsolatedStorage() {
|
| }
|
| }
|
|
|
| - DCHECK(!installs_delayed());
|
| - set_installs_delayed(true);
|
| + DCHECK(!installs_delayed_for_gc());
|
| + set_installs_delayed_for_gc(true);
|
| BrowserContext::GarbageCollectStoragePartitions(
|
| profile_, active_paths.Pass(),
|
| base::Bind(&ExtensionService::OnGarbageCollectIsolatedStorageFinished,
|
| @@ -2803,18 +2958,22 @@ void ExtensionService::GarbageCollectIsolatedStorage() {
|
| }
|
|
|
| void ExtensionService::OnGarbageCollectIsolatedStorageFinished() {
|
| - set_installs_delayed(false);
|
| + set_installs_delayed_for_gc(false);
|
| + MaybeFinishDelayedInstallations();
|
| +}
|
| +
|
| +void ExtensionService::MaybeFinishDelayedInstallations() {
|
| + std::vector<std::string> to_be_installed;
|
| for (ExtensionSet::const_iterator it = delayed_installs_.begin();
|
| it != delayed_installs_.end();
|
| ++it) {
|
| - FinishDelayedInstallation((*it)->id());
|
| + to_be_installed.push_back((*it)->id());
|
| }
|
| - for (ExtensionSet::const_iterator it = delayed_updates_for_idle_.begin();
|
| - it != delayed_updates_for_idle_.end();
|
| + for (std::vector<std::string>::const_iterator it = to_be_installed.begin();
|
| + it != to_be_installed.end();
|
| ++it) {
|
| - MaybeFinishDelayedInstallation((*it)->id());
|
| + MaybeFinishDelayedInstallation(*it);
|
| }
|
| - delayed_installs_.Clear();
|
| }
|
|
|
| void ExtensionService::OnNeedsToGarbageCollectIsolatedStorage() {
|
|
|