shared_module_service.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/shared_module_service.h"
6
7#include <vector>
8
9#include "base/version.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/extensions/install_tracker.h"
12#include "chrome/browser/extensions/pending_extension_manager.h"
13#include "chrome/common/extensions/extension_constants.h"
14#include "extensions/browser/extension_registry.h"
15#include "extensions/browser/extension_system.h"
16#include "extensions/common/extension.h"
17
18namespace extensions {
19
20namespace {
21
22typedef std::vector<SharedModuleInfo::ImportInfo> ImportInfoVector;
23typedef std::list<SharedModuleInfo::ImportInfo> ImportInfoList;
24
25}  // namespace
26
27SharedModuleService::SharedModuleService(content::BrowserContext* context)
28    : context_(context), observing_(false) {
29  InstallTracker::Get(context_)->AddObserver(this);
30}
31
32SharedModuleService::~SharedModuleService() {
33  if (observing_)
34    InstallTracker::Get(context_)->RemoveObserver(this);
35}
36
37SharedModuleService::ImportStatus SharedModuleService::CheckImports(
38    const extensions::Extension* extension,
39    ImportInfoList* missing_modules,
40    ImportInfoList* outdated_modules) {
41  DCHECK(extension);
42  DCHECK(missing_modules && missing_modules->empty());
43  DCHECK(outdated_modules && outdated_modules->empty());
44
45  ImportStatus status = IMPORT_STATUS_OK;
46
47  ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
48  const ImportInfoVector& imports = SharedModuleInfo::GetImports(extension);
49  for (ImportInfoVector::const_iterator iter = imports.begin();
50       iter != imports.end();
51       ++iter) {
52    base::Version version_required(iter->minimum_version);
53    const Extension* imported_module =
54        registry->GetExtensionById(iter->extension_id,
55                                   ExtensionRegistry::EVERYTHING);
56    if (!imported_module) {
57      if (extension->from_webstore()) {
58        status = IMPORT_STATUS_UNSATISFIED;
59        missing_modules->push_back(*iter);
60      } else {
61        return IMPORT_STATUS_UNRECOVERABLE;
62      }
63    } else if (!SharedModuleInfo::IsSharedModule(imported_module)) {
64      return IMPORT_STATUS_UNRECOVERABLE;
65    } else if (version_required.IsValid() &&
66               imported_module->version()->CompareTo(version_required) < 0) {
67      if (imported_module->from_webstore()) {
68        outdated_modules->push_back(*iter);
69        status = IMPORT_STATUS_UNSATISFIED;
70      } else {
71        return IMPORT_STATUS_UNRECOVERABLE;
72      }
73    }
74  }
75
76  return status;
77}
78
79SharedModuleService::ImportStatus SharedModuleService::SatisfyImports(
80    const Extension* extension) {
81  ImportInfoList missing_modules;
82  ImportInfoList outdated_modules;
83  ImportStatus status =
84      CheckImports(extension, &missing_modules, &outdated_modules);
85
86  ExtensionService* service =
87      ExtensionSystem::Get(context_)->extension_service();
88
89  PendingExtensionManager* pending_extension_manager =
90      service->pending_extension_manager();
91  DCHECK(pending_extension_manager);
92
93  if (status == IMPORT_STATUS_UNSATISFIED) {
94    for (ImportInfoList::const_iterator iter = missing_modules.begin();
95         iter != missing_modules.end();
96         ++iter) {
97      pending_extension_manager->AddFromExtensionImport(
98          iter->extension_id,
99          extension_urls::GetWebstoreUpdateUrl(),
100          SharedModuleInfo::IsSharedModule);
101    }
102    service->CheckForUpdatesSoon();
103  }
104  return status;
105}
106
107scoped_ptr<const ExtensionSet> SharedModuleService::GetDependentExtensions(
108    const Extension* extension) {
109  scoped_ptr<ExtensionSet> dependents(new ExtensionSet());
110
111  if (SharedModuleInfo::IsSharedModule(extension)) {
112    ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
113    ExtensionService* service =
114        ExtensionSystem::Get(context_)->extension_service();
115
116    ExtensionSet set_to_check;
117    set_to_check.InsertAll(registry->enabled_extensions());
118    set_to_check.InsertAll(registry->disabled_extensions());
119    set_to_check.InsertAll(*service->delayed_installs());
120
121    for (ExtensionSet::const_iterator iter = set_to_check.begin();
122         iter != set_to_check.end();
123         ++iter) {
124      if (SharedModuleInfo::ImportsExtensionById(iter->get(),
125                                                 extension->id())) {
126        dependents->Insert(*iter);
127      }
128    }
129  }
130  return dependents.PassAs<const ExtensionSet>();
131}
132
133void SharedModuleService::PruneSharedModulesOnUninstall(
134    const Extension* extension) {
135  if (!SharedModuleInfo::ImportsModules(extension))
136    return;
137
138  ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
139  ExtensionService* service =
140      ExtensionSystem::Get(context_)->extension_service();
141
142  const ImportInfoVector& imports = SharedModuleInfo::GetImports(extension);
143  for (ImportInfoVector::const_iterator iter = imports.begin();
144       iter != imports.end();
145       ++iter) {
146    const Extension* imported_module =
147        registry->GetExtensionById(iter->extension_id,
148                                   ExtensionRegistry::EVERYTHING);
149    if (imported_module && imported_module->from_webstore()) {
150      scoped_ptr<const ExtensionSet> dependents =
151          GetDependentExtensions(imported_module);
152      if (dependents->is_empty()) {
153        service->UninstallExtension(iter->extension_id,
154                                    true,  // External uninstall.
155                                    NULL);  // Ignore error.
156      }
157    }
158  }
159}
160
161void SharedModuleService::OnExtensionUninstalled(const Extension* extension) {
162  PruneSharedModulesOnUninstall(extension);
163}
164
165void SharedModuleService::OnShutdown() {
166  InstallTracker::Get(context_)->RemoveObserver(this);
167  observing_ = false;
168}
169
170}  // namespace extensions
171