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
19 namespace extensions {
20
21 namespace {
22
23 typedef std::vector<SharedModuleInfo::ImportInfo> ImportInfoVector;
24 typedef std::list<SharedModuleInfo::ImportInfo> ImportInfoList;
25
26 } // namespace
27
SharedModuleService(content::BrowserContext * context)28 SharedModuleService::SharedModuleService(content::BrowserContext* context)
29 : extension_registry_observer_(this), browser_context_(context) {
30 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
31 }
32
~SharedModuleService()33 SharedModuleService::~SharedModuleService() {
34 }
35
CheckImports(const Extension * extension,ImportInfoList * missing_modules,ImportInfoList * outdated_modules)36 SharedModuleService::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
SatisfyImports(const Extension * extension)78 SharedModuleService::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
GetDependentExtensions(const Extension * extension)106 scoped_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
PruneSharedModules()132 void 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
OnExtensionInstalled(content::BrowserContext * browser_context,const Extension * extension,bool is_update)173 void SharedModuleService::OnExtensionInstalled(
174 content::BrowserContext* browser_context,
175 const Extension* extension,
176 bool is_update) {
177 if (is_update)
178 PruneSharedModules();
179 }
180
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension,extensions::UninstallReason reason)181 void SharedModuleService::OnExtensionUninstalled(
182 content::BrowserContext* browser_context,
183 const Extension* extension,
184 extensions::UninstallReason reason) {
185 PruneSharedModules();
186 }
187
188 } // namespace extensions
189