Chromium Code Reviews| Index: chrome/browser/extensions/extension_service.cc |
| diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc |
| index 1d03d3ab245502d3edca1b46481949929ab43d43..07bf859d571ba8e1542ac6a8fd1de9c9df7e4c15 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" |
| @@ -127,6 +129,7 @@ using extensions::Manifest; |
| using extensions::PermissionMessage; |
| using extensions::PermissionMessages; |
| using extensions::PermissionSet; |
| +using extensions::SharedModuleInfo; |
| using extensions::UnloadedExtensionInfo; |
| namespace errors = extension_manifest_errors; |
| @@ -152,6 +155,10 @@ static const int kUpdateIdleDelay = 5; |
| // Wait this many seconds before trying to garbage collect extensions again. |
| static const int kGarbageCollectRetryDelay = 30; |
| +static bool IsSharedModule(const Extension* extension) { |
| + return SharedModuleInfo::IsSharedModule(extension); |
| +} |
| + |
| } // namespace |
| ExtensionService::ExtensionRuntimeData::ExtensionRuntimeData() |
| @@ -552,8 +559,6 @@ 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. |
| if (g_browser_process->profile_manager() && |
| g_browser_process->profile_manager()->will_import()) { |
| // Do not load any component extensions, since they may conflict with the |
| @@ -576,6 +581,32 @@ void ExtensionService::Init() { |
| GarbageCollectExtensions(); |
| } |
| + // 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), |
| + &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()); |
| + |
| if (extension_prefs_->NeedsStorageGarbageCollection()) { |
| GarbageCollectIsolatedStorage(); |
| extension_prefs_->SetNeedsStorageGarbageCollection(false); |
| @@ -701,7 +732,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; |
| } |
| @@ -829,9 +860,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); |
| @@ -2176,6 +2208,87 @@ void ExtensionService::UpdateActiveExtensionsInCrashReporter() { |
| child_process_logging::SetActiveExtensions(extension_ids); |
| } |
| +bool ExtensionService::CheckImports(const Extension* extension, |
| + bool* unrecoverable) { |
| + bool import_failed = false; |
| + // TODO(elijahtaylor): Message the user if there is a failure that is |
| + // unrecoverable. |
|
asargent_no_longer_on_chrome
2013/06/13 05:47:16
Maybe add "CHECK(unrecoverable != NULL);" here?
elijahtaylor1
2013/06/13 23:32:09
removed
|
| + *unrecoverable = false; |
| + 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) { |
| + import_failed = true; |
| + if (extension->from_webstore()) { |
| + if (pending_extension_manager()->AddFromExtensionImport( |
| + i->extension_id, |
| + extension_urls::GetWebstoreUpdateUrl(), |
| + IsSharedModule)) { |
| + CheckForUpdatesSoon(); |
| + } |
| + } else { |
| + *unrecoverable = true; |
| + } |
| + } else if (!SharedModuleInfo::IsSharedModule(imported_module)) { |
| + import_failed = true; |
| + *unrecoverable = true; |
| + } else if (version_required.IsValid() && |
| + imported_module->version()->CompareTo(version_required) < 0) { |
| + import_failed = true; |
| + if (imported_module->from_webstore()) { |
| + CheckForUpdatesSoon(); |
| + } else { |
| + *unrecoverable = true; |
| + } |
| + } |
| + } |
| + } |
| + return !import_failed; |
| +} |
| + |
| +scoped_ptr<const ExtensionSet> |
| + ExtensionService::GetSharedModuleImporters(const Extension* extension) { |
| + scoped_ptr<ExtensionSet> importers(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())) { |
| + importers->Insert(*iter); |
| + } |
| + } |
| + } |
| + return importers.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> importers = |
| + GetSharedModuleImporters(imported_module); |
| + if (importers->size() == 0) { |
| + UninstallExtension(i->extension_id, false, NULL); |
| + } |
| + } |
| + } |
| + } |
| +} |
| + |
| void ExtensionService::OnExtensionInstalled( |
| const Extension* extension, |
| const syncer::StringOrdinal& page_ordinal, |
| @@ -2258,10 +2371,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( |
| @@ -2273,10 +2386,18 @@ void ExtensionService::OnExtensionInstalled( |
| return; |
| } |
| + bool unrecoverable_import_error; |
|
asargent_no_longer_on_chrome
2013/06/13 05:47:16
consider initializing (otherwise you might get a c
elijahtaylor1
2013/06/13 23:32:09
removed
|
| if (installs_delayed()) { |
| extension_prefs_->SetDelayedInstallInfo(extension, initial_state, |
| - page_ordinal); |
| + extensions::ExtensionPrefs::DELAY_REASON_GLOBAL, page_ordinal); |
| delayed_installs_.Insert(extension); |
| + } else if (!CheckImports(extension, &unrecoverable_import_error)) { |
| + if (!unrecoverable_import_error) { |
| + 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); |
| } |
| @@ -2298,13 +2419,31 @@ 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; |
| + extensions::ExtensionPrefs::DelayReason reason = |
| + extension_prefs_->GetDelayedInstallReason(extension_id); |
| + |
| // Check if the extension is idle. |
| - if (!IsExtensionIdle(extension_id)) |
| + if (reason == extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE && |
| + !IsExtensionIdle(extension_id)) |
| return; |
| + const Extension* extension = delayed_installs_.GetByID(extension_id); |
| + bool unrecoverable_import_error; |
| + if (reason == extensions::ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS && |
| + !CheckImports(extension, &unrecoverable_import_error)) { |
| + if (unrecoverable_import_error) { |
| + 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); |
| } |
| @@ -2313,7 +2452,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(); |
| @@ -2370,11 +2509,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) { |
| @@ -2612,7 +2757,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( |
| @@ -2808,17 +2953,21 @@ void ExtensionService::GarbageCollectIsolatedStorage() { |
| void ExtensionService::OnGarbageCollectIsolatedStorageFinished() { |
| set_installs_delayed(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() { |