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 <set>
8#include <vector>
9
10#include "base/version.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/extensions/pending_extension_manager.h"
13#include "extensions/browser/extension_registry.h"
14#include "extensions/browser/extension_system.h"
15#include "extensions/browser/uninstall_reason.h"
16#include "extensions/common/extension.h"
17#include "extensions/common/extension_urls.h"
18
19namespace extensions {
20
21namespace {
22
23typedef std::vector<SharedModuleInfo::ImportInfo> ImportInfoVector;
24typedef std::list<SharedModuleInfo::ImportInfo> ImportInfoList;
25
26}  // namespace
27
28SharedModuleService::SharedModuleService(content::BrowserContext* context)
29    : extension_registry_observer_(this), browser_context_(context) {
30  extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
31}
32
33SharedModuleService::~SharedModuleService() {
34}
35
36SharedModuleService::ImportStatus SharedModuleService::CheckImports(
37    const Extension* extension,
38    ImportInfoList* missing_modules,
39    ImportInfoList* outdated_modules) {
40  DCHECK(extension);
41  DCHECK(missing_modules && missing_modules->empty());
42  DCHECK(outdated_modules && outdated_modules->empty());
43
44  ImportStatus status = IMPORT_STATUS_OK;
45
46  ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
47  const ImportInfoVector& imports = SharedModuleInfo::GetImports(extension);
48  for (ImportInfoVector::const_iterator iter = imports.begin();
49       iter != imports.end();
50       ++iter) {
51    base::Version version_required(iter->minimum_version);
52    const Extension* imported_module =
53        registry->GetExtensionById(iter->extension_id,
54                                   ExtensionRegistry::EVERYTHING);
55    if (!imported_module) {
56      if (extension->from_webstore()) {
57        status = IMPORT_STATUS_UNSATISFIED;
58        missing_modules->push_back(*iter);
59      } else {
60        return IMPORT_STATUS_UNRECOVERABLE;
61      }
62    } else if (!SharedModuleInfo::IsSharedModule(imported_module)) {
63      return IMPORT_STATUS_UNRECOVERABLE;
64    } else if (version_required.IsValid() &&
65               imported_module->version()->CompareTo(version_required) < 0) {
66      if (imported_module->from_webstore()) {
67        outdated_modules->push_back(*iter);
68        status = IMPORT_STATUS_UNSATISFIED;
69      } else {
70        return IMPORT_STATUS_UNRECOVERABLE;
71      }
72    }
73  }
74
75  return status;
76}
77
78SharedModuleService::ImportStatus SharedModuleService::SatisfyImports(
79    const Extension* extension) {
80  ImportInfoList missing_modules;
81  ImportInfoList outdated_modules;
82  ImportStatus status =
83      CheckImports(extension, &missing_modules, &outdated_modules);
84
85  ExtensionService* service =
86      ExtensionSystem::Get(browser_context_)->extension_service();
87
88  PendingExtensionManager* pending_extension_manager =
89      service->pending_extension_manager();
90  DCHECK(pending_extension_manager);
91
92  if (status == IMPORT_STATUS_UNSATISFIED) {
93    for (ImportInfoList::const_iterator iter = missing_modules.begin();
94         iter != missing_modules.end();
95         ++iter) {
96      pending_extension_manager->AddFromExtensionImport(
97          iter->extension_id,
98          extension_urls::GetWebstoreUpdateUrl(),
99          SharedModuleInfo::IsSharedModule);
100    }
101    service->CheckForUpdatesSoon();
102  }
103  return status;
104}
105
106scoped_ptr<ExtensionSet> SharedModuleService::GetDependentExtensions(
107    const Extension* extension) {
108  scoped_ptr<ExtensionSet> dependents(new ExtensionSet());
109
110  if (SharedModuleInfo::IsSharedModule(extension)) {
111    ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
112    ExtensionService* service =
113        ExtensionSystem::Get(browser_context_)->extension_service();
114
115    ExtensionSet set_to_check;
116    set_to_check.InsertAll(registry->enabled_extensions());
117    set_to_check.InsertAll(registry->disabled_extensions());
118    set_to_check.InsertAll(*service->delayed_installs());
119
120    for (ExtensionSet::const_iterator iter = set_to_check.begin();
121         iter != set_to_check.end();
122         ++iter) {
123      if (SharedModuleInfo::ImportsExtensionById(iter->get(),
124                                                 extension->id())) {
125        dependents->Insert(*iter);
126      }
127    }
128  }
129  return dependents.PassAs<ExtensionSet>();
130}
131
132void SharedModuleService::PruneSharedModules() {
133  ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
134  ExtensionService* service =
135      ExtensionSystem::Get(browser_context_)->extension_service();
136
137  ExtensionSet set_to_check;
138  set_to_check.InsertAll(registry->enabled_extensions());
139  set_to_check.InsertAll(registry->disabled_extensions());
140  set_to_check.InsertAll(*service->delayed_installs());
141
142  std::vector<std::string> shared_modules;
143  std::set<std::string> used_shared_modules;
144
145  for (ExtensionSet::const_iterator iter = set_to_check.begin();
146       iter != set_to_check.end();
147       ++iter) {
148    if (SharedModuleInfo::IsSharedModule(iter->get()))
149      shared_modules.push_back(iter->get()->id());
150
151    const ImportInfoVector& imports = SharedModuleInfo::GetImports(iter->get());
152    for (ImportInfoVector::const_iterator imports_iter = imports.begin();
153         imports_iter != imports.end();
154         ++imports_iter) {
155      used_shared_modules.insert(imports_iter->extension_id);
156    }
157  }
158
159  std::vector<std::string>::const_iterator shared_modules_iter;
160  for (shared_modules_iter = shared_modules.begin();
161       shared_modules_iter != shared_modules.end();
162       shared_modules_iter++) {
163    if (used_shared_modules.count(*shared_modules_iter))
164      continue;
165    service->UninstallExtension(
166        *shared_modules_iter,
167        extensions::UNINSTALL_REASON_ORPHANED_SHARED_MODULE,
168        base::Bind(&base::DoNothing),
169        NULL);  // Ignore error.
170  }
171}
172
173void SharedModuleService::OnExtensionInstalled(
174    content::BrowserContext* browser_context,
175    const Extension* extension,
176    bool is_update) {
177  if (is_update)
178    PruneSharedModules();
179}
180
181void SharedModuleService::OnExtensionUninstalled(
182    content::BrowserContext* browser_context,
183    const Extension* extension,
184    extensions::UninstallReason reason) {
185  PruneSharedModules();
186}
187
188}  // namespace extensions
189