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