[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 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 | #include "content/public/browser/browser_main_runner.h" |
| 6 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 7 | #include "base/base_switches.h" |
| 8 | #include "base/command_line.h" |
[email protected] | 1cefd4b | 2013-11-20 21:55:40 | [diff] [blame] | 9 | #include "base/debug/leak_annotations.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 10 | #include "base/logging.h" |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 11 | #include "base/macros.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 12 | #include "base/metrics/histogram.h" |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 13 | #include "base/metrics/histogram_macros.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 14 | #include "base/metrics/statistics_recorder.h" |
yiyaoliu | 252f915e | 2015-04-23 21:56:36 | [diff] [blame] | 15 | #include "base/profiler/scoped_profile.h" |
yiyaoliu | 9e6a5ab3 | 2015-03-18 18:04:37 | [diff] [blame] | 16 | #include "base/profiler/scoped_tracker.h" |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 17 | #include "base/time/time.h" |
ssid | 3e76561 | 2015-01-28 04:03:42 | [diff] [blame] | 18 | #include "base/trace_event/trace_event.h" |
yiyaoliu | 9e6a5ab3 | 2015-03-18 18:04:37 | [diff] [blame] | 19 | #include "base/tracked_objects.h" |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 20 | #include "build/build_config.h" |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 21 | #include "components/tracing/trace_config_file.h" |
zhenw | ecedcf2 | 2015-08-18 23:37:46 | [diff] [blame] | 22 | #include "components/tracing/tracing_switches.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 23 | #include "content/browser/browser_main_loop.h" |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 24 | #include "content/browser/browser_shutdown_profile_dumper.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 25 | #include "content/browser/notification_service_impl.h" |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 26 | #include "content/public/browser/tracing_controller.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 27 | #include "content/public/common/content_switches.h" |
| 28 | #include "content/public/common/main_function_params.h" |
boliu | 9847d57 | 2015-09-10 20:29:18 | [diff] [blame] | 29 | #include "third_party/skia/include/core/SkGraphics.h" |
[email protected] | 749bf643 | 2013-06-12 16:00:08 | [diff] [blame] | 30 | #include "ui/base/ime/input_method_initializer.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 31 | |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 32 | #if defined(OS_ANDROID) |
| 33 | #include "content/browser/android/tracing_controller_android.h" |
| 34 | #endif |
| 35 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 36 | #if defined(OS_WIN) |
[email protected] | b68b51e | 2012-11-15 01:10:45 | [diff] [blame] | 37 | #include "base/win/windows_version.h" |
[email protected] | 10208ea | 2013-06-06 20:08:03 | [diff] [blame] | 38 | #include "ui/base/win/scoped_ole_initializer.h" |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 39 | #include "ui/gfx/win/direct_write.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 40 | #endif |
| 41 | |
[email protected] | 4648832 | 2012-10-30 03:22:20 | [diff] [blame] | 42 | namespace content { |
[email protected] | eb39819 | 2012-10-22 20:16:19 | [diff] [blame] | 43 | |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 44 | namespace { |
| 45 | |
thestig | 5a551c4c | 2015-08-29 02:45:35 | [diff] [blame] | 46 | bool g_exited_main_message_loop = false; |
| 47 | |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 48 | } // namespace |
| 49 | |
[email protected] | 4648832 | 2012-10-30 03:22:20 | [diff] [blame] | 50 | class BrowserMainRunnerImpl : public BrowserMainRunner { |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 51 | public: |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 52 | BrowserMainRunnerImpl() |
| 53 | : initialization_started_(false), is_shutdown_(false) {} |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 54 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 55 | ~BrowserMainRunnerImpl() override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 56 | if (initialization_started_ && !is_shutdown_) |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 57 | Shutdown(); |
| 58 | } |
| 59 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 60 | int Initialize(const MainFunctionParams& parameters) override { |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 61 | SCOPED_UMA_HISTOGRAM_LONG_TIMER( |
| 62 | "Startup.BrowserMainRunnerImplInitializeLongTime"); |
| 63 | |
yiyaoliu | 9e6a5ab3 | 2015-03-18 18:04:37 | [diff] [blame] | 64 | // TODO(vadimt, yiyaoliu): Remove all tracked_objects references below once |
| 65 | // crbug.com/453640 is fixed. |
| 66 | tracked_objects::ThreadData::InitializeThreadContext("CrBrowserMain"); |
yiyaoliu | 252f915e | 2015-04-23 21:56:36 | [diff] [blame] | 67 | TRACK_SCOPED_REGION( |
| 68 | "Startup", "BrowserMainRunnerImpl::Initialize"); |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 69 | TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize"); |
yiyaoliu | 9e6a5ab3 | 2015-03-18 18:04:37 | [diff] [blame] | 70 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 71 | // On Android we normally initialize the browser in a series of UI thread |
| 72 | // tasks. While this is happening a second request can come from the OS or |
| 73 | // another application to start the browser. If this happens then we must |
| 74 | // not run these parts of initialization twice. |
| 75 | if (!initialization_started_) { |
| 76 | initialization_started_ = true; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 77 | |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 78 | const base::TimeTicks start_time_step1 = base::TimeTicks::Now(); |
| 79 | |
boliu | 9847d57 | 2015-09-10 20:29:18 | [diff] [blame] | 80 | SkGraphics::Init(); |
| 81 | |
[email protected] | 7951bca4 | 2013-06-17 20:01:30 | [diff] [blame] | 82 | #if !defined(OS_IOS) |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 83 | if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) |
| 84 | base::debug::WaitForDebugger(60, true); |
[email protected] | 7951bca4 | 2013-06-17 20:01:30 | [diff] [blame] | 85 | #endif |
| 86 | |
[email protected] | 091e9626 | 2012-09-12 15:35:10 | [diff] [blame] | 87 | #if defined(OS_WIN) |
[email protected] | b4b591bc | 2014-02-03 11:45:05 | [diff] [blame] | 88 | if (base::win::GetVersion() < base::win::VERSION_VISTA) { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 89 | // When "Extend support of advanced text services to all programs" |
| 90 | // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on |
| 91 | // Windows XP and handwriting modules shipped with Office 2003 are |
| 92 | // installed, "penjpn.dll" and "skchui.dll" will be loaded and then |
[email protected] | b4b591bc | 2014-02-03 11:45:05 | [diff] [blame] | 93 | // crash unless a user installs Office 2003 SP3. To prevent these |
| 94 | // modules from being loaded, disable TSF entirely. crbug.com/160914. |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 95 | // TODO(yukawa): Add a high-level wrapper for this instead of calling |
| 96 | // Win32 API here directly. |
| 97 | ImmDisableTextFrameService(static_cast<DWORD>(-1)); |
| 98 | } |
[email protected] | 091e9626 | 2012-09-12 15:35:10 | [diff] [blame] | 99 | #endif // OS_WIN |
| 100 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 101 | base::StatisticsRecorder::Initialize(); |
[email protected] | c480510 | 2012-03-02 23:34:32 | [diff] [blame] | 102 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 103 | notification_service_.reset(new NotificationServiceImpl); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 104 | |
[email protected] | 962a848 | 2013-01-15 03:42:56 | [diff] [blame] | 105 | #if defined(OS_WIN) |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 106 | // Ole must be initialized before starting message pump, so that TSF |
| 107 | // (Text Services Framework) module can interact with the message pump |
| 108 | // on Windows 8 Metro mode. |
| 109 | ole_initializer_.reset(new ui::ScopedOleInitializer); |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 110 | // Enable DirectWrite font rendering if needed. |
ckocagil | 9a4438e | 2014-12-02 04:48:30 | [diff] [blame] | 111 | gfx::win::MaybeInitializeDirectWrite(); |
[email protected] | 962a848 | 2013-01-15 03:42:56 | [diff] [blame] | 112 | #endif // OS_WIN |
| 113 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 114 | main_loop_.reset(new BrowserMainLoop(parameters)); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 115 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 116 | main_loop_->Init(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 117 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 118 | main_loop_->EarlyInitialization(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 119 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 120 | // Must happen before we try to use a message loop or display any UI. |
[email protected] | d957b10 | 2014-04-25 20:17:19 | [diff] [blame] | 121 | if (!main_loop_->InitializeToolkit()) |
| 122 | return 1; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 123 | |
thestig | 7e202d1a | 2015-05-27 21:54:55 | [diff] [blame] | 124 | main_loop_->PreMainMessageLoopStart(); |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 125 | main_loop_->MainMessageLoopStart(); |
thestig | 7e202d1a | 2015-05-27 21:54:55 | [diff] [blame] | 126 | main_loop_->PostMainMessageLoopStart(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 127 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 128 | // WARNING: If we get a WM_ENDSESSION, objects created on the stack here |
| 129 | // are NOT deleted. If you need something to run during WM_ENDSESSION add it |
| 130 | // to browser_shutdown::Shutdown or BrowserProcess::EndSession. |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 131 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 132 | ui::InitializeInputMethod(); |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 133 | UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep1Time", |
| 134 | base::TimeTicks::Now() - start_time_step1); |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 135 | } |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 136 | const base::TimeTicks start_time_step2 = base::TimeTicks::Now(); |
[email protected] | 57624ab | 2013-08-01 16:01:51 | [diff] [blame] | 137 | main_loop_->CreateStartupTasks(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 138 | int result_code = main_loop_->GetResultCode(); |
| 139 | if (result_code > 0) |
| 140 | return result_code; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 141 | |
robliao | 7fac1d8 | 2015-09-23 23:07:40 | [diff] [blame] | 142 | UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep2Time", |
| 143 | base::TimeTicks::Now() - start_time_step2); |
| 144 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 145 | // Return -1 to indicate no early termination. |
| 146 | return -1; |
| 147 | } |
| 148 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 149 | int Run() override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 150 | DCHECK(initialization_started_); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 151 | DCHECK(!is_shutdown_); |
| 152 | main_loop_->RunMainMessageLoopParts(); |
| 153 | return main_loop_->GetResultCode(); |
| 154 | } |
| 155 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 156 | void Shutdown() override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 157 | DCHECK(initialization_started_); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 158 | DCHECK(!is_shutdown_); |
[email protected] | 1cefd4b | 2013-11-20 21:55:40 | [diff] [blame] | 159 | #ifdef LEAK_SANITIZER |
| 160 | // Invoke leak detection now, to avoid dealing with shutdown-only leaks. |
| 161 | // Normally this will have already happened in |
| 162 | // BroserProcessImpl::ReleaseModule(), so this call has no effect. This is |
| 163 | // only for processes which do not instantiate a BrowserProcess. |
| 164 | // If leaks are found, the process will exit here. |
| 165 | __lsan_do_leak_check(); |
| 166 | #endif |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 167 | // If startup tracing has not been finished yet, replace it's dumper |
| 168 | // with special version, which would save trace file on exit (i.e. |
| 169 | // startup tracing becomes a version of shutdown tracing). |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 170 | // There are two cases: |
| 171 | // 1. Startup duration is not reached. |
| 172 | // 2. Or startup duration is not specified for --trace-config-file flag. |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 173 | scoped_ptr<BrowserShutdownProfileDumper> startup_profiler; |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 174 | if (main_loop_->is_tracing_startup_for_duration()) { |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 175 | main_loop_->StopStartupTracingTimer(); |
| 176 | if (main_loop_->startup_trace_file() != |
| 177 | base::FilePath().AppendASCII("none")) { |
| 178 | startup_profiler.reset( |
| 179 | new BrowserShutdownProfileDumper(main_loop_->startup_trace_file())); |
| 180 | } |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 181 | } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled() && |
zhenw | 873bdff | 2015-11-11 22:16:55 | [diff] [blame] | 182 | TracingController::GetInstance()->IsTracing()) { |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 183 | base::FilePath result_file; |
mfomitchev | 2b8b066a | 2016-01-28 19:23:15 | [diff] [blame] | 184 | #if defined(OS_ANDROID) |
zhenw | c074d28 | 2015-08-31 17:28:17 | [diff] [blame] | 185 | TracingControllerAndroid::GenerateTracingFilePath(&result_file); |
| 186 | #else |
| 187 | result_file = tracing::TraceConfigFile::GetInstance()->GetResultFile(); |
| 188 | #endif |
| 189 | startup_profiler.reset(new BrowserShutdownProfileDumper(result_file)); |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 190 | } |
| 191 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 192 | // The shutdown tracing got enabled in AttemptUserExit earlier, but someone |
| 193 | // needs to write the result to disc. For that a dumper needs to get created |
| 194 | // which will dump the traces to disc when it gets destroyed. |
[email protected] | 47927870 | 2014-08-11 20:32:09 | [diff] [blame] | 195 | const base::CommandLine& command_line = |
| 196 | *base::CommandLine::ForCurrentProcess(); |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 197 | scoped_ptr<BrowserShutdownProfileDumper> shutdown_profiler; |
| 198 | if (command_line.HasSwitch(switches::kTraceShutdown)) { |
| 199 | shutdown_profiler.reset(new BrowserShutdownProfileDumper( |
| 200 | BrowserShutdownProfileDumper::GetShutdownProfileFileName())); |
| 201 | } |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 202 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 203 | { |
| 204 | // The trace event has to stay between profiler creation and destruction. |
| 205 | TRACE_EVENT0("shutdown", "BrowserMainRunner"); |
| 206 | g_exited_main_message_loop = true; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 207 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 208 | main_loop_->ShutdownThreadsAndCleanUp(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 209 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 210 | ui::ShutdownInputMethod(); |
| 211 | #if defined(OS_WIN) |
| 212 | ole_initializer_.reset(NULL); |
| 213 | #endif |
[email protected] | d2cdd96 | 2014-06-18 09:04:32 | [diff] [blame] | 214 | #if defined(OS_ANDROID) |
| 215 | // Forcefully terminates the RunLoop inside MessagePumpForUI, ensuring |
| 216 | // proper shutdown for content_browsertests. Shutdown() is not used by |
| 217 | // the actual browser. |
jaekyun | 617edc5 | 2014-11-19 00:11:06 | [diff] [blame] | 218 | if (base::MessageLoop::current()->is_running()) |
| 219 | base::MessageLoop::current()->QuitNow(); |
[email protected] | d2cdd96 | 2014-06-18 09:04:32 | [diff] [blame] | 220 | #endif |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 221 | main_loop_.reset(NULL); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 222 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 223 | notification_service_.reset(NULL); |
| 224 | |
| 225 | is_shutdown_ = true; |
| 226 | } |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 227 | } |
| 228 | |
| 229 | protected: |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 230 | // True if we have started to initialize the runner. |
| 231 | bool initialization_started_; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 232 | |
| 233 | // True if the runner has been shut down. |
| 234 | bool is_shutdown_; |
| 235 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 236 | scoped_ptr<NotificationServiceImpl> notification_service_; |
[email protected] | 4648832 | 2012-10-30 03:22:20 | [diff] [blame] | 237 | scoped_ptr<BrowserMainLoop> main_loop_; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 238 | #if defined(OS_WIN) |
[email protected] | e000ac9 | 2012-10-01 18:20:48 | [diff] [blame] | 239 | scoped_ptr<ui::ScopedOleInitializer> ole_initializer_; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 240 | #endif |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 241 | |
thestig | 5a551c4c | 2015-08-29 02:45:35 | [diff] [blame] | 242 | private: |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 243 | DISALLOW_COPY_AND_ASSIGN(BrowserMainRunnerImpl); |
| 244 | }; |
| 245 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 246 | // static |
| 247 | BrowserMainRunner* BrowserMainRunner::Create() { |
| 248 | return new BrowserMainRunnerImpl(); |
| 249 | } |
| 250 | |
thestig | 5a551c4c | 2015-08-29 02:45:35 | [diff] [blame] | 251 | // static |
| 252 | bool BrowserMainRunner::ExitedMainMessageLoop() { |
| 253 | return g_exited_main_message_loop; |
| 254 | } |
| 255 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 256 | } // namespace content |