[M120 Migration]Fix for crash during chrome exit
[platform/framework/web/chromium-efl.git] / chrome / browser / unload_browsertest.cc
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/command_line.h"
6 #include "base/functional/bind.h"
7 #include "base/run_loop.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "build/build_config.h"
10 #include "build/chromeos_buildflags.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/browser_list.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/test/base/in_process_browser_test.h"
18 #include "chrome/test/base/ui_test_utils.h"
19 #include "components/embedder_support/switches.h"
20 #include "components/javascript_dialogs/app_modal_dialog_controller.h"
21 #include "components/javascript_dialogs/app_modal_dialog_view.h"
22 #include "content/public/browser/browser_task_traits.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/content_switches.h"
27 #include "content/public/test/browser_test.h"
28 #include "content/public/test/browser_test_utils.h"
29 #include "content/public/test/test_navigation_observer.h"
30 #include "net/dns/mock_host_resolver.h"
31 #include "net/test/embedded_test_server/embedded_test_server.h"
32 #include "third_party/blink/public/common/features.h"
33
34 using content::BrowserThread;
35
36 const char NOLISTENERS_HTML[] =
37     "<html><head><title>nolisteners</title></head><body></body></html>";
38
39 const char UNLOAD_HTML[] =
40     "<html><head><title>unload</title></head><body>"
41     "<script>window.onunload=function(e){}</script></body></html>";
42
43 const char BEFORE_UNLOAD_HTML[] =
44     "<html><head><title>beforeunload</title></head><body>"
45     "<script>window.onbeforeunload=function(e){"
46     "setTimeout('document.title=\"cancelled\"', 0);return 'foo'}</script>"
47     "</body></html>";
48
49 const char INNER_FRAME_WITH_FOCUS_HTML[] =
50     "<html><head><title>innerframewithfocus</title></head><body>"
51     "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
52     "<iframe src=\"data:text/html,<html><head><script>window.onload="
53     "function(){document.getElementById('box').focus()}</script>"
54     "<body><input id='box'></input></body></html>\"></iframe>"
55     "</body></html>";
56
57 const char INFINITE_UNLOAD_HTML[] =
58     "<html><head><title>infiniteunload</title></head><body>"
59     "<script>window.onunload=function(e){while(true){}}</script>"
60     "</body></html>";
61
62 const char INFINITE_BEFORE_UNLOAD_HTML[] =
63     "<html><head><title>infinitebeforeunload</title></head><body>"
64     "<script>window.onbeforeunload=function(e){while(true){}}</script>"
65     "</body></html>";
66
67 const char INFINITE_UNLOAD_ALERT_HTML[] =
68     "<html><head><title>infiniteunloadalert</title></head><body>"
69     "<script>window.onunload=function(e){"
70     "while(true){}"
71     "alert('foo');"
72     "}</script></body></html>";
73
74 const char INFINITE_BEFORE_UNLOAD_ALERT_HTML[] =
75     "<html><head><title>infinitebeforeunloadalert</title></head><body>"
76     "<script>window.onbeforeunload=function(e){"
77     "while(true){}"
78     "alert('foo');"
79     "}</script></body></html>";
80
81 const char TWO_SECOND_UNLOAD_ALERT_HTML[] =
82     "<html><head><title>twosecondunloadalert</title></head><body>"
83     "<script>window.onunload=function(e){"
84     "var start = new Date().getTime();"
85     "while(new Date().getTime() - start < 2000){}"
86     "alert('foo');"
87     "}</script></body></html>";
88
89 const char TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML[] =
90     "<html><head><title>twosecondbeforeunloadalert</title></head><body>"
91     "<script>window.onbeforeunload=function(e){"
92     "var start = new Date().getTime();"
93     "while(new Date().getTime() - start < 2000){}"
94     "alert('foo');"
95     "}</script></body></html>";
96
97 const char CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER[] =
98     "<html><head><title>only_one_unload</title></head>"
99     "<script>"
100     "function openPopup() {"
101     "  var w = window.open('about:blank');"
102     "  w.document.write('<html><head><title>popup</title></head></body>');"
103     "}"
104     "</script>"
105     "<body onclick='openPopup()' onbeforeunload='return;'>"
106     "</body></html>";
107
108 class UnloadResults {
109  public:
110   UnloadResults() : successes_(0), aborts_(0) {}
111
112   void AddSuccess(const base::FilePath&) { successes_++; }
113   void AddAbort(const base::FilePath&) { aborts_++; }
114   void AddError(const base::FilePath&) {
115     ADD_FAILURE() << "AddError should not be called.";
116   }
117
118   int get_successes() { return successes_; }
119   int get_aborts() { return aborts_; }
120
121  private:
122   int successes_;
123   int aborts_;
124 };
125
126 class UnloadTest : public InProcessBrowserTest {
127  public:
128   UnloadTest() {
129     scoped_feature_list.InitAndEnableFeature(
130         blink::features::kBeforeunloadEventCancelByPreventDefault);
131   }
132   void SetUpCommandLine(base::CommandLine* command_line) override {
133     const testing::TestInfo* const test_info =
134         testing::UnitTest::GetInstance()->current_test_info();
135     if (strstr(test_info->name(), "BrowserCloseTabWhenOtherTabHasListener") !=
136         nullptr) {
137       command_line->AppendSwitch(embedder_support::kDisablePopupBlocking);
138     } else if (strstr(test_info->name(), "BrowserTerminateBeforeUnload") !=
139                nullptr) {
140 #if BUILDFLAG(IS_POSIX)
141       DisableSIGTERMHandling();
142 #endif
143     }
144   }
145
146   void SetUpOnMainThread() override {
147     host_resolver()->AddRule("*", "127.0.0.1");
148   }
149
150   void CheckTitle(const char* expected_title, bool wait = false) {
151     auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
152     std::u16string expected = base::ASCIIToUTF16(expected_title);
153     std::u16string actual;
154     if (wait)
155       actual = content::TitleWatcher(web_contents, expected).WaitAndGetTitle();
156     else
157       actual = web_contents->GetTitle();
158     EXPECT_EQ(expected, actual);
159   }
160
161   void NavigateToDataURL(const char* html_content, const char* expected_title) {
162     ASSERT_TRUE(ui_test_utils::NavigateToURL(
163         browser(), GURL(std::string("data:text/html,") + html_content)));
164     CheckTitle(expected_title);
165   }
166
167   void NavigateToNolistenersFileTwice() {
168     ASSERT_TRUE(embedded_test_server()->Start());
169     GURL url(embedded_test_server()->GetURL("/title2.html"));
170     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
171     CheckTitle("Title Of Awesomeness");
172     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
173     CheckTitle("Title Of Awesomeness");
174   }
175
176   // Navigates to a URL asynchronously, then again synchronously. The first
177   // load is purposely async to test the case where the user loads another
178   // page without waiting for the first load to complete.
179   void NavigateToNolistenersFileTwiceAsync() {
180     ASSERT_TRUE(embedded_test_server()->Start());
181     GURL url(embedded_test_server()->GetURL("/title2.html"));
182     ui_test_utils::NavigateToURLWithDisposition(
183         browser(), url, WindowOpenDisposition::CURRENT_TAB, 0);
184     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
185     CheckTitle("Title Of Awesomeness");
186   }
187
188   void LoadUrlAndQuitBrowser(const char* html_content,
189                              const char* expected_title) {
190     NavigateToDataURL(html_content, expected_title);
191     CloseBrowserSynchronously(browser());
192   }
193
194   // If |accept| is true, simulates user clicking OK, otherwise simulates
195   // clicking Cancel.
196   void ClickModalDialogButton(bool accept) {
197     javascript_dialogs::AppModalDialogController* dialog =
198         ui_test_utils::WaitForAppModalDialog();
199     if (accept)
200       dialog->view()->AcceptAppModalDialog();
201     else
202       dialog->view()->CancelAppModalDialog();
203   }
204
205   void PrepareForDialog(Browser* browser) {
206     for (int i = 0; i < browser->tab_strip_model()->count(); i++) {
207       content::PrepContentsForBeforeUnloadTest(
208           browser->tab_strip_model()->GetWebContentsAt(i));
209     }
210   }
211
212   void CloseBrowsersVerifyUnloadSuccess(bool force) {
213     UnloadResults unload_results;
214     BrowserList::CloseAllBrowsersWithProfile(
215         browser()->profile(),
216         base::BindRepeating(&UnloadResults::AddSuccess,
217                             base::Unretained(&unload_results)),
218         base::BindRepeating(&UnloadResults::AddAbort,
219                             base::Unretained(&unload_results)),
220         force);
221     ui_test_utils::WaitForBrowserToClose();
222     EXPECT_EQ(1, unload_results.get_successes());
223     EXPECT_EQ(0, unload_results.get_aborts());
224   }
225
226   // The test harness cannot close the window automatically, because it requires
227   // confirmation. We close the window manually instead.
228   void ManuallyCloseWindow() {
229     chrome::CloseWindow(browser());
230     ClickModalDialogButton(true);
231     ui_test_utils::WaitForBrowserToClose();
232   }
233
234   const std::string GenerateDataURL(std::string listener_html,
235                                     bool is_onbeforeunload = true) {
236     std::string listener =
237         is_onbeforeunload
238             ? "window.onbeforeunload=function(event){"
239               "setTimeout('document.title=\"cancelled\"', "
240               "0);" +
241                   listener_html + "}"
242             : "window.addEventListener('beforeunload', function(event){"
243               "setTimeout('document.title=\"cancelled\"', "
244               "0);" +
245                   listener_html + "})";
246     std::string result =
247         "<html><head><title>beforeunload</title></head><body>"
248         "<script>" +
249         listener +
250         "</script>"
251         "</body></html>";
252     return result;
253   }
254
255  private:
256   base::test::ScopedFeatureList scoped_feature_list;
257 };
258
259 // Navigate to a page with an infinite unload handler.
260 // Then two async crosssite requests to ensure
261 // we don't get confused and think we're closing the tab.
262 //
263 // This test is flaky on the valgrind UI bots. http://crbug.com/39057
264 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadAsync) {
265   // Tests makes no sense in single-process mode since the renderer is hung.
266   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
267           switches::kSingleProcess))
268     return;
269
270   NavigateToDataURL(INFINITE_UNLOAD_HTML, "infiniteunload");
271   // Must navigate to a non-data URL to trigger cross-site codepath.
272   NavigateToNolistenersFileTwiceAsync();
273 }
274
275 // Navigate to a page with an infinite unload handler.
276 // Then two sync crosssite requests to ensure
277 // we correctly nav to each one.
278 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
279   // Tests makes no sense in single-process mode since the renderer is hung.
280   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
281           switches::kSingleProcess))
282     return;
283
284   NavigateToDataURL(INFINITE_UNLOAD_HTML, "infiniteunload");
285   // Must navigate to a non-data URL to trigger cross-site codepath.
286   NavigateToNolistenersFileTwice();
287 }
288
289 // Navigate to a page with an infinite beforeunload handler.
290 // Then two two async crosssite requests to ensure
291 // we don't get confused and think we're closing the tab.
292 // This test is flaky on the valgrind UI bots. http://crbug.com/39057 and
293 // http://crbug.com/86469
294 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadAsync) {
295   // Tests makes no sense in single-process mode since the renderer is hung.
296   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
297           switches::kSingleProcess))
298     return;
299
300   NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
301   // Must navigate to a non-data URL to trigger cross-site codepath.
302   NavigateToNolistenersFileTwiceAsync();
303 }
304
305 // Navigate to a page with an infinite beforeunload handler.
306 // Then two two sync crosssite requests to ensure
307 // we correctly nav to each one.
308 // Flaky on Win, Linux, and Mac; http://crbug.com/462671.
309 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_CrossSiteInfiniteBeforeUnloadSync) {
310   // Tests makes no sense in single-process mode since the renderer is hung.
311   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
312           switches::kSingleProcess))
313     return;
314
315   NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
316   // Must navigate to a non-data URL to trigger cross-site codepath.
317   NavigateToNolistenersFileTwice();
318 }
319
320 // Tests closing the browser on a page with no unload listeners registered.
321 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
322   LoadUrlAndQuitBrowser(NOLISTENERS_HTML, "nolisteners");
323 }
324
325 // Tests closing the browser on a page with an unload listener registered.
326 // Test marked as flaky in http://crbug.com/51698
327 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseUnload) {
328   LoadUrlAndQuitBrowser(UNLOAD_HTML, "unload");
329 }
330
331 // Tests closing the browser with a beforeunload handler and clicking
332 // OK in the beforeunload confirm dialog.
333 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
334   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
335   PrepareForDialog(browser());
336
337   chrome::CloseWindow(browser());
338   ClickModalDialogButton(true);
339   ui_test_utils::WaitForBrowserToClose();
340 }
341
342 // Tests closing the browser with a beforeunload handler and clicking
343 // CANCEL in the beforeunload confirm dialog.
344 // If this test flakes, reopen http://crbug.com/123110
345 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
346   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
347   PrepareForDialog(browser());
348   chrome::CloseWindow(browser());
349
350   // We wait for the title to change after cancelling the closure of browser
351   // window, to ensure that in-flight IPCs from the renderer reach the browser.
352   // Otherwise the browser won't put up the beforeunload dialog because it's
353   // waiting for an ack from the renderer.
354   std::u16string expected_title = u"cancelled";
355   content::TitleWatcher title_watcher(
356       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
357   ClickModalDialogButton(false);
358   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
359
360   ManuallyCloseWindow();
361 }
362
363 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile,
364 // on a page with no unload listeners registered.
365 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseNoUnloadListeners) {
366   NavigateToDataURL(NOLISTENERS_HTML, "nolisteners");
367
368   CloseBrowsersVerifyUnloadSuccess(false);
369 }
370
371 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with a
372 // beforeunload handler and clicking Leave in the beforeunload confirm dialog.
373 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseBeforeUnloadOK) {
374   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
375   PrepareForDialog(browser());
376
377   UnloadResults unload_results;
378   BrowserList::CloseAllBrowsersWithProfile(
379       browser()->profile(),
380       base::BindRepeating(&UnloadResults::AddSuccess,
381                           base::Unretained(&unload_results)),
382       base::BindRepeating(&UnloadResults::AddAbort,
383                           base::Unretained(&unload_results)),
384       false);
385   ClickModalDialogButton(true);
386   ui_test_utils::WaitForBrowserToClose();
387   EXPECT_EQ(1, unload_results.get_successes());
388   EXPECT_EQ(0, unload_results.get_aborts());
389 }
390
391 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListForceCloseNoUnloadListeners) {
392   NavigateToDataURL(NOLISTENERS_HTML, "nolisteners");
393
394   CloseBrowsersVerifyUnloadSuccess(true);
395 }
396
397 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListForceCloseWithBeforeUnload) {
398   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
399
400   CloseBrowsersVerifyUnloadSuccess(true);
401 }
402
403 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with a
404 // beforeunload handler and clicking Stay in the beforeunload confirm dialog.
405 // TODO(crbug.com/1372484): Flaky on Mac.
406 #if BUILDFLAG(IS_MAC)
407 #define MAYBE_BrowserListCloseBeforeUnloadCancel \
408   DISABLED_BrowserListCloseBeforeUnloadCancel
409 #else
410 #define MAYBE_BrowserListCloseBeforeUnloadCancel \
411   BrowserListCloseBeforeUnloadCancel
412 #endif
413 IN_PROC_BROWSER_TEST_F(UnloadTest, MAYBE_BrowserListCloseBeforeUnloadCancel) {
414   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
415   PrepareForDialog(browser());
416
417   UnloadResults unload_results;
418   BrowserList::CloseAllBrowsersWithProfile(
419       browser()->profile(),
420       base::BindRepeating(&UnloadResults::AddSuccess,
421                           base::Unretained(&unload_results)),
422       base::BindRepeating(&UnloadResults::AddAbort,
423                           base::Unretained(&unload_results)),
424       false);
425
426   // We wait for the title to change after cancelling the closure of browser
427   // window, to ensure that in-flight IPCs from the renderer reach the browser.
428   // Otherwise the browser won't put up the beforeunload dialog because it's
429   // waiting for an ack from the renderer.
430   std::u16string expected_title = u"cancelled";
431   content::TitleWatcher title_watcher(
432       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
433   ClickModalDialogButton(false);
434   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
435
436   EXPECT_EQ(0, unload_results.get_successes());
437   EXPECT_EQ(1, unload_results.get_aborts());
438
439   ManuallyCloseWindow();
440 }
441
442 // Tests double calls to BrowserList::CloseAllBrowsersWithProfile, with a
443 // beforeunload handler and clicking Leave in the beforeunload confirm dialog.
444 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListDoubleCloseBeforeUnloadOK) {
445   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
446   PrepareForDialog(browser());
447
448   UnloadResults unload_results;
449   BrowserList::CloseAllBrowsersWithProfile(
450       browser()->profile(),
451       base::BindRepeating(&UnloadResults::AddSuccess,
452                           base::Unretained(&unload_results)),
453       base::BindRepeating(&UnloadResults::AddAbort,
454                           base::Unretained(&unload_results)),
455       false);
456   BrowserList::CloseAllBrowsersWithProfile(
457       browser()->profile(),
458       base::BindRepeating(&UnloadResults::AddSuccess,
459                           base::Unretained(&unload_results)),
460       base::BindRepeating(&UnloadResults::AddAbort,
461                           base::Unretained(&unload_results)),
462       false);
463   ClickModalDialogButton(true);
464   ui_test_utils::WaitForBrowserToClose();
465   EXPECT_EQ(1, unload_results.get_successes());
466   EXPECT_EQ(0, unload_results.get_aborts());
467 }
468
469 // Tests double calls to BrowserList::CloseAllBrowsersWithProfile, with a
470 // beforeunload handler and clicking Stay in the beforeunload confirm dialog.
471 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListDoubleCloseBeforeUnloadCancel) {
472   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
473   PrepareForDialog(browser());
474
475   UnloadResults unload_results;
476   BrowserList::CloseAllBrowsersWithProfile(
477       browser()->profile(),
478       base::BindRepeating(&UnloadResults::AddSuccess,
479                           base::Unretained(&unload_results)),
480       base::BindRepeating(&UnloadResults::AddAbort,
481                           base::Unretained(&unload_results)),
482       false);
483   BrowserList::CloseAllBrowsersWithProfile(
484       browser()->profile(),
485       base::BindRepeating(&UnloadResults::AddSuccess,
486                           base::Unretained(&unload_results)),
487       base::BindRepeating(&UnloadResults::AddAbort,
488                           base::Unretained(&unload_results)),
489       false);
490
491   // We wait for the title to change after cancelling the closure of browser
492   // window, to ensure that in-flight IPCs from the renderer reach the browser.
493   // Otherwise the browser won't put up the beforeunload dialog because it's
494   // waiting for an ack from the renderer.
495   std::u16string expected_title = u"cancelled";
496   content::TitleWatcher title_watcher(
497       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
498   ClickModalDialogButton(false);
499   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
500
501   EXPECT_EQ(0, unload_results.get_successes());
502   EXPECT_EQ(1, unload_results.get_aborts());
503
504   ManuallyCloseWindow();
505 }
506
507 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with
508 // a null success callback, a beforeunload handler and clicking Leave in the
509 // beforeunload confirm dialog. The test succeed if no crash happens.
510 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListCloseBeforeUnloadNullCallbackOk) {
511   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
512   PrepareForDialog(browser());
513
514   UnloadResults unload_results;
515   BrowserList::CloseAllBrowsersWithProfile(browser()->profile(),
516                                            BrowserList::CloseCallback(),
517                                            BrowserList::CloseCallback(), false);
518   ClickModalDialogButton(true);
519   ui_test_utils::WaitForBrowserToClose();
520 }
521
522 // Tests closing the browser by BrowserList::CloseAllBrowsersWithProfile, with
523 // a null failure callback, a beforeunload handler and clicking Stay in the
524 // beforeunload confirm dialog. The test succeed if no crash happens.
525 IN_PROC_BROWSER_TEST_F(UnloadTest,
526                        BrowserListCloseBeforeUnloadNullCallbackCancel) {
527   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
528   PrepareForDialog(browser());
529
530   UnloadResults unload_results;
531   BrowserList::CloseAllBrowsersWithProfile(browser()->profile(),
532                                            BrowserList::CloseCallback(),
533                                            BrowserList::CloseCallback(), false);
534
535   // We wait for the title to change after cancelling the closure of browser
536   // window, to ensure that in-flight IPCs from the renderer reach the browser.
537   // Otherwise the browser won't put up the beforeunload dialog because it's
538   // waiting for an ack from the renderer.
539   std::u16string expected_title = u"cancelled";
540   content::TitleWatcher title_watcher(
541       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
542   ClickModalDialogButton(false);
543   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
544
545   ManuallyCloseWindow();
546 }
547
548 // Tests terminating the browser with a beforeunload handler.
549 // Currently only ChromeOS shuts down gracefully.
550 #if BUILDFLAG(IS_CHROMEOS_ASH)
551 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserTerminateBeforeUnload) {
552   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
553   EXPECT_EQ(kill(base::GetCurrentProcessHandle(), SIGTERM), 0);
554 }
555 #endif
556
557 // Tests closing the browser and clicking OK in the beforeunload confirm dialog
558 // if an inner frame has the focus.
559 // If this flakes, use http://crbug.com/32615 and http://crbug.com/45675
560 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithInnerFocusedFrame) {
561   NavigateToDataURL(INNER_FRAME_WITH_FOCUS_HTML, "innerframewithfocus");
562   PrepareForDialog(browser());
563
564   ManuallyCloseWindow();
565 }
566
567 // Tests closing the browser with a beforeunload handler that takes forever
568 // by running an infinite loop.
569 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteBeforeUnload) {
570   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML,
571                         "infinitebeforeunload");
572 }
573
574 // Tests closing the browser on a page with an unload listener registered where
575 // the unload handler has an infinite loop.
576 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
577   // Tests makes no sense in single-process mode since the renderer is hung.
578   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
579           switches::kSingleProcess))
580     return;
581
582   LoadUrlAndQuitBrowser(INFINITE_UNLOAD_HTML, "infiniteunload");
583 }
584
585 // Tests closing the browser on a page with an unload listener registered where
586 // the unload handler has an infinite loop followed by an alert.
587 // If this flakes, use http://crbug.com/86469
588 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
589   // Tests makes no sense in single-process mode since the renderer is hung.
590   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
591           switches::kSingleProcess))
592     return;
593
594   LoadUrlAndQuitBrowser(INFINITE_UNLOAD_ALERT_HTML, "infiniteunloadalert");
595 }
596
597 // Tests closing the browser with a beforeunload handler that hangs then
598 // pops up an alert.
599 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469.
600 IN_PROC_BROWSER_TEST_F(UnloadTest,
601                        DISABLED_BrowserCloseInfiniteBeforeUnloadAlert) {
602   // Tests makes no sense in single-process mode since the renderer is hung.
603   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
604           switches::kSingleProcess))
605     return;
606
607   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_ALERT_HTML,
608                         "infinitebeforeunloadalert");
609 }
610
611 // Tests closing the browser on a page with an unload listener registered where
612 // the unload handler has an 2 second long loop followed by an alert.
613 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
614   LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, "twosecondunloadalert");
615 }
616
617 // Tests closing the browser with a beforeunload handler that takes
618 // two seconds to run then pops up an alert.
619 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
620   LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
621                         "twosecondbeforeunloadalert");
622 }
623
624 // Tests that if there's a renderer process with two tabs, one of which has an
625 // unload handler, and the other doesn't, the tab that doesn't have an unload
626 // handler can be closed.
627 // If this flakes, see http://crbug.com/45162, http://crbug.com/45281 and
628 // http://crbug.com/86769.
629 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) {
630   NavigateToDataURL(CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER, "only_one_unload");
631
632   // Simulate a click to force user_gesture to true; if we don't, the resulting
633   // popup will be constrained, which isn't what we want to test.
634
635   ui_test_utils::TabAddedWaiter tab_add(browser());
636   content::SimulateMouseClick(
637       browser()->tab_strip_model()->GetActiveWebContents(), 0,
638       blink::WebMouseEvent::Button::kLeft);
639   tab_add.Wait();
640   // Need to wait for the title, because the initial page (about:blank) can stop
641   // loading before the click handler calls document.write.
642   CheckTitle("popup", true);
643
644   content::WebContentsDestroyedWatcher destroyed_watcher(
645       browser()->tab_strip_model()->GetActiveWebContents());
646   chrome::CloseTab(browser());
647   destroyed_watcher.Wait();
648
649   CheckTitle("only_one_unload");
650 }
651
652 // Tests that visibilitychange is only dispatched once on tab close.
653 IN_PROC_BROWSER_TEST_F(UnloadTest, VisibilityChangeOnlyDispatchedOnce) {
654   EXPECT_TRUE(embedded_test_server()->Start());
655   // Start on a.com and open a popup to another page in a.com.
656   GURL opener_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
657   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), opener_url));
658   content::WebContents* opener_contents =
659       browser()->tab_strip_model()->GetActiveWebContents();
660
661   GURL popup_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
662   content::TestNavigationObserver popup_observer(nullptr);
663   popup_observer.StartWatchingNewWebContents();
664   EXPECT_TRUE(
665       ExecJs(opener_contents, "window.open('" + popup_url.spec() + "');"));
666   popup_observer.Wait();
667   ASSERT_EQ(2, browser()->tab_strip_model()->count());
668   content::WebContents* popup_contents =
669       browser()->tab_strip_model()->GetActiveWebContents();
670   ASSERT_NE(opener_contents, popup_contents);
671   content::RenderFrameHost* popup_rfh = popup_contents->GetPrimaryMainFrame();
672
673   // In the popup, add a visibilitychange handler that ensures we only see the
674   // visibilitychange event fired once on tab close.
675   EXPECT_TRUE(ExecJs(popup_rfh, R"(
676             localStorage.setItem('visibilitychange_storage', 'not_dispatched');
677             var dispatched_visibilitychange = false;
678             document.onvisibilitychange = function(e) {
679               if (dispatched_visibilitychange) {
680                 // We shouldn't dispatch visibilitychange more than once.
681                 localStorage.setItem('visibilitychange_storage',
682                   'dispatched_more_than_once');
683               } else if (document.visibilityState != 'hidden') {
684                 // We should dispatch the event when the visibilityState is
685                 // 'hidden'.
686                 localStorage.setItem('visibilitychange_storage', 'not_hidden');
687               } else {
688                 localStorage.setItem('visibilitychange_storage',
689                   'dispatched_once');
690               }
691               dispatched_visibilitychange = true;
692             })"));
693
694   // Close the popup.
695   content::WebContentsDestroyedWatcher destroyed_watcher(popup_contents);
696   EXPECT_TRUE(ExecJs(popup_contents, "window.close();"));
697   destroyed_watcher.Wait();
698
699   // Check that we've only dispatched visibilitychange once.
700   EXPECT_EQ("dispatched_once",
701             EvalJs(opener_contents,
702                    "localStorage.getItem('visibilitychange_storage')"));
703 }
704
705 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserListForceCloseAfterNormalClose) {
706   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
707
708   UnloadResults unload_results;
709   BrowserList::CloseAllBrowsersWithProfile(
710       browser()->profile(),
711       base::BindRepeating(&UnloadResults::AddSuccess,
712                           base::Unretained(&unload_results)),
713       base::BindRepeating(&UnloadResults::AddAbort,
714                           base::Unretained(&unload_results)),
715       false);
716   BrowserList::CloseAllBrowsersWithProfile(
717       browser()->profile(),
718       base::BindRepeating(&UnloadResults::AddSuccess,
719                           base::Unretained(&unload_results)),
720       base::BindRepeating(&UnloadResults::AddAbort,
721                           base::Unretained(&unload_results)),
722       true);
723   ui_test_utils::WaitForBrowserToClose();
724   EXPECT_EQ(1, unload_results.get_successes());
725   EXPECT_EQ(0, unload_results.get_aborts());
726 }
727
728 // Tests that a cross-site iframe runs its beforeunload handler when closing
729 // the browser.  See https://crbug.com/853021.
730 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithCrossSiteIframe) {
731   ASSERT_TRUE(embedded_test_server()->Start());
732
733   // Navigate to a page with an iframe.
734   GURL main_url(embedded_test_server()->GetURL("a.com", "/iframe.html"));
735   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
736
737   // Navigate iframe cross-site.
738   GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
739   content::WebContents* web_contents =
740       browser()->tab_strip_model()->GetActiveWebContents();
741   EXPECT_TRUE(NavigateIframeToURL(web_contents, "test", frame_url));
742
743   // Install a dialog-showing beforeunload handler in the iframe.
744   content::RenderFrameHost* child =
745       ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0);
746   EXPECT_TRUE(ExecJs(child, "window.onbeforeunload = () => { return 'x' };"));
747
748   // Close the browser and make sure the beforeunload dialog is shown and can
749   // be clicked.
750   PrepareForDialog(browser());
751   ManuallyCloseWindow();
752 }
753
754 // Tests that a same-site iframe runs its beforeunload handler when closing the
755 // browser.  See https://crbug.com/1010456.
756 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithSameSiteIframe) {
757   ASSERT_TRUE(embedded_test_server()->Start());
758
759   // Navigate to a page with a same-site iframe.
760   GURL main_url(embedded_test_server()->GetURL("a.com", "/iframe.html"));
761   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
762   content::WebContents* web_contents =
763       browser()->tab_strip_model()->GetActiveWebContents();
764   content::RenderFrameHost* child =
765       ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0);
766   EXPECT_EQ(child->GetSiteInstance(),
767             web_contents->GetPrimaryMainFrame()->GetSiteInstance());
768
769   // Install a dialog-showing beforeunload handler in the iframe.
770   EXPECT_TRUE(ExecJs(child, "window.onbeforeunload = () => { return 'x' };"));
771
772   // Close the browser and make sure the beforeunload dialog is shown and can
773   // be clicked.
774   PrepareForDialog(browser());
775   ManuallyCloseWindow();
776 }
777
778 // Tests closing the browser with onbeforeunload handler and
779 // event.preventDefault() will prompt confirmation dialog
780 IN_PROC_BROWSER_TEST_F(UnloadTest, OnBeforeUnloadCancelByPreventDefault) {
781   std::string html =
782       GenerateDataURL("event.preventDefault()", /*is_onbeforeunload=*/true);
783   NavigateToDataURL(html.c_str(), "beforeunload");
784   PrepareForDialog(browser());
785   chrome::CloseWindow(browser());
786
787   // We wait for the title to change after cancelling the closure of browser
788   // window, to ensure that in-flight IPCs from the renderer reach the browser.
789   // Otherwise the browser won't put up the beforeunload dialog because it's
790   // waiting for an ack from the renderer.
791   std::u16string expected_title = u"cancelled";
792   content::TitleWatcher title_watcher(
793       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
794   ClickModalDialogButton(false);
795   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
796
797   ManuallyCloseWindow();
798 }
799
800 // Tests closing the browser with onbeforeunload handler and
801 // setting returnValue will prompt confirmation dialog
802 IN_PROC_BROWSER_TEST_F(UnloadTest, OnBeforeUnloadCancelByReturnValue) {
803   std::string html = GenerateDataURL("event.returnValue = 'hello world'",
804                                      /*is_onbeforeunload=*/true);
805   NavigateToDataURL(html.c_str(), "beforeunload");
806   PrepareForDialog(browser());
807   chrome::CloseWindow(browser());
808
809   // We wait for the title to change after cancelling the closure of browser
810   // window, to ensure that in-flight IPCs from the renderer reach the browser.
811   // Otherwise the browser won't put up the beforeunload dialog because it's
812   // waiting for an ack from the renderer.
813   std::u16string expected_title = u"cancelled";
814   content::TitleWatcher title_watcher(
815       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
816   ClickModalDialogButton(false);
817   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
818
819   ManuallyCloseWindow();
820 }
821
822 // Tests closing the browser with onbeforeunload handler and
823 // setting returnValue empty string will not prompt confirmation dialog
824 IN_PROC_BROWSER_TEST_F(UnloadTest, OnBeforeUnloadCancelByReturnValueEmpty) {
825   std::string html = GenerateDataURL("event.returnValue = ''",
826                                      /*is_onbeforeunload=*/true);
827   NavigateToDataURL(html.c_str(), "beforeunload");
828
829   CloseBrowsersVerifyUnloadSuccess(false);
830 }
831
832 // Tests closing the browser with onbeforeunload handler and
833 // having return value will prompt confirmation dialog
834 IN_PROC_BROWSER_TEST_F(UnloadTest, OnBeforeUnloadCancelByReturn) {
835   std::string html =
836       GenerateDataURL("return 'hello world'", /*is_onbeforeunload=*/true);
837   NavigateToDataURL(html.c_str(), "beforeunload");
838   PrepareForDialog(browser());
839   chrome::CloseWindow(browser());
840
841   // We wait for the title to change after cancelling the closure of browser
842   // window, to ensure that in-flight IPCs from the renderer reach the browser.
843   // Otherwise the browser won't put up the beforeunload dialog because it's
844   // waiting for an ack from the renderer.
845   std::u16string expected_title = u"cancelled";
846   content::TitleWatcher title_watcher(
847       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
848   ClickModalDialogButton(false);
849   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
850
851   ManuallyCloseWindow();
852 }
853
854 // Tests closing the browser with onbeforeunload handler and
855 // returning empty string will prompt confirmation dialog
856 IN_PROC_BROWSER_TEST_F(UnloadTest, OnBeforeUnloadCancelByReturnEmpty) {
857   std::string html = GenerateDataURL("return ''",
858                                      /*is_onbeforeunload=*/true);
859   NavigateToDataURL(html.c_str(), "beforeunload");
860   PrepareForDialog(browser());
861   chrome::CloseWindow(browser());
862
863   // We wait for the title to change after cancelling the closure of browser
864   // window, to ensure that in-flight IPCs from the renderer reach the browser.
865   // Otherwise the browser won't put up the beforeunload dialog because it's
866   // waiting for an ack from the renderer.
867   std::u16string expected_title = u"cancelled";
868   content::TitleWatcher title_watcher(
869       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
870   ClickModalDialogButton(false);
871   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
872
873   ManuallyCloseWindow();
874 }
875
876 // Tests closing the browser with addEventListener('beforeunload') handler and
877 // event.preventDefault() will prompt confirmation dialog
878 IN_PROC_BROWSER_TEST_F(UnloadTest, BeforeUnloadListenerCancelByPreventDefault) {
879   std::string html =
880       GenerateDataURL("event.preventDefault()", /*is_onbeforeunload=*/false);
881   NavigateToDataURL(html.c_str(), "beforeunload");
882   PrepareForDialog(browser());
883   chrome::CloseWindow(browser());
884
885   // We wait for the title to change after cancelling the closure of browser
886   // window, to ensure that in-flight IPCs from the renderer reach the browser.
887   // Otherwise the browser won't put up the beforeunload dialog because it's
888   // waiting for an ack from the renderer.
889   std::u16string expected_title = u"cancelled";
890   content::TitleWatcher title_watcher(
891       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
892   ClickModalDialogButton(false);
893   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
894
895   ManuallyCloseWindow();
896 }
897
898 // Tests closing the browser with addEventListener('beforeunload') handler and
899 // setting returnValue will prompt confirmation dialog
900 IN_PROC_BROWSER_TEST_F(UnloadTest, BeforeUnloadListenerCancelByReturnValue) {
901   std::string html = GenerateDataURL("event.returnValue = 'hello world'",
902                                      /*is_onbeforeunload=*/false);
903   NavigateToDataURL(html.c_str(), "beforeunload");
904   PrepareForDialog(browser());
905   chrome::CloseWindow(browser());
906
907   // We wait for the title to change after cancelling the closure of browser
908   // window, to ensure that in-flight IPCs from the renderer reach the browser.
909   // Otherwise the browser won't put up the beforeunload dialog because it's
910   // waiting for an ack from the renderer.
911   std::u16string expected_title = u"cancelled";
912   content::TitleWatcher title_watcher(
913       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
914   ClickModalDialogButton(false);
915   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
916
917   ManuallyCloseWindow();
918 }
919
920 // Tests closing the browser with addEventListener('beforeunload') handler and
921 // setting returnValue empty string will not prompt confirmation dialog
922 IN_PROC_BROWSER_TEST_F(UnloadTest,
923                        BeforeUnloadListenerCancelByReturnValueEmpty) {
924   std::string html = GenerateDataURL("event.returnValue = ''",
925                                      /*is_onbeforeunload=*/false);
926   NavigateToDataURL(html.c_str(), "beforeunload");
927
928   CloseBrowsersVerifyUnloadSuccess(false);
929 }
930
931 // Tests closing the browser with addEventListener('beforeunload') handler and
932 // having return value will _not_ prompt confirmation dialog
933 // TODO(crbug/809277) Change this test if spec changes
934 IN_PROC_BROWSER_TEST_F(UnloadTest, BeforeUnloadListenerCancelByReturn) {
935   std::string html =
936       GenerateDataURL("return 'hello world'", /*is_onbeforeunload=*/false);
937   NavigateToDataURL(html.c_str(), "beforeunload");
938
939   CloseBrowsersVerifyUnloadSuccess(false);
940 }
941
942 // Tests closing the browser with addEventListener('beforeunload') handler and
943 // returning empty string will not prompt confirmation dialog
944 IN_PROC_BROWSER_TEST_F(UnloadTest, BeforeUnloadListenerCancelByReturnEmpty) {
945   std::string html = GenerateDataURL("return ''",
946                                      /*is_onbeforeunload=*/false);
947   NavigateToDataURL(html.c_str(), "beforeunload");
948
949   CloseBrowsersVerifyUnloadSuccess(false);
950 }
951
952 // TODO(crbug/866818): Remove below test when feature
953 // BeforeunloadEventCancelByPreventDefault is fully stable.
954 class UnloadTestCancelByPreventDefaultDisabled : public UnloadTest {
955  public:
956   UnloadTestCancelByPreventDefaultDisabled() {
957     scoped_feature_list.InitAndDisableFeature(
958         blink::features::kBeforeunloadEventCancelByPreventDefault);
959   }
960
961  private:
962   base::test::ScopedFeatureList scoped_feature_list;
963 };
964
965 // Tests closing the browser with onbeforeunload handler and
966 // event.preventDefault() will not prompt confirmation dialog when
967 // BeforeunloadEventCancelByPreventDefault is disabled.
968 IN_PROC_BROWSER_TEST_F(UnloadTestCancelByPreventDefaultDisabled,
969                        OnBeforeUnloadPreventDefault) {
970   std::string html =
971       GenerateDataURL("event.preventDefault()", /*is_onbeforeunload=*/true);
972   NavigateToDataURL(html.c_str(), "beforeunload");
973
974   CloseBrowsersVerifyUnloadSuccess(false);
975 }
976
977 // Tests closing the browser with onbeforeunload handler and
978 // event.returnValue = "" will prompt confirmation dialog when
979 // BeforeunloadEventCancelByPreventDefault is disabled.
980 IN_PROC_BROWSER_TEST_F(UnloadTestCancelByPreventDefaultDisabled,
981                        OnBeforeUnloadEmptyString) {
982   std::string html = GenerateDataURL("event.returnValue = ''",
983                                      /*is_onbeforeunload=*/true);
984   NavigateToDataURL(html.c_str(), "beforeunload");
985   PrepareForDialog(browser());
986   chrome::CloseWindow(browser());
987
988   // We wait for the title to change after cancelling the closure of browser
989   // window, to ensure that in-flight IPCs from the renderer reach the browser.
990   // Otherwise the browser won't put up the beforeunload dialog because it's
991   // waiting for an ack from the renderer.
992   std::u16string expected_title = u"cancelled";
993   content::TitleWatcher title_watcher(
994       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
995   ClickModalDialogButton(false);
996   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
997
998   ManuallyCloseWindow();
999 }
1000
1001 // Tests closing the browser with addEventListener('beforeunload') handler and
1002 // event.preventDefault() will not prompt confirmation dialog when
1003 // BeforeunloadEventCancelByPreventDefault is disabled.
1004 IN_PROC_BROWSER_TEST_F(UnloadTestCancelByPreventDefaultDisabled,
1005                        BeforeUnloadPreventDefault) {
1006   std::string html =
1007       GenerateDataURL("event.preventDefault()", /*is_onbeforeunload=*/true);
1008   NavigateToDataURL(html.c_str(), "beforeunload");
1009
1010   CloseBrowsersVerifyUnloadSuccess(false);
1011 }
1012
1013 // Tests closing the browser with addEventListener('beforeunload') handler and
1014 // event.returnValue = "" will prompt confirmation dialog when
1015 // BeforeunloadEventCancelByPreventDefault is disabled.
1016 IN_PROC_BROWSER_TEST_F(UnloadTestCancelByPreventDefaultDisabled,
1017                        BeforeUnloadEmptyString) {
1018   std::string html = GenerateDataURL("event.returnValue = ''",
1019                                      /*is_onbeforeunload=*/false);
1020   NavigateToDataURL(html.c_str(), "beforeunload");
1021   PrepareForDialog(browser());
1022   chrome::CloseWindow(browser());
1023
1024   // We wait for the title to change after cancelling the closure of browser
1025   // window, to ensure that in-flight IPCs from the renderer reach the browser.
1026   // Otherwise the browser won't put up the beforeunload dialog because it's
1027   // waiting for an ack from the renderer.
1028   std::u16string expected_title = u"cancelled";
1029   content::TitleWatcher title_watcher(
1030       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
1031   ClickModalDialogButton(false);
1032   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
1033
1034   ManuallyCloseWindow();
1035 }
1036
1037 // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
1038 // and multiple windows.