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/shell/app/shell_main_delegate.h"
6
7 #include <iostream>
8 #include <utility>
9
10 #include "base/base_switches.h"
11 #include "base/command_line.h"
12 #include "base/cpu.h"
13 #include "base/files/file.h"
14 #include "base/files/file_path.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/trace_event/trace_log.h"
19 #include "build/build_config.h"
20 #include "components/crash/core/common/crash_key.h"
21 #include "content/common/content_constants_internal.h"
22 #include "content/public/browser/browser_main_runner.h"
23 #include "content/public/common/content_switches.h"
24 #include "content/public/common/url_constants.h"
25 #include "content/shell/app/shell_crash_reporter_client.h"
26 #include "content/shell/browser/shell_content_browser_client.h"
27 #include "content/shell/common/shell_content_client.h"
28 #include "content/shell/common/shell_switches.h"
29 #include "content/shell/gpu/shell_content_gpu_client.h"
30 #include "content/shell/renderer/shell_content_renderer_client.h"
31 #include "content/shell/utility/shell_content_utility_client.h"
32 #include "ipc/ipc_buildflags.h"
33 #include "net/cookies/cookie_monster.h"
34 #include "ui/base/resource/resource_bundle.h"
35
36 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
37 #define IPC_MESSAGE_MACROS_LOG_ENABLED
38 #include "content/public/common/content_ipc_logging.h"
39 #define IPC_LOG_TABLE_ADD_ENTRY(msg_id, logger) \
40 content::RegisterIPCLogger(msg_id, logger)
41 #endif
42
43 #if !defined(OS_ANDROID)
44 #include "content/web_test/browser/web_test_browser_main_runner.h" // nogncheck
45 #include "content/web_test/browser/web_test_content_browser_client.h" // nogncheck
46 #include "content/web_test/renderer/web_test_content_renderer_client.h" // nogncheck
47 #endif
48
49 #if defined(OS_ANDROID)
50 #include "base/android/apk_assets.h"
51 #include "base/posix/global_descriptors.h"
52 #include "content/public/browser/android/compositor.h"
53 #include "content/shell/android/shell_descriptors.h"
54 #endif
55
56 #if !defined(OS_FUCHSIA)
57 #include "components/crash/core/app/crashpad.h" // nogncheck
58 #endif
59
60 #if defined(OS_MAC)
61 #include "content/shell/app/paths_mac.h"
62 #include "content/shell/app/shell_main_delegate_mac.h"
63 #endif // OS_MAC
64
65 #if defined(OS_WIN)
66 #include <windows.h>
67
68 #include <initguid.h>
69 #include "base/logging_win.h"
70 #include "content/shell/common/v8_crashpad_support_win.h"
71 #endif
72
73 #if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_ANDROID)
74 #include "v8/include/v8-wasm-trap-handler-posix.h"
75 #endif
76
77 #if defined(OS_FUCHSIA)
78 #include "base/base_paths_fuchsia.h"
79 #endif // OS_FUCHSIA
80
81 namespace {
82
83 #if !defined(OS_FUCHSIA)
84 base::LazyInstance<content::ShellCrashReporterClient>::Leaky
85 g_shell_crash_client = LAZY_INSTANCE_INITIALIZER;
86 #endif
87
88 #if defined(OS_WIN)
89 // If "Content Shell" doesn't show up in your list of trace providers in
90 // Sawbuck, add these registry entries to your machine (NOTE the optional
91 // Wow6432Node key for x64 machines):
92 // 1. Find: HKLM\SOFTWARE\[Wow6432Node\]Google\Sawbuck\Providers
93 // 2. Add a subkey with the name "{6A3E50A4-7E15-4099-8413-EC94D8C2A4B6}"
94 // 3. Add these values:
95 // "default_flags"=dword:00000001
96 // "default_level"=dword:00000004
97 // @="Content Shell"
98
99 // {6A3E50A4-7E15-4099-8413-EC94D8C2A4B6}
100 const GUID kContentShellProviderName = {
101 0x6a3e50a4, 0x7e15, 0x4099,
102 { 0x84, 0x13, 0xec, 0x94, 0xd8, 0xc2, 0xa4, 0xb6 } };
103 #endif
104
InitLogging(const base::CommandLine & command_line)105 void InitLogging(const base::CommandLine& command_line) {
106 base::FilePath log_filename =
107 command_line.GetSwitchValuePath(switches::kLogFile);
108 if (log_filename.empty()) {
109 #if defined(OS_FUCHSIA)
110 base::PathService::Get(base::DIR_TEMP, &log_filename);
111 #else
112 base::PathService::Get(base::DIR_EXE, &log_filename);
113 #endif
114 log_filename = log_filename.AppendASCII("content_shell.log");
115 }
116
117 logging::LoggingSettings settings;
118 settings.logging_dest = logging::LOG_TO_ALL;
119 settings.log_file_path = log_filename.value().c_str();
120 settings.delete_old = logging::DELETE_OLD_LOG_FILE;
121 logging::InitLogging(settings);
122 logging::SetLogItems(true /* Process ID */, true /* Thread ID */,
123 true /* Timestamp */, false /* Tick count */);
124 }
125
126 } // namespace
127
128 namespace content {
129
ShellMainDelegate(bool is_content_browsertests)130 ShellMainDelegate::ShellMainDelegate(bool is_content_browsertests)
131 : is_content_browsertests_(is_content_browsertests) {}
132
~ShellMainDelegate()133 ShellMainDelegate::~ShellMainDelegate() {
134 }
135
BasicStartupComplete(int * exit_code)136 bool ShellMainDelegate::BasicStartupComplete(int* exit_code) {
137 int dummy;
138 if (!exit_code)
139 exit_code = &dummy;
140
141 base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
142 if (command_line.HasSwitch("run-layout-test")) {
143 std::cerr << std::string(79, '*') << "\n"
144 << "* The flag --run-layout-test is obsolete. Please use --"
145 << switches::kRunWebTests << " instead. *\n"
146 << std::string(79, '*') << "\n";
147 command_line.AppendSwitch(switches::kRunWebTests);
148 }
149
150 #if defined(OS_ANDROID)
151 Compositor::Initialize();
152 #endif
153
154 #if defined(OS_WIN)
155 // Enable trace control and transport through event tracing for Windows.
156 logging::LogEventProvider::Initialize(kContentShellProviderName);
157
158 v8_crashpad_support::SetUp();
159 #endif
160
161 #if defined(OS_MAC)
162 // Needs to happen before InitializeResourceBundle().
163 OverrideFrameworkBundlePath();
164 OverrideOuterBundlePath();
165 OverrideChildProcessPath();
166 OverrideSourceRootPath();
167 EnsureCorrectResolutionSettings();
168 OverrideBundleID();
169 #endif // OS_MAC
170
171 InitLogging(command_line);
172
173 #if !defined(OS_ANDROID)
174 if (switches::IsRunWebTestsSwitchPresent()) {
175 const bool browser_process =
176 command_line.GetSwitchValueASCII(switches::kProcessType).empty();
177 if (browser_process) {
178 web_test_runner_ = std::make_unique<WebTestBrowserMainRunner>();
179 web_test_runner_->Initialize();
180 }
181 }
182 #endif
183
184 return false;
185 }
186
PreSandboxStartup()187 void ShellMainDelegate::PreSandboxStartup() {
188 #if defined(ARCH_CPU_ARM_FAMILY) && \
189 (defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD))
190 // Create an instance of the CPU class to parse /proc/cpuinfo and cache
191 // cpu_brand info.
192 base::CPU cpu_info;
193 #endif
194
195 // Disable platform crash handling and initialize the crash reporter, if
196 // requested.
197 // TODO(crbug.com/753619): Implement crash reporter integration for Fuchsia.
198 #if !defined(OS_FUCHSIA) && !defined(OS_BSD)
199 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
200 switches::kEnableCrashReporter)) {
201 std::string process_type =
202 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
203 switches::kProcessType);
204 crash_reporter::SetCrashReporterClient(g_shell_crash_client.Pointer());
205 // Reporting for sub-processes will be initialized in ZygoteForked.
206 if (process_type != switches::kZygoteProcess) {
207 crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
208 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
209 crash_reporter::SetFirstChanceExceptionHandler(
210 v8::TryHandleWebAssemblyTrapPosix);
211 #endif
212 }
213 }
214 #endif // !defined(OS_FUCHSIA) && !defined(OS_BSD)
215
216 crash_reporter::InitializeCrashKeys();
217
218 InitializeResourceBundle();
219 }
220
RunProcess(const std::string & process_type,const MainFunctionParams & main_function_params)221 int ShellMainDelegate::RunProcess(
222 const std::string& process_type,
223 const MainFunctionParams& main_function_params) {
224 // For non-browser process, return and have the caller run the main loop.
225 if (!process_type.empty())
226 return -1;
227
228 base::trace_event::TraceLog::GetInstance()->set_process_name("Browser");
229 base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
230 kTraceEventBrowserProcessSortIndex);
231
232 #if !defined(OS_ANDROID)
233 if (switches::IsRunWebTestsSwitchPresent()) {
234 // Web tests implement their own BrowserMain() replacement.
235 web_test_runner_->RunBrowserMain(main_function_params);
236 web_test_runner_.reset();
237 // Returning 0 to indicate that we have replaced BrowserMain() and the
238 // caller should not call BrowserMain() itself. Web tests do not ever
239 // return an error.
240 return 0;
241 }
242
243 // On non-Android, we can return -1 and have the caller run BrowserMain()
244 // normally.
245 return -1;
246 #else
247 // On Android, we defer to the system message loop when the stack unwinds.
248 // So here we only create (and leak) a BrowserMainRunner. The shutdown
249 // of BrowserMainRunner doesn't happen in Chrome Android and doesn't work
250 // properly on Android at all.
251 std::unique_ptr<BrowserMainRunner> main_runner = BrowserMainRunner::Create();
252 // In browser tests, the |main_function_params| contains a |ui_task| which
253 // will execute the testing. The task will be executed synchronously inside
254 // Initialize() so we don't depend on the BrowserMainRunner being Run().
255 int initialize_exit_code = main_runner->Initialize(main_function_params);
256 DCHECK_LT(initialize_exit_code, 0)
257 << "BrowserMainRunner::Initialize failed in ShellMainDelegate";
258 ignore_result(main_runner.release());
259 // Return 0 as BrowserMain() should not be called after this, bounce up to
260 // the system message loop for ContentShell, and we're already done thanks
261 // to the |ui_task| for browser tests.
262 return 0;
263 #endif
264 }
265
266 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
ZygoteForked()267 void ShellMainDelegate::ZygoteForked() {
268 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
269 switches::kEnableCrashReporter)) {
270 std::string process_type =
271 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
272 switches::kProcessType);
273 crash_reporter::InitializeCrashpad(false, process_type);
274 crash_reporter::SetFirstChanceExceptionHandler(
275 v8::TryHandleWebAssemblyTrapPosix);
276 }
277 }
278 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
279
InitializeResourceBundle()280 void ShellMainDelegate::InitializeResourceBundle() {
281 #if defined(OS_ANDROID)
282 // On Android, the renderer runs with a different UID and can never access
283 // the file system. Use the file descriptor passed in at launch time.
284 auto* global_descriptors = base::GlobalDescriptors::GetInstance();
285 int pak_fd = global_descriptors->MaybeGet(kShellPakDescriptor);
286 base::MemoryMappedFile::Region pak_region;
287 if (pak_fd >= 0) {
288 pak_region = global_descriptors->GetRegion(kShellPakDescriptor);
289 } else {
290 pak_fd =
291 base::android::OpenApkAsset("assets/content_shell.pak", &pak_region);
292 // Loaded from disk for browsertests.
293 if (pak_fd < 0) {
294 base::FilePath pak_file;
295 bool r = base::PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_file);
296 DCHECK(r);
297 pak_file = pak_file.Append(FILE_PATH_LITERAL("paks"));
298 pak_file = pak_file.Append(FILE_PATH_LITERAL("content_shell.pak"));
299 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
300 pak_fd = base::File(pak_file, flags).TakePlatformFile();
301 pak_region = base::MemoryMappedFile::Region::kWholeFile;
302 }
303 global_descriptors->Set(kShellPakDescriptor, pak_fd, pak_region);
304 }
305 DCHECK_GE(pak_fd, 0);
306 // This is clearly wrong. See crbug.com/330930
307 ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(base::File(pak_fd),
308 pak_region);
309 ui::ResourceBundle::GetSharedInstance().AddDataPackFromFileRegion(
310 base::File(pak_fd), pak_region, ui::SCALE_FACTOR_100P);
311 #elif defined(OS_MAC)
312 ui::ResourceBundle::InitSharedInstanceWithPakPath(GetResourcesPakFilePath());
313 #else
314 base::FilePath pak_file;
315 bool r = base::PathService::Get(base::DIR_ASSETS, &pak_file);
316 DCHECK(r);
317 pak_file = pak_file.Append(FILE_PATH_LITERAL("content_shell.pak"));
318 ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file);
319 #endif
320 }
321
PreCreateMainMessageLoop()322 void ShellMainDelegate::PreCreateMainMessageLoop() {
323 #if defined(OS_MAC)
324 RegisterShellCrApp();
325 #endif
326 }
327
CreateContentClient()328 ContentClient* ShellMainDelegate::CreateContentClient() {
329 content_client_ = std::make_unique<ShellContentClient>();
330 return content_client_.get();
331 }
332
CreateContentBrowserClient()333 ContentBrowserClient* ShellMainDelegate::CreateContentBrowserClient() {
334 #if !defined(OS_ANDROID)
335 if (switches::IsRunWebTestsSwitchPresent()) {
336 browser_client_ = std::make_unique<WebTestContentBrowserClient>();
337 return browser_client_.get();
338 }
339 #endif
340 browser_client_ = std::make_unique<ShellContentBrowserClient>();
341 return browser_client_.get();
342 }
343
CreateContentGpuClient()344 ContentGpuClient* ShellMainDelegate::CreateContentGpuClient() {
345 gpu_client_ = std::make_unique<ShellContentGpuClient>();
346 return gpu_client_.get();
347 }
348
CreateContentRendererClient()349 ContentRendererClient* ShellMainDelegate::CreateContentRendererClient() {
350 #if !defined(OS_ANDROID)
351 if (switches::IsRunWebTestsSwitchPresent()) {
352 renderer_client_ = std::make_unique<WebTestContentRendererClient>();
353 return renderer_client_.get();
354 }
355 #endif
356 renderer_client_ = std::make_unique<ShellContentRendererClient>();
357 return renderer_client_.get();
358 }
359
CreateContentUtilityClient()360 ContentUtilityClient* ShellMainDelegate::CreateContentUtilityClient() {
361 utility_client_ =
362 std::make_unique<ShellContentUtilityClient>(is_content_browsertests_);
363 return utility_client_.get();
364 }
365
366 } // namespace content
367