web_instance_host.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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. #include "fuchsia_web/webinstance_host/web_instance_host.h"
  5. #include <fuchsia/sys/cpp/fidl.h>
  6. #include <lib/async/default.h>
  7. #include <lib/fdio/directory.h>
  8. #include <lib/fdio/fd.h>
  9. #include <lib/fdio/io.h>
  10. #include <lib/sys/cpp/component_context.h>
  11. #include <lib/sys/cpp/service_directory.h>
  12. #include <stdio.h>
  13. #include <sys/stat.h>
  14. #include <sys/types.h>
  15. #include <unistd.h>
  16. #include <zircon/processargs.h>
  17. #include <string>
  18. #include <utility>
  19. #include <vector>
  20. #include "base/base_switches.h"
  21. #include "base/bind.h"
  22. #include "base/command_line.h"
  23. #include "base/files/file_path.h"
  24. #include "base/fuchsia/file_utils.h"
  25. #include "base/fuchsia/fuchsia_logging.h"
  26. #include "base/fuchsia/process_context.h"
  27. #include "base/logging.h"
  28. #include "base/process/process.h"
  29. #include "base/strings/strcat.h"
  30. #include "base/strings/string_number_conversions.h"
  31. #include "base/strings/string_piece.h"
  32. #include "base/strings/string_util.h"
  33. #include "base/strings/stringprintf.h"
  34. #include "build/build_config.h"
  35. #include "components/fuchsia_component_support/config_reader.h"
  36. #include "components/fuchsia_component_support/feedback_registration.h"
  37. #include "fuchsia_web/common/string_util.h"
  38. #include "fuchsia_web/webengine/features.h"
  39. #include "fuchsia_web/webengine/switches.h"
  40. #include "fuchsia_web/webinstance_host/fuchsia_web_debug_proxy.h"
  41. #include "gpu/command_buffer/service/gpu_switches.h"
  42. #include "gpu/config/gpu_finch_features.h"
  43. #include "net/http/http_util.h"
  44. #include "services/network/public/cpp/features.h"
  45. #include "services/network/public/cpp/network_switches.h"
  46. #include "third_party/widevine/cdm/widevine_cdm_common.h"
  47. #include "ui/gfx/switches.h"
  48. #include "ui/gl/gl_switches.h"
  49. #include "ui/ozone/public/ozone_switches.h"
  50. namespace {
  51. // Production URL for web hosting Component instances.
  52. // TODO(fxbug.dev/51490): Use a programmatic mechanism to obtain this.
  53. const char kWebInstanceComponentUrl[] =
  54. "fuchsia-pkg://fuchsia.com/web_engine#meta/web_instance.cmx";
  55. // Test-only URL for web hosting Component instances with WebUI resources.
  56. const char kWebInstanceWithWebUiComponentUrl[] =
  57. "fuchsia-pkg://fuchsia.com/web_engine_with_webui#meta/web_instance.cmx";
  58. // Use a constexpr instead of the existing base::Feature, because of the
  59. // additional dependencies required.
  60. constexpr char kMixedContentAutoupgradeFeatureName[] =
  61. "AutoupgradeMixedContent";
  62. constexpr char kDisableMixedContentAutoupgradeOrigin[] =
  63. "disable-mixed-content-autoupgrade";
  64. // Use a constexpr instead of the existing switch, because of the additional
  65. // dependencies required.
  66. // Content switches:
  67. constexpr char kRemoteDebuggingPortSwitch[] = "remote-debugging-port";
  68. constexpr char kDisableAcceleratedVideoDecodeSwitch[] =
  69. "disable-accelerated-video-decode";
  70. constexpr char kDisableAudioInputSwitch[] = "disable-audio-input";
  71. constexpr char kDisableAudioOutputSwitch[] = "disable-audio-output";
  72. // Media switches:
  73. constexpr char kDisableGpuSwitch[] = "disable-gpu";
  74. constexpr char kDisableSoftwareRasterizerSwitch[] =
  75. "disable-software-rasterizer";
  76. // Use a constexpr instead of the media::IsClearKey() helper, because of the
  77. // additional dependencies required.
  78. constexpr char kClearKeyKeySystem[] = "org.w3.clearkey";
  79. // Registers product data for the web_instance Component, ensuring it is
  80. // registered regardless of how the Component is launched and without requiring
  81. // all of its clients to provide the required services (until a better solution
  82. // is available - see crbug.com/1211174). This should only be called once per
  83. // process, and the calling thread must have an async_dispatcher.
  84. void RegisterWebInstanceProductData() {
  85. constexpr char kCrashProductName[] = "FuchsiaWebEngine";
  86. constexpr char kFeedbackAnnotationsNamespace[] = "web-engine";
  87. fuchsia_component_support::RegisterProductDataForCrashReporting(
  88. kWebInstanceComponentUrl, kCrashProductName);
  89. fuchsia_component_support::RegisterProductDataForFeedback(
  90. kFeedbackAnnotationsNamespace);
  91. }
  92. // Returns the underlying channel if |directory| is a client endpoint for a
  93. // |fuchsia::io::Directory| protocol. Otherwise, returns an empty channel.
  94. zx::channel ValidateDirectoryAndTakeChannel(
  95. fidl::InterfaceHandle<fuchsia::io::Directory> directory_handle) {
  96. fidl::SynchronousInterfacePtr<fuchsia::io::Directory> directory =
  97. directory_handle.BindSync();
  98. fuchsia::io::NodeInfo info;
  99. zx_status_t status = directory->Describe(&info);
  100. if (status != ZX_OK) {
  101. ZX_DLOG(ERROR, status) << "Describe()";
  102. return {};
  103. }
  104. if (!info.is_directory()) {
  105. DLOG(ERROR) << "Not a directory.";
  106. return {};
  107. }
  108. return directory.Unbind().TakeChannel();
  109. }
  110. // Appends |value| to the value of |switch_name| in the |command_line|.
  111. // The switch is assumed to consist of comma-separated values. If |switch_name|
  112. // is already set in |command_line| then a comma will be appended, followed by
  113. // |value|, otherwise the switch will be set to |value|.
  114. void AppendToSwitch(base::StringPiece switch_name,
  115. base::StringPiece value,
  116. base::CommandLine* command_line) {
  117. if (!command_line->HasSwitch(switch_name)) {
  118. command_line->AppendSwitchNative(switch_name, value);
  119. return;
  120. }
  121. std::string new_value = base::StrCat(
  122. {command_line->GetSwitchValueASCII(switch_name), ",", value});
  123. command_line->RemoveSwitch(switch_name);
  124. command_line->AppendSwitchNative(switch_name, new_value);
  125. }
  126. // File names must not contain directory separators, nor match the special
  127. // current- nor parent-directory filenames.
  128. bool IsValidContentDirectoryName(base::StringPiece file_name) {
  129. if (file_name.find_first_of(base::FilePath::kSeparators, 0,
  130. base::FilePath::kSeparatorsLength - 1) !=
  131. base::StringPiece::npos) {
  132. return false;
  133. }
  134. if (file_name == base::FilePath::kCurrentDirectory ||
  135. file_name == base::FilePath::kParentDirectory) {
  136. return false;
  137. }
  138. return true;
  139. }
  140. bool HandleDataDirectoryParam(fuchsia::web::CreateContextParams* params,
  141. base::CommandLine* launch_args,
  142. fuchsia::sys::LaunchInfo* launch_info) {
  143. if (!params->has_data_directory()) {
  144. // Caller requested a web instance without any peristence.
  145. launch_args->AppendSwitch(switches::kIncognito);
  146. return true;
  147. }
  148. zx::channel data_directory_channel = ValidateDirectoryAndTakeChannel(
  149. std::move(*params->mutable_data_directory()));
  150. if (!data_directory_channel) {
  151. LOG(ERROR) << "Invalid argument |data_directory| in CreateContextParams.";
  152. return false;
  153. }
  154. launch_info->flat_namespace->paths.push_back(
  155. base::kPersistedDataDirectoryPath);
  156. launch_info->flat_namespace->directories.push_back(
  157. std::move(data_directory_channel));
  158. if (params->has_data_quota_bytes()) {
  159. launch_args->AppendSwitchNative(
  160. switches::kDataQuotaBytes,
  161. base::NumberToString(params->data_quota_bytes()));
  162. }
  163. return true;
  164. }
  165. bool HandleCdmDataDirectoryParam(fuchsia::web::CreateContextParams* params,
  166. base::CommandLine* launch_args,
  167. fuchsia::sys::LaunchInfo* launch_info) {
  168. if (!params->has_cdm_data_directory())
  169. return true;
  170. const char kCdmDataPath[] = "/cdm_data";
  171. zx::channel cdm_data_directory_channel = ValidateDirectoryAndTakeChannel(
  172. std::move(*params->mutable_cdm_data_directory()));
  173. if (!cdm_data_directory_channel) {
  174. LOG(ERROR)
  175. << "Invalid argument |cdm_data_directory| in CreateContextParams.";
  176. return false;
  177. }
  178. launch_args->AppendSwitchNative(switches::kCdmDataDirectory, kCdmDataPath);
  179. launch_info->flat_namespace->paths.push_back(kCdmDataPath);
  180. launch_info->flat_namespace->directories.push_back(
  181. std::move(cdm_data_directory_channel));
  182. if (params->has_cdm_data_quota_bytes()) {
  183. launch_args->AppendSwitchNative(
  184. switches::kCdmDataQuotaBytes,
  185. base::NumberToString(params->cdm_data_quota_bytes()));
  186. }
  187. return true;
  188. }
  189. bool HandleUserAgentParams(fuchsia::web::CreateContextParams* params,
  190. base::CommandLine* launch_args) {
  191. if (!params->has_user_agent_product()) {
  192. if (params->has_user_agent_version()) {
  193. LOG(ERROR) << "Embedder version without product.";
  194. return false;
  195. }
  196. return true;
  197. }
  198. if (!net::HttpUtil::IsToken(params->user_agent_product())) {
  199. LOG(ERROR) << "Invalid embedder product.";
  200. return false;
  201. }
  202. std::string product_and_version(params->user_agent_product());
  203. if (params->has_user_agent_version()) {
  204. if (!net::HttpUtil::IsToken(params->user_agent_version())) {
  205. LOG(ERROR) << "Invalid embedder version.";
  206. return false;
  207. }
  208. base::StrAppend(&product_and_version, {"/", params->user_agent_version()});
  209. }
  210. launch_args->AppendSwitchNative(switches::kUserAgentProductAndVersion,
  211. std::move(product_and_version));
  212. return true;
  213. }
  214. void HandleUnsafelyTreatInsecureOriginsAsSecureParam(
  215. fuchsia::web::CreateContextParams* params,
  216. base::CommandLine* launch_args) {
  217. if (!params->has_unsafely_treat_insecure_origins_as_secure())
  218. return;
  219. const std::vector<std::string>& insecure_origins =
  220. params->unsafely_treat_insecure_origins_as_secure();
  221. for (auto origin : insecure_origins) {
  222. if (origin == switches::kAllowRunningInsecureContent) {
  223. launch_args->AppendSwitch(switches::kAllowRunningInsecureContent);
  224. } else if (origin == kDisableMixedContentAutoupgradeOrigin) {
  225. AppendToSwitch(switches::kDisableFeatures,
  226. kMixedContentAutoupgradeFeatureName, launch_args);
  227. } else {
  228. // Pass the rest of the list to the Context process.
  229. AppendToSwitch(network::switches::kUnsafelyTreatInsecureOriginAsSecure,
  230. origin, launch_args);
  231. }
  232. }
  233. }
  234. void HandleCorsExemptHeadersParam(fuchsia::web::CreateContextParams* params,
  235. base::CommandLine* launch_args) {
  236. if (!params->has_cors_exempt_headers())
  237. return;
  238. std::vector<base::StringPiece> cors_exempt_headers;
  239. cors_exempt_headers.reserve(params->cors_exempt_headers().size());
  240. for (const auto& header : params->cors_exempt_headers()) {
  241. cors_exempt_headers.push_back(BytesAsString(header));
  242. }
  243. launch_args->AppendSwitchNative(switches::kCorsExemptHeaders,
  244. base::JoinString(cors_exempt_headers, ","));
  245. }
  246. bool HandleContentDirectoriesParam(fuchsia::web::CreateContextParams* params,
  247. base::CommandLine* launch_args,
  248. fuchsia::sys::LaunchInfo* launch_info) {
  249. DCHECK(launch_info);
  250. DCHECK(launch_info->flat_namespace);
  251. if (!params->has_content_directories())
  252. return true;
  253. auto* directories = params->mutable_content_directories();
  254. for (size_t i = 0; i < directories->size(); ++i) {
  255. fuchsia::web::ContentDirectoryProvider& directory = directories->at(i);
  256. if (!IsValidContentDirectoryName(directory.name())) {
  257. DLOG(ERROR) << "Invalid directory name: " << directory.name();
  258. return false;
  259. }
  260. zx::channel validated_channel = ValidateDirectoryAndTakeChannel(
  261. std::move(*directory.mutable_directory()));
  262. if (!validated_channel) {
  263. DLOG(ERROR) << "Service directory handle not valid for directory: "
  264. << directory.name();
  265. return false;
  266. }
  267. const base::FilePath kContentDirectories("/content-directories");
  268. launch_info->flat_namespace->paths.push_back(
  269. kContentDirectories.Append(directory.name()).value());
  270. launch_info->flat_namespace->directories.push_back(
  271. std::move(validated_channel));
  272. }
  273. launch_args->AppendSwitch(switches::kEnableContentDirectories);
  274. return true;
  275. }
  276. bool HandleKeyboardFeatureFlags(fuchsia::web::ContextFeatureFlags features,
  277. base::CommandLine* launch_args) {
  278. const bool enable_keyboard =
  279. (features & fuchsia::web::ContextFeatureFlags::KEYBOARD) ==
  280. fuchsia::web::ContextFeatureFlags::KEYBOARD;
  281. const bool enable_virtual_keyboard =
  282. (features & fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD) ==
  283. fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD;
  284. if (enable_keyboard) {
  285. AppendToSwitch(switches::kEnableFeatures, features::kKeyboardInput.name,
  286. launch_args);
  287. if (enable_virtual_keyboard) {
  288. AppendToSwitch(switches::kEnableFeatures, features::kVirtualKeyboard.name,
  289. launch_args);
  290. }
  291. } else if (enable_virtual_keyboard) {
  292. LOG(ERROR) << "VIRTUAL_KEYBOARD feature requires KEYBOARD.";
  293. return false;
  294. }
  295. return true;
  296. }
  297. // Returns true if DRM is supported in current configuration. Currently we
  298. // assume that it is supported on ARM64, but not on x64.
  299. //
  300. // TODO(crbug.com/1013412): Detect support for all features required for
  301. // FuchsiaCdm. Specifically we need to verify that protected memory is supported
  302. // and that mediacodec API provides hardware video decoders.
  303. bool IsFuchsiaCdmSupported() {
  304. #if defined(ARCH_CPU_ARM64)
  305. return true;
  306. #else
  307. return false;
  308. #endif
  309. }
  310. // Returns the names of all services required by a web_instance.cmx component
  311. // instance configured with the specified set of feature flags. The caller is
  312. // responsible for verifying that |params| specifies a valid combination of
  313. // settings, before calling this function.
  314. std::vector<std::string> GetRequiredServicesForConfig(
  315. const fuchsia::web::CreateContextParams& params) {
  316. // All web_instance.cmx instances require a common set of services, described
  317. // at:
  318. // https://fuchsia.dev/reference/fidl/fuchsia.web#CreateContextParams.service_directory
  319. std::vector<std::string> services{
  320. "fuchsia.buildinfo.Provider", "fuchsia.device.NameProvider",
  321. "fuchsia.fonts.Provider", "fuchsia.hwinfo.Product",
  322. "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink",
  323. "fuchsia.memorypressure.Provider", "fuchsia.process.Launcher",
  324. "fuchsia.settings.Display", // Used if preferred theme is DEFAULT.
  325. "fuchsia.sysmem.Allocator", "fuchsia.ui.scenic.Scenic"};
  326. // TODO(crbug.com/1209031): Provide these conditionally, once corresponding
  327. // ContextFeatureFlags have been defined.
  328. services.insert(services.end(), {"fuchsia.camera3.DeviceWatcher",
  329. "fuchsia.media.ProfileProvider"});
  330. // Additional services are required depending on particular configuration
  331. // parameters.
  332. if (params.has_playready_key_system()) {
  333. services.emplace_back("fuchsia.media.drm.PlayReady");
  334. }
  335. // Additional services are required dependent on the set of features specified
  336. // for the instance, as described at:
  337. // https://fuchsia.dev/reference/fidl/fuchsia.web#ContextFeatureFlags
  338. // Features are listed here in order of their enum value.
  339. fuchsia::web::ContextFeatureFlags features = {};
  340. if (params.has_features())
  341. features = params.features();
  342. // TODO(crbug.com/1020273): Allow access to network services only if the
  343. // NETWORK feature flag is set.
  344. services.insert(services.end(), {
  345. "fuchsia.net.interfaces.State",
  346. "fuchsia.net.name.Lookup",
  347. "fuchsia.posix.socket.Provider",
  348. });
  349. if ((features & fuchsia::web::ContextFeatureFlags::AUDIO) ==
  350. fuchsia::web::ContextFeatureFlags::AUDIO) {
  351. services.insert(services.end(),
  352. {
  353. "fuchsia.media.Audio",
  354. "fuchsia.media.AudioDeviceEnumerator",
  355. "fuchsia.media.SessionAudioConsumerFactory",
  356. });
  357. }
  358. if ((features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
  359. fuchsia::web::ContextFeatureFlags::VULKAN) {
  360. services.emplace_back("fuchsia.vulkan.loader.Loader");
  361. }
  362. if ((features & fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER) ==
  363. fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER) {
  364. services.emplace_back("fuchsia.mediacodec.CodecFactory");
  365. }
  366. // HARDWARE_VIDEO_DECODER_ONLY does not require any additional services.
  367. if ((features & fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM) ==
  368. fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM) {
  369. services.emplace_back("fuchsia.media.drm.Widevine");
  370. }
  371. // HEADLESS instances cannot create Views and therefore do not require access
  372. // to any View-based services.
  373. if ((features & fuchsia::web::ContextFeatureFlags::HEADLESS) !=
  374. fuchsia::web::ContextFeatureFlags::HEADLESS) {
  375. services.insert(services.end(),
  376. {
  377. "fuchsia.accessibility.semantics.SemanticsManager",
  378. "fuchsia.ui.composition.Allocator",
  379. "fuchsia.ui.composition.Flatland",
  380. "fuchsia.ui.scenic.Scenic",
  381. });
  382. }
  383. if ((features & fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) ==
  384. fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) {
  385. services.emplace_back("fuchsia.legacymetrics.MetricsRecorder");
  386. }
  387. if ((features & fuchsia::web::ContextFeatureFlags::KEYBOARD) ==
  388. fuchsia::web::ContextFeatureFlags::KEYBOARD) {
  389. services.emplace_back("fuchsia.ui.input3.Keyboard");
  390. }
  391. if ((features & fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD) ==
  392. fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD) {
  393. services.emplace_back("fuchsia.input.virtualkeyboard.ControllerCreator");
  394. }
  395. return services;
  396. }
  397. } // namespace
  398. WebInstanceHost::WebInstanceHost() {
  399. // Ensure WebInstance is registered before launching it.
  400. // TODO(crbug.com/1211174): Replace with a different mechanism when available.
  401. RegisterWebInstanceProductData();
  402. }
  403. WebInstanceHost::~WebInstanceHost() = default;
  404. zx_status_t WebInstanceHost::CreateInstanceForContextWithCopiedArgs(
  405. fuchsia::web::CreateContextParams params,
  406. fidl::InterfaceRequest<fuchsia::io::Directory> services_request,
  407. base::CommandLine extra_args) {
  408. DCHECK(services_request);
  409. if (!params.has_service_directory()) {
  410. DLOG(ERROR)
  411. << "Missing argument |service_directory| in CreateContextParams.";
  412. return ZX_ERR_INVALID_ARGS;
  413. }
  414. fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory =
  415. std::move(*params.mutable_service_directory());
  416. if (!service_directory) {
  417. DLOG(ERROR) << "Invalid |service_directory| in CreateContextParams.";
  418. return ZX_ERR_INVALID_ARGS;
  419. }
  420. // Initialize with preliminary arguments.
  421. base::CommandLine launch_args(std::move(extra_args));
  422. // Remove this argument, if it's provided.
  423. launch_args.RemoveSwitch(switches::kContextProvider);
  424. fuchsia::sys::LaunchInfo launch_info;
  425. // TODO(1010222): Make kWebInstanceComponentUrl a relative component URL, and
  426. // remove this workaround.
  427. launch_info.url =
  428. base::CommandLine::ForCurrentProcess()->HasSwitch("with-webui")
  429. ? kWebInstanceWithWebUiComponentUrl
  430. : kWebInstanceComponentUrl;
  431. launch_info.flat_namespace = fuchsia::sys::FlatNamespace::New();
  432. fuchsia::web::ContextFeatureFlags features = {};
  433. if (params.has_features())
  434. features = params.features();
  435. if (params.has_remote_debugging_port()) {
  436. if ((features & fuchsia::web::ContextFeatureFlags::NETWORK) !=
  437. fuchsia::web::ContextFeatureFlags::NETWORK) {
  438. LOG(WARNING) << "Enabling remote debugging requires NETWORK feature.";
  439. }
  440. launch_args.AppendSwitchNative(
  441. kRemoteDebuggingPortSwitch,
  442. base::NumberToString(params.remote_debugging_port()));
  443. }
  444. const bool is_headless =
  445. (features & fuchsia::web::ContextFeatureFlags::HEADLESS) ==
  446. fuchsia::web::ContextFeatureFlags::HEADLESS;
  447. if (is_headless) {
  448. launch_args.AppendSwitchNative(switches::kOzonePlatform,
  449. switches::kHeadless);
  450. launch_args.AppendSwitch(switches::kHeadless);
  451. }
  452. if ((features & fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) ==
  453. fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) {
  454. launch_args.AppendSwitch(switches::kUseLegacyMetricsService);
  455. }
  456. const bool enable_vulkan =
  457. (features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
  458. fuchsia::web::ContextFeatureFlags::VULKAN;
  459. bool enable_widevine =
  460. (features & fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM) ==
  461. fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM;
  462. bool enable_playready = params.has_playready_key_system();
  463. // Verify that the configuration is compatible with DRM, if requested.
  464. if (enable_widevine || enable_playready) {
  465. // VULKAN is required for DRM-protected video playback. Allow DRM to also be
  466. // enabled for HEADLESS Contexts, since Vulkan is never required for audio.
  467. if (!enable_vulkan && !is_headless) {
  468. LOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require VULKAN "
  469. " or HEADLESS.";
  470. return ZX_ERR_NOT_SUPPORTED;
  471. }
  472. if (!params.has_cdm_data_directory()) {
  473. LOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require a "
  474. "|cdm_data_directory|.";
  475. return ZX_ERR_NOT_SUPPORTED;
  476. }
  477. // |cdm_data_directory| will be handled later.
  478. }
  479. // If the system doesn't actually support DRM then disable it. This may result
  480. // in the Context being able to run without using protected buffers.
  481. if (enable_playready && !IsFuchsiaCdmSupported()) {
  482. LOG(WARNING) << "PlayReady is not supported on this device.";
  483. enable_playready = false;
  484. }
  485. if (enable_widevine && !IsFuchsiaCdmSupported()) {
  486. LOG(WARNING) << "Widevine is not supported on this device.";
  487. enable_widevine = false;
  488. }
  489. if (enable_vulkan) {
  490. if (is_headless) {
  491. DLOG(ERROR) << "VULKAN and HEADLESS features cannot be used together.";
  492. return ZX_ERR_NOT_SUPPORTED;
  493. }
  494. VLOG(1) << "Enabling Vulkan GPU acceleration.";
  495. // Vulkan requires use of SkiaRenderer, configured to a use Vulkan context.
  496. launch_args.AppendSwitch(switches::kUseVulkan);
  497. AppendToSwitch(switches::kEnableFeatures, features::kVulkan.name,
  498. &launch_args);
  499. launch_args.AppendSwitchASCII(switches::kUseGL,
  500. gl::kGLImplementationANGLEName);
  501. } else {
  502. VLOG(1) << "Disabling GPU acceleration.";
  503. // Disable use of Vulkan GPU, and use of the software-GL rasterizer. The
  504. // Context will still run a GPU process, but will not support WebGL.
  505. launch_args.AppendSwitch(kDisableGpuSwitch);
  506. launch_args.AppendSwitch(kDisableSoftwareRasterizerSwitch);
  507. }
  508. if (enable_widevine) {
  509. launch_args.AppendSwitch(switches::kEnableWidevine);
  510. }
  511. if (enable_playready) {
  512. const std::string& key_system = params.playready_key_system();
  513. if (key_system == kWidevineKeySystem || key_system == kClearKeyKeySystem) {
  514. LOG(ERROR)
  515. << "Invalid value for CreateContextParams/playready_key_system: "
  516. << key_system;
  517. return ZX_ERR_INVALID_ARGS;
  518. }
  519. launch_args.AppendSwitchNative(switches::kPlayreadyKeySystem, key_system);
  520. }
  521. bool enable_audio = (features & fuchsia::web::ContextFeatureFlags::AUDIO) ==
  522. fuchsia::web::ContextFeatureFlags::AUDIO;
  523. if (!enable_audio) {
  524. // TODO(fxbug.dev/58902): Split up audio input and output in
  525. // ContextFeatureFlags.
  526. launch_args.AppendSwitch(kDisableAudioOutputSwitch);
  527. launch_args.AppendSwitch(kDisableAudioInputSwitch);
  528. }
  529. bool enable_hardware_video_decoder =
  530. (features & fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER) ==
  531. fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER;
  532. if (!enable_hardware_video_decoder)
  533. launch_args.AppendSwitch(kDisableAcceleratedVideoDecodeSwitch);
  534. if (enable_hardware_video_decoder && !enable_vulkan) {
  535. DLOG(ERROR) << "HARDWARE_VIDEO_DECODER requires VULKAN.";
  536. return ZX_ERR_NOT_SUPPORTED;
  537. }
  538. bool disable_software_video_decoder =
  539. (features &
  540. fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY) ==
  541. fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY;
  542. if (disable_software_video_decoder) {
  543. if (!enable_hardware_video_decoder) {
  544. LOG(ERROR) << "Software video decoding may only be disabled if hardware "
  545. "video decoding is enabled.";
  546. return ZX_ERR_INVALID_ARGS;
  547. }
  548. AppendToSwitch(switches::kDisableFeatures,
  549. features::kEnableSoftwareOnlyVideoCodecs.name, &launch_args);
  550. }
  551. if (!HandleCdmDataDirectoryParam(&params, &launch_args, &launch_info)) {
  552. return ZX_ERR_INVALID_ARGS;
  553. }
  554. if (!HandleDataDirectoryParam(&params, &launch_args, &launch_info)) {
  555. return ZX_ERR_INVALID_ARGS;
  556. }
  557. if (!HandleContentDirectoriesParam(&params, &launch_args, &launch_info)) {
  558. return ZX_ERR_INVALID_ARGS;
  559. }
  560. if (!HandleUserAgentParams(&params, &launch_args)) {
  561. return ZX_ERR_INVALID_ARGS;
  562. }
  563. if (!HandleKeyboardFeatureFlags(features, &launch_args)) {
  564. return ZX_ERR_INVALID_ARGS;
  565. }
  566. HandleUnsafelyTreatInsecureOriginsAsSecureParam(&params, &launch_args);
  567. HandleCorsExemptHeadersParam(&params, &launch_args);
  568. // In tests the ContextProvider is configured to log to stderr, so clone the
  569. // handle to allow web instances to also log there.
  570. if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
  571. "enable-logging") == "stderr") {
  572. launch_info.err = fuchsia::sys::FileDescriptor::New();
  573. launch_info.err->type0 = PA_FD;
  574. zx_status_t status = fdio_fd_clone(
  575. STDERR_FILENO, launch_info.err->handle0.reset_and_get_address());
  576. ZX_CHECK(status == ZX_OK, status);
  577. }
  578. if (tmp_dir_.is_valid()) {
  579. launch_info.flat_namespace->paths.push_back("/tmp");
  580. launch_info.flat_namespace->directories.push_back(tmp_dir_.TakeChannel());
  581. }
  582. // Create a request for the new instance's service-directory.
  583. fidl::InterfaceHandle<fuchsia::io::Directory> instance_services_handle;
  584. launch_info.directory_request =
  585. instance_services_handle.NewRequest().TakeChannel();
  586. sys::ServiceDirectory instance_services(std::move(instance_services_handle));
  587. // If one or more Debug protocol clients are active then enable debugging,
  588. // and connect the instance to the fuchsia.web.Debug proxy.
  589. if (debug_proxy_.has_clients()) {
  590. launch_args.AppendSwitch(switches::kEnableRemoteDebugMode);
  591. fidl::InterfaceHandle<fuchsia::web::Debug> debug_handle;
  592. instance_services.Connect(debug_handle.NewRequest());
  593. debug_proxy_.RegisterInstance(std::move(debug_handle));
  594. }
  595. // Pass on the caller's service-directory request.
  596. instance_services.CloneChannel(std::move(services_request));
  597. // Set |additional_services| to redirect requests for only those services
  598. // required for the specified |params|, to be satisfied by the caller-
  599. // supplied service directory. This reduces the risk of an instance being
  600. // able to somehow exploit services other than those that it should be using.
  601. launch_info.additional_services = fuchsia::sys::ServiceList::New();
  602. launch_info.additional_services->names = GetRequiredServicesForConfig(params);
  603. launch_info.additional_services->host_directory =
  604. service_directory.TakeChannel();
  605. // Take the accumulated command line arguments, omitting the program name in
  606. // argv[0], and set them in |launch_info|.
  607. launch_info.arguments = std::vector<std::string>(
  608. launch_args.argv().begin() + 1, launch_args.argv().end());
  609. // Launch the component with the accumulated settings. The Component will
  610. // self-terminate when the fuchsia.web.Context client disconnects.
  611. IsolatedEnvironmentLauncher()->CreateComponent(std::move(launch_info),
  612. nullptr /* controller */);
  613. return ZX_OK;
  614. }
  615. fuchsia::web::Debug* WebInstanceHost::debug_api() {
  616. return &debug_proxy_;
  617. }
  618. fuchsia::sys::Launcher* WebInstanceHost::IsolatedEnvironmentLauncher() {
  619. if (isolated_environment_launcher_)
  620. return isolated_environment_launcher_.get();
  621. // Create the nested isolated Environment. This environment provides only the
  622. // fuchsia.sys.Loader service, which is required to allow the Launcher to
  623. // resolve the web instance package. All other services are provided
  624. // explicitly to each web instance, from those passed to |CreateContext()|.
  625. auto environment = base::ComponentContextForProcess()
  626. ->svc()
  627. ->Connect<fuchsia::sys::Environment>();
  628. // Populate a ServiceList providing only the Loader service.
  629. auto services = fuchsia::sys::ServiceList::New();
  630. services->names.push_back(fuchsia::sys::Loader::Name_);
  631. fidl::InterfaceHandle<::fuchsia::io::Directory> services_channel;
  632. environment->GetDirectory(services_channel.NewRequest().TakeChannel());
  633. services->host_directory = services_channel.TakeChannel();
  634. // Instantiate the isolated environment. This ContextProvider instance's PID
  635. // is included in the label to ensure that concurrent service instances
  636. // launched in the same Environment (e.g. during tests) do not clash.
  637. fuchsia::sys::EnvironmentPtr isolated_environment;
  638. environment->CreateNestedEnvironment(
  639. isolated_environment.NewRequest(),
  640. isolated_environment_controller_.NewRequest(),
  641. base::StringPrintf("web_instances:%lu", base::Process::Current().Pid()),
  642. std::move(services),
  643. {.inherit_parent_services = false,
  644. .use_parent_runners = false,
  645. .delete_storage_on_death = true});
  646. // The ContextProvider only needs to retain the EnvironmentController and
  647. // a connection to the Launcher service for the isolated environment.
  648. isolated_environment->GetLauncher(
  649. isolated_environment_launcher_.NewRequest());
  650. isolated_environment_launcher_.set_error_handler([](zx_status_t status) {
  651. ZX_LOG(ERROR, status) << "Launcher disconnected.";
  652. });
  653. isolated_environment_controller_.set_error_handler([](zx_status_t status) {
  654. ZX_LOG(ERROR, status) << "EnvironmentController disconnected.";
  655. });
  656. return isolated_environment_launcher_.get();
  657. }