blob: 4120d39e22ccf47524931a21f0e27308f8d081f0 [file] [log] [blame]
[email protected]90878c52014-04-04 18:21:021// 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
sdefresne9fb67692015-08-03 18:48:225#include "chrome/browser/extensions/shared_module_service.h"
6
dchengc963c7142016-04-08 03:55:227#include <memory>
limasdf3d102542015-12-09 03:58:458#include <utility>
9
[email protected]90878c52014-04-04 18:21:0210#include "base/memory/ref_counted.h"
[email protected]90878c52014-04-04 18:21:0211#include "base/strings/string16.h"
12#include "base/values.h"
13#include "chrome/browser/extensions/extension_service.h"
[email protected]f484f8d52014-06-12 08:38:1814#include "chrome/browser/extensions/extension_service_test_base.h"
[email protected]90878c52014-04-04 18:21:0215#include "chrome/browser/extensions/pending_extension_manager.h"
[email protected]fdd28372014-08-21 02:27:2616#include "components/crx_file/id_util.h"
skym71603842016-10-10 18:17:3117#include "components/sync/model/string_ordinal.h"
sdefresne9fb67692015-08-03 18:48:2218#include "components/version_info/version_info.h"
[email protected]90878c52014-04-04 18:21:0219#include "extensions/browser/extension_registry.h"
[email protected]4a1d9c0d2014-06-13 12:50:1120#include "extensions/browser/install_flag.h"
[email protected]e43c61f2014-07-20 21:46:3421#include "extensions/browser/uninstall_reason.h"
[email protected]90878c52014-04-04 18:21:0222#include "extensions/common/extension_builder.h"
rdevlin.cronin41227532016-07-13 21:24:3423#include "extensions/common/features/feature_channel.h"
[email protected]90878c52014-04-04 18:21:0224#include "extensions/common/value_builder.h"
[email protected]90878c52014-04-04 18:21:0225
26namespace extensions {
27
28namespace {
29
lazyboyef4fe6a2016-02-29 22:18:0430// Return an extension with |id| which imports all the modules that are in the
31// container |import_ids|.
Devlin Cronin8e5892f2018-10-04 00:13:4332scoped_refptr<const Extension> CreateExtensionImportingModules(
lazyboyef4fe6a2016-02-29 22:18:0433 const std::vector<std::string>& import_ids,
[email protected]38e872532014-07-16 23:27:5134 const std::string& id,
35 const std::string& version) {
36 DictionaryBuilder builder;
37 builder.Set("name", "Has Dependent Modules")
38 .Set("version", version)
39 .Set("manifest_version", 2);
lazyboyef4fe6a2016-02-29 22:18:0440 if (!import_ids.empty()) {
41 ListBuilder import_list;
42 for (const std::string& id : import_ids)
43 import_list.Append(DictionaryBuilder().Set("id", id).Build());
44 builder.Set("import", import_list.Build());
[email protected]38e872532014-07-16 23:27:5145 }
lazyboyef4fe6a2016-02-29 22:18:0446 return ExtensionBuilder()
47 .SetManifest(builder.Build())
48 .AddFlags(Extension::FROM_WEBSTORE)
49 .SetID(id)
50 .Build();
51}
52
Devlin Cronin8e5892f2018-10-04 00:13:4353scoped_refptr<const Extension> CreateSharedModule(
54 const std::string& module_id) {
dchengc963c7142016-04-08 03:55:2255 std::unique_ptr<base::DictionaryValue> manifest =
lazyboyef4fe6a2016-02-29 22:18:0456 DictionaryBuilder()
57 .Set("name", "Shared Module")
58 .Set("version", "1.0")
59 .Set("manifest_version", 2)
60 .Set("export",
61 DictionaryBuilder()
62 .Set("resources", ListBuilder().Append("foo.js").Build())
63 .Build())
64 .Build();
[email protected]90878c52014-04-04 18:21:0265
dcheng1fc00f12015-12-26 22:18:0366 return ExtensionBuilder()
67 .SetManifest(std::move(manifest))
68 .AddFlags(Extension::FROM_WEBSTORE)
lazyboyef4fe6a2016-02-29 22:18:0469 .SetID(crx_file::id_util::GenerateId(module_id))
dcheng1fc00f12015-12-26 22:18:0370 .Build();
[email protected]90878c52014-04-04 18:21:0271}
72
73} // namespace
74
75class SharedModuleServiceUnitTest : public ExtensionServiceTestBase {
[email protected]f043dba82014-05-09 17:13:4876 public:
77 SharedModuleServiceUnitTest() :
78 // The "export" key is open for dev-channel only, but unit tests
79 // run as stable channel on the official Windows build.
sdefresne6e883e42015-07-30 08:05:5480 current_channel_(version_info::Channel::UNKNOWN) {}
[email protected]90878c52014-04-04 18:21:0281 protected:
dcheng72191812014-10-28 20:49:5682 void SetUp() override;
[email protected]90878c52014-04-04 18:21:0283
84 // Install an extension and notify the ExtensionService.
[email protected]38e872532014-07-16 23:27:5185 testing::AssertionResult InstallExtension(const Extension* extension,
86 bool is_update);
[email protected]f043dba82014-05-09 17:13:4887 ScopedCurrentChannel current_channel_;
[email protected]90878c52014-04-04 18:21:0288};
89
90void SharedModuleServiceUnitTest::SetUp() {
91 ExtensionServiceTestBase::SetUp();
92 InitializeGoodInstalledExtensionService();
[email protected]38e872532014-07-16 23:27:5193 service()->Init();
[email protected]90878c52014-04-04 18:21:0294}
95
96testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension(
[email protected]38e872532014-07-16 23:27:5197 const Extension* extension,
98 bool is_update) {
99
100 const Extension* old = registry()->GetExtensionById(
101 extension->id(),
102 ExtensionRegistry::ENABLED);
103
104 // Verify the extension is not already installed, if it is not update.
105 if (!is_update) {
106 if (old)
107 return testing::AssertionFailure() << "Extension already installed.";
108 } else {
109 if (!old)
110 return testing::AssertionFailure() << "The extension does not exist.";
[email protected]90878c52014-04-04 18:21:02111 }
112
113 // Notify the service that the extension is installed. This adds it to the
114 // registry, notifies interested parties, etc.
[email protected]38e872532014-07-16 23:27:51115 service()->OnExtensionInstalled(
[email protected]4a1d9c0d2014-06-13 12:50:11116 extension, syncer::StringOrdinal(), kInstallFlagInstallImmediately);
[email protected]90878c52014-04-04 18:21:02117
118 // Verify that the extension is now installed.
[email protected]f484f8d52014-06-12 08:38:18119 if (!registry()->GetExtensionById(extension->id(),
120 ExtensionRegistry::ENABLED)) {
[email protected]90878c52014-04-04 18:21:02121 return testing::AssertionFailure() << "Could not install extension.";
122 }
123
124 return testing::AssertionSuccess();
125}
126
127TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) {
128 // Create an extension that has a dependency.
[email protected]fdd28372014-08-21 02:27:26129 std::string import_id = crx_file::id_util::GenerateId("id");
130 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
Devlin Cronin8e5892f2018-10-04 00:13:43131 scoped_refptr<const Extension> extension = CreateExtensionImportingModules(
lazyboyef4fe6a2016-02-29 22:18:04132 std::vector<std::string>(1, import_id), extension_id, "1.0");
[email protected]90878c52014-04-04 18:21:02133
134 PendingExtensionManager* pending_extension_manager =
[email protected]38e872532014-07-16 23:27:51135 service()->pending_extension_manager();
[email protected]90878c52014-04-04 18:21:02136
137 // Verify that we don't currently want to install the imported module.
138 EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id));
139
140 // Try to satisfy imports for the extension. This should queue the imported
141 // module's installation.
dchengc7047942014-08-26 05:05:31142 service()->shared_module_service()->SatisfyImports(extension.get());
[email protected]90878c52014-04-04 18:21:02143 EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id));
144}
145
146TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUninstall) {
147 // Create a module which exports a resource, and install it.
Devlin Cronin8e5892f2018-10-04 00:13:43148 scoped_refptr<const Extension> shared_module =
149 CreateSharedModule("shared_module");
[email protected]90878c52014-04-04 18:21:02150
dchengc7047942014-08-26 05:05:31151 EXPECT_TRUE(InstallExtension(shared_module.get(), false));
[email protected]90878c52014-04-04 18:21:02152
[email protected]fdd28372014-08-21 02:27:26153 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
[email protected]90878c52014-04-04 18:21:02154 // Create and install an extension that imports our new module.
Devlin Cronin8e5892f2018-10-04 00:13:43155 scoped_refptr<const Extension> importing_extension =
lazyboyef4fe6a2016-02-29 22:18:04156 CreateExtensionImportingModules(
157 std::vector<std::string>(1, shared_module->id()), extension_id,
158 "1.0");
dchengc7047942014-08-26 05:05:31159 EXPECT_TRUE(InstallExtension(importing_extension.get(), false));
[email protected]90878c52014-04-04 18:21:02160
161 // Uninstall the extension that imports our module.
162 base::string16 error;
[email protected]38e872532014-07-16 23:27:51163 service()->UninstallExtension(importing_extension->id(),
[email protected]e43c61f2014-07-20 21:46:34164 extensions::UNINSTALL_REASON_FOR_TESTING,
165 &error);
[email protected]90878c52014-04-04 18:21:02166 EXPECT_TRUE(error.empty());
167
168 // Since the module was only referenced by that single extension, it should
169 // have been uninstalled as a side-effect of uninstalling the extension that
170 // depended upon it.
[email protected]f484f8d52014-06-12 08:38:18171 EXPECT_FALSE(registry()->GetExtensionById(shared_module->id(),
172 ExtensionRegistry::EVERYTHING));
[email protected]90878c52014-04-04 18:21:02173}
174
[email protected]38e872532014-07-16 23:27:51175TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUpdate) {
176 // Create two modules which export a resource, and install them.
Devlin Cronin8e5892f2018-10-04 00:13:43177 scoped_refptr<const Extension> shared_module_1 =
lazyboyef4fe6a2016-02-29 22:18:04178 CreateSharedModule("shared_module_1");
dchengc7047942014-08-26 05:05:31179 EXPECT_TRUE(InstallExtension(shared_module_1.get(), false));
[email protected]38e872532014-07-16 23:27:51180
dchengc963c7142016-04-08 03:55:22181 std::unique_ptr<base::DictionaryValue> manifest_2 =
[email protected]38e872532014-07-16 23:27:51182 DictionaryBuilder()
183 .Set("name", "Shared Module 2")
184 .Set("version", "1.0")
185 .Set("manifest_version", 2)
186 .Set("export",
dcheng794d2bd2016-02-27 03:51:32187 DictionaryBuilder()
188 .Set("resources", ListBuilder().Append("foo.js").Build())
189 .Build())
limasdf3d102542015-12-09 03:58:45190 .Build();
Devlin Cronin8e5892f2018-10-04 00:13:43191 scoped_refptr<const Extension> shared_module_2 =
lazyboyef4fe6a2016-02-29 22:18:04192 CreateSharedModule("shared_module_2");
dchengc7047942014-08-26 05:05:31193 EXPECT_TRUE(InstallExtension(shared_module_2.get(), false));
[email protected]38e872532014-07-16 23:27:51194
[email protected]fdd28372014-08-21 02:27:26195 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
[email protected]38e872532014-07-16 23:27:51196
197 // Create and install an extension v1.0 that imports our new module 1.
Devlin Cronin8e5892f2018-10-04 00:13:43198 scoped_refptr<const Extension> importing_extension_1 =
lazyboyef4fe6a2016-02-29 22:18:04199 CreateExtensionImportingModules(
200 std::vector<std::string>(1, shared_module_1->id()), extension_id,
201 "1.0");
dchengc7047942014-08-26 05:05:31202 EXPECT_TRUE(InstallExtension(importing_extension_1.get(), false));
[email protected]38e872532014-07-16 23:27:51203
204 // Create and install a new version of the extension that imports our new
205 // module 2.
Devlin Cronin8e5892f2018-10-04 00:13:43206 scoped_refptr<const Extension> importing_extension_2 =
lazyboyef4fe6a2016-02-29 22:18:04207 CreateExtensionImportingModules(
208 std::vector<std::string>(1, shared_module_2->id()), extension_id,
209 "1.1");
dchengc7047942014-08-26 05:05:31210 EXPECT_TRUE(InstallExtension(importing_extension_2.get(), true));
[email protected]38e872532014-07-16 23:27:51211
212 // Since the extension v1.1 depends the module 2 insteand module 1.
213 // So the module 1 should be uninstalled.
214 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
215 ExtensionRegistry::EVERYTHING));
216 EXPECT_TRUE(registry()->GetExtensionById(shared_module_2->id(),
217 ExtensionRegistry::EVERYTHING));
218
219 // Create and install a new version of the extension that does not import any
220 // module.
Devlin Cronin8e5892f2018-10-04 00:13:43221 scoped_refptr<const Extension> importing_extension_3 =
lazyboyef4fe6a2016-02-29 22:18:04222 CreateExtensionImportingModules(std::vector<std::string>(), extension_id,
223 "1.2");
dchengc7047942014-08-26 05:05:31224 EXPECT_TRUE(InstallExtension(importing_extension_3.get(), true));
[email protected]38e872532014-07-16 23:27:51225
226 // Since the extension v1.2 does not depend any module, so the all models
227 // should have been uninstalled.
228 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
229 ExtensionRegistry::EVERYTHING));
230 EXPECT_FALSE(registry()->GetExtensionById(shared_module_2->id(),
231 ExtensionRegistry::EVERYTHING));
232
233}
234
[email protected]f043dba82014-05-09 17:13:48235TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) {
[email protected]fdd28372014-08-21 02:27:26236 std::string whitelisted_id = crx_file::id_util::GenerateId("whitelisted");
237 std::string nonwhitelisted_id =
238 crx_file::id_util::GenerateId("nonwhitelisted");
[email protected]897147a2014-05-02 22:28:04239 // Create a module which exports to a restricted whitelist.
dchengc963c7142016-04-08 03:55:22240 std::unique_ptr<base::DictionaryValue> manifest =
[email protected]897147a2014-05-02 22:28:04241 DictionaryBuilder()
242 .Set("name", "Shared Module")
243 .Set("version", "1.0")
244 .Set("manifest_version", 2)
245 .Set("export",
dcheng794d2bd2016-02-27 03:51:32246 DictionaryBuilder()
247 .Set("whitelist",
248 ListBuilder().Append(whitelisted_id).Build())
249 .Set("resources", ListBuilder().Append("*").Build())
250 .Build())
limasdf3d102542015-12-09 03:58:45251 .Build();
Devlin Cronin8e5892f2018-10-04 00:13:43252 scoped_refptr<const Extension> shared_module =
[email protected]fdd28372014-08-21 02:27:26253 ExtensionBuilder()
dcheng1fc00f12015-12-26 22:18:03254 .SetManifest(std::move(manifest))
[email protected]fdd28372014-08-21 02:27:26255 .AddFlags(Extension::FROM_WEBSTORE)
256 .SetID(crx_file::id_util::GenerateId("shared_module"))
257 .Build();
[email protected]897147a2014-05-02 22:28:04258
dchengc7047942014-08-26 05:05:31259 EXPECT_TRUE(InstallExtension(shared_module.get(), false));
[email protected]897147a2014-05-02 22:28:04260
261 // Create and install an extension with the whitelisted ID.
Devlin Cronin8e5892f2018-10-04 00:13:43262 scoped_refptr<const Extension> whitelisted_extension =
lazyboyef4fe6a2016-02-29 22:18:04263 CreateExtensionImportingModules(
264 std::vector<std::string>(1, shared_module->id()), whitelisted_id,
265 "1.0");
dchengc7047942014-08-26 05:05:31266 EXPECT_TRUE(InstallExtension(whitelisted_extension.get(), false));
[email protected]897147a2014-05-02 22:28:04267
268 // Try to install an extension with an ID that is not whitelisted.
Devlin Cronin8e5892f2018-10-04 00:13:43269 scoped_refptr<const Extension> nonwhitelisted_extension =
lazyboyef4fe6a2016-02-29 22:18:04270 CreateExtensionImportingModules(
271 std::vector<std::string>(1, shared_module->id()), nonwhitelisted_id,
272 "1.0");
elijahtaylor1511c012014-09-23 02:47:18273 // This should succeed because only CRX installer (and by extension the
274 // WebStore Installer) checks the shared module whitelist. InstallExtension
275 // bypasses the whitelist check because the SharedModuleService does not
276 // care about whitelists.
277 EXPECT_TRUE(InstallExtension(nonwhitelisted_extension.get(), false));
[email protected]897147a2014-05-02 22:28:04278}
279
lazyboyef4fe6a2016-02-29 22:18:04280TEST_F(SharedModuleServiceUnitTest, PruneMultipleSharedModules) {
281 // Create two modules which export a resource each, and install it.
Devlin Cronin8e5892f2018-10-04 00:13:43282 scoped_refptr<const Extension> shared_module_one =
lazyboyef4fe6a2016-02-29 22:18:04283 CreateSharedModule("shared_module_one");
284 EXPECT_TRUE(InstallExtension(shared_module_one.get(), false));
Devlin Cronin8e5892f2018-10-04 00:13:43285 scoped_refptr<const Extension> shared_module_two =
lazyboyef4fe6a2016-02-29 22:18:04286 CreateSharedModule("shared_module_two");
287 EXPECT_TRUE(InstallExtension(shared_module_two.get(), false));
288
289 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
290 std::vector<std::string> module_ids;
291 module_ids.push_back(shared_module_one->id());
292 module_ids.push_back(shared_module_two->id());
293 // Create and install an extension that imports both the modules.
Devlin Cronin8e5892f2018-10-04 00:13:43294 scoped_refptr<const Extension> importing_extension =
lazyboyef4fe6a2016-02-29 22:18:04295 CreateExtensionImportingModules(module_ids, extension_id, "1.0");
296 EXPECT_TRUE(InstallExtension(importing_extension.get(), false));
297
298 // Uninstall the extension that imports our modules.
299 base::string16 error;
300 service()->UninstallExtension(importing_extension->id(),
301 extensions::UNINSTALL_REASON_FOR_TESTING,
Devlin Cronin218df7f2017-11-21 21:41:31302 &error);
lazyboyef4fe6a2016-02-29 22:18:04303 EXPECT_TRUE(error.empty());
304
305 // Since the modules were only referenced by that single extension, they
306 // should have been uninstalled as a side-effect of uninstalling the extension
307 // that depended upon it.
308 EXPECT_FALSE(registry()->GetExtensionById(shared_module_one->id(),
309 ExtensionRegistry::EVERYTHING));
310 EXPECT_FALSE(registry()->GetExtensionById(shared_module_two->id(),
311 ExtensionRegistry::EVERYTHING));
312}
313
[email protected]90878c52014-04-04 18:21:02314} // namespace extensions