// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "base/values.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service_unittest.h" #include "chrome/browser/extensions/pending_extension_manager.h" #include "chrome/browser/extensions/shared_module_service.h" #include "chrome/common/extensions/features/feature_channel.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension_builder.h" #include "extensions/common/id_util.h" #include "extensions/common/value_builder.h" #include "sync/api/string_ordinal.h" namespace extensions { namespace { // Return an extension with |id| which imports a module with the given // |import_id|. scoped_refptr CreateExtensionImportingModule( const std::string& import_id, const std::string& id) { scoped_ptr manifest = DictionaryBuilder() .Set("name", "Has Dependent Modules") .Set("version", "1.0") .Set("manifest_version", 2) .Set("import", ListBuilder().Append(DictionaryBuilder().Set("id", import_id))) .Build(); return ExtensionBuilder().SetManifest(manifest.Pass()) .AddFlags(Extension::FROM_WEBSTORE) .SetID(id) .Build(); } } // namespace class SharedModuleServiceUnitTest : public ExtensionServiceTestBase { public: SharedModuleServiceUnitTest() : // The "export" key is open for dev-channel only, but unit tests // run as stable channel on the official Windows build. current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {} protected: virtual void SetUp() OVERRIDE; // Install an extension and notify the ExtensionService. testing::AssertionResult InstallExtension(const Extension* extension); ScopedCurrentChannel current_channel_; }; void SharedModuleServiceUnitTest::SetUp() { ExtensionServiceTestBase::SetUp(); InitializeGoodInstalledExtensionService(); service_->Init(); } testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension( const Extension* extension) { // Verify the extension is not already installed. if (registry_->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED)) { return testing::AssertionFailure() << "Extension already installed."; } // Notify the service that the extension is installed. This adds it to the // registry, notifies interested parties, etc. service_->OnExtensionInstalled(extension, syncer::StringOrdinal(), false, // No requirement errors. NOT_BLACKLISTED, false, // Not ephemeral. false); // Don't wait for idle. // Verify that the extension is now installed. if (!registry_->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED)) { return testing::AssertionFailure() << "Could not install extension."; } return testing::AssertionSuccess(); } TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) { // Create an extension that has a dependency. std::string import_id = id_util::GenerateId("id"); std::string extension_id = id_util::GenerateId("extension_id"); scoped_refptr extension = CreateExtensionImportingModule(import_id, extension_id); PendingExtensionManager* pending_extension_manager = service_->pending_extension_manager(); // Verify that we don't currently want to install the imported module. EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id)); // Try to satisfy imports for the extension. This should queue the imported // module's installation. service_->shared_module_service()->SatisfyImports(extension); EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id)); } TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUninstall) { // Create a module which exports a resource, and install it. scoped_ptr manifest = DictionaryBuilder() .Set("name", "Shared Module") .Set("version", "1.0") .Set("manifest_version", 2) .Set("export", DictionaryBuilder().Set("resources", ListBuilder().Append("foo.js"))).Build(); scoped_refptr shared_module = ExtensionBuilder().SetManifest(manifest.Pass()) .AddFlags(Extension::FROM_WEBSTORE) .SetID(id_util::GenerateId("shared_module")) .Build(); EXPECT_TRUE(InstallExtension(shared_module)); std::string extension_id = id_util::GenerateId("extension_id"); // Create and install an extension that imports our new module. scoped_refptr importing_extension = CreateExtensionImportingModule(shared_module->id(), extension_id); EXPECT_TRUE(InstallExtension(importing_extension)); // Uninstall the extension that imports our module. base::string16 error; service_->UninstallExtension(importing_extension->id(), false, // Not external uninstall. &error); EXPECT_TRUE(error.empty()); // Since the module was only referenced by that single extension, it should // have been uninstalled as a side-effect of uninstalling the extension that // depended upon it. EXPECT_FALSE(registry_->GetExtensionById(shared_module->id(), ExtensionRegistry::EVERYTHING)); } TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) { std::string whitelisted_id = id_util::GenerateId("whitelisted"); std::string nonwhitelisted_id = id_util::GenerateId("nonwhitelisted"); // Create a module which exports to a restricted whitelist. scoped_ptr manifest = DictionaryBuilder() .Set("name", "Shared Module") .Set("version", "1.0") .Set("manifest_version", 2) .Set("export", DictionaryBuilder().Set("whitelist", ListBuilder() .Append(whitelisted_id)) .Set("resources", ListBuilder().Append("*"))).Build(); scoped_refptr shared_module = ExtensionBuilder().SetManifest(manifest.Pass()) .AddFlags(Extension::FROM_WEBSTORE) .SetID(id_util::GenerateId("shared_module")) .Build(); EXPECT_TRUE(InstallExtension(shared_module)); // Create and install an extension with the whitelisted ID. scoped_refptr whitelisted_extension = CreateExtensionImportingModule(shared_module->id(), whitelisted_id); EXPECT_TRUE(InstallExtension(whitelisted_extension)); // Try to install an extension with an ID that is not whitelisted. scoped_refptr nonwhitelisted_extension = CreateExtensionImportingModule(shared_module->id(), nonwhitelisted_id); EXPECT_FALSE(InstallExtension(nonwhitelisted_extension)); } } // namespace extensions