Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / content_script_apitest.cc
blobed60dcb7d1c2c41ac36ceccb64f4ccf2a73dc8a5
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.
5 #include "base/bind.h"
6 #include "base/callback.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/api/permissions/permissions_api.h"
10 #include "chrome/browser/extensions/extension_apitest.h"
11 #include "chrome/browser/extensions/extension_service.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/common/chrome_switches.h"
16 #include "chrome/test/base/ui_test_utils.h"
17 #include "components/app_modal/javascript_dialog_extensions_client.h"
18 #include "components/app_modal/javascript_dialog_manager.h"
19 #include "content/public/browser/javascript_dialog_manager.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_delegate.h"
22 #include "content/public/test/browser_test_utils.h"
23 #include "extensions/browser/notification_types.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/test/extension_test_message_listener.h"
26 #include "extensions/test/result_catcher.h"
27 #include "net/dns/mock_host_resolver.h"
28 #include "net/test/embedded_test_server/embedded_test_server.h"
29 #include "url/gurl.h"
31 namespace extensions {
33 namespace {
35 // A fake webstore domain.
36 const char kWebstoreDomain[] = "cws.com";
38 // Check whether or not style was injected, with |expected_injection| indicating
39 // the expected result. Also ensure that no CSS was added to the
40 // document.styleSheets array.
41 testing::AssertionResult CheckStyleInjection(Browser* browser,
42 const GURL& url,
43 bool expected_injection) {
44 ui_test_utils::NavigateToURL(browser, url);
46 bool css_injected = false;
47 if (!content::ExecuteScriptAndExtractBool(
48 browser->tab_strip_model()->GetActiveWebContents(),
49 "window.domAutomationController.send("
50 " document.defaultView.getComputedStyle(document.body, null)."
51 " getPropertyValue('display') == 'none');",
52 &css_injected)) {
53 return testing::AssertionFailure()
54 << "Failed to execute script and extract bool for injection status.";
57 if (css_injected != expected_injection) {
58 std::string message;
59 if (css_injected)
60 message = "CSS injected when no injection was expected.";
61 else
62 message = "CSS not injected when injection was expected.";
63 return testing::AssertionFailure() << message;
66 bool css_doesnt_add_to_list = false;
67 if (!content::ExecuteScriptAndExtractBool(
68 browser->tab_strip_model()->GetActiveWebContents(),
69 "window.domAutomationController.send("
70 " document.styleSheets.length == 0);",
71 &css_doesnt_add_to_list)) {
72 return testing::AssertionFailure()
73 << "Failed to execute script and extract bool for stylesheets length.";
75 if (!css_doesnt_add_to_list) {
76 return testing::AssertionFailure()
77 << "CSS injection added to number of stylesheets.";
80 return testing::AssertionSuccess();
83 class DialogClient;
85 // A helper class to hijack the dialog manager's ExtensionsClient, so that we
86 // know when dialogs are being opened.
87 // NOTE: The default implementation of the JavaScriptDialogExtensionsClient
88 // doesn't do anything, so it's safe to override it. If, at some stage, this
89 // has behavior (like if we move this into app shell), we'll need to update
90 // this (by, e.g., making DialogClient a wrapper around the implementation).
91 class DialogHelper {
92 public:
93 explicit DialogHelper(content::WebContents* web_contents);
94 ~DialogHelper();
96 // Notifies the DialogHelper that a dialog was opened. Runs |quit_closure_|,
97 // if it is non-null.
98 void DialogOpened();
100 // Closes any active dialogs.
101 void CloseDialogs();
103 void set_quit_closure(const base::Closure& quit_closure) {
104 quit_closure_ = quit_closure;
106 size_t dialog_count() const { return dialog_count_; }
108 private:
109 // The number of dialogs to appear.
110 size_t dialog_count_;
112 // The WebContents this helper is associated with.
113 content::WebContents* web_contents_;
115 // The dialog manager for |web_contents_|.
116 content::JavaScriptDialogManager* dialog_manager_;
118 // The dialog client override.
119 DialogClient* client_;
121 // The quit closure to run when a dialog appears.
122 base::Closure quit_closure_;
124 DISALLOW_COPY_AND_ASSIGN(DialogHelper);
127 // The client override for the DialogHelper.
128 class DialogClient : public app_modal::JavaScriptDialogExtensionsClient {
129 public:
130 explicit DialogClient(DialogHelper* helper) : helper_(helper) {}
131 ~DialogClient() override {}
133 void set_helper(DialogHelper* helper) { helper_ = helper; }
135 private:
136 // app_modal::JavaScriptDialogExtensionsClient:
137 void OnDialogOpened(content::WebContents* web_contents) override {
138 if (helper_)
139 helper_->DialogOpened();
141 void OnDialogClosed(content::WebContents* web_contents) override {}
142 bool GetExtensionName(content::WebContents* web_contents,
143 const GURL& origin_url,
144 std::string* name_out) override {
145 return false;
148 // The dialog helper to notify of any open dialogs.
149 DialogHelper* helper_;
151 DISALLOW_COPY_AND_ASSIGN(DialogClient);
154 DialogHelper::DialogHelper(content::WebContents* web_contents)
155 : dialog_count_(0),
156 web_contents_(web_contents),
157 dialog_manager_(nullptr),
158 client_(nullptr) {
159 app_modal::JavaScriptDialogManager* dialog_manager_impl =
160 app_modal::JavaScriptDialogManager::GetInstance();
161 dialog_manager_ =
162 web_contents_->GetDelegate()->GetJavaScriptDialogManager(web_contents_);
163 DCHECK_EQ(dialog_manager_impl, dialog_manager_);
165 client_ = new DialogClient(this);
166 dialog_manager_impl->SetExtensionsClient(make_scoped_ptr(client_));
169 DialogHelper::~DialogHelper() {
170 client_->set_helper(nullptr);
173 void DialogHelper::CloseDialogs() {
174 dialog_manager_->CancelActiveAndPendingDialogs(web_contents_);
177 void DialogHelper::DialogOpened() {
178 ++dialog_count_;
179 if (!quit_closure_.is_null()) {
180 quit_closure_.Run();
181 quit_closure_ = base::Closure();
185 // Runs all pending tasks in the renderer associated with |web_contents|, and
186 // then all pending tasks in the browser process.
187 // Returns true on success.
188 bool RunAllPending(content::WebContents* web_contents) {
189 // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs
190 // are sent synchronously, anything started prior to this method will finish
191 // before this method returns (as content::ExecuteScript() is synchronous).
192 if (!content::ExecuteScript(web_contents, "1 == 1;"))
193 return false;
194 base::RunLoop().RunUntilIdle();
195 return true;
198 // A simple extension manifest with content scripts on all pages.
199 const char kManifest[] =
201 " \"name\": \"%s\","
202 " \"version\": \"1.0\","
203 " \"manifest_version\": 2,"
204 " \"content_scripts\": [{"
205 " \"matches\": [\"*://*/*\"],"
206 " \"js\": [\"script.js\"],"
207 " \"run_at\": \"%s\""
208 " }]"
209 "}";
211 // A (blocking) content script that pops up an alert.
212 const char kBlockingScript[] = "alert('ALERT');";
214 // A (non-blocking) content script that sends a message.
215 const char kNonBlockingScript[] = "chrome.test.sendMessage('done');";
217 } // namespace
219 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptAllFrames) {
220 ASSERT_TRUE(StartEmbeddedTestServer());
221 ASSERT_TRUE(RunExtensionTest("content_scripts/all_frames")) << message_;
224 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptAboutBlankIframes) {
225 ASSERT_TRUE(StartEmbeddedTestServer());
226 ASSERT_TRUE(
227 RunExtensionTest("content_scripts/about_blank_iframes")) << message_;
230 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptAboutBlankAndSrcdoc) {
231 // The optional "*://*/*" permission is requested after verifying that
232 // content script insertion solely depends on content_scripts[*].matches.
233 // The permission is needed for chrome.tabs.executeScript tests.
234 PermissionsRequestFunction::SetAutoConfirmForTests(true);
235 PermissionsRequestFunction::SetIgnoreUserGestureForTests(true);
237 ASSERT_TRUE(StartEmbeddedTestServer());
238 ASSERT_TRUE(RunExtensionTest("content_scripts/about_blank_srcdoc"))
239 << message_;
242 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionIframe) {
243 ASSERT_TRUE(StartEmbeddedTestServer());
244 ASSERT_TRUE(RunExtensionTest("content_scripts/extension_iframe")) << message_;
247 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionProcess) {
248 ASSERT_TRUE(StartEmbeddedTestServer());
249 ASSERT_TRUE(
250 RunExtensionTest("content_scripts/extension_process")) << message_;
253 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptFragmentNavigation) {
254 ASSERT_TRUE(StartEmbeddedTestServer());
255 const char extension_name[] = "content_scripts/fragment";
256 ASSERT_TRUE(RunExtensionTest(extension_name)) << message_;
259 // Times out on Linux: http://crbug.com/163097
260 #if defined(OS_LINUX)
261 #define MAYBE_ContentScriptIsolatedWorlds DISABLED_ContentScriptIsolatedWorlds
262 #else
263 #define MAYBE_ContentScriptIsolatedWorlds ContentScriptIsolatedWorlds
264 #endif
265 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_ContentScriptIsolatedWorlds) {
266 // This extension runs various bits of script and tests that they all run in
267 // the same isolated world.
268 ASSERT_TRUE(StartEmbeddedTestServer());
269 ASSERT_TRUE(RunExtensionTest("content_scripts/isolated_world1")) << message_;
271 // Now load a different extension, inject into same page, verify worlds aren't
272 // shared.
273 ASSERT_TRUE(RunExtensionTest("content_scripts/isolated_world2")) << message_;
276 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptIgnoreHostPermissions) {
277 host_resolver()->AddRule("a.com", "127.0.0.1");
278 host_resolver()->AddRule("b.com", "127.0.0.1");
279 ASSERT_TRUE(StartEmbeddedTestServer());
280 ASSERT_TRUE(RunExtensionTest(
281 "content_scripts/dont_match_host_permissions")) << message_;
284 // crbug.com/39249 -- content scripts js should not run on view source.
285 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptViewSource) {
286 ASSERT_TRUE(StartEmbeddedTestServer());
287 ASSERT_TRUE(RunExtensionTest("content_scripts/view_source")) << message_;
290 // crbug.com/126257 -- content scripts should not get injected into other
291 // extensions.
292 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptOtherExtensions) {
293 host_resolver()->AddRule("a.com", "127.0.0.1");
294 ASSERT_TRUE(StartEmbeddedTestServer());
295 // First, load extension that sets up content script.
296 ASSERT_TRUE(RunExtensionTest("content_scripts/other_extensions/injector"))
297 << message_;
298 // Then load targeted extension to make sure its content isn't changed.
299 ASSERT_TRUE(RunExtensionTest("content_scripts/other_extensions/victim"))
300 << message_;
303 class ContentScriptCssInjectionTest : public ExtensionApiTest {
304 protected:
305 // TODO(rdevlin.cronin): Make a testing switch that looks like FeatureSwitch,
306 // but takes in an optional value so that we don't have to do this.
307 void SetUpCommandLine(base::CommandLine* command_line) override {
308 ExtensionApiTest::SetUpCommandLine(command_line);
309 // We change the Webstore URL to be http://cws.com. We need to do this so
310 // we can check that css injection is not allowed on the webstore (which
311 // could lead to spoofing). Unfortunately, host_resolver seems to have
312 // problems with redirecting "chrome.google.com" to the test server, so we
313 // can't use the real Webstore's URL. If this changes, we could clean this
314 // up.
315 command_line->AppendSwitchASCII(
316 switches::kAppsGalleryURL,
317 base::StringPrintf("http://%s", kWebstoreDomain));
321 IN_PROC_BROWSER_TEST_F(ContentScriptCssInjectionTest,
322 ContentScriptInjectsStyles) {
323 ASSERT_TRUE(StartEmbeddedTestServer());
324 host_resolver()->AddRule(kWebstoreDomain, "127.0.0.1");
326 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("content_scripts")
327 .AppendASCII("css_injection")));
329 // CSS injection should be allowed on an aribitrary web page.
330 GURL url =
331 embedded_test_server()->GetURL("/extensions/test_file_with_body.html");
332 EXPECT_TRUE(CheckStyleInjection(browser(), url, true));
334 // The loaded extension has an exclude match for "extensions/test_file.html",
335 // so no CSS should be injected.
336 url = embedded_test_server()->GetURL("/extensions/test_file.html");
337 EXPECT_TRUE(CheckStyleInjection(browser(), url, false));
339 // We disallow all injection on the webstore.
340 GURL::Replacements replacements;
341 replacements.SetHostStr(kWebstoreDomain);
342 url = embedded_test_server()->GetURL("/extensions/test_file_with_body.html")
343 .ReplaceComponents(replacements);
344 EXPECT_TRUE(CheckStyleInjection(browser(), url, false));
347 // crbug.com/120762
348 IN_PROC_BROWSER_TEST_F(
349 ExtensionApiTest,
350 DISABLED_ContentScriptStylesInjectedIntoExistingRenderers) {
351 ASSERT_TRUE(StartEmbeddedTestServer());
353 content::WindowedNotificationObserver signal(
354 extensions::NOTIFICATION_USER_SCRIPTS_UPDATED,
355 content::Source<Profile>(browser()->profile()));
357 // Start with a renderer already open at a URL.
358 GURL url(test_server()->GetURL("file/extensions/test_file.html"));
359 ui_test_utils::NavigateToURL(browser(), url);
361 LoadExtension(
362 test_data_dir_.AppendASCII("content_scripts/existing_renderers"));
364 signal.Wait();
366 // And check that its styles were affected by the styles that just got loaded.
367 bool styles_injected;
368 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
369 browser()->tab_strip_model()->GetActiveWebContents(),
370 "window.domAutomationController.send("
371 " document.defaultView.getComputedStyle(document.body, null)."
372 " getPropertyValue('background-color') == 'rgb(255, 0, 0)')",
373 &styles_injected));
374 ASSERT_TRUE(styles_injected);
377 IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
378 ContentScriptCSSLocalization) {
379 ASSERT_TRUE(StartEmbeddedTestServer());
380 ASSERT_TRUE(RunExtensionTest("content_scripts/css_l10n")) << message_;
383 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptExtensionAPIs) {
384 ASSERT_TRUE(StartEmbeddedTestServer());
386 const extensions::Extension* extension = LoadExtension(
387 test_data_dir_.AppendASCII("content_scripts/extension_api"));
389 ResultCatcher catcher;
390 ui_test_utils::NavigateToURL(
391 browser(),
392 embedded_test_server()->GetURL(
393 "/extensions/api_test/content_scripts/extension_api/functions.html"));
394 EXPECT_TRUE(catcher.GetNextResult());
396 // Navigate to a page that will cause a content script to run that starts
397 // listening for an extension event.
398 ui_test_utils::NavigateToURL(
399 browser(),
400 embedded_test_server()->GetURL(
401 "/extensions/api_test/content_scripts/extension_api/events.html"));
403 // Navigate to an extension page that will fire the event events.js is
404 // listening for.
405 ui_test_utils::NavigateToURLWithDisposition(
406 browser(), extension->GetResourceURL("fire_event.html"),
407 NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_NONE);
408 EXPECT_TRUE(catcher.GetNextResult());
411 // Flaky on Windows. http://crbug.com/248418
412 #if defined(OS_WIN)
413 #define MAYBE_ContentScriptPermissionsApi DISABLED_ContentScriptPermissionsApi
414 #else
415 #define MAYBE_ContentScriptPermissionsApi ContentScriptPermissionsApi
416 #endif
417 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_ContentScriptPermissionsApi) {
418 extensions::PermissionsRequestFunction::SetIgnoreUserGestureForTests(true);
419 extensions::PermissionsRequestFunction::SetAutoConfirmForTests(true);
420 host_resolver()->AddRule("*.com", "127.0.0.1");
421 ASSERT_TRUE(StartEmbeddedTestServer());
422 ASSERT_TRUE(RunExtensionTest("content_scripts/permissions")) << message_;
425 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptBypassPageCSP) {
426 ASSERT_TRUE(StartEmbeddedTestServer());
427 ASSERT_TRUE(RunExtensionTest("content_scripts/bypass_page_csp")) << message_;
430 // Test that when injecting a blocking content script, other scripts don't run
431 // until the blocking script finishes.
432 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptBlockingScript) {
433 ASSERT_TRUE(StartEmbeddedTestServer());
435 // Load up two extensions.
436 TestExtensionDir ext_dir1;
437 ext_dir1.WriteManifest(
438 base::StringPrintf(kManifest, "ext1", "document_start"));
439 ext_dir1.WriteFile(FILE_PATH_LITERAL("script.js"), kBlockingScript);
440 const Extension* ext1 = LoadExtension(ext_dir1.unpacked_path());
441 ASSERT_TRUE(ext1);
443 TestExtensionDir ext_dir2;
444 ext_dir2.WriteManifest(base::StringPrintf(kManifest, "ext2", "document_end"));
445 ext_dir2.WriteFile(FILE_PATH_LITERAL("script.js"), kNonBlockingScript);
446 const Extension* ext2 = LoadExtension(ext_dir2.unpacked_path());
447 ASSERT_TRUE(ext2);
449 content::WebContents* web_contents =
450 browser()->tab_strip_model()->GetActiveWebContents();
451 DialogHelper dialog_helper(web_contents);
452 base::RunLoop run_loop;
453 dialog_helper.set_quit_closure(run_loop.QuitClosure());
455 ExtensionTestMessageListener listener("done", false);
456 listener.set_extension_id(ext2->id());
458 // Navigate! Both extensions will try to inject.
459 ui_test_utils::NavigateToURLWithDisposition(
460 browser(),
461 embedded_test_server()->GetURL("/empty.html"),
462 CURRENT_TAB,
463 ui_test_utils::BROWSER_TEST_NONE);
465 run_loop.Run();
466 // Right now, the alert dialog is showing and blocking injection of anything
467 // after it, so the listener shouldn't be satisfied.
468 EXPECT_TRUE(RunAllPending(web_contents));
469 EXPECT_FALSE(listener.was_satisfied());
470 EXPECT_EQ(1u, dialog_helper.dialog_count());
471 dialog_helper.CloseDialogs();
473 // After closing the dialog, the rest of the scripts should be able to
474 // inject.
475 EXPECT_TRUE(listener.WaitUntilSatisfied());
478 // Test that closing a tab with a blocking script results in no further scripts
479 // running (and we don't crash).
480 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptBlockingScriptTabClosed) {
481 ASSERT_TRUE(StartEmbeddedTestServer());
483 // We're going to close a tab in this test, so make a new one (to ensure
484 // we don't close the browser).
485 ui_test_utils::NavigateToURLWithDisposition(
486 browser(),
487 embedded_test_server()->GetURL("/empty.html"),
488 NEW_FOREGROUND_TAB,
489 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
491 // Set up the same as the previous test case.
492 TestExtensionDir ext_dir1;
493 ext_dir1.WriteManifest(
494 base::StringPrintf(kManifest, "ext1", "document_start"));
495 ext_dir1.WriteFile(FILE_PATH_LITERAL("script.js"), kBlockingScript);
496 const Extension* ext1 = LoadExtension(ext_dir1.unpacked_path());
497 ASSERT_TRUE(ext1);
499 TestExtensionDir ext_dir2;
500 ext_dir2.WriteManifest(base::StringPrintf(kManifest, "ext2", "document_end"));
501 ext_dir2.WriteFile(FILE_PATH_LITERAL("script.js"), kNonBlockingScript);
502 const Extension* ext2 = LoadExtension(ext_dir2.unpacked_path());
503 ASSERT_TRUE(ext2);
505 content::WebContents* web_contents =
506 browser()->tab_strip_model()->GetActiveWebContents();
507 DialogHelper dialog_helper(web_contents);
508 base::RunLoop run_loop;
509 dialog_helper.set_quit_closure(run_loop.QuitClosure());
511 ExtensionTestMessageListener listener("done", false);
512 listener.set_extension_id(ext2->id());
514 // Navitate!
515 ui_test_utils::NavigateToURLWithDisposition(
516 browser(),
517 embedded_test_server()->GetURL("/empty.html"),
518 CURRENT_TAB,
519 ui_test_utils::BROWSER_TEST_NONE);
521 // Now, instead of closing the dialog, just close the tab. Later scripts
522 // should never get a chance to run (and we shouldn't crash).
523 run_loop.Run();
524 EXPECT_TRUE(RunAllPending(web_contents));
525 EXPECT_FALSE(listener.was_satisfied());
526 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
527 browser()->tab_strip_model()->active_index(), 0));
528 EXPECT_FALSE(listener.was_satisfied());
531 // There was a bug by which content scripts that blocked and ran on
532 // document_idle could be injected twice (crbug.com/431263). Test for
533 // regression.
534 IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
535 ContentScriptBlockingScriptsDontRunTwice) {
536 ASSERT_TRUE(StartEmbeddedTestServer());
538 // Load up an extension.
539 TestExtensionDir ext_dir1;
540 ext_dir1.WriteManifest(
541 base::StringPrintf(kManifest, "ext1", "document_idle"));
542 ext_dir1.WriteFile(FILE_PATH_LITERAL("script.js"), kBlockingScript);
543 const Extension* ext1 = LoadExtension(ext_dir1.unpacked_path());
544 ASSERT_TRUE(ext1);
546 content::WebContents* web_contents =
547 browser()->tab_strip_model()->GetActiveWebContents();
548 DialogHelper dialog_helper(web_contents);
549 base::RunLoop run_loop;
550 dialog_helper.set_quit_closure(run_loop.QuitClosure());
552 // Navigate!
553 ui_test_utils::NavigateToURLWithDisposition(
554 browser(),
555 embedded_test_server()->GetURL("/empty.html"),
556 CURRENT_TAB,
557 ui_test_utils::BROWSER_TEST_NONE);
559 run_loop.Run();
561 // The extension will have injected at idle, but it should only inject once.
562 EXPECT_TRUE(RunAllPending(web_contents));
563 EXPECT_EQ(1u, dialog_helper.dialog_count());
564 dialog_helper.CloseDialogs();
565 EXPECT_TRUE(RunAllPending(web_contents));
566 EXPECT_EQ(1u, dialog_helper.dialog_count());
569 } // namespace extensions