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