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