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