Fix build break
[chromium-blink-merge.git] / chrome / browser / unload_browsertest.cc
blob0be5f1f72986ac76302fd05ad1371ae81d693951
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 #if defined(OS_POSIX)
6 #include <signal.h>
7 #endif
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/process_util.h"
12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/net/url_request_mock_util.h"
14 #include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
15 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_commands.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/common/chrome_notification_types.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/test/base/in_process_browser_test.h"
23 #include "chrome/test/base/ui_test_utils.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/test/browser_test_utils.h"
28 #include "content/test/net/url_request_mock_http_job.h"
29 #include "net/url_request/url_request_test_util.h"
31 using base::TimeDelta;
32 using content::BrowserThread;
34 const std::string NOLISTENERS_HTML =
35 "<html><head><title>nolisteners</title></head><body></body></html>";
37 const std::string UNLOAD_HTML =
38 "<html><head><title>unload</title></head><body>"
39 "<script>window.onunload=function(e){}</script></body></html>";
41 const std::string BEFORE_UNLOAD_HTML =
42 "<html><head><title>beforeunload</title></head><body>"
43 "<script>window.onbeforeunload=function(e){"
44 "setTimeout('document.title=\"cancelled\"', 0);return 'foo'}</script>"
45 "</body></html>";
47 const std::string INNER_FRAME_WITH_FOCUS_HTML =
48 "<html><head><title>innerframewithfocus</title></head><body>"
49 "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
50 "<iframe src=\"data:text/html,<html><head><script>window.onload="
51 "function(){document.getElementById('box').focus()}</script>"
52 "<body><input id='box'></input></body></html>\"></iframe>"
53 "</body></html>";
55 const std::string TWO_SECOND_BEFORE_UNLOAD_HTML =
56 "<html><head><title>twosecondbeforeunload</title></head><body>"
57 "<script>window.onbeforeunload=function(e){"
58 "var start = new Date().getTime();"
59 "while(new Date().getTime() - start < 2000){}"
60 "return 'foo';"
61 "}</script></body></html>";
63 const std::string INFINITE_UNLOAD_HTML =
64 "<html><head><title>infiniteunload</title></head><body>"
65 "<script>window.onunload=function(e){while(true){}}</script>"
66 "</body></html>";
68 const std::string INFINITE_BEFORE_UNLOAD_HTML =
69 "<html><head><title>infinitebeforeunload</title></head><body>"
70 "<script>window.onbeforeunload=function(e){while(true){}}</script>"
71 "</body></html>";
73 const std::string INFINITE_UNLOAD_ALERT_HTML =
74 "<html><head><title>infiniteunloadalert</title></head><body>"
75 "<script>window.onunload=function(e){"
76 "while(true){}"
77 "alert('foo');"
78 "}</script></body></html>";
80 const std::string INFINITE_BEFORE_UNLOAD_ALERT_HTML =
81 "<html><head><title>infinitebeforeunloadalert</title></head><body>"
82 "<script>window.onbeforeunload=function(e){"
83 "while(true){}"
84 "alert('foo');"
85 "}</script></body></html>";
87 const std::string TWO_SECOND_UNLOAD_ALERT_HTML =
88 "<html><head><title>twosecondunloadalert</title></head><body>"
89 "<script>window.onunload=function(e){"
90 "var start = new Date().getTime();"
91 "while(new Date().getTime() - start < 2000){}"
92 "alert('foo');"
93 "}</script></body></html>";
95 const std::string TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML =
96 "<html><head><title>twosecondbeforeunloadalert</title></head><body>"
97 "<script>window.onbeforeunload=function(e){"
98 "var start = new Date().getTime();"
99 "while(new Date().getTime() - start < 2000){}"
100 "alert('foo');"
101 "}</script></body></html>";
103 const std::string CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER =
104 "<html><head><title>only_one_unload</title></head>"
105 "<body onclick=\"window.open('data:text/html,"
106 "<html><head><title>popup</title></head></body>')\" "
107 "onbeforeunload='return;'>"
108 "</body></html>";
110 class UnloadTest : public InProcessBrowserTest {
111 public:
112 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
113 const testing::TestInfo* const test_info =
114 testing::UnitTest::GetInstance()->current_test_info();
115 if (strcmp(test_info->name(),
116 "BrowserCloseTabWhenOtherTabHasListener") == 0) {
117 command_line->AppendSwitch(switches::kDisablePopupBlocking);
118 } else if (strcmp(test_info->name(), "BrowserTerminateBeforeUnload") == 0) {
119 #if defined(OS_POSIX)
120 DisableSIGTERMHandling();
121 #endif
125 virtual void SetUpOnMainThread() OVERRIDE {
126 BrowserThread::PostTask(
127 BrowserThread::IO, FROM_HERE,
128 base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
131 void CheckTitle(const char* expected_title) {
132 string16 expected = ASCIIToUTF16(expected_title);
133 EXPECT_EQ(expected,
134 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
137 void NavigateToDataURL(const std::string& html_content,
138 const char* expected_title) {
139 ui_test_utils::NavigateToURL(browser(),
140 GURL("data:text/html," + html_content));
141 CheckTitle(expected_title);
144 void NavigateToNolistenersFileTwice() {
145 GURL url(content::URLRequestMockHTTPJob::GetMockUrl(
146 base::FilePath(FILE_PATH_LITERAL("title2.html"))));
147 ui_test_utils::NavigateToURL(browser(), url);
148 CheckTitle("Title Of Awesomeness");
149 ui_test_utils::NavigateToURL(browser(), url);
150 CheckTitle("Title Of Awesomeness");
153 // Navigates to a URL asynchronously, then again synchronously. The first
154 // load is purposely async to test the case where the user loads another
155 // page without waiting for the first load to complete.
156 void NavigateToNolistenersFileTwiceAsync() {
157 GURL url(content::URLRequestMockHTTPJob::GetMockUrl(
158 base::FilePath(FILE_PATH_LITERAL("title2.html"))));
159 ui_test_utils::NavigateToURLWithDisposition(browser(), url, CURRENT_TAB, 0);
160 ui_test_utils::NavigateToURL(browser(), url);
161 CheckTitle("Title Of Awesomeness");
164 void LoadUrlAndQuitBrowser(const std::string& html_content,
165 const char* expected_title) {
166 NavigateToDataURL(html_content, expected_title);
167 content::WindowedNotificationObserver window_observer(
168 chrome::NOTIFICATION_BROWSER_CLOSED,
169 content::NotificationService::AllSources());
170 chrome::CloseWindow(browser());
171 window_observer.Wait();
174 // If |accept| is true, simulates user clicking OK, otherwise simulates
175 // clicking Cancel.
176 void ClickModalDialogButton(bool accept) {
177 AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
178 ASSERT_TRUE(dialog->IsJavaScriptModalDialog());
179 JavaScriptAppModalDialog* js_dialog =
180 static_cast<JavaScriptAppModalDialog*>(dialog);
181 if (accept)
182 js_dialog->native_dialog()->AcceptAppModalDialog();
183 else
184 js_dialog->native_dialog()->CancelAppModalDialog();
188 // Navigate to a page with an infinite unload handler.
189 // Then two async crosssite requests to ensure
190 // we don't get confused and think we're closing the tab.
192 // This test is flaky on the valgrind UI bots. http://crbug.com/39057
193 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadAsync) {
194 // Tests makes no sense in single-process mode since the renderer is hung.
195 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
196 return;
198 NavigateToDataURL(INFINITE_UNLOAD_HTML, "infiniteunload");
199 // Must navigate to a non-data URL to trigger cross-site codepath.
200 NavigateToNolistenersFileTwiceAsync();
203 // Navigate to a page with an infinite unload handler.
204 // Then two sync crosssite requests to ensure
205 // we correctly nav to each one.
206 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
207 // Tests makes no sense in single-process mode since the renderer is hung.
208 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
209 return;
211 NavigateToDataURL(INFINITE_UNLOAD_HTML, "infiniteunload");
212 // Must navigate to a non-data URL to trigger cross-site codepath.
213 NavigateToNolistenersFileTwice();
216 // Navigate to a page with an infinite beforeunload handler.
217 // Then two two async crosssite requests to ensure
218 // we don't get confused and think we're closing the tab.
219 // This test is flaky on the valgrind UI bots. http://crbug.com/39057 and
220 // http://crbug.com/86469
221 IN_PROC_BROWSER_TEST_F(UnloadTest,
222 DISABLED_CrossSiteInfiniteBeforeUnloadAsync) {
223 // Tests makes no sense in single-process mode since the renderer is hung.
224 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
225 return;
227 NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
228 // Must navigate to a non-data URL to trigger cross-site codepath.
229 NavigateToNolistenersFileTwiceAsync();
232 // Navigate to a page with an infinite beforeunload handler.
233 // Then two two sync crosssite requests to ensure
234 // we correctly nav to each one.
235 // If this flakes, reopen bug http://crbug.com/86469.
236 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_CrossSiteInfiniteBeforeUnloadSync) {
237 // Tests makes no sense in single-process mode since the renderer is hung.
238 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
239 return;
241 NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
242 // Must navigate to a non-data URL to trigger cross-site codepath.
243 NavigateToNolistenersFileTwice();
246 // Tests closing the browser on a page with no unload listeners registered.
247 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
248 LoadUrlAndQuitBrowser(NOLISTENERS_HTML, "nolisteners");
251 // Tests closing the browser on a page with an unload listener registered.
252 // Test marked as flaky in http://crbug.com/51698
253 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseUnload) {
254 LoadUrlAndQuitBrowser(UNLOAD_HTML, "unload");
257 // Tests closing the browser with a beforeunload handler and clicking
258 // OK in the beforeunload confirm dialog.
259 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
260 NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
262 content::WindowedNotificationObserver window_observer(
263 chrome::NOTIFICATION_BROWSER_CLOSED,
264 content::NotificationService::AllSources());
265 chrome::CloseWindow(browser());
266 ClickModalDialogButton(true);
267 window_observer.Wait();
270 // Tests closing the browser with a beforeunload handler and clicking
271 // CANCEL in the beforeunload confirm dialog.
272 // If this test flakes, reopen http://crbug.com/123110
273 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
274 NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
275 chrome::CloseWindow(browser());
277 // We wait for the title to change after cancelling the popup to ensure that
278 // in-flight IPCs from the renderer reach the browser. Otherwise the browser
279 // won't put up the beforeunload dialog because it's waiting for an ack from
280 // the renderer.
281 string16 expected_title = ASCIIToUTF16("cancelled");
282 content::TitleWatcher title_watcher(
283 browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
284 ClickModalDialogButton(false);
285 ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
287 content::WindowedNotificationObserver window_observer(
288 chrome::NOTIFICATION_BROWSER_CLOSED,
289 content::NotificationService::AllSources());
290 chrome::CloseWindow(browser());
291 ClickModalDialogButton(true);
292 window_observer.Wait();
295 // Tests terminating the browser with a beforeunload handler.
296 // Currently only ChromeOS shuts down gracefully.
297 #if defined(OS_CHROMEOS)
298 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserTerminateBeforeUnload) {
299 NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
300 EXPECT_EQ(kill(base::GetCurrentProcessHandle(), SIGTERM), 0);
302 #endif
304 // Tests closing the browser and clicking OK in the beforeunload confirm dialog
305 // if an inner frame has the focus.
306 // If this flakes, use http://crbug.com/32615 and http://crbug.com/45675
307 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithInnerFocusedFrame) {
308 NavigateToDataURL(INNER_FRAME_WITH_FOCUS_HTML, "innerframewithfocus");
310 content::WindowedNotificationObserver window_observer(
311 chrome::NOTIFICATION_BROWSER_CLOSED,
312 content::NotificationService::AllSources());
313 chrome::CloseWindow(browser());
314 ClickModalDialogButton(true);
315 window_observer.Wait();
318 // Tests closing the browser with a beforeunload handler that takes
319 // two seconds to run.
320 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnload) {
321 LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_HTML,
322 "twosecondbeforeunload");
325 // Tests closing the browser on a page with an unload listener registered where
326 // the unload handler has an infinite loop.
327 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
328 // Tests makes no sense in single-process mode since the renderer is hung.
329 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
330 return;
332 LoadUrlAndQuitBrowser(INFINITE_UNLOAD_HTML, "infiniteunload");
335 // Tests closing the browser with a beforeunload handler that hangs.
336 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469
337 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseInfiniteBeforeUnload) {
338 // Tests makes no sense in single-process mode since the renderer is hung.
339 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
340 return;
342 LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
345 // Tests closing the browser on a page with an unload listener registered where
346 // the unload handler has an infinite loop followed by an alert.
347 // If this flakes, use http://crbug.com/86469
348 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
349 // Tests makes no sense in single-process mode since the renderer is hung.
350 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
351 return;
353 LoadUrlAndQuitBrowser(INFINITE_UNLOAD_ALERT_HTML, "infiniteunloadalert");
356 // Tests closing the browser with a beforeunload handler that hangs then
357 // pops up an alert.
358 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469.
359 IN_PROC_BROWSER_TEST_F(UnloadTest,
360 DISABLED_BrowserCloseInfiniteBeforeUnloadAlert) {
361 // Tests makes no sense in single-process mode since the renderer is hung.
362 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
363 return;
365 LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_ALERT_HTML,
366 "infinitebeforeunloadalert");
369 // Tests closing the browser on a page with an unload listener registered where
370 // the unload handler has an 2 second long loop followed by an alert.
371 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
372 LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, "twosecondunloadalert");
375 // Tests closing the browser with a beforeunload handler that takes
376 // two seconds to run then pops up an alert.
377 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
378 LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
379 "twosecondbeforeunloadalert");
382 // Tests that if there's a renderer process with two tabs, one of which has an
383 // unload handler, and the other doesn't, the tab that doesn't have an unload
384 // handler can be closed.
385 // If this flakes, see http://crbug.com/45162, http://crbug.com/45281 and
386 // http://crbug.com/86769.
387 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) {
388 NavigateToDataURL(CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER, "only_one_unload");
390 // Simulate a click to force user_gesture to true; if we don't, the resulting
391 // popup will be constrained, which isn't what we want to test.
393 content::WindowedNotificationObserver observer(
394 chrome::NOTIFICATION_TAB_ADDED,
395 content::NotificationService::AllSources());
396 content::WindowedNotificationObserver load_stop_observer(
397 content::NOTIFICATION_LOAD_STOP,
398 content::NotificationService::AllSources());
399 content::SimulateMouseClick(
400 browser()->tab_strip_model()->GetActiveWebContents(), 0,
401 WebKit::WebMouseEvent::ButtonLeft);
402 observer.Wait();
403 load_stop_observer.Wait();
404 CheckTitle("popup");
406 content::WindowedNotificationObserver tab_close_observer(
407 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
408 content::NotificationService::AllSources());
409 chrome::CloseTab(browser());
410 tab_close_observer.Wait();
412 CheckTitle("only_one_unload");
415 // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
416 // and multiple windows.