• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SetUpCommandLine(CommandLine * command_line)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 
SetUpOnMainThread()129   virtual void SetUpOnMainThread() OVERRIDE {
130     BrowserThread::PostTask(
131         BrowserThread::IO, FROM_HERE,
132         base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
133   }
134 
CheckTitle(const char * expected_title)135   void CheckTitle(const char* expected_title) {
136     base::string16 expected = ASCIIToUTF16(expected_title);
137     EXPECT_EQ(expected,
138               browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
139   }
140 
NavigateToDataURL(const std::string & html_content,const char * expected_title)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 
NavigateToNolistenersFileTwice()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.
NavigateToNolistenersFileTwiceAsync()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 
LoadUrlAndQuitBrowser(const std::string & html_content,const char * expected_title)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.
ClickModalDialogButton(bool accept)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
IN_PROC_BROWSER_TEST_F(UnloadTest,CrossSiteInfiniteUnloadAsync)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,CrossSiteInfiniteUnloadSync)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
IN_PROC_BROWSER_TEST_F(UnloadTest,CrossSiteInfiniteBeforeUnloadAsync)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,CrossSiteInfiniteBeforeUnloadSync)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseNoUnloadListeners)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
IN_PROC_BROWSER_TEST_F(UnloadTest,DISABLED_BrowserCloseUnload)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseBeforeUnloadOK)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
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseBeforeUnloadCancel)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   base::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)
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserTerminateBeforeUnload)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
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseWithInnerFocusedFrame)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseTwoSecondBeforeUnload)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseInfiniteUnload)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
IN_PROC_BROWSER_TEST_F(UnloadTest,DISABLED_BrowserCloseInfiniteBeforeUnload)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
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseInfiniteUnloadAlert)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,DISABLED_BrowserCloseInfiniteBeforeUnloadAlert)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseTwoSecondUnloadAlert)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseTwoSecondBeforeUnloadAlert)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.
IN_PROC_BROWSER_TEST_F(UnloadTest,BrowserCloseTabWhenOtherTabHasListener)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       blink::WebMouseEvent::ButtonLeft);
405   observer.Wait();
406   load_stop_observer.Wait();
407   CheckTitle("popup");
408 
409   content::WebContentsDestroyedWatcher destroyed_watcher(
410       browser()->tab_strip_model()->GetActiveWebContents());
411   chrome::CloseTab(browser());
412   destroyed_watcher.Wait();
413 
414   CheckTitle("only_one_unload");
415 }
416 
417 class FastUnloadTest : public UnloadTest {
418  public:
SetUpCommandLine(CommandLine * command_line)419   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
420     UnloadTest::SetUpCommandLine(command_line);
421     command_line->AppendSwitch(switches::kEnableFastUnload);
422   }
423 
SetUpInProcessBrowserTestFixture()424   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
425     ASSERT_TRUE(test_server()->Start());
426   }
427 
TearDownInProcessBrowserTestFixture()428   virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
429     test_server()->Stop();
430   }
431 
GetUrl(const std::string & name)432   GURL GetUrl(const std::string& name) {
433     return GURL(test_server()->GetURL(
434         "files/fast_tab_close/" + name + ".html"));
435   }
436 
NavigateToPage(const char * name)437   void NavigateToPage(const char* name) {
438     ui_test_utils::NavigateToURL(browser(), GetUrl(name));
439     CheckTitle(name);
440   }
441 
NavigateToPageInNewTab(const char * name)442   void NavigateToPageInNewTab(const char* name) {
443     ui_test_utils::NavigateToURLWithDisposition(
444         browser(), GetUrl(name), NEW_FOREGROUND_TAB,
445         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
446     CheckTitle(name);
447   }
448 
GetCookies(const char * name)449   std::string GetCookies(const char* name) {
450     content::WebContents* contents =
451         browser()->tab_strip_model()->GetActiveWebContents();
452     return content::GetCookies(contents->GetBrowserContext(), GetUrl(name));
453   }
454 };
455 
456 class FastTabCloseTabStripModelObserver : public TabStripModelObserver {
457  public:
FastTabCloseTabStripModelObserver(TabStripModel * model,base::RunLoop * run_loop)458   FastTabCloseTabStripModelObserver(TabStripModel* model,
459                                     base::RunLoop* run_loop)
460       : model_(model),
461         run_loop_(run_loop) {
462     model_->AddObserver(this);
463   }
464 
~FastTabCloseTabStripModelObserver()465   virtual ~FastTabCloseTabStripModelObserver() {
466     model_->RemoveObserver(this);
467   }
468 
469   // TabStripModelObserver:
TabDetachedAt(content::WebContents * contents,int index)470   virtual void TabDetachedAt(content::WebContents* contents,
471                              int index) OVERRIDE {
472     run_loop_->Quit();
473   }
474 
475  private:
476   TabStripModel* const model_;
477   base::RunLoop* const run_loop_;
478 };
479 
480 
481 // Test that fast-tab-close works when closing a tab with an unload handler
482 // (http://crbug.com/142458).
483 // Flaky on Windows bots (http://crbug.com/267597).
484 #if defined(OS_WIN)
485 #define MAYBE_UnloadHidden \
486     DISABLED_UnloadHidden
487 #else
488 #define MAYBE_UnloadHidden \
489     UnloadHidden
490 #endif
IN_PROC_BROWSER_TEST_F(FastUnloadTest,MAYBE_UnloadHidden)491 IN_PROC_BROWSER_TEST_F(FastUnloadTest, MAYBE_UnloadHidden) {
492   NavigateToPage("no_listeners");
493   NavigateToPageInNewTab("unload_sets_cookie");
494   EXPECT_EQ("", GetCookies("no_listeners"));
495 
496   content::WebContentsDestroyedWatcher destroyed_watcher(
497       browser()->tab_strip_model()->GetActiveWebContents());
498 
499   {
500     base::RunLoop run_loop;
501     FastTabCloseTabStripModelObserver observer(
502         browser()->tab_strip_model(), &run_loop);
503     chrome::CloseTab(browser());
504     run_loop.Run();
505   }
506 
507   // Check that the browser only has the original tab.
508   CheckTitle("no_listeners");
509   EXPECT_EQ(1, browser()->tab_strip_model()->count());
510 
511   // Wait for the actual destruction.
512   destroyed_watcher.Wait();
513 
514   // Verify that the destruction had the desired effect.
515   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
516 }
517 
518 // Test that fast-tab-close does not break a solo tab.
IN_PROC_BROWSER_TEST_F(FastUnloadTest,PRE_ClosingLastTabFinishesUnload)519 IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_ClosingLastTabFinishesUnload) {
520   // The unload handler sleeps before setting the cookie to catch cases when
521   // unload handlers are not allowed to run to completion. (For example,
522   // using the detached handler for the tab and then closing the browser.)
523   NavigateToPage("unload_sleep_before_cookie");
524   EXPECT_EQ(1, browser()->tab_strip_model()->count());
525   EXPECT_EQ("", GetCookies("unload_sleep_before_cookie"));
526 
527   content::WindowedNotificationObserver window_observer(
528       chrome::NOTIFICATION_BROWSER_CLOSED,
529       content::NotificationService::AllSources());
530   chrome::CloseTab(browser());
531   window_observer.Wait();
532 }
533 
534 // Fails on Mac, Linux, Win7 (http://crbug.com/301173).
IN_PROC_BROWSER_TEST_F(FastUnloadTest,DISABLED_ClosingLastTabFinishesUnload)535 IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_ClosingLastTabFinishesUnload) {
536 #if defined(OS_WIN)
537   // Flaky on Win7+ bots (http://crbug.com/267597).
538   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
539     return;
540 #endif
541   // Check for cookie set in unload handler of PRE_ test.
542   NavigateToPage("no_listeners");
543   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
544 }
545 
546 // Test that fast-tab-close does not break window close.
IN_PROC_BROWSER_TEST_F(FastUnloadTest,PRE_WindowCloseFinishesUnload)547 IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_WindowCloseFinishesUnload) {
548   NavigateToPage("no_listeners");
549 
550   // The unload handler sleeps before setting the cookie to catch cases when
551   // unload handlers are not allowed to run to completion. Without the sleep,
552   // the cookie can get set even if the browser does not wait for
553   // the unload handler to finish.
554   NavigateToPageInNewTab("unload_sleep_before_cookie");
555   EXPECT_EQ(2, browser()->tab_strip_model()->count());
556   EXPECT_EQ("", GetCookies("no_listeners"));
557 
558   content::WindowedNotificationObserver window_observer(
559       chrome::NOTIFICATION_BROWSER_CLOSED,
560       content::NotificationService::AllSources());
561   chrome::CloseWindow(browser());
562   window_observer.Wait();
563 }
564 
565 // Flaky on Windows bots (http://crbug.com/279267) and fails on Mac / Linux bots
566 // (http://crbug.com/301173).
IN_PROC_BROWSER_TEST_F(FastUnloadTest,DISABLED_WindowCloseFinishesUnload)567 IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_WindowCloseFinishesUnload) {
568   // Check for cookie set in unload during PRE_ test.
569   NavigateToPage("no_listeners");
570   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
571 }
572 
573 // Test that a tab crash during unload does not break window close.
574 //
575 // Hits assertion on Linux and Mac:
576 //     [FATAL:profile_destroyer.cc(46)] Check failed:
577 //         hosts.empty() ||
578 //         profile->IsOffTheRecord() ||
579 //         content::RenderProcessHost::run_renderer_in_process().
580 //     More details: The renderer process host matches the closed, crashed tab.
581 //     The |UnloadController| receives |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|
582 //     and proceeds with the close.
IN_PROC_BROWSER_TEST_F(FastUnloadTest,DISABLED_WindowCloseAfterUnloadCrash)583 IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_WindowCloseAfterUnloadCrash) {
584   NavigateToPage("no_listeners");
585   NavigateToPageInNewTab("unload_sets_cookie");
586   content::WebContents* unload_contents =
587       browser()->tab_strip_model()->GetActiveWebContents();
588   EXPECT_EQ("", GetCookies("no_listeners"));
589 
590   {
591     base::RunLoop run_loop;
592     FastTabCloseTabStripModelObserver observer(
593         browser()->tab_strip_model(), &run_loop);
594     chrome::CloseTab(browser());
595     run_loop.Run();
596   }
597 
598   // Check that the browser only has the original tab.
599   CheckTitle("no_listeners");
600   EXPECT_EQ(1, browser()->tab_strip_model()->count());
601 
602   CrashTab(unload_contents);
603 
604   // Check that the browser only has the original tab.
605   CheckTitle("no_listeners");
606   EXPECT_EQ(1, browser()->tab_strip_model()->count());
607 
608   content::WindowedNotificationObserver window_observer(
609       chrome::NOTIFICATION_BROWSER_CLOSED,
610       content::NotificationService::AllSources());
611   chrome::CloseWindow(browser());
612   window_observer.Wait();
613 }
614 
615 // Times out on Windows and Linux.
616 #if defined(OS_WIN) || defined(OS_LINUX)
617 #define MAYBE_WindowCloseAfterBeforeUnloadCrash \
618     DISABLED_WindowCloseAfterBeforeUnloadCrash
619 #else
620 #define MAYBE_WindowCloseAfterBeforeUnloadCrash \
621     WindowCloseAfterBeforeUnloadCrash
622 #endif
IN_PROC_BROWSER_TEST_F(FastUnloadTest,MAYBE_WindowCloseAfterBeforeUnloadCrash)623 IN_PROC_BROWSER_TEST_F(FastUnloadTest,
624                        MAYBE_WindowCloseAfterBeforeUnloadCrash) {
625   // Tests makes no sense in single-process mode since the renderer is hung.
626   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
627     return;
628 
629   NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload");
630   content::WebContents* beforeunload_contents =
631       browser()->tab_strip_model()->GetActiveWebContents();
632 
633   content::WindowedNotificationObserver window_observer(
634         chrome::NOTIFICATION_BROWSER_CLOSED,
635         content::NotificationService::AllSources());
636   chrome::CloseWindow(browser());
637   CrashTab(beforeunload_contents);
638   window_observer.Wait();
639 }
640 
641 // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs
642 // and multiple windows.
643