blob: cfd31c6e461039b95ebf53121c66b33ace9d61a0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/shared_module_service.h"
#include <set>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/version.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_urls.h"
namespace extensions {
namespace {
typedef std::vector<SharedModuleInfo::ImportInfo> ImportInfoVector;
typedef std::list<SharedModuleInfo::ImportInfo> ImportInfoList;
} // namespace
SharedModuleService::SharedModuleService(content::BrowserContext* context)
: browser_context_(context) {
extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
}
SharedModuleService::~SharedModuleService() {
}
SharedModuleService::ImportStatus SharedModuleService::CheckImports(
const Extension* extension,
ImportInfoList* missing_modules,
ImportInfoList* outdated_modules) {
DCHECK(extension);
DCHECK(missing_modules && missing_modules->empty());
DCHECK(outdated_modules && outdated_modules->empty());
ImportStatus status = IMPORT_STATUS_OK;
// TODO(crbug.com/420147): Code like this lives in CrxInstaller and
// UnpackedInstaller. If a change is made here that is important to enforce
// at install time, those locations need to be updated.
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
const ImportInfoVector& imports = SharedModuleInfo::GetImports(extension);
for (auto iter = imports.begin(); iter != imports.end(); ++iter) {
base::Version version_required(iter->minimum_version);
const Extension* imported_module =
registry->GetExtensionById(iter->extension_id,
ExtensionRegistry::EVERYTHING);
if (!imported_module) {
if (extension->from_webstore()) {
status = IMPORT_STATUS_UNSATISFIED;
missing_modules->push_back(*iter);
} 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()) {
outdated_modules->push_back(*iter);
status = IMPORT_STATUS_UNSATISFIED;
} else {
return IMPORT_STATUS_UNRECOVERABLE;
}
}
}
return status;
}
SharedModuleService::ImportStatus SharedModuleService::SatisfyImports(
const Extension* extension) {
ImportInfoList missing_modules;
ImportInfoList outdated_modules;
ImportStatus status =
CheckImports(extension, &missing_modules, &outdated_modules);
ExtensionService* service =
ExtensionSystem::Get(browser_context_)->extension_service();
PendingExtensionManager* pending_extension_manager =
service->pending_extension_manager();
DCHECK(pending_extension_manager);
if (status == IMPORT_STATUS_UNSATISFIED) {
for (ImportInfoList::const_iterator iter = missing_modules.begin();
iter != missing_modules.end();
++iter) {
pending_extension_manager->AddFromExtensionImport(
iter->extension_id,
extension_urls::GetWebstoreUpdateUrl(),
SharedModuleInfo::IsSharedModule);
}
service->CheckForUpdatesSoon();
}
return status;
}
std::unique_ptr<ExtensionSet> SharedModuleService::GetDependentExtensions(
const Extension* extension) {
std::unique_ptr<ExtensionSet> dependents(new ExtensionSet());
if (SharedModuleInfo::IsSharedModule(extension)) {
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
ExtensionService* service =
ExtensionSystem::Get(browser_context_)->extension_service();
ExtensionSet set_to_check;
set_to_check.InsertAll(registry->enabled_extensions());
set_to_check.InsertAll(registry->disabled_extensions());
set_to_check.InsertAll(*service->delayed_installs());
for (ExtensionSet::const_iterator iter = set_to_check.begin();
iter != set_to_check.end();
++iter) {
if (SharedModuleInfo::ImportsExtensionById(iter->get(),
extension->id())) {
dependents->Insert(*iter);
}
}
}
return dependents;
}
InstallGate::Action SharedModuleService::ShouldDelay(const Extension* extension,
bool install_immediately) {
ImportStatus status = SatisfyImports(extension);
switch (status) {
case IMPORT_STATUS_OK:
return INSTALL;
case IMPORT_STATUS_UNSATISFIED:
return DELAY;
case IMPORT_STATUS_UNRECOVERABLE:
return ABORT;
}
NOTREACHED();
return INSTALL;
}
void SharedModuleService::PruneSharedModules() {
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
ExtensionService* service =
ExtensionSystem::Get(browser_context_)->extension_service();
ExtensionSet set_to_check;
set_to_check.InsertAll(registry->enabled_extensions());
set_to_check.InsertAll(registry->disabled_extensions());
set_to_check.InsertAll(*service->delayed_installs());
std::vector<std::string> shared_modules;
std::set<std::string> used_shared_modules;
for (ExtensionSet::const_iterator iter = set_to_check.begin();
iter != set_to_check.end();
++iter) {
if (SharedModuleInfo::IsSharedModule(iter->get()))
shared_modules.push_back(iter->get()->id());
const ImportInfoVector& imports = SharedModuleInfo::GetImports(iter->get());
for (auto imports_iter = imports.begin(); imports_iter != imports.end();
++imports_iter) {
used_shared_modules.insert(imports_iter->extension_id);
}
}
std::vector<std::string>::const_iterator shared_modules_iter;
for (shared_modules_iter = shared_modules.begin();
shared_modules_iter != shared_modules.end();
shared_modules_iter++) {
if (used_shared_modules.count(*shared_modules_iter))
continue;
service->UninstallExtension(
*shared_modules_iter,
extensions::UNINSTALL_REASON_ORPHANED_SHARED_MODULE,
NULL); // Ignore error.
}
}
void SharedModuleService::OnExtensionInstalled(
content::BrowserContext* browser_context,
const Extension* extension,
bool is_update) {
if (is_update)
PruneSharedModules();
}
void SharedModuleService::OnExtensionUninstalled(
content::BrowserContext* browser_context,
const Extension* extension,
extensions::UninstallReason reason) {
// Do not call PruneSharedModules() for an uninstall that we were responsible
// for.
if (reason == extensions::UNINSTALL_REASON_ORPHANED_SHARED_MODULE)
return;
PruneSharedModules();
}
} // namespace extensions