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.
5 #include "chrome/test/base/in_process_browser_test.h"
7 #include "base/auto_reset.h"
8 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/location.h"
15 #include "base/path_service.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/test/test_file_util.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/non_thread_safe.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/devtools/devtools_window.h"
24 #include "chrome/browser/lifetime/application_lifetime.h"
25 #include "chrome/browser/net/net_error_tab_helper.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_commands.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/browser_list.h"
32 #include "chrome/browser/ui/browser_list_observer.h"
33 #include "chrome/browser/ui/browser_navigator.h"
34 #include "chrome/browser/ui/browser_tabstrip.h"
35 #include "chrome/browser/ui/browser_window.h"
36 #include "chrome/browser/ui/host_desktop.h"
37 #include "chrome/browser/ui/tabs/tab_strip_model.h"
38 #include "chrome/common/chrome_constants.h"
39 #include "chrome/common/chrome_paths.h"
40 #include "chrome/common/chrome_switches.h"
41 #include "chrome/common/logging_chrome.h"
42 #include "chrome/common/url_constants.h"
43 #include "chrome/renderer/chrome_content_renderer_client.h"
44 #include "chrome/test/base/chrome_test_suite.h"
45 #include "chrome/test/base/test_launcher_utils.h"
46 #include "chrome/test/base/test_switches.h"
47 #include "chrome/test/base/testing_browser_process.h"
48 #include "components/google/core/browser/google_util.h"
49 #include "components/os_crypt/os_crypt.h"
50 #include "content/public/browser/devtools_agent_host.h"
51 #include "content/public/browser/notification_service.h"
52 #include "content/public/browser/notification_types.h"
53 #include "content/public/test/browser_test_utils.h"
54 #include "content/public/test/test_launcher.h"
55 #include "content/public/test/test_navigation_observer.h"
56 #include "net/test/embedded_test_server/embedded_test_server.h"
57 #include "net/test/spawned_test_server/spawned_test_server.h"
59 #if defined(OS_MACOSX)
60 #include "base/mac/scoped_nsautorelease_pool.h"
61 #include "chrome/test/base/scoped_bundle_swizzler_mac.h"
65 #include "base/win/scoped_com_initializer.h"
66 #include "base/win/windows_version.h"
67 #include "ui/base/win/atl_module.h"
68 #include "win8/test/metro_registration_helper.h"
69 #include "win8/test/test_registrar_constants.h"
72 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
73 #include "chrome/browser/captive_portal/captive_portal_service.h"
76 #if !defined(OS_ANDROID) && !defined(OS_IOS)
77 #include "components/storage_monitor/test_storage_monitor.h"
80 #if defined(OS_CHROMEOS)
81 #include "chrome/browser/chromeos/input_method/input_method_configuration.h"
86 // Passed as value of kTestType.
87 const char kBrowserTestType
[] = "browser";
89 // A BrowserListObserver that makes sure that all browsers created are on the
90 // |allowed_desktop_|.
91 class SingleDesktopTestObserver
: public chrome::BrowserListObserver
,
92 public base::NonThreadSafe
{
94 explicit SingleDesktopTestObserver(chrome::HostDesktopType allowed_desktop
);
95 ~SingleDesktopTestObserver() override
;
97 // chrome::BrowserListObserver:
98 void OnBrowserAdded(Browser
* browser
) override
;
101 chrome::HostDesktopType allowed_desktop_
;
103 DISALLOW_COPY_AND_ASSIGN(SingleDesktopTestObserver
);
106 SingleDesktopTestObserver::SingleDesktopTestObserver(
107 chrome::HostDesktopType allowed_desktop
)
108 : allowed_desktop_(allowed_desktop
) {
109 BrowserList::AddObserver(this);
112 SingleDesktopTestObserver::~SingleDesktopTestObserver() {
113 BrowserList::RemoveObserver(this);
116 void SingleDesktopTestObserver::OnBrowserAdded(Browser
* browser
) {
117 CHECK(CalledOnValidThread());
118 CHECK_EQ(browser
->host_desktop_type(), allowed_desktop_
);
123 // Library used for testing accessibility.
124 const base::FilePath::CharType kAXSTesting
[] =
125 FILE_PATH_LITERAL("third_party/accessibility-audit/axs_testing.js");
126 // JavaScript snippet to configure and run the accessibility audit.
127 const char kAccessibilityTestString
[] =
128 "var config = new axs.AuditConfiguration();"
129 "/* Disable warning about rules that cannot be checked. */"
130 "config.showUnsupportedRulesWarning = false;"
131 "config.auditRulesToIgnore = ["
133 " * The 'elements with meaningful background image' accessibility"
134 " * audit (AX_IMAGE_01) does not apply, since Chrome doesn't"
135 " * disable background images in high-contrast mode like some"
138 " 'elementsWithMeaningfulBackgroundImage',"
140 " * Most WebUI pages are inside an IFrame, so the 'web page should"
141 " * have a title that describes topic or purpose' test (AX_TITLE_01)"
142 " * generally does not apply."
144 " 'pageWithoutTitle',"
146 " * Enable when crbug.com/267035 is fixed."
147 " * Until then it's just noise."
149 " 'lowContrastElements'"
151 "var result = axs.Audit.run(config);"
153 "for (var i = 0; i < result.length; ++i) {"
154 " if (result[i].result == axs.constants.AuditResult.FAIL) {"
155 " error = axs.Audit.createReport(result);"
159 "domAutomationController.send(error);";
161 InProcessBrowserTest::InProcessBrowserTest()
163 exit_when_last_browser_closes_(true),
164 open_about_blank_on_browser_launch_(true),
165 multi_desktop_test_(false),
166 run_accessibility_checks_for_test_case_(false)
167 #if defined(OS_MACOSX)
168 , autorelease_pool_(NULL
)
171 #if defined(OS_MACOSX)
172 // TODO(phajdan.jr): Make browser_tests self-contained on Mac, remove this.
173 // Before we run the browser, we have to hack the path to the exe to match
174 // what it would be if Chrome was running, because it is used to fork renderer
175 // processes, on Linux at least (failure to do so will cause a browser_test to
176 // be run instead of a renderer).
177 base::FilePath chrome_path
;
178 CHECK(PathService::Get(base::FILE_EXE
, &chrome_path
));
179 chrome_path
= chrome_path
.DirName();
180 chrome_path
= chrome_path
.Append(chrome::kBrowserProcessExecutablePath
);
181 CHECK(PathService::Override(base::FILE_EXE
, chrome_path
));
182 #endif // defined(OS_MACOSX)
184 CreateTestServer(base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
185 base::FilePath src_dir
;
186 CHECK(PathService::Get(base::DIR_SOURCE_ROOT
, &src_dir
));
187 base::FilePath test_data_dir
= src_dir
.AppendASCII("chrome/test/data");
188 embedded_test_server()->ServeFilesFromDirectory(test_data_dir
);
190 // chrome::DIR_TEST_DATA isn't going to be setup until after we call
191 // ContentMain. However that is after tests' constructors or SetUp methods,
192 // which sometimes need it. So just override it.
193 CHECK(PathService::Override(chrome::DIR_TEST_DATA
, test_data_dir
));
195 #if defined(OS_MACOSX)
196 bundle_swizzler_
.reset(new ScopedBundleSwizzlerMac
);
200 InProcessBrowserTest::~InProcessBrowserTest() {
203 void InProcessBrowserTest::SetUp() {
204 // Browser tests will create their own g_browser_process later.
205 DCHECK(!g_browser_process
);
207 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
209 // Auto-reload breaks many browser tests, which assume error pages won't be
210 // reloaded out from under them. Tests that expect or desire this behavior can
211 // append switches::kEnableOfflineAutoReload, which will override the disable
213 command_line
->AppendSwitch(switches::kDisableOfflineAutoReload
);
215 // Allow subclasses to change the command line before running any tests.
216 SetUpCommandLine(command_line
);
217 // Add command line arguments that are used by all InProcessBrowserTests.
218 PrepareTestCommandLine(command_line
);
220 // Create a temporary user data directory if required.
221 ASSERT_TRUE(CreateUserDataDirectory())
222 << "Could not create user data directory.";
224 // Allow subclasses the opportunity to make changes to the default user data
225 // dir before running any tests.
226 ASSERT_TRUE(SetUpUserDataDirectory())
227 << "Could not set up user data directory.";
229 #if defined(OS_CHROMEOS)
230 // Make sure that the log directory exists.
231 base::FilePath log_dir
= logging::GetSessionLogFile(*command_line
).DirName();
232 base::CreateDirectory(log_dir
);
233 // Disable IME extension loading to avoid many browser tests failures.
234 chromeos::input_method::DisableExtensionLoading();
235 #endif // defined(OS_CHROMEOS)
237 #if defined(OS_MACOSX)
238 // Always use the MockKeychain if OS encription is used (which is when
239 // anything sensitive gets stored, including Cookies). Without this,
240 // many tests will hang waiting for a user to approve KeyChain access.
241 OSCrypt::UseMockKeychain(true);
244 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
245 CaptivePortalService::set_state_for_testing(
246 CaptivePortalService::DISABLED_FOR_TESTING
);
249 chrome_browser_net::NetErrorTabHelper::set_state_for_testing(
250 chrome_browser_net::NetErrorTabHelper::TESTING_FORCE_DISABLED
);
252 google_util::SetMockLinkDoctorBaseURLForTesting();
255 base::win::Version version
= base::win::GetVersion();
256 // Although Ash officially is only supported for users on Win7+, we still run
257 // ash_unittests on Vista builders, so we still need to initialize COM.
258 if (version
>= base::win::VERSION_VISTA
&&
259 base::CommandLine::ForCurrentProcess()->HasSwitch(
260 switches::kAshBrowserTests
)) {
261 com_initializer_
.reset(new base::win::ScopedCOMInitializer());
262 ui::win::CreateATLModuleIfNeeded();
263 if (version
>= base::win::VERSION_WIN8
)
264 ASSERT_TRUE(win8::MakeTestDefaultBrowserSynchronously());
268 BrowserTestBase::SetUp();
271 void InProcessBrowserTest::PrepareTestCommandLine(
272 base::CommandLine
* command_line
) {
273 // Propagate commandline settings from test_launcher_utils.
274 test_launcher_utils::PrepareBrowserCommandLineForTests(command_line
);
276 // This is a Browser test.
277 command_line
->AppendSwitchASCII(switches::kTestType
, kBrowserTestType
);
280 if (command_line
->HasSwitch(switches::kAshBrowserTests
)) {
281 command_line
->AppendSwitchNative(switches::kViewerLaunchViaAppId
,
282 win8::test::kDefaultTestAppUserModelId
);
283 // Ash already launches with a single browser opened, add kSilentLaunch to
284 // make sure StartupBrowserCreator doesn't attempt to launch a browser on
285 // the native desktop on startup.
286 command_line
->AppendSwitch(switches::kSilentLaunch
);
290 #if defined(OS_MACOSX)
291 // Explicitly set the path of the binary used for child processes, otherwise
292 // they'll try to use browser_tests which doesn't contain ChromeMain.
293 base::FilePath subprocess_path
;
294 PathService::Get(base::FILE_EXE
, &subprocess_path
);
295 // Recreate the real environment, run the helper within the app bundle.
296 subprocess_path
= subprocess_path
.DirName().DirName();
297 DCHECK_EQ(subprocess_path
.BaseName().value(), "Contents");
299 subprocess_path
.Append("Versions").Append(chrome::kChromeVersion
);
301 subprocess_path
.Append(chrome::kHelperProcessExecutablePath
);
302 command_line
->AppendSwitchPath(switches::kBrowserSubprocessPath
,
306 // TODO(pkotwicz): Investigate if we can remove this switch.
307 if (exit_when_last_browser_closes_
)
308 command_line
->AppendSwitch(switches::kDisableZeroBrowsersOpenForTests
);
310 if (open_about_blank_on_browser_launch_
&& command_line
->GetArgs().empty())
311 command_line
->AppendArg(url::kAboutBlankURL
);
314 bool InProcessBrowserTest::RunAccessibilityChecks(std::string
* error_message
) {
316 *error_message
= "browser is NULL";
319 auto tab_strip
= browser()->tab_strip_model();
321 *error_message
= "tab_strip is NULL";
324 auto web_contents
= tab_strip
->GetActiveWebContents();
326 *error_message
= "web_contents is NULL";
329 auto focused_frame
= web_contents
->GetFocusedFrame();
330 if (!focused_frame
) {
331 *error_message
= "focused_frame is NULL";
335 // Load accessibility library.
336 base::FilePath src_dir
;
337 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &src_dir
)) {
338 *error_message
= "PathService::Get failed";
341 base::FilePath script_path
= src_dir
.Append(kAXSTesting
);
343 if (!base::ReadFileToString(script_path
, &script
)) {
344 *error_message
= "Could not read accessibility library";
347 if (!content::ExecuteScript(web_contents
, script
)) {
348 *error_message
= "Failed to load accessibility library";
352 // Run accessibility audit.
353 if (!content::ExecuteScriptAndExtractString(focused_frame
,
354 kAccessibilityTestString
,
356 *error_message
= "Failed to run accessibility audit";
360 // Test result should be empty if there are no errors.
361 return error_message
->empty();
364 bool InProcessBrowserTest::CreateUserDataDirectory() {
365 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
366 base::FilePath user_data_dir
=
367 command_line
->GetSwitchValuePath(switches::kUserDataDir
);
368 if (user_data_dir
.empty()) {
369 if (temp_user_data_dir_
.CreateUniqueTempDir() &&
370 temp_user_data_dir_
.IsValid()) {
371 user_data_dir
= temp_user_data_dir_
.path();
373 LOG(ERROR
) << "Could not create temporary user data directory \""
374 << temp_user_data_dir_
.path().value() << "\".";
378 return test_launcher_utils::OverrideUserDataDir(user_data_dir
);
381 void InProcessBrowserTest::TearDown() {
382 DCHECK(!g_browser_process
);
384 com_initializer_
.reset();
386 BrowserTestBase::TearDown();
389 void InProcessBrowserTest::CloseBrowserSynchronously(Browser
* browser
) {
390 content::WindowedNotificationObserver
observer(
391 chrome::NOTIFICATION_BROWSER_CLOSED
,
392 content::Source
<Browser
>(browser
));
393 CloseBrowserAsynchronously(browser
);
397 void InProcessBrowserTest::CloseBrowserAsynchronously(Browser
* browser
) {
398 browser
->window()->Close();
399 #if defined(OS_MACOSX)
400 // BrowserWindowController depends on the auto release pool being recycled
401 // in the message loop to delete itself.
402 AutoreleasePool()->Recycle();
406 void InProcessBrowserTest::CloseAllBrowsers() {
407 chrome::CloseAllBrowsers();
408 #if defined(OS_MACOSX)
409 // BrowserWindowController depends on the auto release pool being recycled
410 // in the message loop to delete itself.
411 AutoreleasePool()->Recycle();
415 // TODO(alexmos): This function should expose success of the underlying
416 // navigation to tests, which should make sure navigations succeed when
417 // appropriate. See https://crbug.com/425335
418 void InProcessBrowserTest::AddTabAtIndexToBrowser(
422 ui::PageTransition transition
,
423 bool check_navigation_success
) {
424 chrome::NavigateParams
params(browser
, url
, transition
);
425 params
.tabstrip_index
= index
;
426 params
.disposition
= NEW_FOREGROUND_TAB
;
427 chrome::Navigate(¶ms
);
429 if (check_navigation_success
)
430 content::WaitForLoadStop(params
.target_contents
);
432 content::WaitForLoadStopWithoutSuccessCheck(params
.target_contents
);
435 void InProcessBrowserTest::AddTabAtIndex(
438 ui::PageTransition transition
) {
439 AddTabAtIndexToBrowser(browser(), index
, url
, transition
, true);
442 bool InProcessBrowserTest::SetUpUserDataDirectory() {
446 #if !defined(OS_MACOSX)
447 void InProcessBrowserTest::OpenDevToolsWindow(
448 content::WebContents
* web_contents
) {
449 ASSERT_FALSE(content::DevToolsAgentHost::HasFor(web_contents
));
450 DevToolsWindow::OpenDevToolsWindow(web_contents
);
451 ASSERT_TRUE(content::DevToolsAgentHost::HasFor(web_contents
));
454 Browser
* InProcessBrowserTest::OpenURLOffTheRecord(Profile
* profile
,
456 chrome::HostDesktopType active_desktop
= chrome::GetActiveDesktop();
457 chrome::OpenURLOffTheRecord(profile
, url
, active_desktop
);
458 Browser
* browser
= chrome::FindTabbedBrowser(
459 profile
->GetOffTheRecordProfile(), false, active_desktop
);
460 content::TestNavigationObserver
observer(
461 browser
->tab_strip_model()->GetActiveWebContents());
466 // Creates a browser with a single tab (about:blank), waits for the tab to
467 // finish loading and shows the browser.
468 Browser
* InProcessBrowserTest::CreateBrowser(Profile
* profile
) {
469 Browser
* browser
= new Browser(
470 Browser::CreateParams(profile
, chrome::GetActiveDesktop()));
471 AddBlankTabAndShow(browser
);
475 Browser
* InProcessBrowserTest::CreateIncognitoBrowser() {
476 // Create a new browser with using the incognito profile.
477 Browser
* incognito
= new Browser(
478 Browser::CreateParams(browser()->profile()->GetOffTheRecordProfile(),
479 chrome::GetActiveDesktop()));
480 AddBlankTabAndShow(incognito
);
484 Browser
* InProcessBrowserTest::CreateBrowserForPopup(Profile
* profile
) {
486 new Browser(Browser::CreateParams(Browser::TYPE_POPUP
, profile
,
487 chrome::GetActiveDesktop()));
488 AddBlankTabAndShow(browser
);
492 Browser
* InProcessBrowserTest::CreateBrowserForApp(
493 const std::string
& app_name
,
495 Browser
* browser
= new Browser(
496 Browser::CreateParams::CreateForApp(
497 app_name
, false /* trusted_source */, gfx::Rect(), profile
,
498 chrome::GetActiveDesktop()));
499 AddBlankTabAndShow(browser
);
502 #endif // !defined(OS_MACOSX)
504 void InProcessBrowserTest::AddBlankTabAndShow(Browser
* browser
) {
505 content::WindowedNotificationObserver
observer(
506 content::NOTIFICATION_LOAD_STOP
,
507 content::NotificationService::AllSources());
508 chrome::AddSelectedTabWithURL(browser
,
509 GURL(url::kAboutBlankURL
),
510 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
);
513 browser
->window()->Show();
516 #if !defined(OS_MACOSX)
517 base::CommandLine
InProcessBrowserTest::GetCommandLineForRelaunch() {
518 base::CommandLine
new_command_line(
519 base::CommandLine::ForCurrentProcess()->GetProgram());
520 base::CommandLine::SwitchMap switches
=
521 base::CommandLine::ForCurrentProcess()->GetSwitches();
522 switches
.erase(switches::kUserDataDir
);
523 switches
.erase(content::kSingleProcessTestsFlag
);
524 switches
.erase(switches::kSingleProcess
);
525 new_command_line
.AppendSwitch(content::kLaunchAsBrowser
);
527 base::FilePath user_data_dir
;
528 PathService::Get(chrome::DIR_USER_DATA
, &user_data_dir
);
529 new_command_line
.AppendSwitchPath(switches::kUserDataDir
, user_data_dir
);
531 for (base::CommandLine::SwitchMap::const_iterator iter
= switches
.begin();
532 iter
!= switches
.end(); ++iter
) {
533 new_command_line
.AppendSwitchNative((*iter
).first
, (*iter
).second
);
535 return new_command_line
;
539 void InProcessBrowserTest::RunTestOnMainThreadLoop() {
540 // Pump startup related events.
541 content::RunAllPendingInMessageLoop();
543 chrome::HostDesktopType active_desktop
= chrome::GetActiveDesktop();
544 // Self-adds/removes itself from the BrowserList observers.
545 scoped_ptr
<SingleDesktopTestObserver
> single_desktop_test_observer
;
546 if (!multi_desktop_test_
) {
547 single_desktop_test_observer
.reset(
548 new SingleDesktopTestObserver(active_desktop
));
551 const BrowserList
* active_browser_list
=
552 BrowserList::GetInstance(active_desktop
);
553 if (!active_browser_list
->empty()) {
554 browser_
= active_browser_list
->get(0);
556 // There are cases where windows get created maximized by default.
557 if (browser_
->window()->IsMaximized())
558 browser_
->window()->Restore();
560 content::WaitForLoadStop(
561 browser_
->tab_strip_model()->GetActiveWebContents());
564 #if !defined(OS_ANDROID) && !defined(OS_IOS)
565 // Do not use the real StorageMonitor for tests, which introduces another
566 // source of variability and potential slowness.
567 ASSERT_TRUE(storage_monitor::TestStorageMonitor::CreateForBrowserTests());
570 #if defined(OS_MACOSX)
571 // On Mac, without the following autorelease pool, code which is directly
572 // executed (as opposed to executed inside a message loop) would autorelease
573 // objects into a higher-level pool. This pool is not recycled in-sync with
574 // the message loops' pools and causes problems with code relying on
575 // deallocation via an autorelease pool (such as browser window closure and
576 // browser shutdown). To avoid this, the following pool is recycled after each
577 // time code is directly executed.
578 autorelease_pool_
= new base::mac::ScopedNSAutoreleasePool
;
581 // Pump any pending events that were created as a result of creating a
583 content::RunAllPendingInMessageLoop();
585 // run_accessibility_checks_for_test_case_ must be set before calling
586 // SetUpOnMainThread or RunTestOnMainThread so that one or all tests can
587 // enable/disable the accessibility audit.
588 run_accessibility_checks_for_test_case_
= false;
591 #if defined(OS_MACOSX)
592 autorelease_pool_
->Recycle();
595 if (!HasFatalFailure())
596 RunTestOnMainThread();
597 #if defined(OS_MACOSX)
598 autorelease_pool_
->Recycle();
601 if (run_accessibility_checks_for_test_case_
) {
602 std::string error_message
;
603 EXPECT_TRUE(RunAccessibilityChecks(&error_message
));
604 EXPECT_EQ("", error_message
);
607 // Invoke cleanup and quit even if there are failures. This is similar to
608 // gtest in that it invokes TearDown even if Setup fails.
609 TearDownOnMainThread();
610 #if defined(OS_MACOSX)
611 autorelease_pool_
->Recycle();
614 // Sometimes tests leave Quit tasks in the MessageLoop (for shame), so let's
615 // run all pending messages here to avoid preempting the QuitBrowsers tasks.
616 // TODO(jbates) Once crbug.com/134753 is fixed, this can be removed because it
617 // will not be possible to post Quit tasks.
618 content::RunAllPendingInMessageLoop();
621 // All BrowserLists should be empty at this point.
622 for (chrome::HostDesktopType t
= chrome::HOST_DESKTOP_TYPE_FIRST
;
623 t
< chrome::HOST_DESKTOP_TYPE_COUNT
;
624 t
= static_cast<chrome::HostDesktopType
>(t
+ 1)) {
625 CHECK(BrowserList::GetInstance(t
)->empty()) << t
;
629 void InProcessBrowserTest::QuitBrowsers() {
630 if (chrome::GetTotalBrowserCount() == 0) {
631 chrome::NotifyAppTerminating();
635 // Invoke AttemptExit on a running message loop.
636 // AttemptExit exits the message loop after everything has been
637 // shut down properly.
638 base::ThreadTaskRunnerHandle::Get()->PostTask(
639 FROM_HERE
, base::Bind(&chrome::AttemptExit
));
640 content::RunMessageLoop();
642 #if defined(OS_MACOSX)
643 // chrome::AttemptExit() will attempt to close all browsers by deleting
644 // their tab contents. The last tab contents being removed triggers closing of
645 // the browser window.
647 // On the Mac, this eventually reaches
648 // -[BrowserWindowController windowWillClose:], which will post a deferred
649 // -autorelease on itself to ultimately destroy the Browser object. The line
650 // below is necessary to pump these pending messages to ensure all Browsers
652 content::RunAllPendingInMessageLoop();
653 delete autorelease_pool_
;
654 autorelease_pool_
= NULL
;