Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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.
      4 
      5 #include "base/file_util.h"
      6 #include "base/test/test_timeouts.h"
      7 #include "chrome/browser/net/url_request_mock_http_job.h"
      8 #include "chrome/browser/ui/view_ids.h"
      9 #include "chrome/common/chrome_switches.h"
     10 #include "chrome/test/automation/browser_proxy.h"
     11 #include "chrome/test/automation/tab_proxy.h"
     12 #include "chrome/test/automation/window_proxy.h"
     13 #include "chrome/test/ui/ui_test.h"
     14 #include "net/url_request/url_request_test_util.h"
     15 #include "ui/base/events.h"
     16 #include "ui/base/message_box_flags.h"
     17 
     18 const std::string NOLISTENERS_HTML =
     19     "<html><head><title>nolisteners</title></head><body></body></html>";
     20 
     21 const std::string UNLOAD_HTML =
     22     "<html><head><title>unload</title></head><body>"
     23     "<script>window.onunload=function(e){}</script></body></html>";
     24 
     25 const std::string BEFORE_UNLOAD_HTML =
     26     "<html><head><title>beforeunload</title></head><body>"
     27     "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
     28     "</body></html>";
     29 
     30 const std::string INNER_FRAME_WITH_FOCUS_HTML =
     31     "<html><head><title>innerframewithfocus</title></head><body>"
     32     "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
     33     "<iframe src=\"data:text/html,<html><head><script>window.onload="
     34     "function(){document.getElementById('box').focus()}</script>"
     35     "<body><input id='box'></input></body></html>\"></iframe>"
     36     "</body></html>";
     37 
     38 const std::string TWO_SECOND_BEFORE_UNLOAD_HTML =
     39     "<html><head><title>twosecondbeforeunload</title></head><body>"
     40     "<script>window.onbeforeunload=function(e){"
     41       "var start = new Date().getTime();"
     42       "while(new Date().getTime() - start < 2000){}"
     43       "return 'foo';"
     44     "}</script></body></html>";
     45 
     46 const std::string INFINITE_UNLOAD_HTML =
     47     "<html><head><title>infiniteunload</title></head><body>"
     48     "<script>window.onunload=function(e){while(true){}}</script>"
     49     "</body></html>";
     50 
     51 const std::string INFINITE_BEFORE_UNLOAD_HTML =
     52     "<html><head><title>infinitebeforeunload</title></head><body>"
     53     "<script>window.onbeforeunload=function(e){while(true){}}</script>"
     54     "</body></html>";
     55 
     56 const std::string INFINITE_UNLOAD_ALERT_HTML =
     57     "<html><head><title>infiniteunloadalert</title></head><body>"
     58     "<script>window.onunload=function(e){"
     59       "while(true){}"
     60       "alert('foo');"
     61     "}</script></body></html>";
     62 
     63 const std::string INFINITE_BEFORE_UNLOAD_ALERT_HTML =
     64     "<html><head><title>infinitebeforeunloadalert</title></head><body>"
     65     "<script>window.onbeforeunload=function(e){"
     66       "while(true){}"
     67       "alert('foo');"
     68     "}</script></body></html>";
     69 
     70 const std::string TWO_SECOND_UNLOAD_ALERT_HTML =
     71     "<html><head><title>twosecondunloadalert</title></head><body>"
     72     "<script>window.onunload=function(e){"
     73       "var start = new Date().getTime();"
     74       "while(new Date().getTime() - start < 2000){}"
     75       "alert('foo');"
     76     "}</script></body></html>";
     77 
     78 const std::string TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML =
     79     "<html><head><title>twosecondbeforeunloadalert</title></head><body>"
     80     "<script>window.onbeforeunload=function(e){"
     81       "var start = new Date().getTime();"
     82       "while(new Date().getTime() - start < 2000){}"
     83       "alert('foo');"
     84     "}</script></body></html>";
     85 
     86 const std::string CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER =
     87     "<html><head><title>only_one_unload</title></head>"
     88     "<body onclick=\"window.open('data:text/html,"
     89     "<html><head><title>popup</title></head></body>')\" "
     90     "onbeforeunload='return;'>"
     91     "</body></html>";
     92 
     93 class UnloadTest : public UITest {
     94  public:
     95   virtual void SetUp() {
     96     const testing::TestInfo* const test_info =
     97         testing::UnitTest::GetInstance()->current_test_info();
     98     if (strcmp(test_info->name(),
     99         "BrowserCloseTabWhenOtherTabHasListener") == 0) {
    100       launch_arguments_.AppendSwitch(switches::kDisablePopupBlocking);
    101     }
    102 
    103     UITest::SetUp();
    104   }
    105 
    106   void CheckTitle(const std::wstring& expected_title) {
    107     const int kCheckDelayMs = 100;
    108     for (int max_wait_time = TestTimeouts::action_max_timeout_ms();
    109          max_wait_time > 0; max_wait_time -= kCheckDelayMs) {
    110       if (expected_title == GetActiveTabTitle())
    111         break;
    112       base::PlatformThread::Sleep(kCheckDelayMs);
    113     }
    114 
    115     EXPECT_EQ(expected_title, GetActiveTabTitle());
    116   }
    117 
    118   void NavigateToDataURL(const std::string& html_content,
    119                          const std::wstring& expected_title) {
    120     NavigateToURL(GURL("data:text/html," + html_content));
    121     CheckTitle(expected_title);
    122   }
    123 
    124   void NavigateToNolistenersFileTwice() {
    125     NavigateToURL(URLRequestMockHTTPJob::GetMockUrl(
    126                       FilePath(FILE_PATH_LITERAL("title2.html"))));
    127     CheckTitle(L"Title Of Awesomeness");
    128     NavigateToURL(URLRequestMockHTTPJob::GetMockUrl(
    129                       FilePath(FILE_PATH_LITERAL("title2.html"))));
    130     CheckTitle(L"Title Of Awesomeness");
    131   }
    132 
    133   // Navigates to a URL asynchronously, then again synchronously. The first
    134   // load is purposely async to test the case where the user loads another
    135   // page without waiting for the first load to complete.
    136   void NavigateToNolistenersFileTwiceAsync() {
    137     NavigateToURLAsync(
    138         URLRequestMockHTTPJob::GetMockUrl(
    139             FilePath(FILE_PATH_LITERAL("title2.html"))));
    140     NavigateToURL(
    141         URLRequestMockHTTPJob::GetMockUrl(
    142             FilePath(FILE_PATH_LITERAL("title2.html"))));
    143 
    144     CheckTitle(L"Title Of Awesomeness");
    145   }
    146 
    147   void LoadUrlAndQuitBrowser(const std::string& html_content,
    148                              const std::wstring& expected_title = L"") {
    149     scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    150     ASSERT_TRUE(browser.get());
    151     NavigateToDataURL(html_content, expected_title);
    152     bool application_closed = false;
    153     EXPECT_TRUE(CloseBrowser(browser.get(), &application_closed));
    154   }
    155 
    156   void ClickModalDialogButton(ui::MessageBoxFlags::DialogButton button) {
    157     bool modal_dialog_showing = false;
    158     ui::MessageBoxFlags::DialogButton available_buttons;
    159     EXPECT_TRUE(automation()->WaitForAppModalDialog());
    160     EXPECT_TRUE(automation()->GetShowingAppModalDialog(&modal_dialog_showing,
    161         &available_buttons));
    162     ASSERT_TRUE(modal_dialog_showing);
    163     EXPECT_TRUE((button & available_buttons) != 0);
    164     EXPECT_TRUE(automation()->ClickAppModalDialogButton(button));
    165   }
    166 };
    167 
    168 // Navigate to a page with an infinite unload handler.
    169 // Then two async crosssite requests to ensure
    170 // we don't get confused and think we're closing the tab.
    171 //
    172 // This test is flaky on the valgrind UI bots. http://crbug.com/39057
    173 TEST_F(UnloadTest, DISABLED_CrossSiteInfiniteUnloadAsync) {
    174   // Tests makes no sense in single-process mode since the renderer is hung.
    175   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    176     return;
    177 
    178   NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
    179   // Must navigate to a non-data URL to trigger cross-site codepath.
    180   NavigateToNolistenersFileTwiceAsync();
    181   ASSERT_TRUE(IsBrowserRunning());
    182 }
    183 
    184 // Navigate to a page with an infinite unload handler.
    185 // Then two sync crosssite requests to ensure
    186 // we correctly nav to each one.
    187 TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
    188   // Tests makes no sense in single-process mode since the renderer is hung.
    189   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    190     return;
    191 
    192   NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
    193   // Must navigate to a non-data URL to trigger cross-site codepath.
    194   NavigateToNolistenersFileTwice();
    195   ASSERT_TRUE(IsBrowserRunning());
    196 }
    197 
    198 // TODO(creis): This test is currently failing intermittently on Linux and
    199 // consistently on Mac and Vista.  http://crbug.com/38427
    200 #if defined(OS_MACOSX)
    201 #define MAYBE_CrossSiteInfiniteUnloadAsyncInputEvent \
    202     DISABLED_CrossSiteInfiniteUnloadAsyncInputEvent
    203 #elif defined(OS_WIN)
    204 #define MAYBE_CrossSiteInfiniteUnloadAsyncInputEvent \
    205     DISABLED_CrossSiteInfiniteUnloadAsyncInputEvent
    206 #else
    207 // Flaky on Linux.  http://crbug.com/38427
    208 #define MAYBE_CrossSiteInfiniteUnloadAsyncInputEvent \
    209     FLAKY_CrossSiteInfiniteUnloadAsyncInputEvent
    210 #endif
    211 
    212 // Navigate to a page with an infinite unload handler.
    213 // Then an async crosssite request followed by an input event to ensure that
    214 // the short unload timeout (not the long input event timeout) is used.
    215 // See crbug.com/11007.
    216 TEST_F(UnloadTest, MAYBE_CrossSiteInfiniteUnloadAsyncInputEvent) {
    217   // Tests makes no sense in single-process mode since the renderer is hung.
    218   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    219     return;
    220 
    221   NavigateToDataURL(INFINITE_UNLOAD_HTML, L"infiniteunload");
    222 
    223   // Navigate to a new URL asynchronously.
    224   NavigateToURLAsync(
    225       URLRequestMockHTTPJob::GetMockUrl(
    226           FilePath(FILE_PATH_LITERAL("title2.html"))));
    227 
    228   // Now send an input event while we're stalled on the unload handler.
    229   scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    230   ASSERT_TRUE(browser.get());
    231   scoped_refptr<WindowProxy> window(browser->GetWindow());
    232   ASSERT_TRUE(window.get());
    233   gfx::Rect bounds;
    234   ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds, false));
    235   ASSERT_TRUE(browser->SimulateDrag(bounds.CenterPoint(), bounds.CenterPoint(),
    236                                     ui::EF_LEFT_BUTTON_DOWN, false));
    237 
    238   // The title should update before the timeout in CheckTitle.
    239   CheckTitle(L"Title Of Awesomeness");
    240   ASSERT_TRUE(IsBrowserRunning());
    241 }
    242 
    243 // Navigate to a page with an infinite beforeunload handler.
    244 // Then two two async crosssite requests to ensure
    245 // we don't get confused and think we're closing the tab.
    246 // This test is flaky on the valgrind UI bots. http://crbug.com/39057
    247 TEST_F(UnloadTest, FLAKY_CrossSiteInfiniteBeforeUnloadAsync) {
    248   // Tests makes no sense in single-process mode since the renderer is hung.
    249   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    250     return;
    251 
    252   NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
    253   // Must navigate to a non-data URL to trigger cross-site codepath.
    254   NavigateToNolistenersFileTwiceAsync();
    255   ASSERT_TRUE(IsBrowserRunning());
    256 }
    257 
    258 // Navigate to a page with an infinite beforeunload handler.
    259 // Then two two sync crosssite requests to ensure
    260 // we correctly nav to each one.
    261 TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadSync) {
    262   // Tests makes no sense in single-process mode since the renderer is hung.
    263   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    264     return;
    265 
    266   NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
    267   // Must navigate to a non-data URL to trigger cross-site codepath.
    268   NavigateToNolistenersFileTwice();
    269   ASSERT_TRUE(IsBrowserRunning());
    270 }
    271 
    272 // Tests closing the browser on a page with no unload listeners registered.
    273 TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
    274   LoadUrlAndQuitBrowser(NOLISTENERS_HTML, L"nolisteners");
    275 }
    276 
    277 // Tests closing the browser on a page with an unload listener registered.
    278 // Test marked as flaky in http://crbug.com/51698
    279 TEST_F(UnloadTest, FLAKY_BrowserCloseUnload) {
    280   LoadUrlAndQuitBrowser(UNLOAD_HTML, L"unload");
    281 }
    282 
    283 // Tests closing the browser with a beforeunload handler and clicking
    284 // OK in the beforeunload confirm dialog.
    285 TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
    286   scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    287   ASSERT_TRUE(browser.get());
    288   NavigateToDataURL(BEFORE_UNLOAD_HTML, L"beforeunload");
    289 
    290   CloseBrowserAsync(browser.get());
    291   ClickModalDialogButton(ui::MessageBoxFlags::DIALOGBUTTON_OK);
    292 
    293   int exit_code = -1;
    294   ASSERT_TRUE(launcher_->WaitForBrowserProcessToQuit(
    295                   TestTimeouts::action_max_timeout_ms(), &exit_code));
    296   EXPECT_EQ(0, exit_code);  // Expect a clean shutown.
    297 }
    298 
    299 // Tests closing the browser with a beforeunload handler and clicking
    300 // CANCEL in the beforeunload confirm dialog.
    301 TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
    302   scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    303   ASSERT_TRUE(browser.get());
    304   NavigateToDataURL(BEFORE_UNLOAD_HTML, L"beforeunload");
    305 
    306   CloseBrowserAsync(browser.get());
    307   ClickModalDialogButton(ui::MessageBoxFlags::DIALOGBUTTON_CANCEL);
    308 
    309   // There's no real graceful way to wait for something _not_ to happen, so
    310   // we just wait a short period.
    311   base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms());
    312   ASSERT_TRUE(IsBrowserRunning());
    313 
    314   CloseBrowserAsync(browser.get());
    315   ClickModalDialogButton(ui::MessageBoxFlags::DIALOGBUTTON_OK);
    316 
    317   int exit_code = -1;
    318   ASSERT_TRUE(launcher_->WaitForBrowserProcessToQuit(
    319                   TestTimeouts::action_max_timeout_ms(), &exit_code));
    320   EXPECT_EQ(0, exit_code);  // Expect a clean shutdown.
    321 }
    322 
    323 #if defined(OS_LINUX)
    324 // Fails sometimes on Linux valgrind. http://crbug.com/45675
    325 #define MAYBE_BrowserCloseWithInnerFocusedFrame \
    326     FLAKY_BrowserCloseWithInnerFocusedFrame
    327 #else
    328 #define MAYBE_BrowserCloseWithInnerFocusedFrame \
    329     BrowserCloseWithInnerFocusedFrame
    330 #endif
    331 
    332 // Tests closing the browser and clicking OK in the beforeunload confirm dialog
    333 // if an inner frame has the focus.  See crbug.com/32615.
    334 TEST_F(UnloadTest, MAYBE_BrowserCloseWithInnerFocusedFrame) {
    335   scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    336   ASSERT_TRUE(browser.get());
    337 
    338   NavigateToDataURL(INNER_FRAME_WITH_FOCUS_HTML, L"innerframewithfocus");
    339 
    340   CloseBrowserAsync(browser.get());
    341   ClickModalDialogButton(ui::MessageBoxFlags::DIALOGBUTTON_OK);
    342 
    343   int exit_code = -1;
    344   ASSERT_TRUE(launcher_->WaitForBrowserProcessToQuit(
    345                   TestTimeouts::action_max_timeout_ms(), &exit_code));
    346   EXPECT_EQ(0, exit_code);  // Expect a clean shutdown.
    347 }
    348 
    349 // Tests closing the browser with a beforeunload handler that takes
    350 // two seconds to run.
    351 TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnload) {
    352   LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_HTML,
    353                         L"twosecondbeforeunload");
    354 }
    355 
    356 // Tests closing the browser on a page with an unload listener registered where
    357 // the unload handler has an infinite loop.
    358 TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
    359   // Tests makes no sense in single-process mode since the renderer is hung.
    360   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    361     return;
    362 
    363   LoadUrlAndQuitBrowser(INFINITE_UNLOAD_HTML, L"infiniteunload");
    364 }
    365 
    366 #if defined(OS_WIN)
    367 // Flakily fails, times out: http://crbug.com/78803
    368 #define MAYBE_BrowserCloseInfiniteBeforeUnload \
    369     DISABLED_BrowserCloseInfiniteBeforeUnload
    370 #else
    371 #define MAYBE_BrowserCloseInfiniteBeforeUnload BrowserCloseInfiniteBeforeUnload
    372 #endif
    373 // Tests closing the browser with a beforeunload handler that hangs.
    374 TEST_F(UnloadTest, MAYBE_BrowserCloseInfiniteBeforeUnload) {
    375   // Tests makes no sense in single-process mode since the renderer is hung.
    376   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    377     return;
    378 
    379   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML, L"infinitebeforeunload");
    380 }
    381 
    382 // Tests closing the browser on a page with an unload listener registered where
    383 // the unload handler has an infinite loop followed by an alert.
    384 TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
    385   // Tests makes no sense in single-process mode since the renderer is hung.
    386   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    387     return;
    388 
    389   LoadUrlAndQuitBrowser(INFINITE_UNLOAD_ALERT_HTML, L"infiniteunloadalert");
    390 }
    391 
    392 #if defined(OS_WIN)
    393 // Flakily fails, times out: http://crbug.com/78803
    394 #define MAYBE_BrowserCloseInfiniteBeforeUnloadAlert \
    395     DISABLED_BrowserCloseInfiniteBeforeUnloadAlert
    396 #else
    397 #define MAYBE_BrowserCloseInfiniteBeforeUnloadAlert \
    398     BrowserCloseInfiniteBeforeUnloadAlert
    399 #endif
    400 // Tests closing the browser with a beforeunload handler that hangs then
    401 // pops up an alert.
    402 TEST_F(UnloadTest, MAYBE_BrowserCloseInfiniteBeforeUnloadAlert) {
    403   // Tests makes no sense in single-process mode since the renderer is hung.
    404   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    405     return;
    406 
    407   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_ALERT_HTML,
    408                         L"infinitebeforeunloadalert");
    409 }
    410 
    411 // Tests closing the browser on a page with an unload listener registered where
    412 // the unload handler has an 2 second long loop followed by an alert.
    413 TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
    414   LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, L"twosecondunloadalert");
    415 }
    416 
    417 // Tests closing the browser with a beforeunload handler that takes
    418 // two seconds to run then pops up an alert.
    419 TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
    420   LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
    421                         L"twosecondbeforeunloadalert");
    422 }
    423 
    424 #if defined(OS_MACOSX)
    425 // http://crbug.com/45162
    426 #define MAYBE_BrowserCloseTabWhenOtherTabHasListener \
    427     DISABLED_BrowserCloseTabWhenOtherTabHasListener
    428 #elif defined(OS_WIN)
    429 // http://crbug.com/45281
    430 #define MAYBE_BrowserCloseTabWhenOtherTabHasListener \
    431     DISABLED_BrowserCloseTabWhenOtherTabHasListener
    432 #else
    433 #define MAYBE_BrowserCloseTabWhenOtherTabHasListener \
    434     BrowserCloseTabWhenOtherTabHasListener
    435 #endif
    436 
    437 // Tests that if there's a renderer process with two tabs, one of which has an
    438 // unload handler, and the other doesn't, the tab that doesn't have an unload
    439 // handler can be closed.
    440 TEST_F(UnloadTest, MAYBE_BrowserCloseTabWhenOtherTabHasListener) {
    441   NavigateToDataURL(CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER, L"only_one_unload");
    442 
    443   scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
    444   ASSERT_TRUE(browser.get());
    445   scoped_refptr<WindowProxy> window = browser->GetWindow();
    446   ASSERT_TRUE(window.get());
    447 
    448   gfx::Rect tab_view_bounds;
    449   ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
    450               &tab_view_bounds, true));
    451   // Simulate a click to force user_gesture to true; if we don't, the resulting
    452   // popup will be constrained, which isn't what we want to test.
    453   ASSERT_TRUE(window->SimulateOSClick(tab_view_bounds.CenterPoint(),
    454                                       ui::EF_LEFT_BUTTON_DOWN));
    455   ASSERT_TRUE(browser->WaitForTabCountToBecome(2));
    456 
    457   CheckTitle(L"popup");
    458   scoped_refptr<TabProxy> popup_tab(browser->GetActiveTab());
    459   ASSERT_TRUE(popup_tab.get());
    460   EXPECT_TRUE(popup_tab->Close(true));
    461 
    462   ASSERT_TRUE(browser->WaitForTabCountToBecome(1));
    463   scoped_refptr<TabProxy> main_tab(browser->GetActiveTab());
    464   ASSERT_TRUE(main_tab.get());
    465   std::wstring main_title;
    466   EXPECT_TRUE(main_tab->GetTabTitle(&main_title));
    467   EXPECT_EQ(std::wstring(L"only_one_unload"), main_title);
    468 }
    469 
    470 // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
    471 // and multiple windows.
    472