[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 | |
| 7 | #include "base/allocator/allocator_shim.h" |
| 8 | #include "base/base_switches.h" |
| 9 | #include "base/command_line.h" |
[email protected] | 1cefd4b | 2013-11-20 21:55:40 | [diff] [blame] | 10 | #include "base/debug/leak_annotations.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 11 | #include "base/debug/trace_event.h" |
| 12 | #include "base/logging.h" |
| 13 | #include "base/metrics/histogram.h" |
[email protected] | 567d30e | 2012-07-13 21:48:29 | [diff] [blame] | 14 | #include "base/metrics/statistics_recorder.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 15 | #include "content/browser/browser_main_loop.h" |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 16 | #include "content/browser/browser_shutdown_profile_dumper.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 17 | #include "content/browser/notification_service_impl.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 18 | #include "content/public/common/content_switches.h" |
| 19 | #include "content/public/common/main_function_params.h" |
[email protected] | 749bf643 | 2013-06-12 16:00:08 | [diff] [blame] | 20 | #include "ui/base/ime/input_method_initializer.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 21 | |
| 22 | #if defined(OS_WIN) |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 23 | #include "base/win/win_util.h" |
[email protected] | b68b51e | 2012-11-15 01:10:45 | [diff] [blame] | 24 | #include "base/win/windows_version.h" |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 25 | #include "net/cert/sha256_legacy_support_win.h" |
| 26 | #include "sandbox/win/src/sidestep/preamble_patcher.h" |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 27 | #include "skia/ext/fontmgr_default_win.h" |
| 28 | #include "third_party/skia/include/ports/SkFontMgr.h" |
| 29 | #include "third_party/skia/include/ports/SkTypeface_win.h" |
[email protected] | 10208ea | 2013-06-06 20:08:03 | [diff] [blame] | 30 | #include "ui/base/win/scoped_ole_initializer.h" |
ananta | 9fd5754 | 2014-10-30 00:14:43 | [diff] [blame] | 31 | #include "ui/gfx/platform_font_win.h" |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 32 | #include "ui/gfx/switches.h" |
| 33 | #include "ui/gfx/win/direct_write.h" |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 34 | #endif |
| 35 | |
| 36 | bool g_exited_main_message_loop = false; |
| 37 | |
[email protected] | 4648832 | 2012-10-30 03:22:20 | [diff] [blame] | 38 | namespace content { |
[email protected] | eb39819 | 2012-10-22 20:16:19 | [diff] [blame] | 39 | |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 40 | #if defined(OS_WIN) |
| 41 | namespace { |
| 42 | |
| 43 | // Pointer to the original CryptVerifyCertificateSignatureEx function. |
| 44 | net::sha256_interception::CryptVerifyCertificateSignatureExFunc |
| 45 | g_real_crypt_verify_signature_stub = NULL; |
| 46 | |
| 47 | // Stub function that is called whenever the Crypt32 function |
| 48 | // CryptVerifyCertificateSignatureEx is called. It just defers to net to perform |
| 49 | // the actual verification. |
| 50 | BOOL WINAPI CryptVerifyCertificateSignatureExStub( |
| 51 | HCRYPTPROV_LEGACY provider, |
| 52 | DWORD encoding_type, |
| 53 | DWORD subject_type, |
| 54 | void* subject_data, |
| 55 | DWORD issuer_type, |
| 56 | void* issuer_data, |
| 57 | DWORD flags, |
| 58 | void* extra) { |
| 59 | return net::sha256_interception::CryptVerifyCertificateSignatureExHook( |
| 60 | g_real_crypt_verify_signature_stub, provider, encoding_type, subject_type, |
| 61 | subject_data, issuer_type, issuer_data, flags, extra); |
| 62 | } |
| 63 | |
| 64 | // If necessary, install an interception |
| 65 | void InstallSha256LegacyHooks() { |
| 66 | #if defined(_WIN64) |
| 67 | // Interception on x64 is not supported. |
| 68 | return; |
| 69 | #else |
| 70 | if (base::win::MaybeHasSHA256Support()) |
| 71 | return; |
| 72 | |
| 73 | net::sha256_interception::CryptVerifyCertificateSignatureExFunc |
| 74 | cert_verify_signature_ptr = reinterpret_cast< |
| 75 | net::sha256_interception::CryptVerifyCertificateSignatureExFunc>( |
| 76 | ::GetProcAddress(::GetModuleHandle(L"crypt32.dll"), |
| 77 | "CryptVerifyCertificateSignatureEx")); |
| 78 | CHECK(cert_verify_signature_ptr); |
| 79 | |
| 80 | DWORD old_protect = 0; |
| 81 | if (!::VirtualProtect(cert_verify_signature_ptr, 5, PAGE_EXECUTE_READWRITE, |
| 82 | &old_protect)) { |
| 83 | return; |
| 84 | } |
| 85 | |
| 86 | g_real_crypt_verify_signature_stub = |
| 87 | reinterpret_cast< |
| 88 | net::sha256_interception::CryptVerifyCertificateSignatureExFunc>( |
| 89 | VirtualAllocEx(::GetCurrentProcess(), NULL, |
| 90 | sidestep::kMaxPreambleStubSize, MEM_COMMIT, |
| 91 | PAGE_EXECUTE_READWRITE)); |
| 92 | if (g_real_crypt_verify_signature_stub == NULL) { |
| 93 | CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, |
| 94 | &old_protect)); |
| 95 | return; |
| 96 | } |
| 97 | |
| 98 | sidestep::SideStepError patch_result = |
| 99 | sidestep::PreamblePatcher::Patch( |
| 100 | cert_verify_signature_ptr, CryptVerifyCertificateSignatureExStub, |
| 101 | g_real_crypt_verify_signature_stub, sidestep::kMaxPreambleStubSize); |
| 102 | if (patch_result != sidestep::SIDESTEP_SUCCESS) { |
| 103 | CHECK(::VirtualFreeEx(::GetCurrentProcess(), |
| 104 | g_real_crypt_verify_signature_stub, 0, |
| 105 | MEM_RELEASE)); |
| 106 | CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, |
| 107 | &old_protect)); |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | DWORD dummy = 0; |
| 112 | CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, &dummy)); |
| 113 | CHECK(::VirtualProtect(g_real_crypt_verify_signature_stub, |
| 114 | sidestep::kMaxPreambleStubSize, old_protect, |
| 115 | &old_protect)); |
| 116 | #endif // _WIN64 |
| 117 | } |
| 118 | |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 119 | void MaybeEnableDirectWriteFontRendering() { |
| 120 | if (gfx::win::ShouldUseDirectWrite() && |
| 121 | CommandLine::ForCurrentProcess()->HasSwitch( |
| 122 | switches::kEnableDirectWriteForUI) && |
ananta | 9fd5754 | 2014-10-30 00:14:43 | [diff] [blame] | 123 | !CommandLine::ForCurrentProcess()->HasSwitch( |
| 124 | switches::kDisableHarfBuzzRenderText)) { |
| 125 | typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; |
| 126 | HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); |
| 127 | if (!dwrite_dll) |
| 128 | return; |
| 129 | |
| 130 | DWriteCreateFactoryProc dwrite_create_factory_proc = |
| 131 | reinterpret_cast<DWriteCreateFactoryProc>( |
| 132 | GetProcAddress(dwrite_dll, "DWriteCreateFactory")); |
| 133 | // Not finding the DWriteCreateFactory function indicates a corrupt dll. |
| 134 | CHECK(dwrite_create_factory_proc); |
| 135 | |
| 136 | IDWriteFactory* factory = NULL; |
| 137 | |
| 138 | CHECK(SUCCEEDED( |
| 139 | dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_SHARED, |
| 140 | __uuidof(IDWriteFactory), |
| 141 | reinterpret_cast<IUnknown**>(&factory)))); |
| 142 | SetDefaultSkiaFactory(SkFontMgr_New_DirectWrite(factory)); |
| 143 | gfx::PlatformFontWin::set_direct_write_factory(factory); |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 144 | } |
| 145 | } |
| 146 | |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 147 | } // namespace |
| 148 | |
| 149 | #endif // OS_WIN |
| 150 | |
[email protected] | 4648832 | 2012-10-30 03:22:20 | [diff] [blame] | 151 | class BrowserMainRunnerImpl : public BrowserMainRunner { |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 152 | public: |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 153 | BrowserMainRunnerImpl() |
| 154 | : initialization_started_(false), is_shutdown_(false) {} |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 155 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 156 | ~BrowserMainRunnerImpl() override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 157 | if (initialization_started_ && !is_shutdown_) |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 158 | Shutdown(); |
| 159 | } |
| 160 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 161 | int Initialize(const MainFunctionParams& parameters) override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 162 | TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize"); |
| 163 | // On Android we normally initialize the browser in a series of UI thread |
| 164 | // tasks. While this is happening a second request can come from the OS or |
| 165 | // another application to start the browser. If this happens then we must |
| 166 | // not run these parts of initialization twice. |
| 167 | if (!initialization_started_) { |
| 168 | initialization_started_ = true; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 169 | |
[email protected] | 7951bca4 | 2013-06-17 20:01:30 | [diff] [blame] | 170 | #if !defined(OS_IOS) |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 171 | if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) |
| 172 | base::debug::WaitForDebugger(60, true); |
[email protected] | 7951bca4 | 2013-06-17 20:01:30 | [diff] [blame] | 173 | #endif |
| 174 | |
[email protected] | 091e9626 | 2012-09-12 15:35:10 | [diff] [blame] | 175 | #if defined(OS_WIN) |
[email protected] | b4b591bc | 2014-02-03 11:45:05 | [diff] [blame] | 176 | if (base::win::GetVersion() < base::win::VERSION_VISTA) { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 177 | // When "Extend support of advanced text services to all programs" |
| 178 | // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on |
| 179 | // Windows XP and handwriting modules shipped with Office 2003 are |
| 180 | // installed, "penjpn.dll" and "skchui.dll" will be loaded and then |
[email protected] | b4b591bc | 2014-02-03 11:45:05 | [diff] [blame] | 181 | // crash unless a user installs Office 2003 SP3. To prevent these |
| 182 | // modules from being loaded, disable TSF entirely. crbug.com/160914. |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 183 | // TODO(yukawa): Add a high-level wrapper for this instead of calling |
| 184 | // Win32 API here directly. |
| 185 | ImmDisableTextFrameService(static_cast<DWORD>(-1)); |
| 186 | } |
rsleevi | 25e2bc0a | 2014-09-24 03:12:55 | [diff] [blame] | 187 | InstallSha256LegacyHooks(); |
[email protected] | 091e9626 | 2012-09-12 15:35:10 | [diff] [blame] | 188 | #endif // OS_WIN |
| 189 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 190 | base::StatisticsRecorder::Initialize(); |
[email protected] | c480510 | 2012-03-02 23:34:32 | [diff] [blame] | 191 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 192 | notification_service_.reset(new NotificationServiceImpl); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 193 | |
[email protected] | 962a848 | 2013-01-15 03:42:56 | [diff] [blame] | 194 | #if defined(OS_WIN) |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 195 | // Ole must be initialized before starting message pump, so that TSF |
| 196 | // (Text Services Framework) module can interact with the message pump |
| 197 | // on Windows 8 Metro mode. |
| 198 | ole_initializer_.reset(new ui::ScopedOleInitializer); |
ananta | acfcfce | 2014-10-15 01:14:11 | [diff] [blame] | 199 | // Enable DirectWrite font rendering if needed. |
| 200 | MaybeEnableDirectWriteFontRendering(); |
[email protected] | 962a848 | 2013-01-15 03:42:56 | [diff] [blame] | 201 | #endif // OS_WIN |
| 202 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 203 | main_loop_.reset(new BrowserMainLoop(parameters)); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 204 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 205 | main_loop_->Init(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 206 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 207 | main_loop_->EarlyInitialization(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 208 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 209 | // 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] | 210 | if (!main_loop_->InitializeToolkit()) |
| 211 | return 1; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 212 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 213 | main_loop_->MainMessageLoopStart(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 214 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 215 | // WARNING: If we get a WM_ENDSESSION, objects created on the stack here |
| 216 | // are NOT deleted. If you need something to run during WM_ENDSESSION add it |
| 217 | // to browser_shutdown::Shutdown or BrowserProcess::EndSession. |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 218 | |
[email protected] | 749bf643 | 2013-06-12 16:00:08 | [diff] [blame] | 219 | #if defined(OS_WIN) && !defined(NO_TCMALLOC) |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 220 | // When linking shared libraries, NO_TCMALLOC is defined, and dynamic |
| 221 | // allocator selection is not supported. |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 222 | |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 223 | // Make this call before going multithreaded, or spawning any |
| 224 | // subprocesses. |
| 225 | base::allocator::SetupSubprocessAllocator(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 226 | #endif |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 227 | ui::InitializeInputMethod(); |
| 228 | } |
[email protected] | 57624ab | 2013-08-01 16:01:51 | [diff] [blame] | 229 | main_loop_->CreateStartupTasks(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 230 | int result_code = main_loop_->GetResultCode(); |
| 231 | if (result_code > 0) |
| 232 | return result_code; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 233 | |
| 234 | // Return -1 to indicate no early termination. |
| 235 | return -1; |
| 236 | } |
| 237 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 238 | int Run() override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 239 | DCHECK(initialization_started_); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 240 | DCHECK(!is_shutdown_); |
| 241 | main_loop_->RunMainMessageLoopParts(); |
| 242 | return main_loop_->GetResultCode(); |
| 243 | } |
| 244 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 245 | void Shutdown() override { |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 246 | DCHECK(initialization_started_); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 247 | DCHECK(!is_shutdown_); |
[email protected] | 1cefd4b | 2013-11-20 21:55:40 | [diff] [blame] | 248 | #ifdef LEAK_SANITIZER |
| 249 | // Invoke leak detection now, to avoid dealing with shutdown-only leaks. |
| 250 | // Normally this will have already happened in |
| 251 | // BroserProcessImpl::ReleaseModule(), so this call has no effect. This is |
| 252 | // only for processes which do not instantiate a BrowserProcess. |
| 253 | // If leaks are found, the process will exit here. |
| 254 | __lsan_do_leak_check(); |
| 255 | #endif |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 256 | // If startup tracing has not been finished yet, replace it's dumper |
| 257 | // with special version, which would save trace file on exit (i.e. |
| 258 | // startup tracing becomes a version of shutdown tracing). |
| 259 | scoped_ptr<BrowserShutdownProfileDumper> startup_profiler; |
| 260 | if (main_loop_->is_tracing_startup()) { |
| 261 | main_loop_->StopStartupTracingTimer(); |
| 262 | if (main_loop_->startup_trace_file() != |
| 263 | base::FilePath().AppendASCII("none")) { |
| 264 | startup_profiler.reset( |
| 265 | new BrowserShutdownProfileDumper(main_loop_->startup_trace_file())); |
| 266 | } |
| 267 | } |
| 268 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 269 | // The shutdown tracing got enabled in AttemptUserExit earlier, but someone |
| 270 | // needs to write the result to disc. For that a dumper needs to get created |
| 271 | // which will dump the traces to disc when it gets destroyed. |
[email protected] | 47927870 | 2014-08-11 20:32:09 | [diff] [blame] | 272 | const base::CommandLine& command_line = |
| 273 | *base::CommandLine::ForCurrentProcess(); |
[email protected] | cec9563 | 2014-07-02 18:01:50 | [diff] [blame] | 274 | scoped_ptr<BrowserShutdownProfileDumper> shutdown_profiler; |
| 275 | if (command_line.HasSwitch(switches::kTraceShutdown)) { |
| 276 | shutdown_profiler.reset(new BrowserShutdownProfileDumper( |
| 277 | BrowserShutdownProfileDumper::GetShutdownProfileFileName())); |
| 278 | } |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 279 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 280 | { |
| 281 | // The trace event has to stay between profiler creation and destruction. |
| 282 | TRACE_EVENT0("shutdown", "BrowserMainRunner"); |
| 283 | g_exited_main_message_loop = true; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 284 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 285 | main_loop_->ShutdownThreadsAndCleanUp(); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 286 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 287 | ui::ShutdownInputMethod(); |
| 288 | #if defined(OS_WIN) |
| 289 | ole_initializer_.reset(NULL); |
| 290 | #endif |
[email protected] | d2cdd96 | 2014-06-18 09:04:32 | [diff] [blame] | 291 | #if defined(OS_ANDROID) |
| 292 | // Forcefully terminates the RunLoop inside MessagePumpForUI, ensuring |
| 293 | // proper shutdown for content_browsertests. Shutdown() is not used by |
| 294 | // the actual browser. |
| 295 | base::MessageLoop::current()->QuitNow(); |
| 296 | #endif |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 297 | main_loop_.reset(NULL); |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 298 | |
[email protected] | 89af400 | 2013-09-06 07:47:07 | [diff] [blame] | 299 | notification_service_.reset(NULL); |
| 300 | |
| 301 | is_shutdown_ = true; |
| 302 | } |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 303 | } |
| 304 | |
| 305 | protected: |
[email protected] | 232e09d | 2013-08-27 15:29:56 | [diff] [blame] | 306 | // True if we have started to initialize the runner. |
| 307 | bool initialization_started_; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 308 | |
| 309 | // True if the runner has been shut down. |
| 310 | bool is_shutdown_; |
| 311 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 312 | scoped_ptr<NotificationServiceImpl> notification_service_; |
[email protected] | 4648832 | 2012-10-30 03:22:20 | [diff] [blame] | 313 | scoped_ptr<BrowserMainLoop> main_loop_; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 314 | #if defined(OS_WIN) |
[email protected] | e000ac9 | 2012-10-01 18:20:48 | [diff] [blame] | 315 | scoped_ptr<ui::ScopedOleInitializer> ole_initializer_; |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 316 | #endif |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 317 | |
| 318 | DISALLOW_COPY_AND_ASSIGN(BrowserMainRunnerImpl); |
| 319 | }; |
| 320 | |
[email protected] | f573ed6b | 2012-02-10 15:58:52 | [diff] [blame] | 321 | // static |
| 322 | BrowserMainRunner* BrowserMainRunner::Create() { |
| 323 | return new BrowserMainRunnerImpl(); |
| 324 | } |
| 325 | |
| 326 | } // namespace content |