blob: 9f399984e7da6e029498e383c46433fcbf77ae80 [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
5#include "base/memory/ref_counted.h"
6#include "base/memory/scoped_ptr.h"
7#include "base/strings/string16.h"
8#include "base/values.h"
9#include "chrome/browser/extensions/extension_service.h"
[email protected]f484f8d52014-06-12 08:38:1810#include "chrome/browser/extensions/extension_service_test_base.h"
[email protected]90878c52014-04-04 18:21:0211#include "chrome/browser/extensions/pending_extension_manager.h"
12#include "chrome/browser/extensions/shared_module_service.h"
[email protected]f043dba82014-05-09 17:13:4813#include "chrome/common/extensions/features/feature_channel.h"
[email protected]fdd28372014-08-21 02:27:2614#include "components/crx_file/id_util.h"
[email protected]90878c52014-04-04 18:21:0215#include "extensions/browser/extension_registry.h"
[email protected]4a1d9c0d2014-06-13 12:50:1116#include "extensions/browser/install_flag.h"
[email protected]e43c61f2014-07-20 21:46:3417#include "extensions/browser/uninstall_reason.h"
[email protected]90878c52014-04-04 18:21:0218#include "extensions/common/extension_builder.h"
[email protected]90878c52014-04-04 18:21:0219#include "extensions/common/value_builder.h"
20#include "sync/api/string_ordinal.h"
21
22namespace extensions {
23
24namespace {
25
[email protected]897147a2014-05-02 22:28:0426// Return an extension with |id| which imports a module with the given
27// |import_id|.
[email protected]90878c52014-04-04 18:21:0228scoped_refptr<Extension> CreateExtensionImportingModule(
[email protected]38e872532014-07-16 23:27:5129 const std::string& import_id,
30 const std::string& id,
31 const std::string& version) {
32 DictionaryBuilder builder;
33 builder.Set("name", "Has Dependent Modules")
34 .Set("version", version)
35 .Set("manifest_version", 2);
36 if (!import_id.empty()) {
37 builder.Set("import",
38 ListBuilder().Append(DictionaryBuilder().Set("id", import_id)));
39 }
40 scoped_ptr<base::DictionaryValue> manifest = builder.Build();
[email protected]90878c52014-04-04 18:21:0241
42 return ExtensionBuilder().SetManifest(manifest.Pass())
43 .AddFlags(Extension::FROM_WEBSTORE)
[email protected]897147a2014-05-02 22:28:0444 .SetID(id)
[email protected]90878c52014-04-04 18:21:0245 .Build();
46}
47
48} // namespace
49
50class SharedModuleServiceUnitTest : public ExtensionServiceTestBase {
[email protected]f043dba82014-05-09 17:13:4851 public:
52 SharedModuleServiceUnitTest() :
53 // The "export" key is open for dev-channel only, but unit tests
54 // run as stable channel on the official Windows build.
55 current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {}
[email protected]90878c52014-04-04 18:21:0256 protected:
57 virtual void SetUp() OVERRIDE;
58
59 // Install an extension and notify the ExtensionService.
[email protected]38e872532014-07-16 23:27:5160 testing::AssertionResult InstallExtension(const Extension* extension,
61 bool is_update);
[email protected]f043dba82014-05-09 17:13:4862 ScopedCurrentChannel current_channel_;
[email protected]90878c52014-04-04 18:21:0263};
64
65void SharedModuleServiceUnitTest::SetUp() {
66 ExtensionServiceTestBase::SetUp();
67 InitializeGoodInstalledExtensionService();
[email protected]38e872532014-07-16 23:27:5168 service()->Init();
[email protected]90878c52014-04-04 18:21:0269}
70
71testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension(
[email protected]38e872532014-07-16 23:27:5172 const Extension* extension,
73 bool is_update) {
74
75 const Extension* old = registry()->GetExtensionById(
76 extension->id(),
77 ExtensionRegistry::ENABLED);
78
79 // Verify the extension is not already installed, if it is not update.
80 if (!is_update) {
81 if (old)
82 return testing::AssertionFailure() << "Extension already installed.";
83 } else {
84 if (!old)
85 return testing::AssertionFailure() << "The extension does not exist.";
[email protected]90878c52014-04-04 18:21:0286 }
87
88 // Notify the service that the extension is installed. This adds it to the
89 // registry, notifies interested parties, etc.
[email protected]38e872532014-07-16 23:27:5190 service()->OnExtensionInstalled(
[email protected]4a1d9c0d2014-06-13 12:50:1191 extension, syncer::StringOrdinal(), kInstallFlagInstallImmediately);
[email protected]90878c52014-04-04 18:21:0292
93 // Verify that the extension is now installed.
[email protected]f484f8d52014-06-12 08:38:1894 if (!registry()->GetExtensionById(extension->id(),
95 ExtensionRegistry::ENABLED)) {
[email protected]90878c52014-04-04 18:21:0296 return testing::AssertionFailure() << "Could not install extension.";
97 }
98
99 return testing::AssertionSuccess();
100}
101
102TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) {
103 // Create an extension that has a dependency.
[email protected]fdd28372014-08-21 02:27:26104 std::string import_id = crx_file::id_util::GenerateId("id");
105 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
[email protected]90878c52014-04-04 18:21:02106 scoped_refptr<Extension> extension =
[email protected]38e872532014-07-16 23:27:51107 CreateExtensionImportingModule(import_id, extension_id, "1.0");
[email protected]90878c52014-04-04 18:21:02108
109 PendingExtensionManager* pending_extension_manager =
[email protected]38e872532014-07-16 23:27:51110 service()->pending_extension_manager();
[email protected]90878c52014-04-04 18:21:02111
112 // Verify that we don't currently want to install the imported module.
113 EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id));
114
115 // Try to satisfy imports for the extension. This should queue the imported
116 // module's installation.
[email protected]38e872532014-07-16 23:27:51117 service()->shared_module_service()->SatisfyImports(extension);
[email protected]90878c52014-04-04 18:21:02118 EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id));
119}
120
121TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUninstall) {
122 // Create a module which exports a resource, and install it.
123 scoped_ptr<base::DictionaryValue> manifest =
124 DictionaryBuilder()
125 .Set("name", "Shared Module")
126 .Set("version", "1.0")
127 .Set("manifest_version", 2)
128 .Set("export",
129 DictionaryBuilder().Set("resources",
130 ListBuilder().Append("foo.js"))).Build();
131 scoped_refptr<Extension> shared_module =
[email protected]fdd28372014-08-21 02:27:26132 ExtensionBuilder()
133 .SetManifest(manifest.Pass())
134 .AddFlags(Extension::FROM_WEBSTORE)
135 .SetID(crx_file::id_util::GenerateId("shared_module"))
136 .Build();
[email protected]90878c52014-04-04 18:21:02137
[email protected]38e872532014-07-16 23:27:51138 EXPECT_TRUE(InstallExtension(shared_module, false));
[email protected]90878c52014-04-04 18:21:02139
[email protected]fdd28372014-08-21 02:27:26140 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
[email protected]90878c52014-04-04 18:21:02141 // Create and install an extension that imports our new module.
142 scoped_refptr<Extension> importing_extension =
[email protected]38e872532014-07-16 23:27:51143 CreateExtensionImportingModule(shared_module->id(), extension_id, "1.0");
144 EXPECT_TRUE(InstallExtension(importing_extension, false));
[email protected]90878c52014-04-04 18:21:02145
146 // Uninstall the extension that imports our module.
147 base::string16 error;
[email protected]38e872532014-07-16 23:27:51148 service()->UninstallExtension(importing_extension->id(),
[email protected]e43c61f2014-07-20 21:46:34149 extensions::UNINSTALL_REASON_FOR_TESTING,
[email protected]42d58f62014-07-31 01:32:45150 base::Bind(&base::DoNothing),
[email protected]e43c61f2014-07-20 21:46:34151 &error);
[email protected]90878c52014-04-04 18:21:02152 EXPECT_TRUE(error.empty());
153
154 // Since the module was only referenced by that single extension, it should
155 // have been uninstalled as a side-effect of uninstalling the extension that
156 // depended upon it.
[email protected]f484f8d52014-06-12 08:38:18157 EXPECT_FALSE(registry()->GetExtensionById(shared_module->id(),
158 ExtensionRegistry::EVERYTHING));
[email protected]90878c52014-04-04 18:21:02159}
160
[email protected]38e872532014-07-16 23:27:51161TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUpdate) {
162 // Create two modules which export a resource, and install them.
163 scoped_ptr<base::DictionaryValue> manifest_1 =
164 DictionaryBuilder()
165 .Set("name", "Shared Module 1")
166 .Set("version", "1.0")
167 .Set("manifest_version", 2)
168 .Set("export",
169 DictionaryBuilder().Set("resources",
170 ListBuilder().Append("foo.js"))).Build();
171 scoped_refptr<Extension> shared_module_1 =
[email protected]fdd28372014-08-21 02:27:26172 ExtensionBuilder()
173 .SetManifest(manifest_1.Pass())
174 .AddFlags(Extension::FROM_WEBSTORE)
175 .SetID(crx_file::id_util::GenerateId("shared_module_1"))
176 .Build();
[email protected]38e872532014-07-16 23:27:51177 EXPECT_TRUE(InstallExtension(shared_module_1, false));
178
179 scoped_ptr<base::DictionaryValue> manifest_2 =
180 DictionaryBuilder()
181 .Set("name", "Shared Module 2")
182 .Set("version", "1.0")
183 .Set("manifest_version", 2)
184 .Set("export",
185 DictionaryBuilder().Set("resources",
186 ListBuilder().Append("foo.js"))).Build();
187 scoped_refptr<Extension> shared_module_2 =
[email protected]fdd28372014-08-21 02:27:26188 ExtensionBuilder()
189 .SetManifest(manifest_2.Pass())
190 .AddFlags(Extension::FROM_WEBSTORE)
191 .SetID(crx_file::id_util::GenerateId("shared_module_2"))
192 .Build();
[email protected]38e872532014-07-16 23:27:51193 EXPECT_TRUE(InstallExtension(shared_module_2, false));
194
[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.
198 scoped_refptr<Extension> importing_extension_1 =
199 CreateExtensionImportingModule(shared_module_1->id(),
200 extension_id,
201 "1.0");
202 EXPECT_TRUE(InstallExtension(importing_extension_1, false));
203
204 // Create and install a new version of the extension that imports our new
205 // module 2.
206 scoped_refptr<Extension> importing_extension_2 =
207 CreateExtensionImportingModule(shared_module_2->id(),
208 extension_id,
209 "1.1");
210 EXPECT_TRUE(InstallExtension(importing_extension_2, true));
211
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.
221 scoped_refptr<Extension> importing_extension_3 =
222 CreateExtensionImportingModule("", extension_id, "1.2");
223 EXPECT_TRUE(InstallExtension(importing_extension_3, true));
224
225 // Since the extension v1.2 does not depend any module, so the all models
226 // should have been uninstalled.
227 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
228 ExtensionRegistry::EVERYTHING));
229 EXPECT_FALSE(registry()->GetExtensionById(shared_module_2->id(),
230 ExtensionRegistry::EVERYTHING));
231
232}
233
[email protected]f043dba82014-05-09 17:13:48234TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) {
[email protected]fdd28372014-08-21 02:27:26235 std::string whitelisted_id = crx_file::id_util::GenerateId("whitelisted");
236 std::string nonwhitelisted_id =
237 crx_file::id_util::GenerateId("nonwhitelisted");
[email protected]897147a2014-05-02 22:28:04238 // Create a module which exports to a restricted whitelist.
239 scoped_ptr<base::DictionaryValue> manifest =
240 DictionaryBuilder()
241 .Set("name", "Shared Module")
242 .Set("version", "1.0")
243 .Set("manifest_version", 2)
244 .Set("export",
245 DictionaryBuilder().Set("whitelist",
246 ListBuilder()
247 .Append(whitelisted_id))
248 .Set("resources",
249 ListBuilder().Append("*"))).Build();
250 scoped_refptr<Extension> shared_module =
[email protected]fdd28372014-08-21 02:27:26251 ExtensionBuilder()
252 .SetManifest(manifest.Pass())
253 .AddFlags(Extension::FROM_WEBSTORE)
254 .SetID(crx_file::id_util::GenerateId("shared_module"))
255 .Build();
[email protected]897147a2014-05-02 22:28:04256
[email protected]38e872532014-07-16 23:27:51257 EXPECT_TRUE(InstallExtension(shared_module, false));
[email protected]897147a2014-05-02 22:28:04258
259 // Create and install an extension with the whitelisted ID.
260 scoped_refptr<Extension> whitelisted_extension =
[email protected]38e872532014-07-16 23:27:51261 CreateExtensionImportingModule(shared_module->id(),
262 whitelisted_id,
263 "1.0");
264 EXPECT_TRUE(InstallExtension(whitelisted_extension, false));
[email protected]897147a2014-05-02 22:28:04265
266 // Try to install an extension with an ID that is not whitelisted.
267 scoped_refptr<Extension> nonwhitelisted_extension =
[email protected]38e872532014-07-16 23:27:51268 CreateExtensionImportingModule(shared_module->id(),
269 nonwhitelisted_id,
270 "1.0");
271 EXPECT_FALSE(InstallExtension(nonwhitelisted_extension, false));
[email protected]897147a2014-05-02 22:28:04272}
273
[email protected]90878c52014-04-04 18:21:02274} // namespace extensions