• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SetUp()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 
CheckTitle(const std::wstring & expected_title)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 
NavigateToDataURL(const std::string & html_content,const std::wstring & expected_title)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 
NavigateToNolistenersFileTwice()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.
NavigateToNolistenersFileTwiceAsync()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 
LoadUrlAndQuitBrowser(const std::string & html_content,const std::wstring & expected_title=L"")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 
ClickModalDialogButton(ui::MessageBoxFlags::DialogButton button)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
TEST_F(UnloadTest,DISABLED_CrossSiteInfiniteUnloadAsync)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.
TEST_F(UnloadTest,CrossSiteInfiniteUnloadSync)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.
TEST_F(UnloadTest,MAYBE_CrossSiteInfiniteUnloadAsyncInputEvent)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
TEST_F(UnloadTest,FLAKY_CrossSiteInfiniteBeforeUnloadAsync)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.
TEST_F(UnloadTest,CrossSiteInfiniteBeforeUnloadSync)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.
TEST_F(UnloadTest,BrowserCloseNoUnloadListeners)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
TEST_F(UnloadTest,FLAKY_BrowserCloseUnload)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.
TEST_F(UnloadTest,BrowserCloseBeforeUnloadOK)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.
TEST_F(UnloadTest,BrowserCloseBeforeUnloadCancel)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.
TEST_F(UnloadTest,MAYBE_BrowserCloseWithInnerFocusedFrame)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.
TEST_F(UnloadTest,BrowserCloseTwoSecondBeforeUnload)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.
TEST_F(UnloadTest,BrowserCloseInfiniteUnload)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.
TEST_F(UnloadTest,MAYBE_BrowserCloseInfiniteBeforeUnload)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.
TEST_F(UnloadTest,BrowserCloseInfiniteUnloadAlert)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.
TEST_F(UnloadTest,MAYBE_BrowserCloseInfiniteBeforeUnloadAlert)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.
TEST_F(UnloadTest,BrowserCloseTwoSecondUnloadAlert)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.
TEST_F(UnloadTest,BrowserCloseTwoSecondBeforeUnloadAlert)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.
TEST_F(UnloadTest,MAYBE_BrowserCloseTabWhenOtherTabHasListener)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