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