// Copyright (c) 2012 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/command_line.h" #include "base/json/json_file_value_serializer.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/strings/string_util.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/base/testing_profile.h" #include "components/crx_file/id_util.h" #include "content/public/test/test_browser_thread.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/management_policy.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" #include "extensions/common/feature_switch.h" #include "extensions/common/value_builder.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #endif namespace extensions { namespace { const char kAllHostsPermission[] = "*://*/*"; } class ExtensionUITest : public testing::Test { public: ExtensionUITest() : ui_thread_(content::BrowserThread::UI, &message_loop_), file_thread_(content::BrowserThread::FILE, &message_loop_) {} protected: virtual void SetUp() OVERRIDE { // Create an ExtensionService and ManagementPolicy to inject into the // ExtensionSettingsHandler. profile_.reset(new TestingProfile()); TestExtensionSystem* system = static_cast(ExtensionSystem::Get(profile_.get())); extension_service_ = system->CreateExtensionService( CommandLine::ForCurrentProcess(), base::FilePath(), false); management_policy_ = system->management_policy(); handler_.reset(new ExtensionSettingsHandler(extension_service_, management_policy_)); } virtual void TearDown() OVERRIDE { handler_.reset(); profile_.reset(); // Execute any pending deletion tasks. message_loop_.RunUntilIdle(); } static base::DictionaryValue* DeserializeJSONTestData( const base::FilePath& path, std::string *error) { base::Value* value; JSONFileValueSerializer serializer(path); value = serializer.Deserialize(NULL, error); return static_cast(value); } const scoped_refptr CreateExtension( const std::string& name, ListBuilder& permissions) { const std::string kId = crx_file::id_util::GenerateId(name); scoped_refptr extension = ExtensionBuilder().SetManifest( DictionaryBuilder() .Set("name", name) .Set("description", "an extension") .Set("manifest_version", 2) .Set("version", "1.0.0") .Set("permissions", permissions)) .SetLocation(Manifest::INTERNAL) .SetID(kId) .Build(); ExtensionRegistry::Get(profile())->AddEnabled(extension); PermissionsUpdater(profile()).InitializePermissions(extension.get()); return extension; } base::DictionaryValue* CreateExtensionDetailViewFromPath( const base::FilePath& extension_path, const std::vector& pages, Manifest::Location location) { std::string error; base::FilePath manifest_path = extension_path.Append(kManifestFilename); scoped_ptr extension_data(DeserializeJSONTestData( manifest_path, &error)); EXPECT_EQ("", error); scoped_refptr extension(Extension::Create( extension_path, location, *extension_data, Extension::REQUIRE_KEY, &error)); EXPECT_TRUE(extension.get()); EXPECT_EQ("", error); return handler_->CreateExtensionDetailValue(extension.get(), pages, NULL); } void CompareExpectedAndActualOutput( const base::FilePath& extension_path, const std::vector& pages, const base::FilePath& expected_output_path) { std::string error; scoped_ptr expected_output_data( DeserializeJSONTestData(expected_output_path, &error)); EXPECT_EQ("", error); // Produce test output. scoped_ptr actual_output_data( CreateExtensionDetailViewFromPath( extension_path, pages, Manifest::INVALID_LOCATION)); // Compare the outputs. // Ignore unknown fields in the actual output data. std::string paths_details = " - expected (" + expected_output_path.MaybeAsASCII() + ") vs. actual (" + extension_path.MaybeAsASCII() + ")"; for (base::DictionaryValue::Iterator field(*expected_output_data); !field.IsAtEnd(); field.Advance()) { const base::Value* expected_value = &field.value(); base::Value* actual_value = NULL; EXPECT_TRUE(actual_output_data->Get(field.key(), &actual_value)) << field.key() + " is missing" + paths_details; EXPECT_TRUE(expected_value->Equals(actual_value)) << field.key() + paths_details; } } Profile* profile() { return profile_.get(); } ExtensionSettingsHandler* handler() { return handler_.get(); } base::MessageLoop message_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread file_thread_; scoped_ptr profile_; ExtensionService* extension_service_; ManagementPolicy* management_policy_; scoped_ptr handler_; #if defined OS_CHROMEOS chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; chromeos::ScopedTestCrosSettings test_cros_settings_; chromeos::ScopedTestUserManager test_user_manager_; #endif }; TEST_F(ExtensionUITest, GenerateExtensionsJSONData) { base::FilePath data_test_dir_path, extension_path, expected_output_path; EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); // Test Extension1 extension_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("good") .AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"); std::vector pages; pages.push_back(ExtensionPage( GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/bar.html"), 42, 88, false, false)); pages.push_back(ExtensionPage( GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/dog.html"), 0, 0, false, false)); expected_output_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("ui") .AppendASCII("create_extension_detail_value_expected_output") .AppendASCII("good-extension1.json"); CompareExpectedAndActualOutput(extension_path, pages, expected_output_path); #if !defined(OS_CHROMEOS) // Test Extension2 extension_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("good") .AppendASCII("Extensions") .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk") .AppendASCII("2"); expected_output_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("ui") .AppendASCII("create_extension_detail_value_expected_output") .AppendASCII("good-extension2.json"); // It's OK to have duplicate URLs, so long as the IDs are different. pages[1].url = pages[0].url; CompareExpectedAndActualOutput(extension_path, pages, expected_output_path); #endif // Test Extension3 extension_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("good") .AppendASCII("Extensions") .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa") .AppendASCII("1.0"); expected_output_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("ui") .AppendASCII("create_extension_detail_value_expected_output") .AppendASCII("good-extension3.json"); pages.clear(); CompareExpectedAndActualOutput(extension_path, pages, expected_output_path); } // Test that using Manifest::UNPACKED for the extension location triggers the // correct values in the details, including location, order, and allow_reload. TEST_F(ExtensionUITest, LocationLoadPropagation) { base::FilePath data_test_dir_path, extension_path; EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); extension_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("good") .AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"); std::vector pages; scoped_ptr extension_details( CreateExtensionDetailViewFromPath( extension_path, pages, Manifest::UNPACKED)); bool ui_allow_reload = false; bool ui_is_unpacked = false; base::FilePath::StringType ui_path; EXPECT_TRUE(extension_details->GetBoolean("allow_reload", &ui_allow_reload)); EXPECT_TRUE(extension_details->GetBoolean("isUnpacked", &ui_is_unpacked)); EXPECT_TRUE(extension_details->GetString("path", &ui_path)); EXPECT_EQ(true, ui_allow_reload); EXPECT_EQ(true, ui_is_unpacked); EXPECT_EQ(extension_path, base::FilePath(ui_path)); } // Test that using Manifest::EXTERNAL_PREF for the extension location triggers // the correct values in the details, including location, order, and // allow_reload. Contrast to Manifest::UNPACKED, which has somewhat different // values. TEST_F(ExtensionUITest, LocationExternalPrefPropagation) { base::FilePath data_test_dir_path, extension_path; EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); extension_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("good") .AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"); std::vector pages; scoped_ptr extension_details( CreateExtensionDetailViewFromPath( extension_path, pages, Manifest::EXTERNAL_PREF)); bool ui_allow_reload = true; bool ui_is_unpacked = true; base::FilePath::StringType ui_path; EXPECT_TRUE(extension_details->GetBoolean("allow_reload", &ui_allow_reload)); EXPECT_TRUE(extension_details->GetBoolean("isUnpacked", &ui_is_unpacked)); EXPECT_FALSE(extension_details->GetString("path", &ui_path)); EXPECT_FALSE(ui_allow_reload); EXPECT_FALSE(ui_is_unpacked); } // Test that the extension path is correctly propagated into the extension // details. TEST_F(ExtensionUITest, PathPropagation) { base::FilePath data_test_dir_path, extension_path; EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); extension_path = data_test_dir_path.AppendASCII("extensions") .AppendASCII("good") .AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"); std::vector pages; scoped_ptr extension_details( CreateExtensionDetailViewFromPath( extension_path, pages, Manifest::UNPACKED)); base::FilePath::StringType ui_path; EXPECT_TRUE(extension_details->GetString("path", &ui_path)); EXPECT_EQ(extension_path, base::FilePath(ui_path)); } // Test that the all_urls checkbox only shows up for extensions that want all // urls, and only when the switch is on. TEST_F(ExtensionUITest, ExtensionUIAllUrlsCheckbox) { // Start with the switch enabled. scoped_ptr enable_scripts_switch( new FeatureSwitch::ScopedOverride( FeatureSwitch::scripts_require_action(), true)); // Two extensions - one with all urls, one without. scoped_refptr all_urls_extension = CreateExtension( "all_urls", ListBuilder().Append(kAllHostsPermission).Pass()); scoped_refptr no_urls_extension = CreateExtension("no urls", ListBuilder().Pass()); scoped_ptr value(handler()->CreateExtensionDetailValue( all_urls_extension.get(), std::vector(), NULL)); bool result = false; const std::string kWantsAllUrls = "wantsAllUrls"; const std::string kAllowAllUrls = "allowAllUrls"; // The extension should want all urls, but not currently have it. EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); EXPECT_TRUE(result); EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); EXPECT_FALSE(result); // Give the extension all urls. util::SetAllowedScriptingOnAllUrls( all_urls_extension->id(), profile(), true); // Now the extension should both want and have all urls. value.reset(handler()->CreateExtensionDetailValue( all_urls_extension.get(), std::vector(), NULL)); EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); EXPECT_TRUE(result); EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); EXPECT_TRUE(result); // The other extension should neither want nor have all urls. value.reset(handler()->CreateExtensionDetailValue( no_urls_extension.get(), std::vector(), NULL)); EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); EXPECT_FALSE(result); EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); EXPECT_FALSE(result); // Turn off the switch and load another extension (so permissions are // re-initialized). enable_scripts_switch.reset(); // Even though the extension has the all urls preference, the checkbox // shouldn't show up with the switch off. value.reset(handler()->CreateExtensionDetailValue( all_urls_extension.get(), std::vector(), NULL)); EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); EXPECT_FALSE(result); EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); EXPECT_TRUE(result); // Load another extension with all urls (so permissions get re-init'd). all_urls_extension = CreateExtension( "all_urls_II", ListBuilder().Append(kAllHostsPermission).Pass()); // Even though the extension has all_urls permission, the checkbox shouldn't // show up without the switch. value.reset(handler()->CreateExtensionDetailValue( all_urls_extension.get(), std::vector(), NULL)); EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); EXPECT_FALSE(result); EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); EXPECT_FALSE(result); } } // namespace extensions