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