1 // Copyright (c) 2012 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/command_line.h" 6 #include "base/json/json_file_value_serializer.h" 7 #include "base/message_loop/message_loop.h" 8 #include "base/path_service.h" 9 #include "base/strings/string_util.h" 10 #include "chrome/browser/extensions/extension_service.h" 11 #include "chrome/browser/extensions/extension_util.h" 12 #include "chrome/browser/extensions/permissions_updater.h" 13 #include "chrome/browser/extensions/test_extension_system.h" 14 #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" 15 #include "chrome/common/chrome_paths.h" 16 #include "chrome/test/base/testing_profile.h" 17 #include "components/crx_file/id_util.h" 18 #include "content/public/test/test_browser_thread.h" 19 #include "extensions/browser/extension_registry.h" 20 #include "extensions/browser/management_policy.h" 21 #include "extensions/common/constants.h" 22 #include "extensions/common/extension.h" 23 #include "extensions/common/extension_builder.h" 24 #include "extensions/common/feature_switch.h" 25 #include "extensions/common/value_builder.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 #if defined(OS_CHROMEOS) 29 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h" 30 #include "chrome/browser/chromeos/settings/cros_settings.h" 31 #include "chrome/browser/chromeos/settings/device_settings_service.h" 32 #endif 33 34 namespace extensions { 35 36 namespace { 37 const char kAllHostsPermission[] = "*://*/*"; 38 } 39 40 class ExtensionUITest : public testing::Test { 41 public: 42 ExtensionUITest() 43 : ui_thread_(content::BrowserThread::UI, &message_loop_), 44 file_thread_(content::BrowserThread::FILE, &message_loop_) {} 45 46 protected: 47 virtual void SetUp() OVERRIDE { 48 // Create an ExtensionService and ManagementPolicy to inject into the 49 // ExtensionSettingsHandler. 50 profile_.reset(new TestingProfile()); 51 TestExtensionSystem* system = 52 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile_.get())); 53 extension_service_ = system->CreateExtensionService( 54 CommandLine::ForCurrentProcess(), base::FilePath(), false); 55 management_policy_ = system->management_policy(); 56 57 handler_.reset(new ExtensionSettingsHandler(extension_service_, 58 management_policy_)); 59 } 60 61 virtual void TearDown() OVERRIDE { 62 handler_.reset(); 63 profile_.reset(); 64 // Execute any pending deletion tasks. 65 message_loop_.RunUntilIdle(); 66 } 67 68 static base::DictionaryValue* DeserializeJSONTestData( 69 const base::FilePath& path, 70 std::string *error) { 71 base::Value* value; 72 73 JSONFileValueSerializer serializer(path); 74 value = serializer.Deserialize(NULL, error); 75 76 return static_cast<base::DictionaryValue*>(value); 77 } 78 79 const scoped_refptr<const Extension> CreateExtension( 80 const std::string& name, 81 ListBuilder& permissions) { 82 const std::string kId = crx_file::id_util::GenerateId(name); 83 scoped_refptr<const Extension> extension = 84 ExtensionBuilder().SetManifest( 85 DictionaryBuilder() 86 .Set("name", name) 87 .Set("description", "an extension") 88 .Set("manifest_version", 2) 89 .Set("version", "1.0.0") 90 .Set("permissions", permissions)) 91 .SetLocation(Manifest::INTERNAL) 92 .SetID(kId) 93 .Build(); 94 95 ExtensionRegistry::Get(profile())->AddEnabled(extension); 96 PermissionsUpdater(profile()).InitializePermissions(extension.get()); 97 return extension; 98 } 99 100 base::DictionaryValue* CreateExtensionDetailViewFromPath( 101 const base::FilePath& extension_path, 102 const std::vector<ExtensionPage>& pages, 103 Manifest::Location location) { 104 std::string error; 105 106 base::FilePath manifest_path = extension_path.Append(kManifestFilename); 107 scoped_ptr<base::DictionaryValue> extension_data(DeserializeJSONTestData( 108 manifest_path, &error)); 109 EXPECT_EQ("", error); 110 111 scoped_refptr<Extension> extension(Extension::Create( 112 extension_path, location, *extension_data, Extension::REQUIRE_KEY, 113 &error)); 114 EXPECT_TRUE(extension.get()); 115 EXPECT_EQ("", error); 116 117 return handler_->CreateExtensionDetailValue(extension.get(), pages, NULL); 118 } 119 120 void CompareExpectedAndActualOutput( 121 const base::FilePath& extension_path, 122 const std::vector<ExtensionPage>& pages, 123 const base::FilePath& expected_output_path) { 124 std::string error; 125 126 scoped_ptr<base::DictionaryValue> expected_output_data( 127 DeserializeJSONTestData(expected_output_path, &error)); 128 EXPECT_EQ("", error); 129 130 // Produce test output. 131 scoped_ptr<base::DictionaryValue> actual_output_data( 132 CreateExtensionDetailViewFromPath( 133 extension_path, pages, Manifest::INVALID_LOCATION)); 134 135 // Compare the outputs. 136 // Ignore unknown fields in the actual output data. 137 std::string paths_details = " - expected (" + 138 expected_output_path.MaybeAsASCII() + ") vs. actual (" + 139 extension_path.MaybeAsASCII() + ")"; 140 for (base::DictionaryValue::Iterator field(*expected_output_data); 141 !field.IsAtEnd(); field.Advance()) { 142 const base::Value* expected_value = &field.value(); 143 base::Value* actual_value = NULL; 144 EXPECT_TRUE(actual_output_data->Get(field.key(), &actual_value)) << 145 field.key() + " is missing" + paths_details; 146 EXPECT_TRUE(expected_value->Equals(actual_value)) << field.key() + 147 paths_details; 148 } 149 } 150 151 Profile* profile() { return profile_.get(); } 152 ExtensionSettingsHandler* handler() { return handler_.get(); } 153 154 base::MessageLoop message_loop_; 155 content::TestBrowserThread ui_thread_; 156 content::TestBrowserThread file_thread_; 157 scoped_ptr<TestingProfile> profile_; 158 ExtensionService* extension_service_; 159 ManagementPolicy* management_policy_; 160 scoped_ptr<ExtensionSettingsHandler> handler_; 161 162 #if defined OS_CHROMEOS 163 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 164 chromeos::ScopedTestCrosSettings test_cros_settings_; 165 chromeos::ScopedTestUserManager test_user_manager_; 166 #endif 167 }; 168 169 TEST_F(ExtensionUITest, GenerateExtensionsJSONData) { 170 base::FilePath data_test_dir_path, extension_path, expected_output_path; 171 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); 172 173 // Test Extension1 174 extension_path = data_test_dir_path.AppendASCII("extensions") 175 .AppendASCII("good") 176 .AppendASCII("Extensions") 177 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") 178 .AppendASCII("1.0.0.0"); 179 180 std::vector<ExtensionPage> pages; 181 pages.push_back(ExtensionPage( 182 GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/bar.html"), 183 42, 88, false, false)); 184 pages.push_back(ExtensionPage( 185 GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/dog.html"), 186 0, 0, false, false)); 187 188 expected_output_path = data_test_dir_path.AppendASCII("extensions") 189 .AppendASCII("ui") 190 .AppendASCII("create_extension_detail_value_expected_output") 191 .AppendASCII("good-extension1.json"); 192 193 CompareExpectedAndActualOutput(extension_path, pages, expected_output_path); 194 195 #if !defined(OS_CHROMEOS) 196 // Test Extension2 197 extension_path = data_test_dir_path.AppendASCII("extensions") 198 .AppendASCII("good") 199 .AppendASCII("Extensions") 200 .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk") 201 .AppendASCII("2"); 202 203 expected_output_path = data_test_dir_path.AppendASCII("extensions") 204 .AppendASCII("ui") 205 .AppendASCII("create_extension_detail_value_expected_output") 206 .AppendASCII("good-extension2.json"); 207 208 // It's OK to have duplicate URLs, so long as the IDs are different. 209 pages[1].url = pages[0].url; 210 211 CompareExpectedAndActualOutput(extension_path, pages, expected_output_path); 212 #endif 213 214 // Test Extension3 215 extension_path = data_test_dir_path.AppendASCII("extensions") 216 .AppendASCII("good") 217 .AppendASCII("Extensions") 218 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa") 219 .AppendASCII("1.0"); 220 221 expected_output_path = data_test_dir_path.AppendASCII("extensions") 222 .AppendASCII("ui") 223 .AppendASCII("create_extension_detail_value_expected_output") 224 .AppendASCII("good-extension3.json"); 225 226 pages.clear(); 227 228 CompareExpectedAndActualOutput(extension_path, pages, expected_output_path); 229 } 230 231 // Test that using Manifest::UNPACKED for the extension location triggers the 232 // correct values in the details, including location, order, and allow_reload. 233 TEST_F(ExtensionUITest, LocationLoadPropagation) { 234 base::FilePath data_test_dir_path, extension_path; 235 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); 236 237 extension_path = data_test_dir_path.AppendASCII("extensions") 238 .AppendASCII("good") 239 .AppendASCII("Extensions") 240 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") 241 .AppendASCII("1.0.0.0"); 242 243 std::vector<ExtensionPage> pages; 244 245 scoped_ptr<base::DictionaryValue> extension_details( 246 CreateExtensionDetailViewFromPath( 247 extension_path, pages, Manifest::UNPACKED)); 248 249 bool ui_allow_reload = false; 250 bool ui_is_unpacked = false; 251 base::FilePath::StringType ui_path; 252 253 EXPECT_TRUE(extension_details->GetBoolean("allow_reload", &ui_allow_reload)); 254 EXPECT_TRUE(extension_details->GetBoolean("isUnpacked", &ui_is_unpacked)); 255 EXPECT_TRUE(extension_details->GetString("path", &ui_path)); 256 EXPECT_EQ(true, ui_allow_reload); 257 EXPECT_EQ(true, ui_is_unpacked); 258 EXPECT_EQ(extension_path, base::FilePath(ui_path)); 259 } 260 261 // Test that using Manifest::EXTERNAL_PREF for the extension location triggers 262 // the correct values in the details, including location, order, and 263 // allow_reload. Contrast to Manifest::UNPACKED, which has somewhat different 264 // values. 265 TEST_F(ExtensionUITest, LocationExternalPrefPropagation) { 266 base::FilePath data_test_dir_path, extension_path; 267 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); 268 269 extension_path = data_test_dir_path.AppendASCII("extensions") 270 .AppendASCII("good") 271 .AppendASCII("Extensions") 272 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") 273 .AppendASCII("1.0.0.0"); 274 275 std::vector<ExtensionPage> pages; 276 277 scoped_ptr<base::DictionaryValue> extension_details( 278 CreateExtensionDetailViewFromPath( 279 extension_path, pages, Manifest::EXTERNAL_PREF)); 280 281 bool ui_allow_reload = true; 282 bool ui_is_unpacked = true; 283 base::FilePath::StringType ui_path; 284 285 EXPECT_TRUE(extension_details->GetBoolean("allow_reload", &ui_allow_reload)); 286 EXPECT_TRUE(extension_details->GetBoolean("isUnpacked", &ui_is_unpacked)); 287 EXPECT_FALSE(extension_details->GetString("path", &ui_path)); 288 EXPECT_FALSE(ui_allow_reload); 289 EXPECT_FALSE(ui_is_unpacked); 290 } 291 292 // Test that the extension path is correctly propagated into the extension 293 // details. 294 TEST_F(ExtensionUITest, PathPropagation) { 295 base::FilePath data_test_dir_path, extension_path; 296 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path)); 297 298 extension_path = data_test_dir_path.AppendASCII("extensions") 299 .AppendASCII("good") 300 .AppendASCII("Extensions") 301 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") 302 .AppendASCII("1.0.0.0"); 303 304 std::vector<ExtensionPage> pages; 305 306 scoped_ptr<base::DictionaryValue> extension_details( 307 CreateExtensionDetailViewFromPath( 308 extension_path, pages, Manifest::UNPACKED)); 309 310 base::FilePath::StringType ui_path; 311 312 EXPECT_TRUE(extension_details->GetString("path", &ui_path)); 313 EXPECT_EQ(extension_path, base::FilePath(ui_path)); 314 } 315 316 // Test that the all_urls checkbox only shows up for extensions that want all 317 // urls, and only when the switch is on. 318 TEST_F(ExtensionUITest, ExtensionUIAllUrlsCheckbox) { 319 // Start with the switch enabled. 320 scoped_ptr<FeatureSwitch::ScopedOverride> enable_scripts_switch( 321 new FeatureSwitch::ScopedOverride( 322 FeatureSwitch::scripts_require_action(), true)); 323 // Two extensions - one with all urls, one without. 324 scoped_refptr<const Extension> all_urls_extension = CreateExtension( 325 "all_urls", ListBuilder().Append(kAllHostsPermission).Pass()); 326 scoped_refptr<const Extension> no_urls_extension = 327 CreateExtension("no urls", ListBuilder().Pass()); 328 329 scoped_ptr<base::DictionaryValue> value(handler()->CreateExtensionDetailValue( 330 all_urls_extension.get(), std::vector<ExtensionPage>(), NULL)); 331 bool result = false; 332 const std::string kWantsAllUrls = "wantsAllUrls"; 333 const std::string kAllowAllUrls = "allowAllUrls"; 334 335 // The extension should want all urls, but not currently have it. 336 EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); 337 EXPECT_TRUE(result); 338 EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); 339 EXPECT_FALSE(result); 340 341 // Give the extension all urls. 342 util::SetAllowedScriptingOnAllUrls( 343 all_urls_extension->id(), profile(), true); 344 345 // Now the extension should both want and have all urls. 346 value.reset(handler()->CreateExtensionDetailValue( 347 all_urls_extension.get(), std::vector<ExtensionPage>(), NULL)); 348 EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); 349 EXPECT_TRUE(result); 350 EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); 351 EXPECT_TRUE(result); 352 353 // The other extension should neither want nor have all urls. 354 value.reset(handler()->CreateExtensionDetailValue( 355 no_urls_extension.get(), std::vector<ExtensionPage>(), NULL)); 356 EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); 357 EXPECT_FALSE(result); 358 EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); 359 EXPECT_FALSE(result); 360 361 // Turn off the switch and load another extension (so permissions are 362 // re-initialized). 363 enable_scripts_switch.reset(); 364 365 // Even though the extension has the all urls preference, the checkbox 366 // shouldn't show up with the switch off. 367 value.reset(handler()->CreateExtensionDetailValue( 368 all_urls_extension.get(), std::vector<ExtensionPage>(), NULL)); 369 EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); 370 EXPECT_FALSE(result); 371 EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); 372 EXPECT_TRUE(result); 373 374 // Load another extension with all urls (so permissions get re-init'd). 375 all_urls_extension = CreateExtension( 376 "all_urls_II", ListBuilder().Append(kAllHostsPermission).Pass()); 377 378 // Even though the extension has all_urls permission, the checkbox shouldn't 379 // show up without the switch. 380 value.reset(handler()->CreateExtensionDetailValue( 381 all_urls_extension.get(), std::vector<ExtensionPage>(), NULL)); 382 EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result)); 383 EXPECT_FALSE(result); 384 EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result)); 385 EXPECT_FALSE(result); 386 } 387 388 } // namespace extensions 389