| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/scoped_command_line.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/tabs/split_tab_metrics.h" |
| #include "chrome/browser/ui/tabs/tab_enums.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/tabs/tab_icon.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/embedder_support/switches.h" |
| #include "components/javascript_dialogs/app_modal_dialog_manager.h" |
| #include "components/javascript_dialogs/tab_modal_dialog_manager.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/isolated_world_ids.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/metrics/public/cpp/ukm_source.h" |
| #include "third_party/blink/public/common/features.h" |
| |
| using DismissalCause = |
| javascript_dialogs::TabModalDialogManager::DismissalCause; |
| |
| class JavaScriptDialogTest : public InProcessBrowserTest { |
| public: |
| void SetUpOnMainThread() override { |
| InProcessBrowserTest::SetUpOnMainThread(); |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| private: |
| friend class JavaScriptDialogDismissalCauseTester; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, ReloadDoesntHang) { |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| javascript_dialogs::TabModalDialogManager* js_helper = |
| javascript_dialogs::TabModalDialogManager::FromWebContents(tab); |
| |
| // Show a dialog. |
| scoped_refptr<content::MessageLoopRunner> runner = |
| new content::MessageLoopRunner; |
| js_helper->SetDialogShownCallbackForTesting(runner->QuitClosure()); |
| tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests( |
| u"alert()", base::NullCallback(), content::ISOLATED_WORLD_ID_GLOBAL); |
| runner->Run(); |
| |
| // Try reloading. |
| tab->GetController().Reload(content::ReloadType::NORMAL, false); |
| EXPECT_TRUE(content::WaitForLoadStop(tab)); |
| |
| // If the WaitForLoadStop doesn't hang forever, we've passed. |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, |
| ClosingPageSharingRendererDoesntHang) { |
| // Turn off popup blocking. |
| base::test::ScopedCommandLine scoped_command_line; |
| scoped_command_line.GetProcessCommandLine()->AppendSwitch( |
| embedder_support::kDisablePopupBlocking); |
| |
| // Two tabs, one render process. |
| content::WebContents* tab1 = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ui_test_utils::AllBrowserTabAddedWaiter tab_added_waiter; |
| tab1->GetPrimaryMainFrame()->ExecuteJavaScriptForTests( |
| u"window.open('about:blank');", base::NullCallback(), |
| content::ISOLATED_WORLD_ID_GLOBAL); |
| content::WebContents* tab2 = tab_added_waiter.Wait(); |
| ASSERT_NE(tab1, tab2); |
| ASSERT_EQ(tab1->GetPrimaryMainFrame()->GetProcess(), |
| tab2->GetPrimaryMainFrame()->GetProcess()); |
| |
| // Tab two shows a dialog. |
| scoped_refptr<content::MessageLoopRunner> runner = |
| new content::MessageLoopRunner; |
| javascript_dialogs::TabModalDialogManager* js_helper2 = |
| javascript_dialogs::TabModalDialogManager::FromWebContents(tab2); |
| js_helper2->SetDialogShownCallbackForTesting(runner->QuitClosure()); |
| tab2->GetPrimaryMainFrame()->ExecuteJavaScriptForTests( |
| u"alert()", base::NullCallback(), content::ISOLATED_WORLD_ID_GLOBAL); |
| runner->Run(); |
| |
| // Tab two is closed while the dialog is up. |
| int tab2_index = browser()->tab_strip_model()->GetIndexOfWebContents(tab2); |
| browser()->tab_strip_model()->CloseWebContentsAt(tab2_index, |
| TabCloseTypes::CLOSE_NONE); |
| |
| // Try reloading tab one. |
| tab1->GetController().Reload(content::ReloadType::NORMAL, false); |
| EXPECT_TRUE(content::WaitForLoadStop(tab1)); |
| |
| // If the WaitForLoadStop doesn't hang forever, we've passed. |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, |
| ClosingPageWithSubframeAlertingDoesntCrash) { |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| javascript_dialogs::TabModalDialogManager* js_helper = |
| javascript_dialogs::TabModalDialogManager::FromWebContents(tab); |
| |
| // A subframe shows a dialog. |
| std::string dialog_url = "data:text/html,<script>alert(\"hi\");</script>"; |
| std::string script = |
| "var iframe = document.createElement('iframe');" |
| "iframe.src = '" + |
| dialog_url + |
| "';" |
| "document.body.appendChild(iframe);"; |
| scoped_refptr<content::MessageLoopRunner> runner = |
| new content::MessageLoopRunner; |
| js_helper->SetDialogShownCallbackForTesting(runner->QuitClosure()); |
| tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests( |
| base::UTF8ToUTF16(script), base::NullCallback(), |
| content::ISOLATED_WORLD_ID_GLOBAL); |
| runner->Run(); |
| |
| // The tab is closed while the dialog is up. |
| int tab_index = browser()->tab_strip_model()->GetIndexOfWebContents(tab); |
| browser()->tab_strip_model()->CloseWebContentsAt(tab_index, |
| TabCloseTypes::CLOSE_NONE); |
| |
| // No crash is good news. |
| } |
| |
| class JavaScriptCallbackHelper { |
| public: |
| javascript_dialogs::TabModalDialogManager::DialogClosedCallback |
| GetCallback() { |
| return base::BindOnce(&JavaScriptCallbackHelper::DialogClosed, |
| base::Unretained(this)); |
| } |
| |
| bool last_success() { return last_success_; } |
| std::u16string last_input() { return last_input_; } |
| |
| private: |
| void DialogClosed(bool success, const std::u16string& user_input) { |
| last_success_ = success; |
| last_input_ = user_input; |
| } |
| |
| bool last_success_; |
| std::u16string last_input_; |
| }; |
| |
| // Tests to make sure HandleJavaScriptDialog works correctly. |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, HandleJavaScriptDialog) { |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderFrameHost* frame = tab->GetPrimaryMainFrame(); |
| javascript_dialogs::TabModalDialogManager* js_helper = |
| javascript_dialogs::TabModalDialogManager::FromWebContents(tab); |
| |
| JavaScriptCallbackHelper callback_helper; |
| |
| // alert |
| bool did_suppress = false; |
| js_helper->RunJavaScriptDialog( |
| tab, frame, content::JAVASCRIPT_DIALOG_TYPE_ALERT, std::u16string(), |
| std::u16string(), callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| js_helper->HandleJavaScriptDialog(tab, true, nullptr); |
| ASSERT_FALSE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_TRUE(callback_helper.last_success()); |
| ASSERT_EQ(std::u16string(), callback_helper.last_input()); |
| |
| // confirm |
| for (auto response : {true, false}) { |
| js_helper->RunJavaScriptDialog( |
| tab, frame, content::JAVASCRIPT_DIALOG_TYPE_CONFIRM, std::u16string(), |
| std::u16string(), callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| js_helper->HandleJavaScriptDialog(tab, response, nullptr); |
| ASSERT_FALSE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_EQ(response, callback_helper.last_success()); |
| ASSERT_EQ(std::u16string(), callback_helper.last_input()); |
| } |
| |
| // prompt, cancel |
| js_helper->RunJavaScriptDialog( |
| tab, frame, content::JAVASCRIPT_DIALOG_TYPE_PROMPT, u"Label", |
| std::u16string(), callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| js_helper->HandleJavaScriptDialog(tab, false, nullptr); |
| ASSERT_FALSE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_FALSE(callback_helper.last_success()); |
| ASSERT_EQ(std::u16string(), callback_helper.last_input()); |
| |
| std::u16string value1 = u"abc"; |
| std::u16string value2 = u"123"; |
| |
| // prompt, ok + override |
| js_helper->RunJavaScriptDialog( |
| tab, frame, content::JAVASCRIPT_DIALOG_TYPE_PROMPT, u"Label", value1, |
| callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| js_helper->HandleJavaScriptDialog(tab, true, &value2); |
| ASSERT_FALSE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_TRUE(callback_helper.last_success()); |
| ASSERT_EQ(value2, callback_helper.last_input()); |
| |
| // prompt, ok + no override |
| js_helper->RunJavaScriptDialog( |
| tab, frame, content::JAVASCRIPT_DIALOG_TYPE_PROMPT, u"Label", value1, |
| callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| js_helper->HandleJavaScriptDialog(tab, true, nullptr); |
| ASSERT_FALSE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_TRUE(callback_helper.last_success()); |
| ASSERT_EQ(value1, callback_helper.last_input()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, JavascriptDialogFollowsModalUI) { |
| content::WebContents* tab_web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| tabs::TabInterface* tab_interface = |
| tabs::TabInterface::MaybeGetFromContents(tab_web_contents); |
| content::RenderFrameHost* frame = tab_web_contents->GetPrimaryMainFrame(); |
| javascript_dialogs::TabModalDialogManager* dialog_manager = |
| javascript_dialogs::TabModalDialogManager::FromWebContents( |
| tab_web_contents); |
| |
| // Open a modal dialog. |
| auto scoped_tab_modal_ui = tab_interface->ShowModalUI(); |
| |
| // Try to open a javascript dialog. |
| bool did_suppress = false; |
| dialog_manager->RunJavaScriptDialog( |
| tab_web_contents, frame, content::JAVASCRIPT_DIALOG_TYPE_ALERT, |
| std::u16string(), std::u16string(), base::NullCallback(), &did_suppress); |
| |
| // Verify the dialog is not shown. |
| EXPECT_FALSE(dialog_manager->IsShowingDialogForTesting()); |
| |
| // Close the modal dialog. |
| scoped_tab_modal_ui.reset(); |
| |
| // Open a javascript dialog. |
| dialog_manager->RunJavaScriptDialog( |
| tab_web_contents, frame, content::JAVASCRIPT_DIALOG_TYPE_ALERT, |
| std::u16string(), std::u16string(), base::NullCallback(), &did_suppress); |
| |
| // Verify the javascript dialog is shown. |
| EXPECT_TRUE(dialog_manager->IsShowingDialogForTesting()); |
| |
| // Verify a model dialog cannot be shown. |
| EXPECT_FALSE(tab_interface->CanShowModalUI()); |
| } |
| |
| class JavaScriptDialogDismissalCauseTester { |
| public: |
| explicit JavaScriptDialogDismissalCauseTester(JavaScriptDialogTest* test) |
| : tab_(test->browser()->tab_strip_model()->GetActiveWebContents()), |
| frame_(tab_->GetPrimaryMainFrame()), |
| js_helper_( |
| javascript_dialogs::TabModalDialogManager::FromWebContents(tab_)) { |
| js_helper_->SetDialogDismissedCallbackForTesting(base::BindOnce( |
| &JavaScriptDialogDismissalCauseTester::SetLastDismissalCause, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void PopupDialog(content::JavaScriptDialogType type) { |
| bool did_suppress = false; |
| js_helper_->RunJavaScriptDialog(tab_, frame_, type, u"Label", u"abc", {}, |
| &did_suppress); |
| } |
| |
| void ClickDialogButton(bool accept, const std::u16string& user_input) { |
| EXPECT_TRUE(js_helper_->IsShowingDialogForTesting()); |
| js_helper_->ClickDialogButtonForTesting(accept, user_input); |
| } |
| |
| void Reload() { |
| tab_->GetController().Reload(content::ReloadType::NORMAL, false); |
| EXPECT_TRUE(content::WaitForLoadStop(tab_)); |
| } |
| |
| void CallHandleDialog(bool accept, const std::u16string* prompt_override) { |
| EXPECT_TRUE(js_helper_->IsShowingDialogForTesting()); |
| js_helper_->HandleJavaScriptDialog(tab_, accept, prompt_override); |
| } |
| |
| void CallCancelDialogs(bool reset_state) { |
| EXPECT_TRUE(js_helper_->IsShowingDialogForTesting()); |
| js_helper_->CancelDialogs(tab_, reset_state); |
| } |
| |
| std::optional<DismissalCause> GetLastDismissalCause() { |
| return dismissal_cause_; |
| } |
| |
| void SetLastDismissalCause(DismissalCause cause) { dismissal_cause_ = cause; } |
| |
| private: |
| raw_ptr<content::WebContents, DanglingUntriaged> tab_; |
| raw_ptr<content::RenderFrameHost, DanglingUntriaged> frame_; |
| raw_ptr<javascript_dialogs::TabModalDialogManager, DanglingUntriaged> |
| js_helper_; |
| |
| std::optional<DismissalCause> dismissal_cause_; |
| |
| base::WeakPtrFactory<JavaScriptDialogDismissalCauseTester> weak_factory_{ |
| this}; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, DismissalCausePromptAcceptButton) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| tester.ClickDialogButton(true, std::u16string()); |
| EXPECT_EQ(DismissalCause::kDialogButtonClicked, |
| tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, DismissalCausePromptCancelButton) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| tester.ClickDialogButton(false, std::u16string()); |
| EXPECT_EQ(DismissalCause::kDialogButtonClicked, |
| tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, DismissalCausePromptHandleDialog) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| tester.CallHandleDialog(true, nullptr); |
| EXPECT_EQ(DismissalCause::kHandleDialogCalled, |
| tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, |
| DismissalCausePromptCancelDialogs) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| tester.CallCancelDialogs(false); |
| EXPECT_EQ(DismissalCause::kCancelDialogsCalled, |
| tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, |
| DismissalCausePromptTabClosedByUser) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| chrome::CloseTab(browser()); |
| // There are differences in the implementations of Views on different platforms |
| // that cause different dismissal causes. |
| #if BUILDFLAG(IS_MAC) |
| // On MacOS 10.13, |kDialogClosed| is logged, while for other versions |
| // |kCancelDialogsCalled| is logged. Expect only one but not both. |
| EXPECT_TRUE(tester.GetLastDismissalCause() == |
| DismissalCause::kCancelDialogsCalled xor |
| tester.GetLastDismissalCause() == DismissalCause::kDialogClosed); |
| #else |
| EXPECT_EQ(DismissalCause::kDialogClosed, tester.GetLastDismissalCause()); |
| #endif |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, DismissalCausePromptTabHidden) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| chrome::NewTab(browser()); |
| EXPECT_EQ(DismissalCause::kTabHidden, tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, |
| DismissalCausePromptBrowserSwitched) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| ui_test_utils::OpenNewEmptyWindowAndWaitUntilActivated(browser()->profile()); |
| EXPECT_EQ(DismissalCause::kBrowserSwitched, tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, DismissalCausePromptTabNavigated) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| tester.Reload(); |
| EXPECT_EQ(DismissalCause::kTabNavigated, tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, |
| DismissalCausePromptSubsequentDialogShown) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_PROMPT); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_ALERT); |
| EXPECT_EQ(DismissalCause::kSubsequentDialogShown, |
| tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, NoDismissalAlertTabHidden) { |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_ALERT); |
| chrome::NewTab(browser()); |
| EXPECT_EQ(std::nullopt, tester.GetLastDismissalCause()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogTest, DismissalCauseUkm) { |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| GURL url = embedded_test_server()->GetURL("/title1.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| JavaScriptDialogDismissalCauseTester tester(this); |
| tester.PopupDialog(content::JAVASCRIPT_DIALOG_TYPE_CONFIRM); |
| tester.ClickDialogButton(true, std::u16string()); |
| |
| auto entries = ukm_recorder.GetEntriesByName( |
| ukm::builders::AbusiveExperienceHeuristic_JavaScriptDialog::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| ukm_recorder.ExpectEntrySourceHasUrl(entries.front(), url); |
| ukm_recorder.ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AbusiveExperienceHeuristic_JavaScriptDialog:: |
| kDismissalCauseName, |
| static_cast<int64_t>(DismissalCause::kDialogButtonClicked)); |
| } |
| |
| class JavaScriptDialogOriginTest |
| : public JavaScriptDialogTest, |
| public testing::WithParamInterface<const char*> {}; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| /* no prefix */, |
| JavaScriptDialogOriginTest, |
| ::testing::Values("data:text/html,<p></p>", |
| "javascript:undefined", |
| "about:blank")); |
| |
| // Tests that the title for a dialog generated from a page with a non-HTTP URL |
| // that was spawned by an HTTP URL has that HTTP URL used for the title. |
| IN_PROC_BROWSER_TEST_P(JavaScriptDialogOriginTest, TitleForNonHTTPOrigin) { |
| GURL url = embedded_test_server()->GetURL("a.com", "/title1.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Create a subframe. |
| content::TestNavigationObserver nav_observer(tab); |
| GURL test_url(GetParam()); |
| std::string script = content::JsReplace(R"( |
| var iframe = document.createElement('iframe'); |
| iframe.src = $1; |
| document.body.appendChild(iframe);)", |
| test_url); |
| ASSERT_TRUE(content::ExecJs(tab, script)); |
| if (!test_url.SchemeIs("javascript")) { |
| // content::TestNavigationObserver times out if asked to wait for the |
| // loading of a javascript: URL. |
| nav_observer.Wait(); |
| } |
| |
| content::RenderFrameHost* subframe = |
| content::ChildFrameAt(tab->GetPrimaryMainFrame(), 0); |
| ASSERT_TRUE(subframe); |
| |
| // Verify the title that would be used for a dialog spawned by that subframe. |
| javascript_dialogs::AppModalDialogManager* dialog_manager = |
| javascript_dialogs::AppModalDialogManager::GetInstance(); |
| EXPECT_EQ(base::UTF8ToUTF16(base::StringPrintf( |
| "a.com:%d says", embedded_test_server()->port())), |
| dialog_manager->GetTitle(tab, subframe->GetLastCommittedOrigin())); |
| } |
| |
| class JavaScriptDialogForSplitViewTest : public JavaScriptDialogTest { |
| public: |
| JavaScriptDialogForSplitViewTest() { |
| scoped_feature_list_.InitWithFeatures({features::kSideBySide}, {}); |
| } |
| |
| TabStripModel* tab_strip_model() { return browser()->tab_strip_model(); } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogForSplitViewTest, |
| ActivatesTabWithDialogWhenSwappingIntoSplit) { |
| // Create three tabs with the first two in a split view. |
| chrome::NewTab(browser()); |
| tab_strip_model()->ActivateTabAt(0); |
| chrome::NewSplitTab(browser(), |
| split_tabs::SplitTabCreatedSource::kToolbarButton); |
| |
| // Open a alert dialog from the third tab. |
| tab_strip_model()->ActivateTabAt(2); |
| content::WebContents* web_contents = |
| tab_strip_model()->GetActiveWebContents(); |
| javascript_dialogs::TabModalDialogManager* js_helper = |
| javascript_dialogs::TabModalDialogManager::FromWebContents(web_contents); |
| JavaScriptCallbackHelper callback_helper; |
| bool did_suppress = false; |
| js_helper->RunJavaScriptDialog( |
| web_contents, web_contents->GetPrimaryMainFrame(), |
| content::JAVASCRIPT_DIALOG_TYPE_ALERT, std::u16string(), std::u16string(), |
| callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| |
| // Switch to the split view which should hide the dialog and show tab |
| // attention indicator. |
| tab_strip_model()->ActivateTabAt(0); |
| ASSERT_TRUE(browser() |
| ->GetBrowserView() |
| .tabstrip() |
| ->tab_at(2) |
| ->GetTabIconForTesting() |
| ->GetShowingAttentionIndicator()); |
| |
| // Swapping the third tab with the inactive tab in the split should cause that |
| // tab to become active. The dialog will still be showing. |
| tab_strip_model()->UpdateTabInSplit(tab_strip_model()->GetTabAtIndex(1), 2, |
| TabStripModel::SplitUpdateType::kSwap); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_EQ(1, tab_strip_model()->active_index()); |
| |
| // Triggering the tab without the dialog shouldn't work. |
| tab_strip_model()->ActivateTabAt(0); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| ASSERT_EQ(1, tab_strip_model()->active_index()); |
| } |
| |
| class JavaScriptDialogForPrerenderTest : public JavaScriptDialogTest { |
| public: |
| JavaScriptDialogForPrerenderTest() |
| : prerender_helper_( |
| base::BindRepeating(&JavaScriptDialogForPrerenderTest::web_contents, |
| base::Unretained(this))) {} |
| |
| void SetUp() override { |
| prerender_helper_.RegisterServerRequestMonitor(embedded_test_server()); |
| JavaScriptDialogTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| web_contents_ = browser()->tab_strip_model()->GetActiveWebContents(); |
| JavaScriptDialogTest::SetUpOnMainThread(); |
| } |
| |
| content::WebContents* web_contents() { return web_contents_; } |
| |
| protected: |
| raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> web_contents_ = |
| nullptr; |
| content::test::PrerenderTestHelper prerender_helper_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(JavaScriptDialogForPrerenderTest, NoDismissalDialog) { |
| GURL url(embedded_test_server()->GetURL("/empty.html")); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| javascript_dialogs::TabModalDialogManager* js_helper = |
| javascript_dialogs::TabModalDialogManager::FromWebContents(web_contents_); |
| JavaScriptCallbackHelper callback_helper; |
| bool did_suppress = false; |
| |
| GURL prerender_url = embedded_test_server()->GetURL("/title1.html"); |
| |
| // Prerender to another site. |
| prerender_helper_.AddPrerenderAsync(prerender_url); |
| |
| // Show an alert dialog |
| js_helper->RunJavaScriptDialog( |
| web_contents_, web_contents_->GetPrimaryMainFrame(), |
| content::JAVASCRIPT_DIALOG_TYPE_ALERT, std::u16string(), std::u16string(), |
| callback_helper.GetCallback(), &did_suppress); |
| ASSERT_TRUE(js_helper->IsShowingDialogForTesting()); |
| |
| prerender_helper_.WaitForPrerenderLoadCompletion(prerender_url); |
| |
| EXPECT_TRUE(js_helper->IsShowingDialogForTesting()); |
| |
| // Navigate to the prerendered site. |
| prerender_helper_.NavigatePrimaryPage(prerender_url); |
| |
| EXPECT_FALSE(js_helper->IsShowingDialogForTesting()); |
| } |