1 // Copyright 2015 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 "headless/lib/browser/headless_content_browser_client.h"
6 
7 #include <memory>
8 #include <unordered_set>
9 
10 #include "base/base_switches.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/command_line.h"
14 #include "base/i18n/rtl.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "build/build_config.h"
19 #include "components/embedder_support/switches.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/client_certificate_delegate.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/storage_partition.h"
26 #include "content/public/common/content_switches.h"
27 #include "headless/app/headless_shell_switches.h"
28 #include "headless/lib/browser/headless_browser_context_impl.h"
29 #include "headless/lib/browser/headless_browser_impl.h"
30 #include "headless/lib/browser/headless_browser_main_parts.h"
31 #include "headless/lib/browser/headless_devtools_manager_delegate.h"
32 #include "headless/lib/browser/headless_quota_permission_context.h"
33 #include "headless/lib/headless_macros.h"
34 #include "net/base/url_util.h"
35 #include "net/ssl/client_cert_identity.h"
36 #include "printing/buildflags/buildflags.h"
37 #include "sandbox/policy/switches.h"
38 #include "ui/base/ui_base_switches.h"
39 #include "ui/gfx/switches.h"
40 
41 #if defined(HEADLESS_USE_BREAKPAD)
42 #include "base/debug/leak_annotations.h"
43 #include "components/crash/content/browser/crash_handler_host_linux.h"
44 #include "components/crash/core/app/breakpad_linux.h"
45 #include "content/public/common/content_descriptors.h"
46 #endif  // defined(HEADLESS_USE_BREAKPAD)
47 
48 namespace headless {
49 
50 namespace {
51 
52 #if defined(HEADLESS_USE_BREAKPAD)
CreateCrashHandlerHost(const std::string & process_type,const HeadlessBrowser::Options & options)53 breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
54     const std::string& process_type,
55     const HeadlessBrowser::Options& options) {
56   base::FilePath dumps_path = options.crash_dumps_dir;
57   if (dumps_path.empty()) {
58     bool ok = base::PathService::Get(base::DIR_MODULE, &dumps_path);
59     DCHECK(ok);
60   }
61 
62   {
63     ANNOTATE_SCOPED_MEMORY_LEAK;
64 #if defined(OFFICIAL_BUILD)
65     // Upload crash dumps in official builds, unless we're running in unattended
66     // mode (not to be confused with headless mode in general -- see
67     // chrome/common/env_vars.cc).
68     static const char kHeadless[] = "CHROME_HEADLESS";
69     bool upload = (getenv(kHeadless) == nullptr);
70 #else
71     bool upload = false;
72 #endif
73     breakpad::CrashHandlerHostLinux* crash_handler =
74         new breakpad::CrashHandlerHostLinux(process_type, dumps_path, upload);
75     crash_handler->StartUploaderThread();
76     return crash_handler;
77   }
78 }
79 
GetCrashSignalFD(const base::CommandLine & command_line,const HeadlessBrowser::Options & options)80 int GetCrashSignalFD(const base::CommandLine& command_line,
81                      const HeadlessBrowser::Options& options) {
82   if (!breakpad::IsCrashReporterEnabled())
83     return -1;
84 
85   std::string process_type =
86       command_line.GetSwitchValueASCII(::switches::kProcessType);
87 
88   if (process_type == ::switches::kRendererProcess) {
89     static breakpad::CrashHandlerHostLinux* crash_handler =
90         CreateCrashHandlerHost(process_type, options);
91     return crash_handler->GetDeathSignalSocket();
92   }
93 
94   if (process_type == ::switches::kPpapiPluginProcess) {
95     static breakpad::CrashHandlerHostLinux* crash_handler =
96         CreateCrashHandlerHost(process_type, options);
97     return crash_handler->GetDeathSignalSocket();
98   }
99 
100   if (process_type == ::switches::kGpuProcess) {
101     static breakpad::CrashHandlerHostLinux* crash_handler =
102         CreateCrashHandlerHost(process_type, options);
103     return crash_handler->GetDeathSignalSocket();
104   }
105 
106   return -1;
107 }
108 #endif  // defined(HEADLESS_USE_BREAKPAD)
109 
110 }  // namespace
111 
HeadlessContentBrowserClient(HeadlessBrowserImpl * browser)112 HeadlessContentBrowserClient::HeadlessContentBrowserClient(
113     HeadlessBrowserImpl* browser)
114     : browser_(browser),
115       append_command_line_flags_callback_(
116           browser_->options()->append_command_line_flags_callback) {}
117 
118 HeadlessContentBrowserClient::~HeadlessContentBrowserClient() = default;
119 
120 std::unique_ptr<content::BrowserMainParts>
CreateBrowserMainParts(const content::MainFunctionParams & parameters)121 HeadlessContentBrowserClient::CreateBrowserMainParts(
122     const content::MainFunctionParams& parameters) {
123   auto browser_main_parts =
124       std::make_unique<HeadlessBrowserMainParts>(parameters, browser_);
125 
126   browser_->set_browser_main_parts(browser_main_parts.get());
127 
128   return browser_main_parts;
129 }
130 
OverrideWebkitPrefs(content::RenderViewHost * render_view_host,blink::web_pref::WebPreferences * prefs)131 void HeadlessContentBrowserClient::OverrideWebkitPrefs(
132     content::RenderViewHost* render_view_host,
133     blink::web_pref::WebPreferences* prefs) {
134   auto* browser_context = HeadlessBrowserContextImpl::From(
135       render_view_host->GetProcess()->GetBrowserContext());
136   base::RepeatingCallback<void(blink::web_pref::WebPreferences*)> callback =
137       browser_context->options()->override_web_preferences_callback();
138   if (callback)
139     callback.Run(prefs);
140 }
141 
142 content::DevToolsManagerDelegate*
GetDevToolsManagerDelegate()143 HeadlessContentBrowserClient::GetDevToolsManagerDelegate() {
144   return new HeadlessDevToolsManagerDelegate(browser_->GetWeakPtr());
145 }
146 
147 scoped_refptr<content::QuotaPermissionContext>
CreateQuotaPermissionContext()148 HeadlessContentBrowserClient::CreateQuotaPermissionContext() {
149   return new HeadlessQuotaPermissionContext();
150 }
151 
152 content::GeneratedCodeCacheSettings
GetGeneratedCodeCacheSettings(content::BrowserContext * context)153 HeadlessContentBrowserClient::GetGeneratedCodeCacheSettings(
154     content::BrowserContext* context) {
155   // If we pass 0 for size, disk_cache will pick a default size using the
156   // heuristics based on available disk size. These are implemented in
157   // disk_cache::PreferredCacheSize in net/disk_cache/cache_util.cc.
158   return content::GeneratedCodeCacheSettings(true, 0, context->GetPath());
159 }
160 
161 #if defined(OS_POSIX) && !defined(OS_MAC)
GetAdditionalMappedFilesForChildProcess(const base::CommandLine & command_line,int child_process_id,content::PosixFileDescriptorInfo * mappings)162 void HeadlessContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
163     const base::CommandLine& command_line,
164     int child_process_id,
165     content::PosixFileDescriptorInfo* mappings) {
166 #if defined(HEADLESS_USE_BREAKPAD)
167   int crash_signal_fd = GetCrashSignalFD(command_line, *browser_->options());
168   if (crash_signal_fd >= 0)
169     mappings->Share(kCrashDumpSignal, crash_signal_fd);
170 #endif  // defined(HEADLESS_USE_BREAKPAD)
171 }
172 #endif  // defined(OS_POSIX) && !defined(OS_MAC)
173 
AppendExtraCommandLineSwitches(base::CommandLine * command_line,int child_process_id)174 void HeadlessContentBrowserClient::AppendExtraCommandLineSwitches(
175     base::CommandLine* command_line,
176     int child_process_id) {
177   // NOTE: We may be called on the UI or IO thread. If called on the IO thread,
178   // |browser_| may have already been destroyed.
179 
180   command_line->AppendSwitch(::switches::kHeadless);
181   const base::CommandLine& old_command_line(
182       *base::CommandLine::ForCurrentProcess());
183   if (old_command_line.HasSwitch(switches::kUserAgent)) {
184     command_line->AppendSwitchNative(
185         switches::kUserAgent,
186         old_command_line.GetSwitchValueNative(switches::kUserAgent));
187   }
188 #if defined(HEADLESS_USE_BREAKPAD)
189   // This flag tells child processes to also turn on crash reporting.
190   if (breakpad::IsCrashReporterEnabled())
191     command_line->AppendSwitch(::switches::kEnableCrashReporter);
192 #endif  // defined(HEADLESS_USE_BREAKPAD)
193 
194   if (old_command_line.HasSwitch(switches::kExportTaggedPDF))
195     command_line->AppendSwitch(switches::kExportTaggedPDF);
196 
197   // If we're spawning a renderer, then override the language switch.
198   std::string process_type =
199       command_line->GetSwitchValueASCII(::switches::kProcessType);
200   if (process_type == ::switches::kRendererProcess) {
201     // Renderer processes are initialized on the UI thread, so this is safe.
202     content::RenderProcessHost* render_process_host =
203         content::RenderProcessHost::FromID(child_process_id);
204     if (render_process_host) {
205       HeadlessBrowserContextImpl* headless_browser_context_impl =
206           HeadlessBrowserContextImpl::From(
207               render_process_host->GetBrowserContext());
208 
209       std::vector<base::StringPiece> languages = base::SplitStringPiece(
210           headless_browser_context_impl->options()->accept_language(), ",",
211           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
212       if (!languages.empty()) {
213         command_line->AppendSwitchASCII(::switches::kLang,
214                                         languages[0].as_string());
215       }
216     }
217 
218     // Please keep this in alphabetical order.
219     static const char* const kSwitchNames[] = {
220         embedder_support::kOriginTrialDisabledFeatures,
221         embedder_support::kOriginTrialDisabledTokens,
222         embedder_support::kOriginTrialPublicKey,
223     };
224     command_line->CopySwitchesFrom(old_command_line, kSwitchNames,
225                                    base::size(kSwitchNames));
226   }
227 
228   if (append_command_line_flags_callback_) {
229     HeadlessBrowserContextImpl* headless_browser_context_impl = nullptr;
230     if (process_type == ::switches::kRendererProcess) {
231       // Renderer processes are initialized on the UI thread, so this is safe.
232       content::RenderProcessHost* render_process_host =
233           content::RenderProcessHost::FromID(child_process_id);
234       if (render_process_host) {
235         headless_browser_context_impl = HeadlessBrowserContextImpl::From(
236             render_process_host->GetBrowserContext());
237       }
238     }
239     append_command_line_flags_callback_.Run(command_line,
240                                             headless_browser_context_impl,
241                                             process_type, child_process_id);
242   }
243 
244 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
245   // Processes may only query perf_event_open with the BPF sandbox disabled.
246   if (old_command_line.HasSwitch(::switches::kEnableThreadInstructionCount) &&
247       old_command_line.HasSwitch(sandbox::policy::switches::kNoSandbox)) {
248     command_line->AppendSwitch(::switches::kEnableThreadInstructionCount);
249   }
250 #endif
251 }
252 
GetApplicationLocale()253 std::string HeadlessContentBrowserClient::GetApplicationLocale() {
254   return base::i18n::GetConfiguredLocale();
255 }
256 
GetAcceptLangs(content::BrowserContext * context)257 std::string HeadlessContentBrowserClient::GetAcceptLangs(
258     content::BrowserContext* context) {
259   return browser_->options()->accept_language;
260 }
261 
AllowCertificateError(content::WebContents * web_contents,int cert_error,const net::SSLInfo & ssl_info,const GURL & request_url,bool is_main_frame_request,bool strict_enforcement,base::OnceCallback<void (content::CertificateRequestResultType)> callback)262 void HeadlessContentBrowserClient::AllowCertificateError(
263     content::WebContents* web_contents,
264     int cert_error,
265     const net::SSLInfo& ssl_info,
266     const GURL& request_url,
267     bool is_main_frame_request,
268     bool strict_enforcement,
269     base::OnceCallback<void(content::CertificateRequestResultType)> callback) {
270   if (!callback.is_null()) {
271     // If --allow-insecure-localhost is specified, and the request
272     // was for localhost, then the error was not fatal.
273     bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
274         ::switches::kAllowInsecureLocalhost);
275     if (allow_localhost && net::IsLocalhost(request_url)) {
276       std::move(callback).Run(
277           content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE);
278       return;
279     }
280 
281     std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
282   }
283 }
284 
SelectClientCertificate(content::WebContents * web_contents,net::SSLCertRequestInfo * cert_request_info,net::ClientCertIdentityList client_certs,std::unique_ptr<content::ClientCertificateDelegate> delegate)285 base::OnceClosure HeadlessContentBrowserClient::SelectClientCertificate(
286     content::WebContents* web_contents,
287     net::SSLCertRequestInfo* cert_request_info,
288     net::ClientCertIdentityList client_certs,
289     std::unique_ptr<content::ClientCertificateDelegate> delegate) {
290   delegate->ContinueWithCertificate(nullptr, nullptr);
291   return base::OnceClosure();
292 }
293 
ShouldEnableStrictSiteIsolation()294 bool HeadlessContentBrowserClient::ShouldEnableStrictSiteIsolation() {
295   // TODO(lukasza): https://crbug.com/869494: Instead of overriding
296   // ShouldEnableStrictSiteIsolation, //headless should inherit the default
297   // site-per-process setting from //content - this way tools (tests, but also
298   // production cases like screenshot or pdf generation) based on //headless
299   // will use a mode that is actually shipping in Chrome.
300   return browser_->options()->site_per_process;
301 }
302 
ConfigureNetworkContextParams(content::BrowserContext * context,bool in_memory,const base::FilePath & relative_partition_path,::network::mojom::NetworkContextParams * network_context_params,::network::mojom::CertVerifierCreationParams * cert_verifier_creation_params)303 void HeadlessContentBrowserClient::ConfigureNetworkContextParams(
304     content::BrowserContext* context,
305     bool in_memory,
306     const base::FilePath& relative_partition_path,
307     ::network::mojom::NetworkContextParams* network_context_params,
308     ::network::mojom::CertVerifierCreationParams*
309         cert_verifier_creation_params) {
310   HeadlessBrowserContextImpl::From(context)->ConfigureNetworkContextParams(
311       in_memory, relative_partition_path, network_context_params,
312       cert_verifier_creation_params);
313 }
314 
GetProduct()315 std::string HeadlessContentBrowserClient::GetProduct() {
316   return browser_->options()->product_name_and_version;
317 }
318 
GetUserAgent()319 std::string HeadlessContentBrowserClient::GetUserAgent() {
320   return browser_->options()->user_agent;
321 }
322 
323 }  // namespace headless
324