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 3 // found in the LICENSE file. 4 5 #include "base/files/file_path.h" 6 #include "base/macros.h" 7 #include "base/strings/stringprintf.h" 8 #include "chrome/browser/extensions/active_script_controller.h" 9 #include "chrome/browser/extensions/extension_action.h" 10 #include "chrome/browser/extensions/extension_browsertest.h" 11 #include "chrome/browser/extensions/extension_test_message_listener.h" 12 #include "chrome/browser/extensions/location_bar_controller.h" 13 #include "chrome/browser/extensions/tab_helper.h" 14 #include "chrome/browser/extensions/test_extension_dir.h" 15 #include "chrome/browser/ui/browser.h" 16 #include "chrome/browser/ui/tabs/tab_strip_model.h" 17 #include "chrome/test/base/ui_test_utils.h" 18 #include "content/public/test/browser_test_utils.h" 19 #include "extensions/common/feature_switch.h" 20 #include "extensions/common/switches.h" 21 #include "net/test/embedded_test_server/embedded_test_server.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 24 namespace extensions { 25 26 namespace { 27 28 const char kAllHostsScheme[] = "*://*/*"; 29 const char kExplicitHostsScheme[] = "http://127.0.0.1/*"; 30 const char kBackgroundScript[] = 31 "\"background\": {\"scripts\": [\"script.js\"]}"; 32 const char kBackgroundScriptSource[] = 33 "var listener = function(tabId) {\n" 34 " chrome.tabs.onUpdated.removeListener(listener);\n" 35 " chrome.tabs.executeScript(tabId, {\n" 36 " code: \"chrome.test.sendMessage('inject succeeded');\"\n" 37 " });" 38 " chrome.test.sendMessage('inject attempted');\n" 39 "};\n" 40 "chrome.tabs.onUpdated.addListener(listener);"; 41 const char kContentScriptSource[] = 42 "chrome.test.sendMessage('inject succeeded');"; 43 44 const char kInjectAttempted[] = "inject attempted"; 45 const char kInjectSucceeded[] = "inject succeeded"; 46 47 enum InjectionType { 48 CONTENT_SCRIPT, 49 EXECUTE_SCRIPT 50 }; 51 52 enum HostType { 53 ALL_HOSTS, 54 EXPLICIT_HOSTS 55 }; 56 57 enum RequiresConsent { 58 REQUIRES_CONSENT, 59 DOES_NOT_REQUIRE_CONSENT 60 }; 61 62 } // namespace 63 64 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest { 65 public: 66 ActiveScriptControllerBrowserTest() {} 67 68 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE; 69 virtual void CleanUpOnMainThread() OVERRIDE; 70 71 // Returns an extension with the given |host_type| and |injection_type|. If 72 // one already exists, the existing extension will be returned. Othewrwise, 73 // one will be created. 74 // This could potentially return NULL if LoadExtension() fails. 75 const Extension* CreateExtension(HostType host_type, 76 InjectionType injection_type); 77 78 private: 79 ScopedVector<TestExtensionDir> test_extension_dirs_; 80 std::vector<const Extension*> extensions_; 81 }; 82 83 void ActiveScriptControllerBrowserTest::SetUpCommandLine( 84 base::CommandLine* command_line) { 85 ExtensionBrowserTest::SetUpCommandLine(command_line); 86 // We append the actual switch to the commandline because it needs to be 87 // passed over to the renderer, which a FeatureSwitch::ScopedOverride will 88 // not do. 89 command_line->AppendSwitch(switches::kEnableScriptsRequireAction); 90 } 91 92 void ActiveScriptControllerBrowserTest::CleanUpOnMainThread() { 93 test_extension_dirs_.clear(); 94 } 95 96 const Extension* ActiveScriptControllerBrowserTest::CreateExtension( 97 HostType host_type, InjectionType injection_type) { 98 std::string name = 99 base::StringPrintf( 100 "%s %s", 101 injection_type == CONTENT_SCRIPT ? 102 "content_script" : "execute_script", 103 host_type == ALL_HOSTS ? "all_hosts" : "explicit_hosts"); 104 105 const char* permission_scheme = 106 host_type == ALL_HOSTS ? kAllHostsScheme : kExplicitHostsScheme; 107 108 std::string permissions = base::StringPrintf( 109 "\"permissions\": [\"tabs\", \"%s\"]", permission_scheme); 110 111 std::string scripts; 112 std::string script_source; 113 if (injection_type == CONTENT_SCRIPT) { 114 scripts = base::StringPrintf( 115 "\"content_scripts\": [" 116 " {" 117 " \"matches\": [\"%s\"]," 118 " \"js\": [\"script.js\"]," 119 " \"run_at\": \"document_start\"" 120 " }" 121 "]", 122 permission_scheme); 123 } else { 124 scripts = kBackgroundScript; 125 } 126 127 std::string manifest = base::StringPrintf( 128 "{" 129 " \"name\": \"%s\"," 130 " \"version\": \"1.0\"," 131 " \"manifest_version\": 2," 132 " %s," 133 " %s" 134 "}", 135 name.c_str(), 136 permissions.c_str(), 137 scripts.c_str()); 138 139 scoped_ptr<TestExtensionDir> dir(new TestExtensionDir); 140 dir->WriteManifest(manifest); 141 dir->WriteFile(FILE_PATH_LITERAL("script.js"), 142 injection_type == CONTENT_SCRIPT ? kContentScriptSource : 143 kBackgroundScriptSource); 144 145 const Extension* extension = LoadExtension(dir->unpacked_path()); 146 if (extension) { 147 test_extension_dirs_.push_back(dir.release()); 148 extensions_.push_back(extension); 149 } 150 151 // If extension is NULL here, it will be caught later in the test. 152 return extension; 153 } 154 155 class ActiveScriptTester { 156 public: 157 ActiveScriptTester(const std::string& name, 158 const Extension* extension, 159 Browser* browser, 160 RequiresConsent requires_consent, 161 InjectionType type); 162 ~ActiveScriptTester(); 163 164 testing::AssertionResult Verify(); 165 166 private: 167 // Returns the location bar controller, or NULL if one does not exist. 168 LocationBarController* GetLocationBarController(); 169 170 // Returns the active script controller, or NULL if one does not exist. 171 ActiveScriptController* GetActiveScriptController(); 172 173 // Get the ExtensionAction for this extension, or NULL if one does not exist. 174 ExtensionAction* GetAction(); 175 176 // The name of the extension, and also the message it sends. 177 std::string name_; 178 179 // The extension associated with this tester. 180 const Extension* extension_; 181 182 // The browser the tester is running in. 183 Browser* browser_; 184 185 // Whether or not the extension has permission to run the script without 186 // asking the user. 187 RequiresConsent requires_consent_; 188 189 // The type of injection this tester uses. 190 InjectionType type_; 191 192 // All of these extensions should inject a script (either through content 193 // scripts or through chrome.tabs.executeScript()) that sends a message with 194 // the |kInjectSucceeded| message. 195 linked_ptr<ExtensionTestMessageListener> inject_attempt_listener_; 196 197 // After trying to inject the script, extensions sending the script via 198 // chrome.tabs.executeScript() send a |kInjectAttempted| message. 199 linked_ptr<ExtensionTestMessageListener> inject_success_listener_; 200 }; 201 202 ActiveScriptTester::ActiveScriptTester(const std::string& name, 203 const Extension* extension, 204 Browser* browser, 205 RequiresConsent requires_consent, 206 InjectionType type) 207 : name_(name), 208 extension_(extension), 209 browser_(browser), 210 requires_consent_(requires_consent), 211 type_(type), 212 inject_attempt_listener_( 213 new ExtensionTestMessageListener(kInjectAttempted, 214 false /* won't reply */)), 215 inject_success_listener_( 216 new ExtensionTestMessageListener(kInjectSucceeded, 217 false /* won't reply */)) { 218 inject_attempt_listener_->set_extension_id(extension->id()); 219 inject_success_listener_->set_extension_id(extension->id()); 220 } 221 222 ActiveScriptTester::~ActiveScriptTester() { 223 } 224 225 testing::AssertionResult ActiveScriptTester::Verify() { 226 if (!extension_) 227 return testing::AssertionFailure() << "Could not load extension: " << name_; 228 229 // If it's not a content script, the Extension lets us know when it has 230 // attempted to inject the script. 231 // This means there is a potential for a race condition with content scripts; 232 // however, since they are all injected at document_start, this shouldn't be 233 // a problem. If these tests start failing, though, that might be it. 234 if (type_ == EXECUTE_SCRIPT) 235 inject_attempt_listener_->WaitUntilSatisfied(); 236 237 // Make sure all running tasks are complete. 238 content::RunAllPendingInMessageLoop(); 239 240 LocationBarController* location_bar_controller = GetLocationBarController(); 241 if (!location_bar_controller) { 242 return testing::AssertionFailure() 243 << "Could not find location bar controller"; 244 } 245 246 ActiveScriptController* controller = 247 location_bar_controller->active_script_controller(); 248 if (!controller) 249 return testing::AssertionFailure() << "Could not find controller."; 250 251 ExtensionAction* action = GetAction(); 252 bool has_action = action != NULL; 253 254 // An extension should have an action displayed iff it requires user consent. 255 if ((requires_consent_ == REQUIRES_CONSENT && !has_action) || 256 (requires_consent_ == DOES_NOT_REQUIRE_CONSENT && has_action)) { 257 return testing::AssertionFailure() 258 << "Improper action status for " << name_ << ": expected " 259 << (requires_consent_ == REQUIRES_CONSENT) << ", found " << has_action; 260 } 261 262 // If the extension has permission, we should be able to simply wait for it 263 // to execute. 264 if (requires_consent_ == DOES_NOT_REQUIRE_CONSENT) { 265 inject_success_listener_->WaitUntilSatisfied(); 266 return testing::AssertionSuccess(); 267 } 268 269 // Otherwise, we don't have permission, and have to grant it. Ensure the 270 // script has *not* already executed. 271 if (inject_success_listener_->was_satisfied()) { 272 return testing::AssertionFailure() << 273 name_ << "'s script ran without permission."; 274 } 275 276 // If we reach this point, we should always have an action. 277 DCHECK(action); 278 279 // Grant permission by clicking on the extension action. 280 location_bar_controller->OnClicked(action); 281 282 // Now, the extension should be able to inject the script. 283 inject_success_listener_->WaitUntilSatisfied(); 284 285 // The Action should have disappeared. 286 has_action = GetAction() != NULL; 287 if (has_action) { 288 return testing::AssertionFailure() 289 << "Extension " << name_ << " has lingering action."; 290 } 291 292 return testing::AssertionSuccess(); 293 } 294 295 LocationBarController* ActiveScriptTester::GetLocationBarController() { 296 content::WebContents* web_contents = 297 browser_ ? browser_->tab_strip_model()->GetActiveWebContents() : NULL; 298 299 if (!web_contents) 300 return NULL; 301 302 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); 303 return tab_helper ? tab_helper->location_bar_controller() : NULL; 304 } 305 306 ActiveScriptController* ActiveScriptTester::GetActiveScriptController() { 307 LocationBarController* location_bar_controller = GetLocationBarController(); 308 return location_bar_controller ? 309 location_bar_controller->active_script_controller() : NULL; 310 } 311 312 ExtensionAction* ActiveScriptTester::GetAction() { 313 ActiveScriptController* controller = GetActiveScriptController(); 314 return controller ? controller->GetActionForExtension(extension_) : NULL; 315 } 316 317 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, 318 ActiveScriptsAreDisplayedAndDelayExecution) { 319 base::FilePath active_script_path = 320 test_data_dir_.AppendASCII("active_script"); 321 322 const char* kExtensionNames[] = { 323 "inject_scripts_all_hosts", 324 "inject_scripts_explicit_hosts", 325 "content_scripts_all_hosts", 326 "content_scripts_explicit_hosts" 327 }; 328 329 // First, we load up three extensions: 330 // - An extension that injects scripts into all hosts, 331 // - An extension that injects scripts into explicit hosts, 332 // - An extension with a content script that runs on all hosts, 333 // - An extension with a content script that runs on explicit hosts. 334 // The extensions that operate on explicit hosts have permission; the ones 335 // that request all hosts require user consent. 336 ActiveScriptTester testers[] = { 337 ActiveScriptTester( 338 kExtensionNames[0], 339 CreateExtension(ALL_HOSTS, EXECUTE_SCRIPT), 340 browser(), 341 REQUIRES_CONSENT, 342 EXECUTE_SCRIPT), 343 ActiveScriptTester( 344 kExtensionNames[1], 345 CreateExtension(EXPLICIT_HOSTS, EXECUTE_SCRIPT), 346 browser(), 347 DOES_NOT_REQUIRE_CONSENT, 348 EXECUTE_SCRIPT), 349 ActiveScriptTester( 350 kExtensionNames[2], 351 CreateExtension(ALL_HOSTS, CONTENT_SCRIPT), 352 browser(), 353 REQUIRES_CONSENT, 354 CONTENT_SCRIPT), 355 ActiveScriptTester( 356 kExtensionNames[3], 357 CreateExtension(EXPLICIT_HOSTS, CONTENT_SCRIPT), 358 browser(), 359 DOES_NOT_REQUIRE_CONSENT, 360 CONTENT_SCRIPT), 361 }; 362 363 // Navigate to an URL (which matches the explicit host specified in the 364 // extension content_scripts_explicit_hosts). All four extensions should 365 // inject the script. 366 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 367 ui_test_utils::NavigateToURL( 368 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); 369 370 for (size_t i = 0u; i < arraysize(testers); ++i) 371 EXPECT_TRUE(testers[i].Verify()) << kExtensionNames[i]; 372 } 373 374 // Test that removing an extension with pending injections a) removes the 375 // pending injections for that extension, and b) does not affect pending 376 // injections for other extensions. 377 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest, 378 RemoveExtensionWithPendingInjections) { 379 // Load up two extensions, each with content scripts. 380 const Extension* extension1 = CreateExtension(ALL_HOSTS, CONTENT_SCRIPT); 381 ASSERT_TRUE(extension1); 382 const Extension* extension2 = CreateExtension(ALL_HOSTS, CONTENT_SCRIPT); 383 ASSERT_TRUE(extension2); 384 385 ASSERT_NE(extension1->id(), extension2->id()); 386 387 content::WebContents* web_contents = 388 browser()->tab_strip_model()->GetActiveWebContents(); 389 ASSERT_TRUE(web_contents); 390 ActiveScriptController* active_script_controller = 391 ActiveScriptController::GetForWebContents(web_contents); 392 ASSERT_TRUE(active_script_controller); 393 394 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 395 ui_test_utils::NavigateToURL( 396 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); 397 398 // Both extensions should have pending requests. 399 EXPECT_TRUE(active_script_controller->GetActionForExtension(extension1)); 400 EXPECT_TRUE(active_script_controller->GetActionForExtension(extension2)); 401 402 // Unload one of the extensions. 403 UnloadExtension(extension2->id()); 404 405 // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs 406 // are sent synchronously, the renderer will be notified of the extension 407 // being unloaded before the script is executed, and, since ExecuteScript() is 408 // synchronous, the renderer is guaranteed to be done updating scripts. 409 EXPECT_TRUE(content::ExecuteScript(web_contents, "1 == 1;")); 410 411 // We should have pending requests for extension1, but not the removed 412 // extension2. 413 EXPECT_TRUE(active_script_controller->GetActionForExtension(extension1)); 414 EXPECT_FALSE(active_script_controller->GetActionForExtension(extension2)); 415 416 // We should still be able to run the request for extension1. 417 ExtensionTestMessageListener inject_success_listener( 418 new ExtensionTestMessageListener(kInjectSucceeded, 419 false /* won't reply */)); 420 inject_success_listener.set_extension_id(extension1->id()); 421 active_script_controller->OnClicked(extension1); 422 inject_success_listener.WaitUntilSatisfied(); 423 } 424 425 // A version of the test with the flag off, in order to test that everything 426 // still works as expected. 427 class FlagOffActiveScriptControllerBrowserTest 428 : public ActiveScriptControllerBrowserTest { 429 private: 430 // Simply don't append the flag. 431 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { 432 ExtensionBrowserTest::SetUpCommandLine(command_line); 433 } 434 }; 435 436 IN_PROC_BROWSER_TEST_F(FlagOffActiveScriptControllerBrowserTest, 437 ScriptsExecuteWhenFlagAbsent) { 438 const char* kExtensionNames[] = { 439 "content_scripts_all_hosts", 440 "inject_scripts_all_hosts", 441 }; 442 ActiveScriptTester testers[] = { 443 ActiveScriptTester( 444 kExtensionNames[0], 445 CreateExtension(ALL_HOSTS, CONTENT_SCRIPT), 446 browser(), 447 DOES_NOT_REQUIRE_CONSENT, 448 CONTENT_SCRIPT), 449 ActiveScriptTester( 450 kExtensionNames[1], 451 CreateExtension(ALL_HOSTS, EXECUTE_SCRIPT), 452 browser(), 453 DOES_NOT_REQUIRE_CONSENT, 454 EXECUTE_SCRIPT), 455 }; 456 457 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 458 ui_test_utils::NavigateToURL( 459 browser(), embedded_test_server()->GetURL("/extensions/test_file.html")); 460 461 for (size_t i = 0u; i < arraysize(testers); ++i) 462 EXPECT_TRUE(testers[i].Verify()) << kExtensionNames[i]; 463 } 464 465 } // namespace extensions 466