| Index: content/browser/frame_host/render_frame_host_impl_browsertest.cc
|
| diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
|
| index 6547788162e29da908890e6a50eea3035d1c3ede..5f467554e62c1979c685e02fcf56874736d739e0 100644
|
| --- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
|
| +++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
|
| @@ -6,9 +6,12 @@
|
|
|
| #include "base/macros.h"
|
| #include "content/browser/web_contents/web_contents_impl.h"
|
| +#include "content/common/frame_messages.h"
|
| +#include "content/public/browser/javascript_dialog_manager.h"
|
| #include "content/public/browser/render_frame_host.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "content/public/common/content_client.h"
|
| +#include "content/public/test/browser_test_utils.h"
|
| #include "content/public/test/content_browser_test.h"
|
| #include "content/public/test/content_browser_test_utils.h"
|
| #include "content/public/test/test_utils.h"
|
| @@ -156,4 +159,126 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
|
| SetBrowserClientForTesting(old_client);
|
| }
|
|
|
| +namespace {
|
| +
|
| +class TestJavaScriptDialogManager : public JavaScriptDialogManager,
|
| + public WebContentsDelegate {
|
| + public:
|
| + TestJavaScriptDialogManager() : message_loop_runner_(new MessageLoopRunner) {}
|
| + ~TestJavaScriptDialogManager() override {}
|
| +
|
| + void Wait() {
|
| + message_loop_runner_->Run();
|
| + message_loop_runner_ = new MessageLoopRunner;
|
| + }
|
| +
|
| + DialogClosedCallback& callback() { return callback_; }
|
| +
|
| + // WebContentsDelegate
|
| +
|
| + JavaScriptDialogManager* GetJavaScriptDialogManager(
|
| + WebContents* source) override {
|
| + return this;
|
| + }
|
| +
|
| + // JavaScriptDialogManager
|
| +
|
| + void RunJavaScriptDialog(WebContents* web_contents,
|
| + const GURL& origin_url,
|
| + JavaScriptDialogType dialog_type,
|
| + const base::string16& message_text,
|
| + const base::string16& default_prompt_text,
|
| + const DialogClosedCallback& callback,
|
| + bool* did_suppress_message) override {}
|
| +
|
| + void RunBeforeUnloadDialog(WebContents* web_contents,
|
| + bool is_reload,
|
| + const DialogClosedCallback& callback) override {
|
| + callback_ = callback;
|
| + message_loop_runner_->Quit();
|
| + }
|
| +
|
| + bool HandleJavaScriptDialog(WebContents* web_contents,
|
| + bool accept,
|
| + const base::string16* prompt_override) override {
|
| + return true;
|
| + }
|
| +
|
| + void CancelDialogs(WebContents* web_contents, bool reset_state) override {}
|
| +
|
| + private:
|
| + DialogClosedCallback callback_;
|
| +
|
| + // The MessageLoopRunner used to spin the message loop.
|
| + scoped_refptr<MessageLoopRunner> message_loop_runner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager);
|
| +};
|
| +
|
| +class DropBeforeUnloadACKFilter : public BrowserMessageFilter {
|
| + public:
|
| + DropBeforeUnloadACKFilter() : BrowserMessageFilter(FrameMsgStart) {}
|
| +
|
| + protected:
|
| + ~DropBeforeUnloadACKFilter() override {}
|
| +
|
| + private:
|
| + // BrowserMessageFilter:
|
| + bool OnMessageReceived(const IPC::Message& message) override {
|
| + return message.type() == FrameHostMsg_BeforeUnload_ACK::ID;
|
| + }
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DropBeforeUnloadACKFilter);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// Tests that a beforeunload dialog in an iframe doesn't stop the beforeunload
|
| +// timer of a parent frame.
|
| +IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
|
| + IframeBeforeUnloadParentHang) {
|
| + WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
|
| + TestJavaScriptDialogManager dialog_manager;
|
| + wc->SetDelegate(&dialog_manager);
|
| +
|
| + EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
|
| + // Make an iframe with a beforeunload handler.
|
| + std::string script =
|
| + "var iframe = document.createElement('iframe');"
|
| + "document.body.appendChild(iframe);"
|
| + "iframe.contentWindow.onbeforeunload=function(e){return 'x'};";
|
| + EXPECT_TRUE(content::ExecuteScript(wc, script));
|
| + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
|
| +
|
| + // Force a process switch by going to a privileged page. The beforeunload
|
| + // timer will be started on the top-level frame but will be paused while the
|
| + // beforeunload dialog is shown by the subframe.
|
| + GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
|
| + std::string(kChromeUIGpuHost));
|
| + shell()->LoadURL(web_ui_page);
|
| + dialog_manager.Wait();
|
| +
|
| + RenderFrameHostImpl* main_frame =
|
| + static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
|
| + EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_ack());
|
| +
|
| + // Set up a filter to make sure that when the dialog is answered below and the
|
| + // renderer sends the beforeunload ACK, it gets... ahem... lost.
|
| + scoped_refptr<DropBeforeUnloadACKFilter> filter =
|
| + new DropBeforeUnloadACKFilter();
|
| + main_frame->GetProcess()->AddFilter(filter.get());
|
| +
|
| + // Answer the dialog.
|
| + dialog_manager.callback().Run(true, base::string16());
|
| +
|
| + // There will be no beforeunload ACK, so if the beforeunload ACK timer isn't
|
| + // functioning then the navigation will hang forever and this test will time
|
| + // out. If this waiting for the load stop works, this test won't time out.
|
| + EXPECT_TRUE(WaitForLoadStop(wc));
|
| + EXPECT_EQ(web_ui_page, wc->GetLastCommittedURL());
|
| +
|
| + wc->SetDelegate(nullptr);
|
| + wc->SetJavaScriptDialogManagerForTesting(nullptr);
|
| +}
|
| +
|
| } // namespace content
|
|
|