Home | History | Annotate | Download | only in browser
      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.
      4 
      5 #if defined(OS_POSIX)
      6 #include <signal.h>
      7 #endif
      8 
      9 #include "base/command_line.h"
     10 #include "base/logging.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/chrome_notification_types.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_switches.h"
     21 #include "chrome/test/base/in_process_browser_test.h"
     22 #include "chrome/test/base/ui_test_utils.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "content/public/browser/notification_service.h"
     25 #include "content/public/browser/web_contents.h"
     26 #include "content/public/test/browser_test_utils.h"
     27 #include "content/test/net/url_request_mock_http_job.h"
     28 #include "net/url_request/url_request_test_util.h"
     29 
     30 #if defined(OS_WIN)
     31 // For version specific disabled tests below (http://crbug.com/267597).
     32 #include "base/win/windows_version.h"
     33 #endif
     34 
     35 using base::TimeDelta;
     36 using content::BrowserThread;
     37 
     38 const std::string NOLISTENERS_HTML =
     39     "<html><head><title>nolisteners</title></head><body></body></html>";
     40 
     41 const std::string UNLOAD_HTML =
     42     "<html><head><title>unload</title></head><body>"
     43     "<script>window.onunload=function(e){}</script></body></html>";
     44 
     45 const std::string BEFORE_UNLOAD_HTML =
     46     "<html><head><title>beforeunload</title></head><body>"
     47     "<script>window.onbeforeunload=function(e){"
     48     "setTimeout('document.title=\"cancelled\"', 0);return 'foo'}</script>"
     49     "</body></html>";
     50 
     51 const std::string INNER_FRAME_WITH_FOCUS_HTML =
     52     "<html><head><title>innerframewithfocus</title></head><body>"
     53     "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
     54     "<iframe src=\"data:text/html,<html><head><script>window.onload="
     55     "function(){document.getElementById('box').focus()}</script>"
     56     "<body><input id='box'></input></body></html>\"></iframe>"
     57     "</body></html>";
     58 
     59 const std::string TWO_SECOND_BEFORE_UNLOAD_HTML =
     60     "<html><head><title>twosecondbeforeunload</title></head><body>"
     61     "<script>window.onbeforeunload=function(e){"
     62       "var start = new Date().getTime();"
     63       "while(new Date().getTime() - start < 2000){}"
     64       "return 'foo';"
     65     "}</script></body></html>";
     66 
     67 const std::string INFINITE_UNLOAD_HTML =
     68     "<html><head><title>infiniteunload</title></head><body>"
     69     "<script>window.onunload=function(e){while(true){}}</script>"
     70     "</body></html>";
     71 
     72 const std::string INFINITE_BEFORE_UNLOAD_HTML =
     73     "<html><head><title>infinitebeforeunload</title></head><body>"
     74     "<script>window.onbeforeunload=function(e){while(true){}}</script>"
     75     "</body></html>";
     76 
     77 const std::string INFINITE_UNLOAD_ALERT_HTML =
     78     "<html><head><title>infiniteunloadalert</title></head><body>"
     79     "<script>window.onunload=function(e){"
     80       "while(true){}"
     81       "alert('foo');"
     82     "}</script></body></html>";
     83 
     84 const std::string INFINITE_BEFORE_UNLOAD_ALERT_HTML =
     85     "<html><head><title>infinitebeforeunloadalert</title></head><body>"
     86     "<script>window.onbeforeunload=function(e){"
     87       "while(true){}"
     88       "alert('foo');"
     89     "}</script></body></html>";
     90 
     91 const std::string TWO_SECOND_UNLOAD_ALERT_HTML =
     92     "<html><head><title>twosecondunloadalert</title></head><body>"
     93     "<script>window.onunload=function(e){"
     94       "var start = new Date().getTime();"
     95       "while(new Date().getTime() - start < 2000){}"
     96       "alert('foo');"
     97     "}</script></body></html>";
     98 
     99 const std::string TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML =
    100     "<html><head><title>twosecondbeforeunloadalert</title></head><body>"
    101     "<script>window.onbeforeunload=function(e){"
    102       "var start = new Date().getTime();"
    103       "while(new Date().getTime() - start < 2000){}"
    104       "alert('foo');"
    105     "}</script></body></html>";
    106 
    107 const std::string CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER =
    108     "<html><head><title>only_one_unload</title></head>"
    109     "<body onclick=\"window.open('data:text/html,"
    110     "<html><head><title>popup</title></head></body>')\" "
    111     "onbeforeunload='return;'>"
    112     "</body></html>";
    113 
    114 class UnloadTest : public InProcessBrowserTest {
    115  public:
    116   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    117     const testing::TestInfo* const test_info =
    118         testing::UnitTest::GetInstance()->current_test_info();
    119     if (strcmp(test_info->name(),
    120         "BrowserCloseTabWhenOtherTabHasListener") == 0) {
    121       command_line->AppendSwitch(switches::kDisablePopupBlocking);
    122     } else if (strcmp(test_info->name(), "BrowserTerminateBeforeUnload") == 0) {
    123 #if defined(OS_POSIX)
    124       DisableSIGTERMHandling();
    125 #endif
    126     }
    127   }
    128 
    129   virtual void SetUpOnMainThread() OVERRIDE {
    130     BrowserThread::PostTask(
    131         BrowserThread::IO, FROM_HERE,
    132         base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
    133   }
    134 
    135   void CheckTitle(const char* expected_title) {
    136     string16 expected = ASCIIToUTF16(expected_title);
    137     EXPECT_EQ(expected,
    138               browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
    139   }
    140 
    141   void NavigateToDataURL(const std::string& html_content,
    142                          const char* expected_title) {
    143     ui_test_utils::NavigateToURL(browser(),
    144                                  GURL("data:text/html," + html_content));
    145     CheckTitle(expected_title);
    146   }
    147 
    148   void NavigateToNolistenersFileTwice() {
    149     GURL url(content::URLRequestMockHTTPJob::GetMockUrl(
    150         base::FilePath(FILE_PATH_LITERAL("title2.html"))));
    151     ui_test_utils::NavigateToURL(browser(), url);
    152     CheckTitle("Title Of Awesomeness");
    153     ui_test_utils::NavigateToURL(browser(), url);
    154     CheckTitle("Title Of Awesomeness");
    155   }
    156 
    157   // Navigates to a URL asynchronously, then again synchronously. The first
    158   // load is purposely async to test the case where the user loads another
    159   // page without waiting for the first load to complete.
    160   void NavigateToNolistenersFileTwiceAsync() {
    161     GURL url(content::URLRequestMockHTTPJob::GetMockUrl(
    162         base::FilePath(FILE_PATH_LITERAL("title2.html"))));
    163     ui_test_utils::NavigateToURLWithDisposition(browser(), url, CURRENT_TAB, 0);
    164     ui_test_utils::NavigateToURL(browser(), url);
    165     CheckTitle("Title Of Awesomeness");
    166   }
    167 
    168   void LoadUrlAndQuitBrowser(const std::string& html_content,
    169                              const char* expected_title) {
    170     NavigateToDataURL(html_content, expected_title);
    171     content::WindowedNotificationObserver window_observer(
    172         chrome::NOTIFICATION_BROWSER_CLOSED,
    173         content::NotificationService::AllSources());
    174     chrome::CloseWindow(browser());
    175     window_observer.Wait();
    176   }
    177 
    178   // If |accept| is true, simulates user clicking OK, otherwise simulates
    179   // clicking Cancel.
    180   void ClickModalDialogButton(bool accept) {
    181     AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
    182     ASSERT_TRUE(dialog->IsJavaScriptModalDialog());
    183     JavaScriptAppModalDialog* js_dialog =
    184         static_cast<JavaScriptAppModalDialog*>(dialog);
    185     if (accept)
    186       js_dialog->native_dialog()->AcceptAppModalDialog();
    187     else
    188       js_dialog->native_dialog()->CancelAppModalDialog();
    189   }
    190 };
    191 
    192 // Navigate to a page with an infinite unload handler.
    193 // Then two async crosssite requests to ensure
    194 // we don't get confused and think we're closing the tab.
    195 //
    196 // This test is flaky on the valgrind UI bots. http://crbug.com/39057
    197 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadAsync) {
    198   // Tests makes no sense in single-process mode since the renderer is hung.
    199   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    200     return;
    201 
    202   NavigateToDataURL(INFINITE_UNLOAD_HTML, "infiniteunload");
    203   // Must navigate to a non-data URL to trigger cross-site codepath.
    204   NavigateToNolistenersFileTwiceAsync();
    205 }
    206 
    207 // Navigate to a page with an infinite unload handler.
    208 // Then two sync crosssite requests to ensure
    209 // we correctly nav to each one.
    210 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteUnloadSync) {
    211   // Tests makes no sense in single-process mode since the renderer is hung.
    212   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    213     return;
    214 
    215   NavigateToDataURL(INFINITE_UNLOAD_HTML, "infiniteunload");
    216   // Must navigate to a non-data URL to trigger cross-site codepath.
    217   NavigateToNolistenersFileTwice();
    218 }
    219 
    220 // Navigate to a page with an infinite beforeunload handler.
    221 // Then two two async crosssite requests to ensure
    222 // we don't get confused and think we're closing the tab.
    223 // This test is flaky on the valgrind UI bots. http://crbug.com/39057 and
    224 // http://crbug.com/86469
    225 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadAsync) {
    226   // Tests makes no sense in single-process mode since the renderer is hung.
    227   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    228     return;
    229 
    230   NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
    231   // Must navigate to a non-data URL to trigger cross-site codepath.
    232   NavigateToNolistenersFileTwiceAsync();
    233 }
    234 
    235 // Navigate to a page with an infinite beforeunload handler.
    236 // Then two two sync crosssite requests to ensure
    237 // we correctly nav to each one.
    238 // If this flakes, see bug http://crbug.com/86469.
    239 IN_PROC_BROWSER_TEST_F(UnloadTest, CrossSiteInfiniteBeforeUnloadSync) {
    240   // Tests makes no sense in single-process mode since the renderer is hung.
    241   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    242     return;
    243 
    244   NavigateToDataURL(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
    245   // Must navigate to a non-data URL to trigger cross-site codepath.
    246   NavigateToNolistenersFileTwice();
    247 }
    248 
    249 // Tests closing the browser on a page with no unload listeners registered.
    250 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseNoUnloadListeners) {
    251   LoadUrlAndQuitBrowser(NOLISTENERS_HTML, "nolisteners");
    252 }
    253 
    254 // Tests closing the browser on a page with an unload listener registered.
    255 // Test marked as flaky in http://crbug.com/51698
    256 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseUnload) {
    257   LoadUrlAndQuitBrowser(UNLOAD_HTML, "unload");
    258 }
    259 
    260 // Tests closing the browser with a beforeunload handler and clicking
    261 // OK in the beforeunload confirm dialog.
    262 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadOK) {
    263   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
    264 
    265   content::WindowedNotificationObserver window_observer(
    266         chrome::NOTIFICATION_BROWSER_CLOSED,
    267         content::NotificationService::AllSources());
    268   chrome::CloseWindow(browser());
    269   ClickModalDialogButton(true);
    270   window_observer.Wait();
    271 }
    272 
    273 // Tests closing the browser with a beforeunload handler and clicking
    274 // CANCEL in the beforeunload confirm dialog.
    275 // If this test flakes, reopen http://crbug.com/123110
    276 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseBeforeUnloadCancel) {
    277   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
    278   chrome::CloseWindow(browser());
    279 
    280   // We wait for the title to change after cancelling the popup to ensure that
    281   // in-flight IPCs from the renderer reach the browser. Otherwise the browser
    282   // won't put up the beforeunload dialog because it's waiting for an ack from
    283   // the renderer.
    284   string16 expected_title = ASCIIToUTF16("cancelled");
    285   content::TitleWatcher title_watcher(
    286       browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
    287   ClickModalDialogButton(false);
    288   ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
    289 
    290   content::WindowedNotificationObserver window_observer(
    291         chrome::NOTIFICATION_BROWSER_CLOSED,
    292         content::NotificationService::AllSources());
    293   chrome::CloseWindow(browser());
    294   ClickModalDialogButton(true);
    295   window_observer.Wait();
    296 }
    297 
    298 // Tests terminating the browser with a beforeunload handler.
    299 // Currently only ChromeOS shuts down gracefully.
    300 #if defined(OS_CHROMEOS)
    301 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserTerminateBeforeUnload) {
    302   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
    303   EXPECT_EQ(kill(base::GetCurrentProcessHandle(), SIGTERM), 0);
    304 }
    305 #endif
    306 
    307 // Tests closing the browser and clicking OK in the beforeunload confirm dialog
    308 // if an inner frame has the focus.
    309 // If this flakes, use http://crbug.com/32615 and http://crbug.com/45675
    310 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseWithInnerFocusedFrame) {
    311   NavigateToDataURL(INNER_FRAME_WITH_FOCUS_HTML, "innerframewithfocus");
    312 
    313   content::WindowedNotificationObserver window_observer(
    314         chrome::NOTIFICATION_BROWSER_CLOSED,
    315         content::NotificationService::AllSources());
    316   chrome::CloseWindow(browser());
    317   ClickModalDialogButton(true);
    318   window_observer.Wait();
    319 }
    320 
    321 // Tests closing the browser with a beforeunload handler that takes
    322 // two seconds to run.
    323 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnload) {
    324   LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_HTML,
    325                         "twosecondbeforeunload");
    326 }
    327 
    328 // Tests closing the browser on a page with an unload listener registered where
    329 // the unload handler has an infinite loop.
    330 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnload) {
    331   // Tests makes no sense in single-process mode since the renderer is hung.
    332   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    333     return;
    334 
    335   LoadUrlAndQuitBrowser(INFINITE_UNLOAD_HTML, "infiniteunload");
    336 }
    337 
    338 // Tests closing the browser with a beforeunload handler that hangs.
    339 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469
    340 IN_PROC_BROWSER_TEST_F(UnloadTest, DISABLED_BrowserCloseInfiniteBeforeUnload) {
    341   // Tests makes no sense in single-process mode since the renderer is hung.
    342   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    343     return;
    344 
    345   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_HTML, "infinitebeforeunload");
    346 }
    347 
    348 // Tests closing the browser on a page with an unload listener registered where
    349 // the unload handler has an infinite loop followed by an alert.
    350 // If this flakes, use http://crbug.com/86469
    351 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseInfiniteUnloadAlert) {
    352   // Tests makes no sense in single-process mode since the renderer is hung.
    353   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    354     return;
    355 
    356   LoadUrlAndQuitBrowser(INFINITE_UNLOAD_ALERT_HTML, "infiniteunloadalert");
    357 }
    358 
    359 // Tests closing the browser with a beforeunload handler that hangs then
    360 // pops up an alert.
    361 // If this flakes, use http://crbug.com/78803 and http://crbug.com/86469.
    362 IN_PROC_BROWSER_TEST_F(UnloadTest,
    363                        DISABLED_BrowserCloseInfiniteBeforeUnloadAlert) {
    364   // Tests makes no sense in single-process mode since the renderer is hung.
    365   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    366     return;
    367 
    368   LoadUrlAndQuitBrowser(INFINITE_BEFORE_UNLOAD_ALERT_HTML,
    369                         "infinitebeforeunloadalert");
    370 }
    371 
    372 // Tests closing the browser on a page with an unload listener registered where
    373 // the unload handler has an 2 second long loop followed by an alert.
    374 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondUnloadAlert) {
    375   LoadUrlAndQuitBrowser(TWO_SECOND_UNLOAD_ALERT_HTML, "twosecondunloadalert");
    376 }
    377 
    378 // Tests closing the browser with a beforeunload handler that takes
    379 // two seconds to run then pops up an alert.
    380 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTwoSecondBeforeUnloadAlert) {
    381   LoadUrlAndQuitBrowser(TWO_SECOND_BEFORE_UNLOAD_ALERT_HTML,
    382                         "twosecondbeforeunloadalert");
    383 }
    384 
    385 // Tests that if there's a renderer process with two tabs, one of which has an
    386 // unload handler, and the other doesn't, the tab that doesn't have an unload
    387 // handler can be closed.
    388 // If this flakes, see http://crbug.com/45162, http://crbug.com/45281 and
    389 // http://crbug.com/86769.
    390 IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) {
    391   NavigateToDataURL(CLOSE_TAB_WHEN_OTHER_TAB_HAS_LISTENER, "only_one_unload");
    392 
    393   // Simulate a click to force user_gesture to true; if we don't, the resulting
    394   // popup will be constrained, which isn't what we want to test.
    395 
    396   content::WindowedNotificationObserver observer(
    397         chrome::NOTIFICATION_TAB_ADDED,
    398         content::NotificationService::AllSources());
    399   content::WindowedNotificationObserver load_stop_observer(
    400       content::NOTIFICATION_LOAD_STOP,
    401       content::NotificationService::AllSources());
    402   content::SimulateMouseClick(
    403       browser()->tab_strip_model()->GetActiveWebContents(), 0,
    404       WebKit::WebMouseEvent::ButtonLeft);
    405   observer.Wait();
    406   load_stop_observer.Wait();
    407   CheckTitle("popup");
    408 
    409   content::WindowedNotificationObserver tab_close_observer(
    410       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
    411       content::NotificationService::AllSources());
    412   chrome::CloseTab(browser());
    413   tab_close_observer.Wait();
    414 
    415   CheckTitle("only_one_unload");
    416 }
    417 
    418 class FastUnloadTest : public UnloadTest {
    419  public:
    420   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    421     UnloadTest::SetUpCommandLine(command_line);
    422     command_line->AppendSwitch(switches::kEnableFastUnload);
    423   }
    424 
    425   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
    426     ASSERT_TRUE(test_server()->Start());
    427   }
    428 
    429   virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
    430     test_server()->Stop();
    431   }
    432 
    433   GURL GetUrl(const std::string& name) {
    434     return GURL(test_server()->GetURL(
    435         "files/fast_tab_close/" + name + ".html"));
    436   }
    437 
    438   void NavigateToPage(const char* name) {
    439     ui_test_utils::NavigateToURL(browser(), GetUrl(name));
    440     CheckTitle(name);
    441   }
    442 
    443   void NavigateToPageInNewTab(const char* name) {
    444     ui_test_utils::NavigateToURLWithDisposition(
    445         browser(), GetUrl(name), NEW_FOREGROUND_TAB,
    446         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    447     CheckTitle(name);
    448   }
    449 
    450   std::string GetCookies(const char* name) {
    451     content::WebContents* contents =
    452         browser()->tab_strip_model()->GetActiveWebContents();
    453     return content::GetCookies(contents->GetBrowserContext(), GetUrl(name));
    454   }
    455 };
    456 
    457 class FastTabCloseTabStripModelObserver : public TabStripModelObserver {
    458  public:
    459   FastTabCloseTabStripModelObserver(TabStripModel* model,
    460                                     base::RunLoop* run_loop)
    461       : model_(model),
    462         run_loop_(run_loop) {
    463     model_->AddObserver(this);
    464   }
    465 
    466   virtual ~FastTabCloseTabStripModelObserver() {
    467     model_->RemoveObserver(this);
    468   }
    469 
    470   // TabStripModelObserver:
    471   virtual void TabDetachedAt(content::WebContents* contents,
    472                              int index) OVERRIDE {
    473     run_loop_->Quit();
    474   }
    475 
    476  private:
    477   TabStripModel* const model_;
    478   base::RunLoop* const run_loop_;
    479 };
    480 
    481 
    482 // Test that fast-tab-close works when closing a tab with an unload handler
    483 // (http://crbug.com/142458).
    484 IN_PROC_BROWSER_TEST_F(FastUnloadTest, UnloadHidden) {
    485 #if defined(OS_WIN)
    486   // Flaky on Win7+ bots (http://crbug.com/267597).
    487   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
    488     return;
    489 #endif
    490   NavigateToPage("no_listeners");
    491   NavigateToPageInNewTab("unload_sets_cookie");
    492   EXPECT_EQ("", GetCookies("no_listeners"));
    493 
    494   {
    495     base::RunLoop run_loop;
    496     FastTabCloseTabStripModelObserver observer(
    497         browser()->tab_strip_model(), &run_loop);
    498     chrome::CloseTab(browser());
    499     run_loop.Run();
    500   }
    501 
    502   // Check that the browser only has the original tab.
    503   CheckTitle("no_listeners");
    504   EXPECT_EQ(1, browser()->tab_strip_model()->count());
    505 
    506   // Show that the web contents to go away after the was removed.
    507   // Without unload-detached, this times-out because it happens earlier.
    508   content::WindowedNotificationObserver contents_destroyed_observer(
    509       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
    510       content::NotificationService::AllSources());
    511   contents_destroyed_observer.Wait();
    512 
    513   // Browser still has the same tab.
    514   CheckTitle("no_listeners");
    515   EXPECT_EQ(1, browser()->tab_strip_model()->count());
    516   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
    517 }
    518 
    519 // Test that fast-tab-close does not break a solo tab.
    520 IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_ClosingLastTabFinishesUnload) {
    521   // The unload handler sleeps before setting the cookie to catch cases when
    522   // unload handlers are not allowed to run to completion. (For example,
    523   // using the detached handler for the tab and then closing the browser.)
    524   NavigateToPage("unload_sleep_before_cookie");
    525   EXPECT_EQ(1, browser()->tab_strip_model()->count());
    526   EXPECT_EQ("", GetCookies("unload_sleep_before_cookie"));
    527 
    528   content::WindowedNotificationObserver window_observer(
    529       chrome::NOTIFICATION_BROWSER_CLOSED,
    530       content::NotificationService::AllSources());
    531   chrome::CloseTab(browser());
    532   window_observer.Wait();
    533 }
    534 IN_PROC_BROWSER_TEST_F(FastUnloadTest, ClosingLastTabFinishesUnload) {
    535 #if defined(OS_WIN)
    536   // Flaky on Win7+ bots (http://crbug.com/267597).
    537   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
    538     return;
    539 #endif
    540   // Check for cookie set in unload handler of PRE_ test.
    541   NavigateToPage("no_listeners");
    542   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
    543 }
    544 
    545 // Test that fast-tab-close does not break window close.
    546 IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_WindowCloseFinishesUnload) {
    547   NavigateToPage("no_listeners");
    548 
    549   // The unload handler sleeps before setting the cookie to catch cases when
    550   // unload handlers are not allowed to run to completion. Without the sleep,
    551   // the cookie can get set even if the browser does not wait for
    552   // the unload handler to finish.
    553   NavigateToPageInNewTab("unload_sleep_before_cookie");
    554   EXPECT_EQ(2, browser()->tab_strip_model()->count());
    555   EXPECT_EQ("", GetCookies("no_listeners"));
    556 
    557   content::WindowedNotificationObserver window_observer(
    558       chrome::NOTIFICATION_BROWSER_CLOSED,
    559       content::NotificationService::AllSources());
    560   chrome::CloseWindow(browser());
    561   window_observer.Wait();
    562 }
    563 IN_PROC_BROWSER_TEST_F(FastUnloadTest, WindowCloseFinishesUnload) {
    564 #if defined(OS_WIN)
    565   // Flaky on Win7+ bots (http://crbug.com/267597).
    566   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
    567     return;
    568 #endif
    569   // Check for cookie set in unload during PRE_ test.
    570   NavigateToPage("no_listeners");
    571   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
    572 }
    573 
    574 // Test that a tab crash during unload does not break window close.
    575 //
    576 // Hits assertion on Linux and Mac:
    577 //     [FATAL:profile_destroyer.cc(46)] Check failed:
    578 //         hosts.empty() ||
    579 //         profile->IsOffTheRecord() ||
    580 //         content::RenderProcessHost::run_renderer_in_process().
    581 //     More details: The renderer process host matches the closed, crashed tab.
    582 //     The |UnloadController| receives |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|
    583 //     and proceeds with the close.
    584 IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_WindowCloseAfterUnloadCrash) {
    585   NavigateToPage("no_listeners");
    586   NavigateToPageInNewTab("unload_sets_cookie");
    587   content::WebContents* unload_contents =
    588       browser()->tab_strip_model()->GetActiveWebContents();
    589   EXPECT_EQ("", GetCookies("no_listeners"));
    590 
    591   {
    592     base::RunLoop run_loop;
    593     FastTabCloseTabStripModelObserver observer(
    594         browser()->tab_strip_model(), &run_loop);
    595     chrome::CloseTab(browser());
    596     run_loop.Run();
    597   }
    598 
    599   // Check that the browser only has the original tab.
    600   CheckTitle("no_listeners");
    601   EXPECT_EQ(1, browser()->tab_strip_model()->count());
    602 
    603   CrashTab(unload_contents);
    604 
    605   // Check that the browser only has the original tab.
    606   CheckTitle("no_listeners");
    607   EXPECT_EQ(1, browser()->tab_strip_model()->count());
    608 
    609   content::WindowedNotificationObserver window_observer(
    610       chrome::NOTIFICATION_BROWSER_CLOSED,
    611       content::NotificationService::AllSources());
    612   chrome::CloseWindow(browser());
    613   window_observer.Wait();
    614 }
    615 
    616 // Times out on Windows and Linux.
    617 #if defined(OS_WIN) || defined(OS_LINUX)
    618 #define MAYBE_WindowCloseAfterBeforeUnloadCrash \
    619     DISABLED_WindowCloseAfterBeforeUnloadCrash
    620 #else
    621 #define MAYBE_WindowCloseAfterBeforeUnloadCrash \
    622     WindowCloseAfterBeforeUnloadCrash
    623 #endif
    624 IN_PROC_BROWSER_TEST_F(FastUnloadTest,
    625                        MAYBE_WindowCloseAfterBeforeUnloadCrash) {
    626   // Tests makes no sense in single-process mode since the renderer is hung.
    627   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
    628     return;
    629 
    630   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
    631   content::WebContents* beforeunload_contents =
    632       browser()->tab_strip_model()->GetActiveWebContents();
    633 
    634   content::WindowedNotificationObserver window_observer(
    635         chrome::NOTIFICATION_BROWSER_CLOSED,
    636         content::NotificationService::AllSources());
    637   chrome::CloseWindow(browser());
    638   CrashTab(beforeunload_contents);
    639   window_observer.Wait();
    640 }
    641 
    642 // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
    643 // and multiple windows.
    644