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() { |