| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/memory/ref_counted.h" | 5 #include "base/memory/ref_counted.h" |
| 6 #include "base/memory/scoped_ptr.h" | 6 #include "base/memory/scoped_ptr.h" |
| 7 #include "base/strings/string16.h" | 7 #include "base/strings/string16.h" |
| 8 #include "base/values.h" | 8 #include "base/values.h" |
| 9 #include "chrome/browser/extensions/extension_service.h" | 9 #include "chrome/browser/extensions/extension_service.h" |
| 10 #include "chrome/browser/extensions/extension_service_test_base.h" | 10 #include "chrome/browser/extensions/extension_service_test_base.h" |
| 11 #include "chrome/browser/extensions/pending_extension_manager.h" | 11 #include "chrome/browser/extensions/pending_extension_manager.h" |
| 12 #include "chrome/browser/extensions/shared_module_service.h" | 12 #include "chrome/browser/extensions/shared_module_service.h" |
| 13 #include "chrome/common/extensions/features/feature_channel.h" | 13 #include "chrome/common/extensions/features/feature_channel.h" |
| 14 #include "extensions/browser/extension_registry.h" | 14 #include "extensions/browser/extension_registry.h" |
| 15 #include "extensions/browser/install_flag.h" | 15 #include "extensions/browser/install_flag.h" |
| 16 #include "extensions/common/extension_builder.h" | 16 #include "extensions/common/extension_builder.h" |
| 17 #include "extensions/common/id_util.h" | 17 #include "extensions/common/id_util.h" |
| 18 #include "extensions/common/value_builder.h" | 18 #include "extensions/common/value_builder.h" |
| 19 #include "sync/api/string_ordinal.h" | 19 #include "sync/api/string_ordinal.h" |
| 20 | 20 |
| 21 namespace extensions { | 21 namespace extensions { |
| 22 | 22 |
| 23 namespace { | 23 namespace { |
| 24 | 24 |
| 25 // Return an extension with |id| which imports a module with the given | 25 // Return an extension with |id| which imports a module with the given |
| 26 // |import_id|. | 26 // |import_id|. |
| 27 scoped_refptr<Extension> CreateExtensionImportingModule( | 27 scoped_refptr<Extension> CreateExtensionImportingModule( |
| 28 const std::string& import_id, const std::string& id) { | 28 const std::string& import_id, |
| 29 scoped_ptr<base::DictionaryValue> manifest = | 29 const std::string& id, |
| 30 DictionaryBuilder() | 30 const std::string& version) { |
| 31 .Set("name", "Has Dependent Modules") | 31 DictionaryBuilder builder; |
| 32 .Set("version", "1.0") | 32 builder.Set("name", "Has Dependent Modules") |
| 33 .Set("manifest_version", 2) | 33 .Set("version", version) |
| 34 .Set("import", | 34 .Set("manifest_version", 2); |
| 35 ListBuilder().Append(DictionaryBuilder().Set("id", import_id))) | 35 if (!import_id.empty()) { |
| 36 .Build(); | 36 builder.Set("import", |
| 37 ListBuilder().Append(DictionaryBuilder().Set("id", import_id))); |
| 38 } |
| 39 scoped_ptr<base::DictionaryValue> manifest = builder.Build(); |
| 37 | 40 |
| 38 return ExtensionBuilder().SetManifest(manifest.Pass()) | 41 return ExtensionBuilder().SetManifest(manifest.Pass()) |
| 39 .AddFlags(Extension::FROM_WEBSTORE) | 42 .AddFlags(Extension::FROM_WEBSTORE) |
| 40 .SetID(id) | 43 .SetID(id) |
| 41 .Build(); | 44 .Build(); |
| 42 } | 45 } |
| 43 | 46 |
| 44 } // namespace | 47 } // namespace |
| 45 | 48 |
| 46 class SharedModuleServiceUnitTest : public ExtensionServiceTestBase { | 49 class SharedModuleServiceUnitTest : public ExtensionServiceTestBase { |
| 47 public: | 50 public: |
| 48 SharedModuleServiceUnitTest() : | 51 SharedModuleServiceUnitTest() : |
| 49 // The "export" key is open for dev-channel only, but unit tests | 52 // The "export" key is open for dev-channel only, but unit tests |
| 50 // run as stable channel on the official Windows build. | 53 // run as stable channel on the official Windows build. |
| 51 current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {} | 54 current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {} |
| 52 protected: | 55 protected: |
| 53 virtual void SetUp() OVERRIDE; | 56 virtual void SetUp() OVERRIDE; |
| 54 | 57 |
| 55 // Install an extension and notify the ExtensionService. | 58 // Install an extension and notify the ExtensionService. |
| 56 testing::AssertionResult InstallExtension(const Extension* extension); | 59 testing::AssertionResult InstallExtension(const Extension* extension, |
| 60 bool is_update); |
| 57 ScopedCurrentChannel current_channel_; | 61 ScopedCurrentChannel current_channel_; |
| 58 }; | 62 }; |
| 59 | 63 |
| 60 void SharedModuleServiceUnitTest::SetUp() { | 64 void SharedModuleServiceUnitTest::SetUp() { |
| 61 ExtensionServiceTestBase::SetUp(); | 65 ExtensionServiceTestBase::SetUp(); |
| 62 InitializeGoodInstalledExtensionService(); | 66 InitializeGoodInstalledExtensionService(); |
| 63 service_->Init(); | 67 service()->Init(); |
| 64 } | 68 } |
| 65 | 69 |
| 66 testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension( | 70 testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension( |
| 67 const Extension* extension) { | 71 const Extension* extension, |
| 68 // Verify the extension is not already installed. | 72 bool is_update) { |
| 69 if (registry()->GetExtensionById(extension->id(), | 73 |
| 70 ExtensionRegistry::ENABLED)) { | 74 const Extension* old = registry()->GetExtensionById( |
| 71 return testing::AssertionFailure() << "Extension already installed."; | 75 extension->id(), |
| 76 ExtensionRegistry::ENABLED); |
| 77 |
| 78 // Verify the extension is not already installed, if it is not update. |
| 79 if (!is_update) { |
| 80 if (old) |
| 81 return testing::AssertionFailure() << "Extension already installed."; |
| 82 } else { |
| 83 if (!old) |
| 84 return testing::AssertionFailure() << "The extension does not exist."; |
| 72 } | 85 } |
| 73 | 86 |
| 74 // Notify the service that the extension is installed. This adds it to the | 87 // Notify the service that the extension is installed. This adds it to the |
| 75 // registry, notifies interested parties, etc. | 88 // registry, notifies interested parties, etc. |
| 76 service_->OnExtensionInstalled( | 89 service()->OnExtensionInstalled( |
| 77 extension, syncer::StringOrdinal(), kInstallFlagInstallImmediately); | 90 extension, syncer::StringOrdinal(), kInstallFlagInstallImmediately); |
| 78 | 91 |
| 79 // Verify that the extension is now installed. | 92 // Verify that the extension is now installed. |
| 80 if (!registry()->GetExtensionById(extension->id(), | 93 if (!registry()->GetExtensionById(extension->id(), |
| 81 ExtensionRegistry::ENABLED)) { | 94 ExtensionRegistry::ENABLED)) { |
| 82 return testing::AssertionFailure() << "Could not install extension."; | 95 return testing::AssertionFailure() << "Could not install extension."; |
| 83 } | 96 } |
| 84 | 97 |
| 85 return testing::AssertionSuccess(); | 98 return testing::AssertionSuccess(); |
| 86 } | 99 } |
| 87 | 100 |
| 88 TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) { | 101 TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) { |
| 89 // Create an extension that has a dependency. | 102 // Create an extension that has a dependency. |
| 90 std::string import_id = id_util::GenerateId("id"); | 103 std::string import_id = id_util::GenerateId("id"); |
| 91 std::string extension_id = id_util::GenerateId("extension_id"); | 104 std::string extension_id = id_util::GenerateId("extension_id"); |
| 92 scoped_refptr<Extension> extension = | 105 scoped_refptr<Extension> extension = |
| 93 CreateExtensionImportingModule(import_id, extension_id); | 106 CreateExtensionImportingModule(import_id, extension_id, "1.0"); |
| 94 | 107 |
| 95 PendingExtensionManager* pending_extension_manager = | 108 PendingExtensionManager* pending_extension_manager = |
| 96 service_->pending_extension_manager(); | 109 service()->pending_extension_manager(); |
| 97 | 110 |
| 98 // Verify that we don't currently want to install the imported module. | 111 // Verify that we don't currently want to install the imported module. |
| 99 EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id)); | 112 EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id)); |
| 100 | 113 |
| 101 // Try to satisfy imports for the extension. This should queue the imported | 114 // Try to satisfy imports for the extension. This should queue the imported |
| 102 // module's installation. | 115 // module's installation. |
| 103 service_->shared_module_service()->SatisfyImports(extension); | 116 service()->shared_module_service()->SatisfyImports(extension); |
| 104 EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id)); | 117 EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id)); |
| 105 } | 118 } |
| 106 | 119 |
| 107 TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUninstall) { | 120 TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUninstall) { |
| 108 // Create a module which exports a resource, and install it. | 121 // Create a module which exports a resource, and install it. |
| 109 scoped_ptr<base::DictionaryValue> manifest = | 122 scoped_ptr<base::DictionaryValue> manifest = |
| 110 DictionaryBuilder() | 123 DictionaryBuilder() |
| 111 .Set("name", "Shared Module") | 124 .Set("name", "Shared Module") |
| 112 .Set("version", "1.0") | 125 .Set("version", "1.0") |
| 113 .Set("manifest_version", 2) | 126 .Set("manifest_version", 2) |
| 114 .Set("export", | 127 .Set("export", |
| 115 DictionaryBuilder().Set("resources", | 128 DictionaryBuilder().Set("resources", |
| 116 ListBuilder().Append("foo.js"))).Build(); | 129 ListBuilder().Append("foo.js"))).Build(); |
| 117 scoped_refptr<Extension> shared_module = | 130 scoped_refptr<Extension> shared_module = |
| 118 ExtensionBuilder().SetManifest(manifest.Pass()) | 131 ExtensionBuilder().SetManifest(manifest.Pass()) |
| 119 .AddFlags(Extension::FROM_WEBSTORE) | 132 .AddFlags(Extension::FROM_WEBSTORE) |
| 120 .SetID(id_util::GenerateId("shared_module")) | 133 .SetID(id_util::GenerateId("shared_module")) |
| 121 .Build(); | 134 .Build(); |
| 122 | 135 |
| 123 EXPECT_TRUE(InstallExtension(shared_module)); | 136 EXPECT_TRUE(InstallExtension(shared_module, false)); |
| 124 | 137 |
| 125 std::string extension_id = id_util::GenerateId("extension_id"); | 138 std::string extension_id = id_util::GenerateId("extension_id"); |
| 126 // Create and install an extension that imports our new module. | 139 // Create and install an extension that imports our new module. |
| 127 scoped_refptr<Extension> importing_extension = | 140 scoped_refptr<Extension> importing_extension = |
| 128 CreateExtensionImportingModule(shared_module->id(), extension_id); | 141 CreateExtensionImportingModule(shared_module->id(), extension_id, "1.0"); |
| 129 EXPECT_TRUE(InstallExtension(importing_extension)); | 142 EXPECT_TRUE(InstallExtension(importing_extension, false)); |
| 130 | 143 |
| 131 // Uninstall the extension that imports our module. | 144 // Uninstall the extension that imports our module. |
| 132 base::string16 error; | 145 base::string16 error; |
| 133 service_->UninstallExtension(importing_extension->id(), | 146 service()->UninstallExtension(importing_extension->id(), |
| 134 ExtensionService::UNINSTALL_REASON_FOR_TESTING, | 147 ExtensionService::UNINSTALL_REASON_FOR_TESTING, |
| 135 &error); | 148 &error); |
| 136 EXPECT_TRUE(error.empty()); | 149 EXPECT_TRUE(error.empty()); |
| 137 | 150 |
| 138 // Since the module was only referenced by that single extension, it should | 151 // Since the module was only referenced by that single extension, it should |
| 139 // have been uninstalled as a side-effect of uninstalling the extension that | 152 // have been uninstalled as a side-effect of uninstalling the extension that |
| 140 // depended upon it. | 153 // depended upon it. |
| 141 EXPECT_FALSE(registry()->GetExtensionById(shared_module->id(), | 154 EXPECT_FALSE(registry()->GetExtensionById(shared_module->id(), |
| 142 ExtensionRegistry::EVERYTHING)); | 155 ExtensionRegistry::EVERYTHING)); |
| 143 } | 156 } |
| 144 | 157 |
| 158 TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUpdate) { |
| 159 // Create two modules which export a resource, and install them. |
| 160 scoped_ptr<base::DictionaryValue> manifest_1 = |
| 161 DictionaryBuilder() |
| 162 .Set("name", "Shared Module 1") |
| 163 .Set("version", "1.0") |
| 164 .Set("manifest_version", 2) |
| 165 .Set("export", |
| 166 DictionaryBuilder().Set("resources", |
| 167 ListBuilder().Append("foo.js"))).Build(); |
| 168 scoped_refptr<Extension> shared_module_1 = |
| 169 ExtensionBuilder().SetManifest(manifest_1.Pass()) |
| 170 .AddFlags(Extension::FROM_WEBSTORE) |
| 171 .SetID(id_util::GenerateId("shared_module_1")) |
| 172 .Build(); |
| 173 EXPECT_TRUE(InstallExtension(shared_module_1, false)); |
| 174 |
| 175 scoped_ptr<base::DictionaryValue> manifest_2 = |
| 176 DictionaryBuilder() |
| 177 .Set("name", "Shared Module 2") |
| 178 .Set("version", "1.0") |
| 179 .Set("manifest_version", 2) |
| 180 .Set("export", |
| 181 DictionaryBuilder().Set("resources", |
| 182 ListBuilder().Append("foo.js"))).Build(); |
| 183 scoped_refptr<Extension> shared_module_2 = |
| 184 ExtensionBuilder().SetManifest(manifest_2.Pass()) |
| 185 .AddFlags(Extension::FROM_WEBSTORE) |
| 186 .SetID(id_util::GenerateId("shared_module_2")) |
| 187 .Build(); |
| 188 EXPECT_TRUE(InstallExtension(shared_module_2, false)); |
| 189 |
| 190 std::string extension_id = id_util::GenerateId("extension_id"); |
| 191 |
| 192 // Create and install an extension v1.0 that imports our new module 1. |
| 193 scoped_refptr<Extension> importing_extension_1 = |
| 194 CreateExtensionImportingModule(shared_module_1->id(), |
| 195 extension_id, |
| 196 "1.0"); |
| 197 EXPECT_TRUE(InstallExtension(importing_extension_1, false)); |
| 198 |
| 199 // Create and install a new version of the extension that imports our new |
| 200 // module 2. |
| 201 scoped_refptr<Extension> importing_extension_2 = |
| 202 CreateExtensionImportingModule(shared_module_2->id(), |
| 203 extension_id, |
| 204 "1.1"); |
| 205 EXPECT_TRUE(InstallExtension(importing_extension_2, true)); |
| 206 |
| 207 // Since the extension v1.1 depends the module 2 insteand module 1. |
| 208 // So the module 1 should be uninstalled. |
| 209 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(), |
| 210 ExtensionRegistry::EVERYTHING)); |
| 211 EXPECT_TRUE(registry()->GetExtensionById(shared_module_2->id(), |
| 212 ExtensionRegistry::EVERYTHING)); |
| 213 |
| 214 // Create and install a new version of the extension that does not import any |
| 215 // module. |
| 216 scoped_refptr<Extension> importing_extension_3 = |
| 217 CreateExtensionImportingModule("", extension_id, "1.2"); |
| 218 EXPECT_TRUE(InstallExtension(importing_extension_3, true)); |
| 219 |
| 220 // Since the extension v1.2 does not depend any module, so the all models |
| 221 // should have been uninstalled. |
| 222 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(), |
| 223 ExtensionRegistry::EVERYTHING)); |
| 224 EXPECT_FALSE(registry()->GetExtensionById(shared_module_2->id(), |
| 225 ExtensionRegistry::EVERYTHING)); |
| 226 |
| 227 } |
| 228 |
| 145 TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) { | 229 TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) { |
| 146 std::string whitelisted_id = id_util::GenerateId("whitelisted"); | 230 std::string whitelisted_id = id_util::GenerateId("whitelisted"); |
| 147 std::string nonwhitelisted_id = id_util::GenerateId("nonwhitelisted"); | 231 std::string nonwhitelisted_id = id_util::GenerateId("nonwhitelisted"); |
| 148 // Create a module which exports to a restricted whitelist. | 232 // Create a module which exports to a restricted whitelist. |
| 149 scoped_ptr<base::DictionaryValue> manifest = | 233 scoped_ptr<base::DictionaryValue> manifest = |
| 150 DictionaryBuilder() | 234 DictionaryBuilder() |
| 151 .Set("name", "Shared Module") | 235 .Set("name", "Shared Module") |
| 152 .Set("version", "1.0") | 236 .Set("version", "1.0") |
| 153 .Set("manifest_version", 2) | 237 .Set("manifest_version", 2) |
| 154 .Set("export", | 238 .Set("export", |
| 155 DictionaryBuilder().Set("whitelist", | 239 DictionaryBuilder().Set("whitelist", |
| 156 ListBuilder() | 240 ListBuilder() |
| 157 .Append(whitelisted_id)) | 241 .Append(whitelisted_id)) |
| 158 .Set("resources", | 242 .Set("resources", |
| 159 ListBuilder().Append("*"))).Build(); | 243 ListBuilder().Append("*"))).Build(); |
| 160 scoped_refptr<Extension> shared_module = | 244 scoped_refptr<Extension> shared_module = |
| 161 ExtensionBuilder().SetManifest(manifest.Pass()) | 245 ExtensionBuilder().SetManifest(manifest.Pass()) |
| 162 .AddFlags(Extension::FROM_WEBSTORE) | 246 .AddFlags(Extension::FROM_WEBSTORE) |
| 163 .SetID(id_util::GenerateId("shared_module")) | 247 .SetID(id_util::GenerateId("shared_module")) |
| 164 .Build(); | 248 .Build(); |
| 165 | 249 |
| 166 EXPECT_TRUE(InstallExtension(shared_module)); | 250 EXPECT_TRUE(InstallExtension(shared_module, false)); |
| 167 | 251 |
| 168 // Create and install an extension with the whitelisted ID. | 252 // Create and install an extension with the whitelisted ID. |
| 169 scoped_refptr<Extension> whitelisted_extension = | 253 scoped_refptr<Extension> whitelisted_extension = |
| 170 CreateExtensionImportingModule(shared_module->id(), whitelisted_id); | 254 CreateExtensionImportingModule(shared_module->id(), |
| 171 EXPECT_TRUE(InstallExtension(whitelisted_extension)); | 255 whitelisted_id, |
| 256 "1.0"); |
| 257 EXPECT_TRUE(InstallExtension(whitelisted_extension, false)); |
| 172 | 258 |
| 173 // Try to install an extension with an ID that is not whitelisted. | 259 // Try to install an extension with an ID that is not whitelisted. |
| 174 scoped_refptr<Extension> nonwhitelisted_extension = | 260 scoped_refptr<Extension> nonwhitelisted_extension = |
| 175 CreateExtensionImportingModule(shared_module->id(), nonwhitelisted_id); | 261 CreateExtensionImportingModule(shared_module->id(), |
| 176 EXPECT_FALSE(InstallExtension(nonwhitelisted_extension)); | 262 nonwhitelisted_id, |
| 263 "1.0"); |
| 264 EXPECT_FALSE(InstallExtension(nonwhitelisted_extension, false)); |
| 177 } | 265 } |
| 178 | 266 |
| 179 } // namespace extensions | 267 } // namespace extensions |
| OLD | NEW |