OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <stddef.h> | 5 #include <stddef.h> |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/macros.h" | 9 #include "base/macros.h" |
10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
| 11 #include "base/run_loop.h" |
11 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
12 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
13 #include "build/build_config.h" | 14 #include "build/build_config.h" |
14 #include "chrome/browser/extensions/api/permissions/permissions_api.h" | 15 #include "chrome/browser/extensions/api/permissions/permissions_api.h" |
15 #include "chrome/browser/extensions/extension_apitest.h" | 16 #include "chrome/browser/extensions/extension_apitest.h" |
16 #include "chrome/browser/extensions/extension_management_test_util.h" | 17 #include "chrome/browser/extensions/extension_management_test_util.h" |
17 #include "chrome/browser/extensions/extension_service.h" | 18 #include "chrome/browser/extensions/extension_service.h" |
18 #include "chrome/browser/extensions/extension_with_management_policy_apitest.h" | 19 #include "chrome/browser/extensions/extension_with_management_policy_apitest.h" |
19 #include "chrome/browser/extensions/test_extension_dir.h" | 20 #include "chrome/browser/extensions/test_extension_dir.h" |
20 #include "chrome/browser/ui/browser.h" | 21 #include "chrome/browser/ui/browser.h" |
| 22 #include "chrome/browser/ui/javascript_dialogs/javascript_dialog_tab_helper.h" |
21 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 23 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
22 #include "chrome/common/chrome_switches.h" | 24 #include "chrome/common/chrome_switches.h" |
23 #include "chrome/test/base/ui_test_utils.h" | 25 #include "chrome/test/base/ui_test_utils.h" |
24 #include "components/app_modal/javascript_dialog_extensions_client.h" | |
25 #include "components/app_modal/javascript_dialog_manager.h" | |
26 #include "content/public/browser/javascript_dialog_manager.h" | 26 #include "content/public/browser/javascript_dialog_manager.h" |
27 #include "content/public/browser/render_frame_host.h" | 27 #include "content/public/browser/render_frame_host.h" |
28 #include "content/public/browser/web_contents.h" | 28 #include "content/public/browser/web_contents.h" |
29 #include "content/public/browser/web_contents_delegate.h" | 29 #include "content/public/browser/web_contents_delegate.h" |
30 #include "content/public/test/browser_test_utils.h" | 30 #include "content/public/test/browser_test_utils.h" |
31 #include "extensions/browser/notification_types.h" | 31 #include "extensions/browser/notification_types.h" |
32 #include "extensions/common/extension.h" | 32 #include "extensions/common/extension.h" |
33 #include "extensions/common/switches.h" | 33 #include "extensions/common/switches.h" |
34 #include "extensions/test/extension_test_message_listener.h" | 34 #include "extensions/test/extension_test_message_listener.h" |
35 #include "extensions/test/result_catcher.h" | 35 #include "extensions/test/result_catcher.h" |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 << "Failed to execute script and extract bool for stylesheets length."; | 82 << "Failed to execute script and extract bool for stylesheets length."; |
83 } | 83 } |
84 if (!css_doesnt_add_to_list) { | 84 if (!css_doesnt_add_to_list) { |
85 return testing::AssertionFailure() | 85 return testing::AssertionFailure() |
86 << "CSS injection added to number of stylesheets."; | 86 << "CSS injection added to number of stylesheets."; |
87 } | 87 } |
88 | 88 |
89 return testing::AssertionSuccess(); | 89 return testing::AssertionSuccess(); |
90 } | 90 } |
91 | 91 |
92 class DialogClient; | |
93 | |
94 // A helper class to hijack the dialog manager's ExtensionsClient, so that we | |
95 // know when dialogs are being opened. | |
96 // NOTE: The default implementation of the JavaScriptDialogExtensionsClient | |
97 // doesn't do anything, so it's safe to override it. If, at some stage, this | |
98 // has behavior (like if we move this into app shell), we'll need to update | |
99 // this (by, e.g., making DialogClient a wrapper around the implementation). | |
100 class DialogHelper { | |
101 public: | |
102 explicit DialogHelper(content::WebContents* web_contents); | |
103 ~DialogHelper(); | |
104 | |
105 // Notifies the DialogHelper that a dialog was opened. Runs |quit_closure_|, | |
106 // if it is non-null. | |
107 void DialogOpened(); | |
108 | |
109 // Closes any active dialogs. | |
110 void CloseDialogs(); | |
111 | |
112 void set_quit_closure(const base::Closure& quit_closure) { | |
113 quit_closure_ = quit_closure; | |
114 } | |
115 size_t dialog_count() const { return dialog_count_; } | |
116 | |
117 private: | |
118 // The number of dialogs to appear. | |
119 size_t dialog_count_; | |
120 | |
121 // The WebContents this helper is associated with. | |
122 content::WebContents* web_contents_; | |
123 | |
124 // The dialog manager for |web_contents_|. | |
125 content::JavaScriptDialogManager* dialog_manager_; | |
126 | |
127 // The dialog client override. | |
128 DialogClient* client_; | |
129 | |
130 // The quit closure to run when a dialog appears. | |
131 base::Closure quit_closure_; | |
132 | |
133 DISALLOW_COPY_AND_ASSIGN(DialogHelper); | |
134 }; | |
135 | |
136 // The client override for the DialogHelper. | |
137 class DialogClient : public app_modal::JavaScriptDialogExtensionsClient { | |
138 public: | |
139 explicit DialogClient(DialogHelper* helper) : helper_(helper) {} | |
140 ~DialogClient() override {} | |
141 | |
142 void set_helper(DialogHelper* helper) { helper_ = helper; } | |
143 | |
144 private: | |
145 // app_modal::JavaScriptDialogExtensionsClient: | |
146 void OnDialogOpened(content::WebContents* web_contents) override { | |
147 if (helper_) | |
148 helper_->DialogOpened(); | |
149 } | |
150 void OnDialogClosed(content::WebContents* web_contents) override {} | |
151 bool GetExtensionName(content::WebContents* web_contents, | |
152 const GURL& origin_url, | |
153 std::string* name_out) override { | |
154 return false; | |
155 } | |
156 | |
157 // The dialog helper to notify of any open dialogs. | |
158 DialogHelper* helper_; | |
159 | |
160 DISALLOW_COPY_AND_ASSIGN(DialogClient); | |
161 }; | |
162 | |
163 DialogHelper::DialogHelper(content::WebContents* web_contents) | |
164 : dialog_count_(0), | |
165 web_contents_(web_contents), | |
166 dialog_manager_(nullptr), | |
167 client_(nullptr) { | |
168 app_modal::JavaScriptDialogManager* dialog_manager_impl = | |
169 app_modal::JavaScriptDialogManager::GetInstance(); | |
170 client_ = new DialogClient(this); | |
171 dialog_manager_impl->SetExtensionsClient(base::WrapUnique(client_)); | |
172 | |
173 dialog_manager_ = | |
174 web_contents_->GetDelegate()->GetJavaScriptDialogManager(web_contents_); | |
175 } | |
176 | |
177 DialogHelper::~DialogHelper() { | |
178 client_->set_helper(nullptr); | |
179 } | |
180 | |
181 void DialogHelper::CloseDialogs() { | |
182 dialog_manager_->CancelDialogs(web_contents_, false); | |
183 } | |
184 | |
185 void DialogHelper::DialogOpened() { | |
186 ++dialog_count_; | |
187 if (!quit_closure_.is_null()) { | |
188 quit_closure_.Run(); | |
189 quit_closure_ = base::Closure(); | |
190 } | |
191 } | |
192 | |
193 // Runs all pending tasks in the renderer associated with |web_contents|, and | 92 // Runs all pending tasks in the renderer associated with |web_contents|, and |
194 // then all pending tasks in the browser process. | 93 // then all pending tasks in the browser process. |
195 // Returns true on success. | 94 // Returns true on success. |
196 bool RunAllPending(content::WebContents* web_contents) { | 95 bool RunAllPending(content::WebContents* web_contents) { |
197 // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs | 96 // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs |
198 // are sent synchronously, anything started prior to this method will finish | 97 // are sent synchronously, anything started prior to this method will finish |
199 // before this method returns (as content::ExecuteScript() is synchronous). | 98 // before this method returns (as content::ExecuteScript() is synchronous). |
200 if (!content::ExecuteScript(web_contents, "1 == 1;")) | 99 if (!content::ExecuteScript(web_contents, "1 == 1;")) |
201 return false; | 100 return false; |
202 base::RunLoop().RunUntilIdle(); | 101 base::RunLoop().RunUntilIdle(); |
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 ASSERT_TRUE(ext1); | 435 ASSERT_TRUE(ext1); |
537 | 436 |
538 TestExtensionDir ext_dir2; | 437 TestExtensionDir ext_dir2; |
539 ext_dir2.WriteManifest(base::StringPrintf(kManifest, "ext2", "document_end")); | 438 ext_dir2.WriteManifest(base::StringPrintf(kManifest, "ext2", "document_end")); |
540 ext_dir2.WriteFile(FILE_PATH_LITERAL("script.js"), kNonBlockingScript); | 439 ext_dir2.WriteFile(FILE_PATH_LITERAL("script.js"), kNonBlockingScript); |
541 const Extension* ext2 = LoadExtension(ext_dir2.UnpackedPath()); | 440 const Extension* ext2 = LoadExtension(ext_dir2.UnpackedPath()); |
542 ASSERT_TRUE(ext2); | 441 ASSERT_TRUE(ext2); |
543 | 442 |
544 content::WebContents* web_contents = | 443 content::WebContents* web_contents = |
545 browser()->tab_strip_model()->GetActiveWebContents(); | 444 browser()->tab_strip_model()->GetActiveWebContents(); |
546 DialogHelper dialog_helper(web_contents); | 445 JavaScriptDialogTabHelper* js_helper = |
547 base::RunLoop run_loop; | 446 JavaScriptDialogTabHelper::FromWebContents(web_contents); |
548 dialog_helper.set_quit_closure(run_loop.QuitClosure()); | 447 base::RunLoop dialog_wait; |
| 448 js_helper->SetDialogShownCallbackForTesting(dialog_wait.QuitClosure()); |
549 | 449 |
550 ExtensionTestMessageListener listener("done", false); | 450 ExtensionTestMessageListener listener("done", false); |
551 listener.set_extension_id(ext2->id()); | 451 listener.set_extension_id(ext2->id()); |
552 | 452 |
553 // Navigate! Both extensions will try to inject. | 453 // Navigate! Both extensions will try to inject. |
554 ui_test_utils::NavigateToURLWithDisposition( | 454 ui_test_utils::NavigateToURLWithDisposition( |
555 browser(), embedded_test_server()->GetURL("/empty.html"), | 455 browser(), embedded_test_server()->GetURL("/empty.html"), |
556 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); | 456 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); |
557 | 457 |
558 run_loop.Run(); | 458 dialog_wait.Run(); |
559 // Right now, the alert dialog is showing and blocking injection of anything | 459 // Right now, the alert dialog is showing and blocking injection of anything |
560 // after it, so the listener shouldn't be satisfied. | 460 // after it, so the listener shouldn't be satisfied. |
561 EXPECT_FALSE(listener.was_satisfied()); | 461 EXPECT_FALSE(listener.was_satisfied()); |
562 EXPECT_EQ(1u, dialog_helper.dialog_count()); | 462 js_helper->HandleJavaScriptDialog(web_contents, true, nullptr); |
563 dialog_helper.CloseDialogs(); | |
564 | 463 |
565 // After closing the dialog, the rest of the scripts should be able to | 464 // After closing the dialog, the rest of the scripts should be able to |
566 // inject. | 465 // inject. |
567 EXPECT_TRUE(listener.WaitUntilSatisfied()); | 466 EXPECT_TRUE(listener.WaitUntilSatisfied()); |
568 } | 467 } |
569 | 468 |
570 // Test that closing a tab with a blocking script results in no further scripts | 469 // Test that closing a tab with a blocking script results in no further scripts |
571 // running (and we don't crash). | 470 // running (and we don't crash). |
572 IN_PROC_BROWSER_TEST_P(ContentScriptApiTest, | 471 IN_PROC_BROWSER_TEST_P(ContentScriptApiTest, |
573 ContentScriptBlockingScriptTabClosed) { | 472 ContentScriptBlockingScriptTabClosed) { |
(...skipping 15 matching lines...) Expand all Loading... |
589 ASSERT_TRUE(ext1); | 488 ASSERT_TRUE(ext1); |
590 | 489 |
591 TestExtensionDir ext_dir2; | 490 TestExtensionDir ext_dir2; |
592 ext_dir2.WriteManifest(base::StringPrintf(kManifest, "ext2", "document_end")); | 491 ext_dir2.WriteManifest(base::StringPrintf(kManifest, "ext2", "document_end")); |
593 ext_dir2.WriteFile(FILE_PATH_LITERAL("script.js"), kNonBlockingScript); | 492 ext_dir2.WriteFile(FILE_PATH_LITERAL("script.js"), kNonBlockingScript); |
594 const Extension* ext2 = LoadExtension(ext_dir2.UnpackedPath()); | 493 const Extension* ext2 = LoadExtension(ext_dir2.UnpackedPath()); |
595 ASSERT_TRUE(ext2); | 494 ASSERT_TRUE(ext2); |
596 | 495 |
597 content::WebContents* web_contents = | 496 content::WebContents* web_contents = |
598 browser()->tab_strip_model()->GetActiveWebContents(); | 497 browser()->tab_strip_model()->GetActiveWebContents(); |
599 DialogHelper dialog_helper(web_contents); | 498 JavaScriptDialogTabHelper* js_helper = |
600 base::RunLoop run_loop; | 499 JavaScriptDialogTabHelper::FromWebContents(web_contents); |
601 dialog_helper.set_quit_closure(run_loop.QuitClosure()); | 500 base::RunLoop dialog_wait; |
| 501 js_helper->SetDialogShownCallbackForTesting(dialog_wait.QuitClosure()); |
602 | 502 |
603 ExtensionTestMessageListener listener("done", false); | 503 ExtensionTestMessageListener listener("done", false); |
604 listener.set_extension_id(ext2->id()); | 504 listener.set_extension_id(ext2->id()); |
605 | 505 |
606 // Navitate! | 506 // Navigate! |
607 ui_test_utils::NavigateToURLWithDisposition( | 507 ui_test_utils::NavigateToURLWithDisposition( |
608 browser(), embedded_test_server()->GetURL("/empty.html"), | 508 browser(), embedded_test_server()->GetURL("/empty.html"), |
609 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); | 509 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); |
610 | 510 |
611 // Now, instead of closing the dialog, just close the tab. Later scripts | 511 // Now, instead of closing the dialog, just close the tab. Later scripts |
612 // should never get a chance to run (and we shouldn't crash). | 512 // should never get a chance to run (and we shouldn't crash). |
613 run_loop.Run(); | 513 dialog_wait.Run(); |
614 EXPECT_FALSE(listener.was_satisfied()); | 514 EXPECT_FALSE(listener.was_satisfied()); |
615 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt( | 515 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt( |
616 browser()->tab_strip_model()->active_index(), 0)); | 516 browser()->tab_strip_model()->active_index(), 0)); |
617 EXPECT_FALSE(listener.was_satisfied()); | 517 EXPECT_FALSE(listener.was_satisfied()); |
618 } | 518 } |
619 | 519 |
620 // There was a bug by which content scripts that blocked and ran on | 520 // There was a bug by which content scripts that blocked and ran on |
621 // document_idle could be injected twice (crbug.com/431263). Test for | 521 // document_idle could be injected twice (crbug.com/431263). Test for |
622 // regression. | 522 // regression. |
623 IN_PROC_BROWSER_TEST_P(ContentScriptApiTest, | 523 IN_PROC_BROWSER_TEST_P(ContentScriptApiTest, |
624 ContentScriptBlockingScriptsDontRunTwice) { | 524 ContentScriptBlockingScriptsDontRunTwice) { |
625 ASSERT_TRUE(StartEmbeddedTestServer()); | 525 ASSERT_TRUE(StartEmbeddedTestServer()); |
626 | 526 |
627 // Load up an extension. | 527 // Load up an extension. |
628 TestExtensionDir ext_dir1; | 528 TestExtensionDir ext_dir1; |
629 ext_dir1.WriteManifest( | 529 ext_dir1.WriteManifest( |
630 base::StringPrintf(kManifest, "ext1", "document_idle")); | 530 base::StringPrintf(kManifest, "ext1", "document_idle")); |
631 ext_dir1.WriteFile(FILE_PATH_LITERAL("script.js"), kBlockingScript); | 531 ext_dir1.WriteFile(FILE_PATH_LITERAL("script.js"), kBlockingScript); |
632 const Extension* ext1 = LoadExtension(ext_dir1.UnpackedPath()); | 532 const Extension* ext1 = LoadExtension(ext_dir1.UnpackedPath()); |
633 ASSERT_TRUE(ext1); | 533 ASSERT_TRUE(ext1); |
634 | 534 |
635 content::WebContents* web_contents = | 535 content::WebContents* web_contents = |
636 browser()->tab_strip_model()->GetActiveWebContents(); | 536 browser()->tab_strip_model()->GetActiveWebContents(); |
637 DialogHelper dialog_helper(web_contents); | 537 JavaScriptDialogTabHelper* js_helper = |
638 base::RunLoop run_loop; | 538 JavaScriptDialogTabHelper::FromWebContents(web_contents); |
639 dialog_helper.set_quit_closure(run_loop.QuitClosure()); | 539 base::RunLoop dialog_wait; |
| 540 js_helper->SetDialogShownCallbackForTesting(dialog_wait.QuitClosure()); |
640 | 541 |
641 // Navigate! | 542 // Navigate! |
642 ui_test_utils::NavigateToURLWithDisposition( | 543 ui_test_utils::NavigateToURLWithDisposition( |
643 browser(), embedded_test_server()->GetURL("/empty.html"), | 544 browser(), embedded_test_server()->GetURL("/empty.html"), |
644 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); | 545 WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); |
645 | 546 |
646 run_loop.Run(); | 547 dialog_wait.Run(); |
647 | 548 |
648 // The extension will have injected at idle, but it should only inject once. | 549 // The extension will have injected at idle, but it should only inject once. |
649 EXPECT_EQ(1u, dialog_helper.dialog_count()); | 550 js_helper->HandleJavaScriptDialog(web_contents, true, nullptr); |
650 dialog_helper.CloseDialogs(); | |
651 EXPECT_TRUE(RunAllPending(web_contents)); | 551 EXPECT_TRUE(RunAllPending(web_contents)); |
652 EXPECT_EQ(1u, dialog_helper.dialog_count()); | 552 EXPECT_FALSE(js_helper->IsShowingDialogForTesting()); |
653 } | 553 } |
654 | 554 |
655 // Bug fix for crbug.com/507461. | 555 // Bug fix for crbug.com/507461. |
656 IN_PROC_BROWSER_TEST_P(ContentScriptApiTest, | 556 IN_PROC_BROWSER_TEST_P(ContentScriptApiTest, |
657 DocumentStartInjectionFromExtensionTabNavigation) { | 557 DocumentStartInjectionFromExtensionTabNavigation) { |
658 ASSERT_TRUE(StartEmbeddedTestServer()); | 558 ASSERT_TRUE(StartEmbeddedTestServer()); |
659 | 559 |
660 TestExtensionDir new_tab_override_dir; | 560 TestExtensionDir new_tab_override_dir; |
661 new_tab_override_dir.WriteManifest(kNewTabOverrideManifest); | 561 new_tab_override_dir.WriteManifest(kNewTabOverrideManifest); |
662 new_tab_override_dir.WriteFile(FILE_PATH_LITERAL("newtab.html"), kNewTabHtml); | 562 new_tab_override_dir.WriteFile(FILE_PATH_LITERAL("newtab.html"), kNewTabHtml); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
708 EXPECT_FALSE(content_script_listener.was_satisfied()); | 608 EXPECT_FALSE(content_script_listener.was_satisfied()); |
709 } | 609 } |
710 | 610 |
711 INSTANTIATE_TEST_CASE_P( | 611 INSTANTIATE_TEST_CASE_P( |
712 ContentScriptApiTests, | 612 ContentScriptApiTests, |
713 ContentScriptApiTest, | 613 ContentScriptApiTest, |
714 testing::Values(TestConfig::kDefault, | 614 testing::Values(TestConfig::kDefault, |
715 TestConfig::kYieldBetweenContentScriptRunsEnabled)); | 615 TestConfig::kYieldBetweenContentScriptRunsEnabled)); |
716 | 616 |
717 } // namespace extensions | 617 } // namespace extensions |
OLD | NEW |