| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "content/browser/frame_host/render_frame_host_impl.h" | 5 #include "content/browser/frame_host/render_frame_host_impl.h" |
| 6 | 6 |
| 7 #include "base/macros.h" | 7 #include "base/macros.h" |
| 8 #include "content/browser/web_contents/web_contents_impl.h" | 8 #include "content/browser/web_contents/web_contents_impl.h" |
| 9 #include "content/common/frame_messages.h" | |
| 10 #include "content/public/browser/javascript_dialog_manager.h" | |
| 11 #include "content/public/browser/render_frame_host.h" | 9 #include "content/public/browser/render_frame_host.h" |
| 12 #include "content/public/browser/web_contents.h" | 10 #include "content/public/browser/web_contents.h" |
| 13 #include "content/public/common/content_client.h" | 11 #include "content/public/common/content_client.h" |
| 14 #include "content/public/test/browser_test_utils.h" | |
| 15 #include "content/public/test/content_browser_test.h" | 12 #include "content/public/test/content_browser_test.h" |
| 16 #include "content/public/test/content_browser_test_utils.h" | 13 #include "content/public/test/content_browser_test_utils.h" |
| 17 #include "content/public/test/test_utils.h" | 14 #include "content/public/test/test_utils.h" |
| 18 #include "content/shell/browser/shell.h" | 15 #include "content/shell/browser/shell.h" |
| 19 #include "content/test/test_content_browser_client.h" | 16 #include "content/test/test_content_browser_client.h" |
| 20 | 17 |
| 21 namespace content { | 18 namespace content { |
| 22 | 19 |
| 23 namespace { | 20 namespace { |
| 24 | 21 |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 152 EXPECT_EQ(blink::WebPageVisibilityStateVisible, | 149 EXPECT_EQ(blink::WebPageVisibilityStateVisible, |
| 153 web_contents->GetMainFrame()->GetVisibilityState()); | 150 web_contents->GetMainFrame()->GetVisibilityState()); |
| 154 | 151 |
| 155 new_client.EnableVisibilityOverride(blink::WebPageVisibilityStatePrerender); | 152 new_client.EnableVisibilityOverride(blink::WebPageVisibilityStatePrerender); |
| 156 EXPECT_EQ(blink::WebPageVisibilityStatePrerender, | 153 EXPECT_EQ(blink::WebPageVisibilityStatePrerender, |
| 157 web_contents->GetMainFrame()->GetVisibilityState()); | 154 web_contents->GetMainFrame()->GetVisibilityState()); |
| 158 | 155 |
| 159 SetBrowserClientForTesting(old_client); | 156 SetBrowserClientForTesting(old_client); |
| 160 } | 157 } |
| 161 | 158 |
| 162 namespace { | |
| 163 | |
| 164 class TestJavaScriptDialogManager : public JavaScriptDialogManager, | |
| 165 public WebContentsDelegate { | |
| 166 public: | |
| 167 TestJavaScriptDialogManager() : message_loop_runner_(new MessageLoopRunner) {} | |
| 168 ~TestJavaScriptDialogManager() override {} | |
| 169 | |
| 170 void Wait() { | |
| 171 message_loop_runner_->Run(); | |
| 172 message_loop_runner_ = new MessageLoopRunner; | |
| 173 } | |
| 174 | |
| 175 DialogClosedCallback& callback() { return callback_; } | |
| 176 | |
| 177 // WebContentsDelegate | |
| 178 | |
| 179 JavaScriptDialogManager* GetJavaScriptDialogManager( | |
| 180 WebContents* source) override { | |
| 181 return this; | |
| 182 } | |
| 183 | |
| 184 // JavaScriptDialogManager | |
| 185 | |
| 186 void RunJavaScriptDialog(WebContents* web_contents, | |
| 187 const GURL& origin_url, | |
| 188 JavaScriptDialogType dialog_type, | |
| 189 const base::string16& message_text, | |
| 190 const base::string16& default_prompt_text, | |
| 191 const DialogClosedCallback& callback, | |
| 192 bool* did_suppress_message) override {} | |
| 193 | |
| 194 void RunBeforeUnloadDialog(WebContents* web_contents, | |
| 195 bool is_reload, | |
| 196 const DialogClosedCallback& callback) override { | |
| 197 callback_ = callback; | |
| 198 message_loop_runner_->Quit(); | |
| 199 } | |
| 200 | |
| 201 bool HandleJavaScriptDialog(WebContents* web_contents, | |
| 202 bool accept, | |
| 203 const base::string16* prompt_override) override { | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 void CancelDialogs(WebContents* web_contents, bool reset_state) override {} | |
| 208 | |
| 209 private: | |
| 210 DialogClosedCallback callback_; | |
| 211 | |
| 212 // The MessageLoopRunner used to spin the message loop. | |
| 213 scoped_refptr<MessageLoopRunner> message_loop_runner_; | |
| 214 | |
| 215 DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager); | |
| 216 }; | |
| 217 | |
| 218 class DropBeforeUnloadACKFilter : public BrowserMessageFilter { | |
| 219 public: | |
| 220 DropBeforeUnloadACKFilter() : BrowserMessageFilter(FrameMsgStart) {} | |
| 221 | |
| 222 protected: | |
| 223 ~DropBeforeUnloadACKFilter() override {} | |
| 224 | |
| 225 private: | |
| 226 // BrowserMessageFilter: | |
| 227 bool OnMessageReceived(const IPC::Message& message) override { | |
| 228 return message.type() == FrameHostMsg_BeforeUnload_ACK::ID; | |
| 229 } | |
| 230 | |
| 231 DISALLOW_COPY_AND_ASSIGN(DropBeforeUnloadACKFilter); | |
| 232 }; | |
| 233 | |
| 234 } // namespace | |
| 235 | |
| 236 // Tests that a beforeunload dialog in an iframe doesn't stop the beforeunload | |
| 237 // timer of a parent frame. | |
| 238 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, | |
| 239 IframeBeforeUnloadParentHang) { | |
| 240 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); | |
| 241 TestJavaScriptDialogManager dialog_manager; | |
| 242 wc->SetDelegate(&dialog_manager); | |
| 243 | |
| 244 EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank"))); | |
| 245 // Make an iframe with a beforeunload handler. | |
| 246 std::string script = | |
| 247 "var iframe = document.createElement('iframe');" | |
| 248 "document.body.appendChild(iframe);" | |
| 249 "iframe.contentWindow.onbeforeunload=function(e){return 'x'};"; | |
| 250 EXPECT_TRUE(content::ExecuteScript(wc, script)); | |
| 251 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | |
| 252 | |
| 253 // Force a process switch by going to a privileged page. The beforeunload | |
| 254 // timer will be started on the top-level frame but will be paused while the | |
| 255 // beforeunload dialog is shown by the subframe. | |
| 256 GURL web_ui_page(std::string(kChromeUIScheme) + "://" + | |
| 257 std::string(kChromeUIGpuHost)); | |
| 258 shell()->LoadURL(web_ui_page); | |
| 259 dialog_manager.Wait(); | |
| 260 | |
| 261 RenderFrameHostImpl* main_frame = | |
| 262 static_cast<RenderFrameHostImpl*>(wc->GetMainFrame()); | |
| 263 EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_ack()); | |
| 264 | |
| 265 // Set up a filter to make sure that when the dialog is answered below and the | |
| 266 // renderer sends the beforeunload ACK, it gets... ahem... lost. | |
| 267 scoped_refptr<DropBeforeUnloadACKFilter> filter = | |
| 268 new DropBeforeUnloadACKFilter(); | |
| 269 main_frame->GetProcess()->AddFilter(filter.get()); | |
| 270 | |
| 271 // Answer the dialog. | |
| 272 dialog_manager.callback().Run(true, base::string16()); | |
| 273 | |
| 274 // There will be no beforeunload ACK, so if the beforeunload ACK timer isn't | |
| 275 // functioning then the navigation will hang forever and this test will time | |
| 276 // out. If this waiting for the load stop works, this test won't time out. | |
| 277 EXPECT_TRUE(WaitForLoadStop(wc)); | |
| 278 EXPECT_EQ(web_ui_page, wc->GetLastCommittedURL()); | |
| 279 | |
| 280 wc->SetDelegate(nullptr); | |
| 281 wc->SetJavaScriptDialogManagerForTesting(nullptr); | |
| 282 } | |
| 283 | |
| 284 } // namespace content | 159 } // namespace content |
| OLD | NEW |