Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2037)

Unified Diff: chrome/browser/extensions/extension_service.cc

Issue 14973007: Auto-install/uninstall shared module dependencies for extensions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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() {
« no previous file with comments | « chrome/browser/extensions/extension_service.h ('k') | chrome/browser/extensions/extension_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698