Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / extensions / active_script_controller_browsertest.cc
blobe28bf6627dbf11cc000c50baf38ff38e6f98ad10
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.
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_util.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/browser/extensions/test_extension_dir.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/test/base/ui_test_utils.h"
17 #include "content/public/test/browser_test_utils.h"
18 #include "extensions/common/feature_switch.h"
19 #include "extensions/common/switches.h"
20 #include "extensions/test/extension_test_message_listener.h"
21 #include "net/test/embedded_test_server/embedded_test_server.h"
22 #include "testing/gtest/include/gtest/gtest.h"
24 namespace extensions {
26 namespace {
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 "};\n"
39 "chrome.tabs.onUpdated.addListener(listener);";
40 const char kContentScriptSource[] =
41 "chrome.test.sendMessage('inject succeeded');";
43 const char kInjectSucceeded[] = "inject succeeded";
45 enum InjectionType {
46 CONTENT_SCRIPT,
47 EXECUTE_SCRIPT
50 enum HostType {
51 ALL_HOSTS,
52 EXPLICIT_HOSTS
55 enum RequiresConsent {
56 REQUIRES_CONSENT,
57 DOES_NOT_REQUIRE_CONSENT
60 // Runs all pending tasks in the renderer associated with |web_contents|.
61 // Returns true on success.
62 bool RunAllPendingInRenderer(content::WebContents* web_contents) {
63 // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs
64 // are sent synchronously, anything started prior to this method will finish
65 // before this method returns (as content::ExecuteScript() is synchronous).
66 return content::ExecuteScript(web_contents, "1 == 1;");
69 } // namespace
71 class ActiveScriptControllerBrowserTest : public ExtensionBrowserTest {
72 public:
73 ActiveScriptControllerBrowserTest() {}
75 void SetUpCommandLine(base::CommandLine* command_line) override;
76 void TearDownOnMainThread() override;
78 // Returns an extension with the given |host_type| and |injection_type|. If
79 // one already exists, the existing extension will be returned. Othewrwise,
80 // one will be created.
81 // This could potentially return NULL if LoadExtension() fails.
82 const Extension* CreateExtension(HostType host_type,
83 InjectionType injection_type);
85 private:
86 ScopedVector<TestExtensionDir> test_extension_dirs_;
87 std::vector<const Extension*> extensions_;
90 void ActiveScriptControllerBrowserTest::SetUpCommandLine(
91 base::CommandLine* command_line) {
92 ExtensionBrowserTest::SetUpCommandLine(command_line);
93 // We append the actual switch to the commandline because it needs to be
94 // passed over to the renderer, which a FeatureSwitch::ScopedOverride will
95 // not do.
96 command_line->AppendSwitch(switches::kEnableScriptsRequireAction);
99 void ActiveScriptControllerBrowserTest::TearDownOnMainThread() {
100 test_extension_dirs_.clear();
103 const Extension* ActiveScriptControllerBrowserTest::CreateExtension(
104 HostType host_type, InjectionType injection_type) {
105 std::string name =
106 base::StringPrintf(
107 "%s %s",
108 injection_type == CONTENT_SCRIPT ?
109 "content_script" : "execute_script",
110 host_type == ALL_HOSTS ? "all_hosts" : "explicit_hosts");
112 const char* const permission_scheme =
113 host_type == ALL_HOSTS ? kAllHostsScheme : kExplicitHostsScheme;
115 std::string permissions = base::StringPrintf(
116 "\"permissions\": [\"tabs\", \"%s\"]", permission_scheme);
118 std::string scripts;
119 std::string script_source;
120 if (injection_type == CONTENT_SCRIPT) {
121 scripts = base::StringPrintf(
122 "\"content_scripts\": ["
123 " {"
124 " \"matches\": [\"%s\"],"
125 " \"js\": [\"script.js\"],"
126 " \"run_at\": \"document_start\""
127 " }"
128 "]",
129 permission_scheme);
130 } else {
131 scripts = kBackgroundScript;
134 std::string manifest = base::StringPrintf(
136 " \"name\": \"%s\","
137 " \"version\": \"1.0\","
138 " \"manifest_version\": 2,"
139 " %s,"
140 " %s"
141 "}",
142 name.c_str(),
143 permissions.c_str(),
144 scripts.c_str());
146 scoped_ptr<TestExtensionDir> dir(new TestExtensionDir);
147 dir->WriteManifest(manifest);
148 dir->WriteFile(FILE_PATH_LITERAL("script.js"),
149 injection_type == CONTENT_SCRIPT ? kContentScriptSource :
150 kBackgroundScriptSource);
152 const Extension* extension = LoadExtension(dir->unpacked_path());
153 if (extension) {
154 test_extension_dirs_.push_back(dir.release());
155 extensions_.push_back(extension);
158 // If extension is NULL here, it will be caught later in the test.
159 return extension;
162 class ActiveScriptTester {
163 public:
164 ActiveScriptTester(const std::string& name,
165 const Extension* extension,
166 Browser* browser,
167 RequiresConsent requires_consent,
168 InjectionType type);
169 ~ActiveScriptTester();
171 testing::AssertionResult Verify();
173 private:
174 // Returns the active script controller, or NULL if one does not exist.
175 ActiveScriptController* GetActiveScriptController();
177 // Returns true if the extension has a pending request with the active script
178 // controller.
179 bool WantsToRun();
181 // The name of the extension, and also the message it sends.
182 std::string name_;
184 // The extension associated with this tester.
185 const Extension* extension_;
187 // The browser the tester is running in.
188 Browser* browser_;
190 // Whether or not the extension has permission to run the script without
191 // asking the user.
192 RequiresConsent requires_consent_;
194 // The type of injection this tester uses.
195 InjectionType type_;
197 // All of these extensions should inject a script (either through content
198 // scripts or through chrome.tabs.executeScript()) that sends a message with
199 // the |kInjectSucceeded| message.
200 linked_ptr<ExtensionTestMessageListener> inject_success_listener_;
203 ActiveScriptTester::ActiveScriptTester(const std::string& name,
204 const Extension* extension,
205 Browser* browser,
206 RequiresConsent requires_consent,
207 InjectionType type)
208 : name_(name),
209 extension_(extension),
210 browser_(browser),
211 requires_consent_(requires_consent),
212 type_(type),
213 inject_success_listener_(
214 new ExtensionTestMessageListener(kInjectSucceeded,
215 false /* won't reply */)) {
216 inject_success_listener_->set_extension_id(extension->id());
219 ActiveScriptTester::~ActiveScriptTester() {
222 testing::AssertionResult ActiveScriptTester::Verify() {
223 if (!extension_)
224 return testing::AssertionFailure() << "Could not load extension: " << name_;
226 content::WebContents* web_contents =
227 browser_ ? browser_->tab_strip_model()->GetActiveWebContents() : NULL;
228 if (!web_contents)
229 return testing::AssertionFailure() << "No web contents.";
231 // Give the extension plenty of time to inject.
232 if (!RunAllPendingInRenderer(web_contents))
233 return testing::AssertionFailure() << "Could not run pending in renderer.";
235 // Make sure all running tasks are complete.
236 content::RunAllPendingInMessageLoop();
238 ActiveScriptController* controller = GetActiveScriptController();
239 if (!controller)
240 return testing::AssertionFailure() << "Could not find controller.";
242 bool wants_to_run = WantsToRun();
244 // An extension should have an action displayed iff it requires user consent.
245 if ((requires_consent_ == REQUIRES_CONSENT && !wants_to_run) ||
246 (requires_consent_ == DOES_NOT_REQUIRE_CONSENT && wants_to_run)) {
247 return testing::AssertionFailure()
248 << "Improper wants to run for " << name_ << ": expected "
249 << (requires_consent_ == REQUIRES_CONSENT) << ", found "
250 << wants_to_run;
253 // If the extension has permission, we should be able to simply wait for it
254 // to execute.
255 if (requires_consent_ == DOES_NOT_REQUIRE_CONSENT) {
256 inject_success_listener_->WaitUntilSatisfied();
257 return testing::AssertionSuccess();
260 // Otherwise, we don't have permission, and have to grant it. Ensure the
261 // script has *not* already executed.
262 if (inject_success_listener_->was_satisfied()) {
263 return testing::AssertionFailure() <<
264 name_ << "'s script ran without permission.";
267 // If we reach this point, we should always want to run.
268 DCHECK(wants_to_run);
270 // Grant permission by clicking on the extension action.
271 controller->OnClicked(extension_);
273 // Now, the extension should be able to inject the script.
274 inject_success_listener_->WaitUntilSatisfied();
276 // The extension should no longer want to run.
277 wants_to_run = WantsToRun();
278 if (wants_to_run) {
279 return testing::AssertionFailure()
280 << "Extension " << name_ << " still wants to run after injecting.";
283 return testing::AssertionSuccess();
286 ActiveScriptController* ActiveScriptTester::GetActiveScriptController() {
287 content::WebContents* web_contents =
288 browser_ ? browser_->tab_strip_model()->GetActiveWebContents() : NULL;
290 if (!web_contents)
291 return NULL;
293 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
294 return tab_helper ? tab_helper->active_script_controller() : NULL;
297 bool ActiveScriptTester::WantsToRun() {
298 ActiveScriptController* controller = GetActiveScriptController();
299 return controller ? controller->WantsToRun(extension_) : false;
302 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest,
303 ActiveScriptsAreDisplayedAndDelayExecution) {
304 base::FilePath active_script_path =
305 test_data_dir_.AppendASCII("active_script");
307 const char* const kExtensionNames[] = {
308 "inject_scripts_all_hosts",
309 "inject_scripts_explicit_hosts",
310 "content_scripts_all_hosts",
311 "content_scripts_explicit_hosts"
314 // First, we load up three extensions:
315 // - An extension that injects scripts into all hosts,
316 // - An extension that injects scripts into explicit hosts,
317 // - An extension with a content script that runs on all hosts,
318 // - An extension with a content script that runs on explicit hosts.
319 // The extensions that operate on explicit hosts have permission; the ones
320 // that request all hosts require user consent.
321 ActiveScriptTester testers[] = {
322 ActiveScriptTester(
323 kExtensionNames[0],
324 CreateExtension(ALL_HOSTS, EXECUTE_SCRIPT),
325 browser(),
326 REQUIRES_CONSENT,
327 EXECUTE_SCRIPT),
328 ActiveScriptTester(
329 kExtensionNames[1],
330 CreateExtension(EXPLICIT_HOSTS, EXECUTE_SCRIPT),
331 browser(),
332 DOES_NOT_REQUIRE_CONSENT,
333 EXECUTE_SCRIPT),
334 ActiveScriptTester(
335 kExtensionNames[2],
336 CreateExtension(ALL_HOSTS, CONTENT_SCRIPT),
337 browser(),
338 REQUIRES_CONSENT,
339 CONTENT_SCRIPT),
340 ActiveScriptTester(
341 kExtensionNames[3],
342 CreateExtension(EXPLICIT_HOSTS, CONTENT_SCRIPT),
343 browser(),
344 DOES_NOT_REQUIRE_CONSENT,
345 CONTENT_SCRIPT),
348 // Navigate to an URL (which matches the explicit host specified in the
349 // extension content_scripts_explicit_hosts). All four extensions should
350 // inject the script.
351 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
352 ui_test_utils::NavigateToURL(
353 browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
355 for (size_t i = 0u; i < arraysize(testers); ++i)
356 EXPECT_TRUE(testers[i].Verify()) << kExtensionNames[i];
359 // Test that removing an extension with pending injections a) removes the
360 // pending injections for that extension, and b) does not affect pending
361 // injections for other extensions.
362 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest,
363 RemoveExtensionWithPendingInjections) {
364 // Load up two extensions, each with content scripts.
365 const Extension* extension1 = CreateExtension(ALL_HOSTS, CONTENT_SCRIPT);
366 ASSERT_TRUE(extension1);
367 const Extension* extension2 = CreateExtension(ALL_HOSTS, CONTENT_SCRIPT);
368 ASSERT_TRUE(extension2);
370 ASSERT_NE(extension1->id(), extension2->id());
372 content::WebContents* web_contents =
373 browser()->tab_strip_model()->GetActiveWebContents();
374 ASSERT_TRUE(web_contents);
375 ActiveScriptController* active_script_controller =
376 ActiveScriptController::GetForWebContents(web_contents);
377 ASSERT_TRUE(active_script_controller);
379 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
380 ui_test_utils::NavigateToURL(
381 browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
383 // Both extensions should have pending requests.
384 EXPECT_TRUE(active_script_controller->WantsToRun(extension1));
385 EXPECT_TRUE(active_script_controller->WantsToRun(extension2));
387 // Unload one of the extensions.
388 UnloadExtension(extension2->id());
390 EXPECT_TRUE(RunAllPendingInRenderer(web_contents));
392 // We should have pending requests for extension1, but not the removed
393 // extension2.
394 EXPECT_TRUE(active_script_controller->WantsToRun(extension1));
395 EXPECT_FALSE(active_script_controller->WantsToRun(extension2));
397 // We should still be able to run the request for extension1.
398 ExtensionTestMessageListener inject_success_listener(
399 new ExtensionTestMessageListener(kInjectSucceeded,
400 false /* won't reply */));
401 inject_success_listener.set_extension_id(extension1->id());
402 active_script_controller->OnClicked(extension1);
403 inject_success_listener.WaitUntilSatisfied();
406 // Test that granting the extension all urls permission allows it to run on
407 // pages, and that the permission update is sent to existing renderers.
408 IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest,
409 GrantExtensionAllUrlsPermission) {
411 // Loadup an extension and navigate.
412 const Extension* extension = CreateExtension(ALL_HOSTS, CONTENT_SCRIPT);
413 ASSERT_TRUE(extension);
415 content::WebContents* web_contents =
416 browser()->tab_strip_model()->GetActiveWebContents();
417 ASSERT_TRUE(web_contents);
418 ActiveScriptController* active_script_controller =
419 ActiveScriptController::GetForWebContents(web_contents);
420 ASSERT_TRUE(active_script_controller);
422 ExtensionTestMessageListener inject_success_listener(
423 new ExtensionTestMessageListener(kInjectSucceeded,
424 false /* won't reply */));
425 inject_success_listener.set_extension_id(extension->id());
427 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
428 GURL url = embedded_test_server()->GetURL("/extensions/test_file.html");
429 ui_test_utils::NavigateToURL(browser(), url);
431 // The extension shouldn't be allowed to run.
432 EXPECT_TRUE(active_script_controller->WantsToRun(extension));
433 EXPECT_EQ(1, active_script_controller->num_page_requests());
434 EXPECT_FALSE(inject_success_listener.was_satisfied());
436 // Enable the extension to run on all urls.
437 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
438 EXPECT_TRUE(RunAllPendingInRenderer(web_contents));
440 // Navigate again - this time, the extension should execute immediately (and
441 // should not need to ask the script controller for permission).
442 ui_test_utils::NavigateToURL(browser(), url);
443 EXPECT_FALSE(active_script_controller->WantsToRun(extension));
444 EXPECT_EQ(0, active_script_controller->num_page_requests());
445 EXPECT_TRUE(inject_success_listener.WaitUntilSatisfied());
447 // Revoke all urls permissions.
448 inject_success_listener.Reset();
449 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
450 EXPECT_TRUE(RunAllPendingInRenderer(web_contents));
452 // Re-navigate; the extension should again need permission to run.
453 ui_test_utils::NavigateToURL(browser(), url);
454 EXPECT_TRUE(active_script_controller->WantsToRun(extension));
455 EXPECT_EQ(1, active_script_controller->num_page_requests());
456 EXPECT_FALSE(inject_success_listener.was_satisfied());
459 // A version of the test with the flag off, in order to test that everything
460 // still works as expected.
461 class FlagOffActiveScriptControllerBrowserTest
462 : public ActiveScriptControllerBrowserTest {
463 private:
464 // Simply don't append the flag.
465 void SetUpCommandLine(base::CommandLine* command_line) override {
466 ExtensionBrowserTest::SetUpCommandLine(command_line);
470 IN_PROC_BROWSER_TEST_F(FlagOffActiveScriptControllerBrowserTest,
471 ScriptsExecuteWhenFlagAbsent) {
472 const char* const kExtensionNames[] = {
473 "content_scripts_all_hosts",
474 "inject_scripts_all_hosts",
476 ActiveScriptTester testers[] = {
477 ActiveScriptTester(
478 kExtensionNames[0],
479 CreateExtension(ALL_HOSTS, CONTENT_SCRIPT),
480 browser(),
481 DOES_NOT_REQUIRE_CONSENT,
482 CONTENT_SCRIPT),
483 ActiveScriptTester(
484 kExtensionNames[1],
485 CreateExtension(ALL_HOSTS, EXECUTE_SCRIPT),
486 browser(),
487 DOES_NOT_REQUIRE_CONSENT,
488 EXECUTE_SCRIPT),
491 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
492 ui_test_utils::NavigateToURL(
493 browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
495 for (size_t i = 0u; i < arraysize(testers); ++i)
496 EXPECT_TRUE(testers[i].Verify()) << kExtensionNames[i];
499 } // namespace extensions