1 // Copyright 2018 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 "fuchsia/engine/context_provider_impl.h"
6
7 #include <fuchsia/sys/cpp/fidl.h>
8 #include <lib/async/default.h>
9 #include <lib/fdio/directory.h>
10 #include <lib/fdio/fd.h>
11 #include <lib/fdio/io.h>
12 #include <lib/zx/job.h>
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <zircon/processargs.h>
18
19 #include <string>
20 #include <utility>
21 #include <vector>
22
23 #include "base/base_paths_fuchsia.h"
24 #include "base/base_switches.h"
25 #include "base/bind.h"
26 #include "base/command_line.h"
27 #include "base/files/file_path.h"
28 #include "base/files/file_util.h"
29 #include "base/files/scoped_file.h"
30 #include "base/fuchsia/default_job.h"
31 #include "base/fuchsia/fuchsia_logging.h"
32 #include "base/json/json_reader.h"
33 #include "base/logging.h"
34 #include "base/path_service.h"
35 #include "base/process/launch.h"
36 #include "base/stl_util.h"
37 #include "base/strings/strcat.h"
38 #include "base/strings/string_number_conversions.h"
39 #include "base/strings/string_piece.h"
40 #include "base/strings/string_util.h"
41 #include "base/values.h"
42 #include "build/build_config.h"
43 #include "cc/base/switches.h"
44 #include "components/viz/common/features.h"
45 #include "components/viz/common/switches.h"
46 #include "content/public/common/content_switches.h"
47 #include "fuchsia/base/config_reader.h"
48 #include "fuchsia/base/string_util.h"
49 #include "fuchsia/engine/common/web_engine_content_client.h"
50 #include "fuchsia/engine/switches.h"
51 #include "gpu/command_buffer/service/gpu_switches.h"
52 #include "gpu/config/gpu_finch_features.h"
53 #include "gpu/config/gpu_switches.h"
54 #include "media/base/key_system_names.h"
55 #include "media/base/media_switches.h"
56 #include "net/http/http_util.h"
57 #include "sandbox/policy/fuchsia/sandbox_policy_fuchsia.h"
58 #include "services/network/public/cpp/features.h"
59 #include "services/network/public/cpp/network_switches.h"
60 #include "third_party/blink/public/common/switches.h"
61 #include "third_party/widevine/cdm/widevine_cdm_common.h"
62 #include "ui/gfx/switches.h"
63 #include "ui/gl/gl_switches.h"
64 #include "ui/ozone/public/ozone_switches.h"
65
66 namespace {
67
68 // Use a constexpr instead of the existing base::Feature, because of the
69 // additional dependencies required.
70 constexpr char kMixedContentAutoupgradeFeatureName[] =
71 "AutoupgradeMixedContent";
72 constexpr char kDisableMixedContentAutoupgradeOrigin[] =
73 "disable-mixed-content-autoupgrade";
74
75 // Returns the underlying channel if |directory| is a client endpoint for a
76 // |fuchsia::io::Directory| protocol. Otherwise, returns an empty channel.
ValidateDirectoryAndTakeChannel(fidl::InterfaceHandle<fuchsia::io::Directory> directory_handle)77 zx::channel ValidateDirectoryAndTakeChannel(
78 fidl::InterfaceHandle<fuchsia::io::Directory> directory_handle) {
79 fidl::SynchronousInterfacePtr<fuchsia::io::Directory> directory =
80 directory_handle.BindSync();
81 zx_status_t status = ZX_ERR_INTERNAL;
82 std::vector<uint8_t> entries;
83
84 directory->ReadDirents(0, &status, &entries);
85 if (status == ZX_OK) {
86 return directory.Unbind().TakeChannel();
87 }
88
89 // Not a directory.
90 return zx::channel();
91 }
92
93 // Populates a CommandLine with content directory name/handle pairs.
SetContentDirectoriesInCommandLine(std::vector<fuchsia::web::ContentDirectoryProvider> directories,base::CommandLine * command_line,base::LaunchOptions * launch_options)94 bool SetContentDirectoriesInCommandLine(
95 std::vector<fuchsia::web::ContentDirectoryProvider> directories,
96 base::CommandLine* command_line,
97 base::LaunchOptions* launch_options) {
98 DCHECK(command_line);
99 DCHECK(launch_options);
100
101 std::vector<std::string> directory_pairs;
102 for (size_t i = 0; i < directories.size(); ++i) {
103 fuchsia::web::ContentDirectoryProvider& directory = directories[i];
104
105 if (directory.name().find('=') != std::string::npos ||
106 directory.name().find(',') != std::string::npos) {
107 DLOG(ERROR) << "Invalid character in directory name: "
108 << directory.name();
109 return false;
110 }
111
112 if (!directory.directory().is_valid()) {
113 DLOG(ERROR) << "Service directory handle not valid for directory: "
114 << directory.name();
115 return false;
116 }
117
118 uint32_t directory_handle_id = base::LaunchOptions::AddHandleToTransfer(
119 &launch_options->handles_to_transfer,
120 directory.mutable_directory()->TakeChannel().release());
121 directory_pairs.emplace_back(
122 base::StrCat({directory.name().c_str(), "=",
123 base::NumberToString(directory_handle_id)}));
124 }
125
126 command_line->AppendSwitchASCII(switches::kContentDirectories,
127 base::JoinString(directory_pairs, ","));
128
129 return true;
130 }
131
AppendFeature(base::StringPiece features_flag,base::StringPiece feature_string,base::CommandLine * command_line)132 void AppendFeature(base::StringPiece features_flag,
133 base::StringPiece feature_string,
134 base::CommandLine* command_line) {
135 if (!command_line->HasSwitch(features_flag)) {
136 command_line->AppendSwitchNative(features_flag.as_string(),
137 feature_string.as_string());
138 return;
139 }
140
141 std::string new_feature_string =
142 command_line->GetSwitchValueASCII(features_flag);
143 new_feature_string.append(",").append(feature_string.as_string());
144 command_line->RemoveSwitch(features_flag);
145 command_line->AppendSwitchNative(features_flag.as_string(),
146 new_feature_string);
147 }
148
149 // Returns false if the config is present but has invalid contents.
MaybeAddCommandLineArgsFromConfig(const base::Value & config,base::CommandLine * command_line)150 bool MaybeAddCommandLineArgsFromConfig(const base::Value& config,
151 base::CommandLine* command_line) {
152 const base::Value* args = config.FindDictKey("command-line-args");
153 if (!args)
154 return true;
155
156 static const base::StringPiece kAllowedArgs[] = {
157 blink::switches::kGpuRasterizationMSAASampleCount,
158 blink::switches::kMinHeightForGpuRasterTile,
159 cc::switches::kEnableGpuBenchmarking,
160 cc::switches::kEnableClippedImageScaling,
161 switches::kDisableFeatures,
162 switches::kDisableGpuWatchdog,
163 switches::kDisableMipmapGeneration,
164 // TODO(crbug.com/1082821): Remove this switch from the allow-list.
165 switches::kEnableCastStreamingReceiver,
166 switches::kEnableFeatures,
167 switches::kEnableLowEndDeviceMode,
168 switches::kForceGpuMemAvailableMb,
169 switches::kForceGpuMemDiscardableLimitMb,
170 switches::kForceMaxTextureSize,
171 switches::kGoogleApiKey,
172 switches::kMaxDecodedImageSizeMb,
173 switches::kRendererProcessLimit,
174 switches::kUseLegacyAndroidUserAgent,
175 switches::kWebglAntialiasingMode,
176 switches::kWebglMSAASampleCount,
177 switches::kVulkanHeapMemoryLimitMb,
178 switches::kVulkanSyncCpuMemoryLimitMb,
179 };
180
181 for (const auto& arg : args->DictItems()) {
182 if (!base::Contains(kAllowedArgs, arg.first)) {
183 // TODO(https://crbug.com/1032439): Increase severity and return false
184 // once we have a mechanism for soft transitions of supported arguments.
185 LOG(WARNING) << "Unknown command-line arg: '" << arg.first
186 << "'. Config file and WebEngine version may not match.";
187 continue;
188 }
189
190 DCHECK(!command_line->HasSwitch(arg.first));
191 if (arg.second.is_none()) {
192 command_line->AppendSwitch(arg.first);
193 } else if (arg.second.is_string()) {
194 command_line->AppendSwitchNative(arg.first, arg.second.GetString());
195 } else {
196 LOG(ERROR) << "Config command-line arg must be a string: " << arg.first;
197 return false;
198 }
199
200 // TODO(https://crbug.com/1023012): enable-low-end-device-mode currently
201 // fakes 512MB total physical memory, which triggers RGB4444 textures,
202 // which
203 // we don't yet support.
204 if (arg.first == switches::kEnableLowEndDeviceMode)
205 command_line->AppendSwitch(blink::switches::kDisableRGBA4444Textures);
206 }
207
208 return true;
209 }
210
211 // Returns true if DRM is supported in current configuration. Currently we
212 // assume that it is supported on ARM64, but not on x64.
213 //
214 // TODO(crbug.com/1013412): Detect support for all features required for
215 // FuchsiaCdm. Specifically we need to verify that protected memory is supported
216 // and that mediacodec API provides hardware video decoders.
IsFuchsiaCdmSupported()217 bool IsFuchsiaCdmSupported() {
218 #if defined(ARCH_CPU_ARM64)
219 return true;
220 #else
221 return false;
222 #endif
223 }
224
225 } // namespace
226
227 const uint32_t ContextProviderImpl::kContextRequestHandleId =
228 PA_HND(PA_USER0, 0);
229
230 ContextProviderImpl::ContextProviderImpl() = default;
231
232 ContextProviderImpl::~ContextProviderImpl() = default;
233
Create(fuchsia::web::CreateContextParams params,fidl::InterfaceRequest<fuchsia::web::Context> context_request)234 void ContextProviderImpl::Create(
235 fuchsia::web::CreateContextParams params,
236 fidl::InterfaceRequest<fuchsia::web::Context> context_request) {
237 if (!context_request.is_valid()) {
238 DLOG(ERROR) << "Invalid |context_request|.";
239 return;
240 }
241 if (!params.has_service_directory()) {
242 DLOG(ERROR)
243 << "Missing argument |service_directory| in CreateContextParams.";
244 context_request.Close(ZX_ERR_INVALID_ARGS);
245 return;
246 }
247
248 fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory =
249 std::move(*params.mutable_service_directory());
250 if (!service_directory) {
251 DLOG(ERROR) << "Invalid |service_directory| in CreateContextParams.";
252 context_request.Close(ZX_ERR_INVALID_ARGS);
253 return;
254 }
255
256 base::LaunchOptions launch_options;
257 launch_options.process_name_suffix = ":context";
258
259 sandbox::policy::SandboxPolicyFuchsia sandbox_policy(
260 sandbox::policy::SandboxType::kWebContext);
261 sandbox_policy.SetServiceDirectory(std::move(service_directory));
262 sandbox_policy.UpdateLaunchOptionsForSandbox(&launch_options);
263
264 // SandboxPolicyFuchsia should isolate each Context in its own job.
265 DCHECK_NE(launch_options.job_handle, ZX_HANDLE_INVALID);
266
267 // Transfer the ContextRequest handle to a well-known location in the child
268 // process' handle table.
269 launch_options.handles_to_transfer.push_back(
270 {kContextRequestHandleId, context_request.channel().get()});
271
272 base::CommandLine launch_command(*base::CommandLine::ForCurrentProcess());
273
274 // Bind |data_directory| to /data directory, if provided.
275 zx::channel data_directory_channel;
276 if (params.has_data_directory()) {
277 data_directory_channel = ValidateDirectoryAndTakeChannel(
278 std::move(*params.mutable_data_directory()));
279 if (!data_directory_channel) {
280 DLOG(ERROR)
281 << "Invalid argument |data_directory| in CreateContextParams.";
282 context_request.Close(ZX_ERR_INVALID_ARGS);
283 return;
284 }
285
286 base::FilePath data_path;
287 if (!base::PathService::Get(base::DIR_APP_DATA, &data_path)) {
288 DLOG(ERROR) << "Failed to get data directory service path.";
289 context_request.Close(ZX_ERR_INTERNAL);
290 return;
291 }
292 launch_options.paths_to_transfer.push_back(
293 base::PathToTransfer{data_path, data_directory_channel.release()});
294
295 if (params.has_data_quota_bytes()) {
296 launch_command.AppendSwitchNative(
297 switches::kDataQuotaBytes,
298 base::NumberToString(params.data_quota_bytes()));
299 }
300 }
301
302 // Process command-line settings specified in our package config-data.
303 base::Value web_engine_config;
304 if (config_for_test_.is_none()) {
305 const base::Optional<base::Value>& config = cr_fuchsia::LoadPackageConfig();
306 web_engine_config =
307 config ? config->Clone() : base::Value(base::Value::Type::DICTIONARY);
308 } else {
309 web_engine_config = std::move(config_for_test_);
310 }
311 if (!MaybeAddCommandLineArgsFromConfig(web_engine_config, &launch_command)) {
312 context_request.Close(ZX_ERR_INTERNAL);
313 return;
314 }
315
316 if (params.has_remote_debugging_port()) {
317 launch_command.AppendSwitchNative(
318 switches::kRemoteDebuggingPort,
319 base::NumberToString(params.remote_debugging_port()));
320 }
321
322 std::vector<zx::channel> devtools_listener_channels;
323 if (devtools_listeners_.size() != 0) {
324 // Connect DevTools listeners to the new Context process.
325 std::vector<std::string> handles_ids;
326 for (auto& devtools_listener : devtools_listeners_.ptrs()) {
327 fidl::InterfaceHandle<fuchsia::web::DevToolsPerContextListener>
328 client_listener;
329 devtools_listener.get()->get()->OnContextDevToolsAvailable(
330 client_listener.NewRequest());
331 devtools_listener_channels.emplace_back(client_listener.TakeChannel());
332 handles_ids.push_back(
333 base::NumberToString(base::LaunchOptions::AddHandleToTransfer(
334 &launch_options.handles_to_transfer,
335 devtools_listener_channels.back().get())));
336 }
337 launch_command.AppendSwitchNative(switches::kRemoteDebuggerHandles,
338 base::JoinString(handles_ids, ","));
339 }
340
341 fuchsia::web::ContextFeatureFlags features = {};
342 if (params.has_features())
343 features = params.features();
344
345 const bool is_headless =
346 (features & fuchsia::web::ContextFeatureFlags::HEADLESS) ==
347 fuchsia::web::ContextFeatureFlags::HEADLESS;
348 if (is_headless) {
349 launch_command.AppendSwitchNative(switches::kOzonePlatform,
350 switches::kHeadless);
351 launch_command.AppendSwitch(switches::kHeadless);
352 }
353
354 if ((features & fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) ==
355 fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) {
356 launch_command.AppendSwitch(switches::kUseLegacyMetricsService);
357 }
358
359 const bool enable_vulkan =
360 (features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
361 fuchsia::web::ContextFeatureFlags::VULKAN;
362 bool enable_widevine =
363 (features & fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM) ==
364 fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM;
365 bool enable_playready = params.has_playready_key_system();
366
367 // VULKAN is required for DRM-protected video playback. Allow DRM to also be
368 // enabled for HEADLESS Contexts, since Vulkan is never required for audio.
369 if ((enable_widevine || enable_playready) && !enable_vulkan && !is_headless) {
370 DLOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require VULKAN or "
371 "HEADLESS.";
372 context_request.Close(ZX_ERR_NOT_SUPPORTED);
373 return;
374 }
375 if ((enable_widevine || enable_playready) &&
376 !params.has_cdm_data_directory()) {
377 LOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require a "
378 "|cdm_data_directory|.";
379 context_request.Close(ZX_ERR_NOT_SUPPORTED);
380 return;
381 }
382
383 // If the system doesn't actually support DRM then disable it. This may result
384 // in the Context being able to run without using protected buffers.
385 if (enable_playready && !IsFuchsiaCdmSupported()) {
386 LOG(WARNING) << "PlayReady is not supported on this device.";
387 enable_playready = false;
388 }
389 if (enable_widevine && !IsFuchsiaCdmSupported()) {
390 LOG(WARNING) << "Widevine is not supported on this device.";
391 enable_widevine = false;
392 }
393
394 bool allow_protected_graphics =
395 web_engine_config.FindBoolPath("allow-protected-graphics")
396 .value_or(false);
397 bool force_protected_graphics =
398 web_engine_config.FindBoolPath("force-protected-graphics")
399 .value_or(false);
400 bool enable_protected_graphics =
401 ((enable_playready || enable_widevine) && allow_protected_graphics) ||
402 force_protected_graphics;
403 bool use_overlays_for_video =
404 web_engine_config.FindBoolPath("use-overlays-for-video").value_or(false);
405
406 if (enable_protected_graphics) {
407 launch_command.AppendSwitch(switches::kEnableVulkanProtectedMemory);
408 // TODO(crbug.com/1143764): Remove this after underlays are stable.
409 if (force_protected_graphics || !use_overlays_for_video) {
410 launch_command.AppendSwitch(switches::kEnforceVulkanProtectedMemory);
411 }
412 launch_command.AppendSwitch(switches::kEnableProtectedVideoBuffers);
413 bool force_protected_video_buffers =
414 web_engine_config.FindBoolPath("force-protected-video-buffers")
415 .value_or(false);
416 if (force_protected_video_buffers) {
417 launch_command.AppendSwitch(switches::kForceProtectedVideoOutputBuffers);
418 }
419 }
420
421 if (use_overlays_for_video) {
422 // Overlays are only available if OutputPresenterFuchsia is in use.
423 AppendFeature(switches::kEnableFeatures,
424 features::kUseSkiaOutputDeviceBufferQueue.name,
425 &launch_command);
426 AppendFeature(switches::kEnableFeatures,
427 features::kUseRealBuffersForPageFlipTest.name,
428 &launch_command);
429 launch_command.AppendSwitchASCII(switches::kEnableHardwareOverlays,
430 "underlay");
431 launch_command.AppendSwitch(switches::kUseOverlaysForVideo);
432 }
433
434 if (enable_vulkan) {
435 if (is_headless) {
436 DLOG(ERROR) << "VULKAN and HEADLESS features cannot be used together.";
437 context_request.Close(ZX_ERR_NOT_SUPPORTED);
438 return;
439 }
440
441 VLOG(1) << "Enabling Vulkan GPU acceleration.";
442 // Vulkan requires use of SkiaRenderer, configured to a use Vulkan context.
443 launch_command.AppendSwitch(switches::kUseVulkan);
444 const std::vector<base::StringPiece> enabled_features = {
445 features::kUseSkiaRenderer.name, features::kVulkan.name};
446 AppendFeature(switches::kEnableFeatures,
447 base::JoinString(enabled_features, ","), &launch_command);
448
449 // SkiaRenderer requires out-of-process rasterization be enabled.
450 launch_command.AppendSwitch(switches::kEnableOopRasterization);
451
452 launch_command.AppendSwitchASCII(switches::kUseGL,
453 gl::kGLImplementationANGLEName);
454 } else {
455 VLOG(1) << "Disabling GPU acceleration.";
456 // Disable use of Vulkan GPU, and use of the software-GL rasterizer. The
457 // Context will still run a GPU process, but will not support WebGL.
458 launch_command.AppendSwitch(switches::kDisableGpu);
459 launch_command.AppendSwitch(switches::kDisableSoftwareRasterizer);
460 }
461
462 if (enable_widevine) {
463 launch_command.AppendSwitch(switches::kEnableWidevine);
464 }
465
466 if (enable_playready) {
467 const std::string& key_system = params.playready_key_system();
468 if (key_system == kWidevineKeySystem || media::IsClearKey(key_system)) {
469 LOG(ERROR)
470 << "Invalid value for CreateContextParams/playready_key_system: "
471 << key_system;
472 context_request.Close(ZX_ERR_INVALID_ARGS);
473 return;
474 }
475 launch_command.AppendSwitchNative(switches::kPlayreadyKeySystem,
476 key_system);
477 }
478
479 bool enable_audio = (features & fuchsia::web::ContextFeatureFlags::AUDIO) ==
480 fuchsia::web::ContextFeatureFlags::AUDIO;
481 if (!enable_audio) {
482 // TODO(fxbug.dev/58902): Split up audio input and output in
483 // ContextFeatureFlags.
484 launch_command.AppendSwitch(switches::kDisableAudioOutput);
485 launch_command.AppendSwitch(switches::kDisableAudioInput);
486 }
487
488 zx::channel cdm_data_directory_channel;
489 if (enable_widevine || enable_playready) {
490 DCHECK(params.has_cdm_data_directory());
491 const char kCdmDataPath[] = "/cdm_data";
492
493 cdm_data_directory_channel = ValidateDirectoryAndTakeChannel(
494 std::move(*params.mutable_cdm_data_directory()));
495 if (!cdm_data_directory_channel) {
496 LOG(ERROR)
497 << "Invalid argument |cdm_data_directory| in CreateContextParams.";
498 context_request.Close(ZX_ERR_INVALID_ARGS);
499 return;
500 }
501
502 launch_command.AppendSwitchNative(switches::kCdmDataDirectory,
503 kCdmDataPath);
504 launch_options.paths_to_transfer.push_back(base::PathToTransfer{
505 base::FilePath(kCdmDataPath), cdm_data_directory_channel.get()});
506
507 if (params.has_cdm_data_quota_bytes()) {
508 launch_command.AppendSwitchNative(
509 switches::kCdmDataQuotaBytes,
510 base::NumberToString(params.cdm_data_quota_bytes()));
511 }
512 }
513
514 bool enable_hardware_video_decoder =
515 (features & fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER) ==
516 fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER;
517 if (!enable_hardware_video_decoder)
518 launch_command.AppendSwitch(switches::kDisableAcceleratedVideoDecode);
519
520 if (enable_hardware_video_decoder && !enable_vulkan) {
521 DLOG(ERROR) << "HARDWARE_VIDEO_DECODER requires VULKAN.";
522 context_request.Close(ZX_ERR_NOT_SUPPORTED);
523 return;
524 }
525
526 bool disable_software_video_decoder =
527 (features &
528 fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY) ==
529 fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY;
530 if (disable_software_video_decoder) {
531 if (!enable_hardware_video_decoder) {
532 LOG(ERROR) << "Software video decoding may only be disabled if hardware "
533 "video decoding is enabled.";
534 context_request.Close(ZX_ERR_INVALID_ARGS);
535 return;
536 }
537
538 launch_command.AppendSwitch(switches::kDisableSoftwareVideoDecoders);
539 }
540
541 // Validate embedder-supplied product, and optional version, and pass it to
542 // the Context to include in the UserAgent.
543 if (params.has_user_agent_product()) {
544 if (!net::HttpUtil::IsToken(params.user_agent_product())) {
545 LOG(ERROR) << "Invalid embedder product.";
546 context_request.Close(ZX_ERR_INVALID_ARGS);
547 return;
548 }
549 std::string product_tag(params.user_agent_product());
550 if (params.has_user_agent_version()) {
551 if (!net::HttpUtil::IsToken(params.user_agent_version())) {
552 LOG(ERROR) << "Invalid embedder version.";
553 context_request.Close(ZX_ERR_INVALID_ARGS);
554 return;
555 }
556 product_tag += "/" + params.user_agent_version();
557 }
558 launch_command.AppendSwitchNative(switches::kUserAgentProductAndVersion,
559 std::move(product_tag));
560 } else if (params.has_user_agent_version()) {
561 LOG(ERROR) << "Embedder version without product.";
562 context_request.Close(ZX_ERR_INVALID_ARGS);
563 return;
564 }
565
566 if (params.has_content_directories() &&
567 !SetContentDirectoriesInCommandLine(
568 std::move(*params.mutable_content_directories()), &launch_command,
569 &launch_options)) {
570 LOG(ERROR) << "Invalid content directories specified.";
571 context_request.Close(ZX_ERR_INVALID_ARGS);
572 return;
573 }
574
575 if (params.has_unsafely_treat_insecure_origins_as_secure()) {
576 const std::vector<std::string>& insecure_origins =
577 params.unsafely_treat_insecure_origins_as_secure();
578 for (auto origin : insecure_origins) {
579 if (origin == switches::kAllowRunningInsecureContent) {
580 launch_command.AppendSwitch(switches::kAllowRunningInsecureContent);
581 } else if (origin == kDisableMixedContentAutoupgradeOrigin) {
582 AppendFeature(switches::kDisableFeatures,
583 kMixedContentAutoupgradeFeatureName, &launch_command);
584 } else {
585 // Pass the rest of the list to the Context process.
586 AppendFeature(network::switches::kUnsafelyTreatInsecureOriginAsSecure,
587 origin, &launch_command);
588 }
589 }
590 }
591
592 if (params.has_cors_exempt_headers()) {
593 std::vector<base::StringPiece> cors_exempt_headers;
594 for (const auto& header : params.cors_exempt_headers()) {
595 cors_exempt_headers.push_back(cr_fuchsia::BytesAsString(header));
596 }
597
598 launch_command.AppendSwitchNative(
599 switches::kCorsExemptHeaders,
600 base::JoinString(cors_exempt_headers, ","));
601 }
602
603 base::Process context_process;
604 if (launch_for_test_) {
605 context_process = launch_for_test_.Run(launch_command, launch_options);
606 } else {
607 context_process = base::LaunchProcess(launch_command, launch_options);
608 }
609
610 // |context_request|, any DevTools channels and data directory channels were
611 // transferred (not copied) to the Context process.
612 ignore_result(context_request.TakeChannel().release());
613 for (auto& channel : devtools_listener_channels)
614 ignore_result(channel.release());
615 ignore_result(data_directory_channel.release());
616 ignore_result(cdm_data_directory_channel.release());
617 }
618
SetLaunchCallbackForTest(LaunchCallbackForTest launch)619 void ContextProviderImpl::SetLaunchCallbackForTest(
620 LaunchCallbackForTest launch) {
621 launch_for_test_ = std::move(launch);
622 }
623
EnableDevTools(fidl::InterfaceHandle<fuchsia::web::DevToolsListener> listener,EnableDevToolsCallback callback)624 void ContextProviderImpl::EnableDevTools(
625 fidl::InterfaceHandle<fuchsia::web::DevToolsListener> listener,
626 EnableDevToolsCallback callback) {
627 devtools_listeners_.AddInterfacePtr(listener.Bind());
628 callback();
629 }
630