0

Reland "[fuchsia] Migrate web_engine_shell to launch web_instance.cm"

This is a reland of commit 986da2599c
with the following differences:

- web_engine_shell.cmx has been removed, as the use of v2 APIs to launch
  web_instance.cm renders it inoperable.
- Documentation has been updated to illustrate how to run
  web_engine_shell.cm.
- Application of the deprecated-allowed-packages test facet has been
  fixed.
- fuchsia.tracing.provider.Registry is now routed dynamically by
  WebInstanceHost conditionally based on the VULKAN ContextFeatureFlag.

Original change's description:
> [fuchsia] Migrate web_engine_shell to launch web_instance.cm
>
> In so doing:
>
> - A relauncher is introduced to launch a new component between
>   test_manager and web_engine_shell when WebInstanceHost is used, as
>   this is required for it to route capabilities to WebEngine.
> - WebInstanceHost now supports the test-only --with-webui switch to run
>   a WebEngine with access to WebUI resources.
> - WebInstanceHost now destroys any active children upon destruction
>   rather than requiring that the consumer wait for them to exit.
>
> Bug: 1280703
> Change-Id: I8481b854542524f7eef69d66059ff1767ba60f93
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4134846
> Reviewed-by: Wez <wez@chromium.org>
> Auto-Submit: Greg Thompson <grt@chromium.org>
> Commit-Queue: Greg Thompson <grt@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1095363}

Bug: 1280703
Change-Id: Ic29f67e3876ab2304bc515af85b742b7a596994f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4200488
Reviewed-by: David Dorwin <ddorwin@chromium.org>
Reviewed-by: Wez <wez@chromium.org>
Commit-Queue: Greg Thompson <grt@chromium.org>
Auto-Submit: Greg Thompson <grt@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1104206}
This commit is contained in:
Greg Thompson
2023-02-11 10:12:04 +00:00
committed by Chromium LUCI CQ
parent b682e914e2
commit 78920be75f
16 changed files with 439 additions and 149 deletions

@@ -12,6 +12,7 @@
#include "base/check.h" #include "base/check.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/ranges/algorithm.h"
using ::component_testing::ChildRef; using ::component_testing::ChildRef;
using ::component_testing::Directory; using ::component_testing::Directory;
@@ -22,24 +23,61 @@ using ::component_testing::Route;
namespace test { namespace test {
namespace {
void AppendCommandLineArgumentsToProgram(
fuchsia::component::decl::Program& program,
const base::CommandLine& command_line) {
// Find the "args" list in the program declaration.
fuchsia::data::DictionaryEntry* args_entry = nullptr;
auto* entries = program.mutable_info()->mutable_entries();
for (auto& entry : *entries) {
if (entry.key == "args") {
DCHECK(entry.value->is_str_vec());
args_entry = &entry;
break;
}
}
if (!args_entry) {
// Create a new "args" list and insert it at the proper location in the
// program's entries; entries' keys must be sorted as per
// https://fuchsia.dev/reference/fidl/fuchsia.data?hl=en#Dictionary.
auto lower_bound = base::ranges::lower_bound(
*entries, "args", /*comp=*/{},
[](const fuchsia::data::DictionaryEntry& entry) { return entry.key; });
auto it = entries->emplace(lower_bound);
it->key = "args";
it->value = fuchsia::data::DictionaryValue::New();
it->value->set_str_vec({});
args_entry = &*it;
}
// Append all args following the program name in `command_line` to the
// program's args.
args_entry->value->str_vec().insert(args_entry->value->str_vec().end(),
command_line.argv().begin() + 1,
command_line.argv().end());
}
} // namespace
void AppendCommandLineArguments(RealmBuilder& realm_builder, void AppendCommandLineArguments(RealmBuilder& realm_builder,
base::StringPiece child_name, base::StringPiece child_name,
const base::CommandLine& command_line) { const base::CommandLine& command_line) {
const std::string child_name_str(child_name); const std::string child_name_str(child_name);
auto context_provider_decl = realm_builder.GetComponentDecl(child_name_str); auto child_component_decl = realm_builder.GetComponentDecl(child_name_str);
for (auto& entry : *context_provider_decl.mutable_program() AppendCommandLineArgumentsToProgram(*child_component_decl.mutable_program(),
->mutable_info() command_line);
->mutable_entries()) {
if (entry.key == "args") {
DCHECK(entry.value->is_str_vec());
entry.value->str_vec().insert(entry.value->str_vec().end(),
command_line.argv().begin() + 1,
command_line.argv().end());
break;
}
}
realm_builder.ReplaceComponentDecl(child_name_str, realm_builder.ReplaceComponentDecl(child_name_str,
std::move(context_provider_decl)); std::move(child_component_decl));
}
void AppendCommandLineArgumentsForRealm(
::component_testing::RealmBuilder& realm_builder,
const base::CommandLine& command_line) {
auto decl = realm_builder.GetRealmDecl();
AppendCommandLineArgumentsToProgram(*decl.mutable_program(), command_line);
realm_builder.ReplaceRealmDecl(std::move(decl));
} }
void AddSyslogRoutesFromParent(RealmBuilder& realm_builder, void AddSyslogRoutesFromParent(RealmBuilder& realm_builder,

@@ -24,6 +24,12 @@ void AppendCommandLineArguments(
base::StringPiece child_name, base::StringPiece child_name,
const base::CommandLine& command_line); const base::CommandLine& command_line);
// Appends the arguments of `command_line` (ignoring the program name at
// position zero) to the command line for the realm.
void AppendCommandLineArgumentsForRealm(
::component_testing::RealmBuilder& realm_builder,
const base::CommandLine& command_line);
// In the functions below, the term "parent" is the `#realm_builder` collection // In the functions below, the term "parent" is the `#realm_builder` collection
// for the test component that is using these helpers to build a test realm. // for the test component that is using these helpers to build a test realm.
// Each test component must use a .cml fragment that routes the required // Each test component must use a .cml fragment that routes the required

@@ -39,9 +39,13 @@ fuchsia_component("web_engine_shell_component") {
data_deps = [ ":web_engine_shell_exec" ] data_deps = [ ":web_engine_shell_exec" ]
} }
fuchsia_component("web_engine_shell_component_v1") { # WebInstanceHost needs to serve capabilities to web_instance. Since the
# primary web_engine_shell component is a test component, it is not able to do
# this directly. Instead, it launches this here component to start instances via
# WebInstanceHost.
fuchsia_component("web_engine_shell_for_web_instance_host_component") {
testonly = true testonly = true
manifest = "web_engine_shell.cmx" manifest = "web_engine_shell_for_web_instance_host.cml"
data_deps = [ ":web_engine_shell_exec" ] data_deps = [ ":web_engine_shell_exec" ]
} }
@@ -52,7 +56,7 @@ fuchsia_package("web_engine_shell_pkg") {
package_name = "web_engine_shell" package_name = "web_engine_shell"
deps = [ deps = [
":web_engine_shell_component", ":web_engine_shell_component",
":web_engine_shell_component_v1", ":web_engine_shell_for_web_instance_host_component",
] ]
} }
@@ -82,11 +86,15 @@ executable("web_engine_shell_exec") {
":remote_debugging_port", ":remote_debugging_port",
"//base", "//base",
"//fuchsia_web/common", "//fuchsia_web/common",
"//fuchsia_web/webinstance_host:webinstance_host_v1", "//fuchsia_web/common/test:test_support",
"//fuchsia_web/webinstance_host:webinstance_host",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.component:fuchsia.component_hlcpp",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.policy:fuchsia.ui.policy_hlcpp", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.policy:fuchsia.ui.policy_hlcpp",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web:fuchsia.web_hlcpp", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web:fuchsia.web_hlcpp",
"//third_party/fuchsia-sdk/sdk/pkg/fdio", "//third_party/fuchsia-sdk/sdk/pkg/fdio",
"//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp",
"//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
"//third_party/fuchsia-sdk/sdk/pkg/sys_component_cpp_testing",
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
"//third_party/widevine/cdm:buildflags", "//third_party/widevine/cdm:buildflags",
"//url", "//url",

@@ -12,8 +12,7 @@ To build and run WebEngine Shell, execute the following commands:
$ autoninja -C $OUTDIR web_engine_shell $ autoninja -C $OUTDIR web_engine_shell
$ $OUTDIR/bin/deploy_web_engine_shell --fuchsia-out-dir $FUCHSIA_OUTDIR $ $OUTDIR/bin/deploy_web_engine_shell --fuchsia-out-dir $FUCHSIA_OUTDIR
$ cd $FUCHSIA $ cd $FUCHSIA
$ fx shell $ ffx test run fuchsia-pkg://fuchsia.com/web_engine_shell#meta/web_engine_shell.cm -- --remote-debugging-port=1234 http://www.example.com
$ run fuchsia-pkg://fuchsia.com/web_engine_shell#meta/web_engine_shell.cmx --remote-debugging-port=1234 http://www.example.com
``` ```
Local files can be deployed with the WebEngine Shell and accessed via the Local files can be deployed with the WebEngine Shell and accessed via the
@@ -22,5 +21,5 @@ by placing them under the path `//fuchsia_web/webengine/data`.
Here is an example command line which loads a local file: Here is an example command line which loads a local file:
``` ```
$ run fuchsia-pkg://fuchsia.com/web_engine_shell#meta/web_engine_shell.cmx fuchsia-dir://data/index.html $ ffx test run fuchsia-pkg://fuchsia.com/web_engine_shell#meta/web_engine_shell.cm -- fuchsia-dir://data/index.html
``` ```

@@ -2,16 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/ui/policy/cpp/fidl.h> #include <fuchsia/ui/policy/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.h> #include <fuchsia/web/cpp/fidl.h>
#include <lib/fdio/directory.h> #include <lib/fdio/directory.h>
#include <lib/fidl/cpp/interface_ptr.h>
#include <lib/sys/component/cpp/testing/realm_builder.h>
#include <lib/sys/cpp/component_context.h> #include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/ui/scenic/cpp/view_token_pair.h> #include <lib/ui/scenic/cpp/view_token_pair.h>
#include <lib/vfs/cpp/pseudo_file.h>
#include <iostream> #include <iostream>
#include <utility> #include <utility>
#include "base/base_paths.h" #include "base/base_paths.h"
#include "base/check.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/fuchsia/file_utils.h" #include "base/fuchsia/file_utils.h"
@@ -27,9 +32,10 @@
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "fuchsia_web/common/init_logging.h" #include "fuchsia_web/common/init_logging.h"
#include "fuchsia_web/common/test/test_realm_support.h"
#include "fuchsia_web/shell/remote_debugging_port.h" #include "fuchsia_web/shell/remote_debugging_port.h"
#include "fuchsia_web/webinstance_host/web_instance_host.h"
#include "fuchsia_web/webinstance_host/web_instance_host_constants.h" #include "fuchsia_web/webinstance_host/web_instance_host_constants.h"
#include "fuchsia_web/webinstance_host/web_instance_host_v1.h"
#include "third_party/widevine/cdm/buildflags.h" #include "third_party/widevine/cdm/buildflags.h"
#include "url/gurl.h" #include "url/gurl.h"
@@ -38,6 +44,9 @@ namespace {
constexpr char kHeadlessSwitch[] = "headless"; constexpr char kHeadlessSwitch[] = "headless";
constexpr char kEnableProtectedMediaIdentifier[] = constexpr char kEnableProtectedMediaIdentifier[] =
"enable-protected-media-identifier"; "enable-protected-media-identifier";
// Included on the command line when the shell is relaunched for use of
// WebInstanceHost; see web_engine_shell_for_web_instance_host.cml.
constexpr char kFromLauncher[] = "from-launcher";
constexpr char kUseWebInstance[] = "use-web-instance"; constexpr char kUseWebInstance[] = "use-web-instance";
constexpr char kEnableWebInstanceTmp[] = "enable-web-instance-tmp"; constexpr char kEnableWebInstanceTmp[] = "enable-web-instance-tmp";
@@ -69,17 +78,51 @@ GURL GetUrlFromArgs(const base::CommandLine::StringVector& args) {
return url; return url;
} }
// web_engine_shell needs to provide capabilities to children it launches (via
// WebInstanceHost, for example). Test components are not able to do this, so
// use RealmBuilder to relaunch web_engine_shell via
// `web_engine_shell_for_web_instance_host_component` (which includes
// `--from-launcher` on its command line) with the contents of this process's
// command line.
int RelaunchForWebInstanceHost(const base::CommandLine& command_line) {
auto realm_builder = component_testing::RealmBuilder::CreateFromRelativeUrl(
"#meta/web_engine_shell_for_web_instance_host.cm");
test::AppendCommandLineArgumentsForRealm(realm_builder, // IN-TEST
command_line);
auto realm = realm_builder.Build();
fuchsia::component::BinderPtr binder_proxy =
realm.component().Connect<fuchsia::component::Binder>();
// Wait for binder_proxy to be closed.
base::RunLoop run_loop;
binder_proxy.set_error_handler(
[quit_closure = run_loop.QuitClosure()](zx_status_t status) {
std::move(quit_closure).Run();
});
run_loop.Run();
// Nothing depends on the process exit code of web_engine_shell today, so
// simply return success in all cases.
return 0;
}
} // namespace } // namespace
int main(int argc, char** argv) { int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
CHECK(InitLoggingFromCommandLine(*command_line));
base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO); base::SingleThreadTaskExecutor executor(base::MessagePumpType::IO);
// Parse the command line arguments and set up logging. const bool is_run_from_launcher = command_line->HasSwitch(kFromLauncher);
CHECK(base::CommandLine::Init(argc, argv)); const bool use_context_provider = !command_line->HasSwitch(kUseWebInstance);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (!is_run_from_launcher && !use_context_provider) {
return RelaunchForWebInstanceHost(*command_line);
CHECK(InitLoggingFromCommandLineDefaultingToStderrForTest( // IN-TEST }
command_line));
absl::optional<uint16_t> remote_debugging_port = absl::optional<uint16_t> remote_debugging_port =
GetRemoteDebuggingPort(*command_line); GetRemoteDebuggingPort(*command_line);
@@ -91,7 +134,6 @@ int main(int argc, char** argv) {
const bool is_headless = command_line->HasSwitch(kHeadlessSwitch); const bool is_headless = command_line->HasSwitch(kHeadlessSwitch);
const bool enable_protected_media_identifier_access = const bool enable_protected_media_identifier_access =
command_line->HasSwitch(kEnableProtectedMediaIdentifier); command_line->HasSwitch(kEnableProtectedMediaIdentifier);
const bool use_context_provider = !command_line->HasSwitch(kUseWebInstance);
const bool enable_web_instance_tmp = const bool enable_web_instance_tmp =
command_line->HasSwitch(kEnableWebInstanceTmp); command_line->HasSwitch(kEnableWebInstanceTmp);
const bool with_webui = command_line->HasSwitch(switches::kWithWebui); const bool with_webui = command_line->HasSwitch(switches::kWithWebui);
@@ -173,12 +215,9 @@ int main(int argc, char** argv) {
base::RunLoop run_loop; base::RunLoop run_loop;
// Create the browser |context|.
fuchsia::web::ContextPtr context;
// Keep alive in run_loop scope.
fuchsia::web::ContextProviderPtr web_context_provider; fuchsia::web::ContextProviderPtr web_context_provider;
std::unique_ptr<WebInstanceHostV1> web_instance_host; std::unique_ptr<WebInstanceHost> web_instance_host;
fuchsia::web::ContextPtr context;
fuchsia::io::DirectoryHandle tmp_directory; fuchsia::io::DirectoryHandle tmp_directory;
if (use_context_provider) { if (use_context_provider) {
@@ -189,7 +228,7 @@ int main(int argc, char** argv) {
web_context_provider->Create(std::move(create_context_params), web_context_provider->Create(std::move(create_context_params),
context.NewRequest()); context.NewRequest());
} else { } else {
web_instance_host = std::make_unique<WebInstanceHostV1>(); web_instance_host = std::make_unique<WebInstanceHost>();
if (enable_web_instance_tmp) { if (enable_web_instance_tmp) {
const zx_status_t status = fdio_open( const zx_status_t status = fdio_open(
"/tmp", "/tmp",
@@ -290,6 +329,8 @@ int main(int argc, char** argv) {
LOG(INFO) << "Launched browser at URL " << url.spec(); LOG(INFO) << "Launched browser at URL " << url.spec();
base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo();
// Run until the process is killed with CTRL-C or the connections to Web // Run until the process is killed with CTRL-C or the connections to Web
// Engine interfaces are dropped. // Engine interfaces are dropped.
run_loop.Run(); run_loop.Run();

@@ -10,38 +10,36 @@
// to function correctly. // to function correctly.
"//build/config/fuchsia/test/chromium_test_facet.shard.test-cml", "//build/config/fuchsia/test/chromium_test_facet.shard.test-cml",
"//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml", "//build/config/fuchsia/test/elf_test_ambient_exec_runner.shard.test-cml",
"sys/component/realm_builder_absolute.shard.cml",
"syslog/client.shard.cml", "syslog/client.shard.cml",
], ],
program: { program: {
binary: "web_engine_shell_exec", binary: "web_engine_shell_exec",
}, },
use: [ use: [
{
protocol: [
"fuchsia.feedback.ComponentDataRegister",
"fuchsia.feedback.CrashReportingProductRegister",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.sys.Loader",
],
},
// Used conditionally based on the absence of `--use-web-instance` on the // Used conditionally based on the absence of `--use-web-instance` on the
// command line at runtime. // command line at runtime.
{ {
protocol: "fuchsia.web.ContextProvider", protocol: "fuchsia.web.ContextProvider",
availability: "optional", availability: "optional",
}, },
// Used to hold the cdm_data directory passed to web_instance.
{ {
storage: "data", storage: "data",
path: "/data", path: "/data",
}, },
// Needed when launched with --enable-web-instance-tmp.
{ {
storage: "tmp", storage: "tmp",
path: "/tmp", path: "/tmp",
availability: "optional",
}, },
// Uses below this line are for web_instance.cmx // Uses below this line are for web_instance.cmx.
// TODO(crbug.com/1280703): Revise as needed when context_provider is
// migrated to launch web_instance.cm.
{ {
protocol: [ protocol: [
"fuchsia.buildinfo.Provider", "fuchsia.buildinfo.Provider",
@@ -93,10 +91,103 @@
availability: "optional", availability: "optional",
}, },
], ],
offer: [
{
storage: [
"data",
"tmp",
],
from: "parent",
to: "#realm_builder",
},
{
directory: "root-ssl-certificates",
from: "parent",
to: "#realm_builder",
},
// Offers for web_instance.cm.
// The chromium test realm offers the system-wide config-data dir to test
// components. Route the web_engine sub-directory of this as required by
// WebInstanceHost.
{
directory: "config-data",
from: "parent",
as: "config-data-for-web-instance",
to: "#realm_builder",
subdir: "web_engine",
availability: "optional",
},
{
protocol: [
"fuchsia.buildinfo.Provider",
"fuchsia.device.NameProvider",
"fuchsia.feedback.ComponentDataRegister",
"fuchsia.feedback.CrashReportingProductRegister",
"fuchsia.fonts.Provider",
"fuchsia.hwinfo.Product",
"fuchsia.input.virtualkeyboard.ControllerCreator",
"fuchsia.intl.PropertyProvider",
"fuchsia.kernel.VmexResource",
"fuchsia.media.Audio",
"fuchsia.media.AudioDeviceEnumerator",
"fuchsia.media.ProfileProvider",
"fuchsia.media.SessionAudioConsumerFactory",
"fuchsia.mediacodec.CodecFactory",
"fuchsia.memorypressure.Provider",
"fuchsia.net.interfaces.State",
"fuchsia.net.name.Lookup",
"fuchsia.posix.socket.Provider",
"fuchsia.process.Launcher",
"fuchsia.sysmem.Allocator",
"fuchsia.ui.input3.Keyboard",
],
from: "parent",
to: "#realm_builder",
},
// Used conditionally based on the value of `enable_widevine` at build time.
// TODO(crbug.com/1379411): Use a shard to conditionally use based on
// build-time config.
{
protocol: "fuchsia.media.drm.Widevine",
from: "parent",
to: "#realm_builder",
availability: "optional",
},
// Used conditionally based on the absence of `--headless` on the command
// line at runtime.
{
protocol: [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.tracing.provider.Registry",
"fuchsia.ui.composition.Allocator",
"fuchsia.ui.composition.Flatland",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic",
"fuchsia.vulkan.loader.Loader",
],
from: "parent",
to: "#realm_builder",
availability: "optional",
},
// Optional capabilities, dependent on availability of tracing services.
{
protocol: "fuchsia.tracing.perfetto.ProducerConnector",
from: "parent",
to: "#realm_builder",
availability: "optional",
},
],
facets: { facets: {
"fuchsia.test.deprecated-allowed-packages": [ "fuchsia.test": {
"web_engine", "deprecated-allowed-packages": [
"web_engine_with_webui", "test_manager",
], "web_engine",
"web_engine_with_webui",
],
},
}, },
} }

@@ -1,50 +0,0 @@
{
"program": {
"binary": "web_engine_shell_exec"
},
"sandbox": {
"features": [
"deprecated-ambient-replace-as-executable",
"isolated-persistent-storage",
"isolated-temp"
],
"services": [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.buildinfo.Provider",
"fuchsia.camera3.DeviceWatcher",
"fuchsia.device.NameProvider",
"fuchsia.feedback.ComponentDataRegister",
"fuchsia.feedback.CrashReportingProductRegister",
"fuchsia.fonts.Provider",
"fuchsia.hwinfo.Product",
"fuchsia.input.virtualkeyboard.ControllerCreator",
"fuchsia.intl.PropertyProvider",
"fuchsia.kernel.VmexResource",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
"fuchsia.media.AudioDeviceEnumerator",
"fuchsia.media.ProfileProvider",
"fuchsia.media.SessionAudioConsumerFactory",
"fuchsia.media.drm.Widevine",
"fuchsia.mediacodec.CodecFactory",
"fuchsia.memorypressure.Provider",
"fuchsia.net.interfaces.State",
"fuchsia.net.name.Lookup",
"fuchsia.posix.socket.Provider",
"fuchsia.process.Launcher",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.sys.Loader",
"fuchsia.sysmem.Allocator",
"fuchsia.tracing.perfetto.ProducerConnector",
"fuchsia.tracing.provider.Registry",
"fuchsia.ui.composition.Allocator",
"fuchsia.ui.composition.Flatland",
"fuchsia.ui.input3.Keyboard",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic",
"fuchsia.vulkan.loader.Loader",
"fuchsia.web.ContextProvider"
]
}
}

@@ -0,0 +1,47 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// A component that is launched by web_engine_shell since web_engine_shell
// itself runs as a test component, and therefore cannot run WebInstanceHost
// directly.
{
include: [
"//fuchsia_web/webinstance_host/web_instance_host.shard.cml",
"syslog/client.shard.cml",
],
program: {
runner: "elf",
binary: "web_engine_shell_exec",
args: [
// Inform web_engine_shell that it is running as the sub-process in which
// WebInstanceHost may be used. In this scenario, the main test component
// is running only as a launcher of this child.
"--from-launcher",
],
// Required to allow JIT in child processes such as renderers.
// Known as 'deprecated-ambient-replace-as-executable' in CFv1.
job_policy_ambient_mark_vmo_exec: "true",
},
use: [
// Required if not run with --headless.
{
protocol: "fuchsia.ui.policy.Presenter",
availability: "optional",
},
// Used to hold the cdm_data directory passed to web_instance.
{
storage: "data",
path: "/data",
},
// Needed when launched with --enable-web-instance-tmp.
{
storage: "tmp",
path: "/tmp",
availability: "optional",
},
],
}

@@ -491,6 +491,7 @@ fuchsia_package_installer("web_engine_installer") {
fuchsia_package("web_engine_with_webui") { fuchsia_package("web_engine_with_webui") {
# TODO(fxbug.dev/100944): Add appropriate visibility when fixed. # TODO(fxbug.dev/100944): Add appropriate visibility when fixed.
deps = [ deps = [
":web_instance_component",
":web_instance_component_cfv1", ":web_instance_component_cfv1",
":webui_resources", ":webui_resources",
] ]

@@ -77,6 +77,13 @@
path: "/config/command-line", path: "/config/command-line",
availability: "optional", availability: "optional",
}, },
// Temporary directory specified by WebInstanceHost.set_tmp_dir.
{
directory: "tmp",
path: "/tmp",
rights: [ "rw*" ],
availability: "optional",
},
{ {
// Required capabilities for all configurations. // Required capabilities for all configurations.
protocol: [ protocol: [
@@ -155,10 +162,5 @@
], ],
availability: "optional", availability: "optional",
}, },
{
storage: "cache",
path: "/cache",
availability: "optional",
},
] ]
} }

@@ -29,7 +29,10 @@ source_set("webinstance_host") {
"web_instance_host_internal.cc", "web_instance_host_internal.cc",
"web_instance_host_internal.h", "web_instance_host_internal.h",
] ]
public = [ "web_instance_host.h" ] public = [
"web_instance_host.h",
"web_instance_host_constants.h",
]
deps = [ deps = [
"//base:base_static", "//base:base_static",
"//build:chromecast_buildflags", "//build:chromecast_buildflags",

@@ -20,6 +20,7 @@
#include <vector> #include <vector>
#include "base/check.h" #include "base/check.h"
#include "base/containers/fixed_flat_map.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h" #include "base/fuchsia/process_context.h"
#include "base/logging.h" #include "base/logging.h"
@@ -32,6 +33,7 @@
#include "base/types/expected.h" #include "base/types/expected.h"
#include "components/fuchsia_component_support/serialize_arguments.h" #include "components/fuchsia_component_support/serialize_arguments.h"
#include "fuchsia_web/webengine/switches.h" #include "fuchsia_web/webengine/switches.h"
#include "fuchsia_web/webinstance_host/web_instance_host_constants.h"
#include "fuchsia_web/webinstance_host/web_instance_host_internal.h" #include "fuchsia_web/webinstance_host/web_instance_host_internal.h"
namespace { namespace {
@@ -43,6 +45,10 @@ namespace fcdecl = ::fuchsia::component::decl;
constexpr char kWebInstanceComponentUrl[] = constexpr char kWebInstanceComponentUrl[] =
"fuchsia-pkg://fuchsia.com/web_engine#meta/web_instance.cm"; "fuchsia-pkg://fuchsia.com/web_engine#meta/web_instance.cm";
// Test-only URL for web hosting Component instances with WebUI resources.
const char kWebInstanceWithWebUiComponentUrl[] =
"fuchsia-pkg://fuchsia.com/web_engine_with_webui#meta/web_instance.cm";
// The name of the component collection hosting the instances. // The name of the component collection hosting the instances.
constexpr char kCollectionName[] = "web_instances"; constexpr char kCollectionName[] = "web_instances";
@@ -58,7 +64,8 @@ std::string InstanceNameFromId(const base::GUID& id) {
return base::StrCat({kCollectionName, "_", id.AsLowercaseString()}); return base::StrCat({kCollectionName, "_", id.AsLowercaseString()});
} }
void DestroyChild(fuchsia::component::Realm& realm, const std::string& name) { void DestroyInstance(fuchsia::component::Realm& realm,
const std::string& name) {
realm.DestroyChild( realm.DestroyChild(
fcdecl::ChildRef{.name = name, .collection = kCollectionName}, fcdecl::ChildRef{.name = name, .collection = kCollectionName},
[](::fuchsia::component::Realm_DestroyChild_Result destroy_result) { [](::fuchsia::component::Realm_DestroyChild_Result destroy_result) {
@@ -67,8 +74,8 @@ void DestroyChild(fuchsia::component::Realm& realm, const std::string& name) {
}); });
} }
void DestroyChildDirectory(vfs::PseudoDir* instances_dir, void DestroyInstanceDirectory(vfs::PseudoDir* instances_dir,
const std::string& name) { const std::string& name) {
zx_status_t status = instances_dir->RemoveEntry(name); zx_status_t status = instances_dir->RemoveEntry(name);
ZX_DCHECK(status == ZX_OK, status); ZX_DCHECK(status == ZX_OK, status);
} }
@@ -138,13 +145,50 @@ class InstanceBuilder {
// directory. // directory.
void ServeCommandLine(); void ServeCommandLine();
// Serves `directory` as `name` in the instance's subtree as a read-only or // Adds offers from `void` for any offered directories that are not being
// a read-write (if `writeable`) directory. `name` is both the name of the // served for the invoker.
// directory and the name of the capability expected by the instance. void OfferMissingDirectoriesFromVoid();
void ServeDirectory(base::StringPiece name,
std::unique_ptr<vfs::internal::Directory> directory, // The directories that are optionally offered to `web_instance.cm` based on
// the invoker's configuration.
enum class OptionalDirectory {
kFirst = 0,
kCdmData = kFirst,
kCommandLineConfig,
kContentDirectories,
kData,
kTmp,
kCount,
};
// Returns a bitmask for `directory` for use with the `served_directories_`
// bitfield.
static uint32_t directory_bitmask(OptionalDirectory directory) {
return 1u << static_cast<int>(directory);
}
// Returns true if the host will serve `directory` to the instance.
bool is_directory_served(OptionalDirectory directory) const {
return served_directories_ & directory_bitmask(directory);
}
// Records that `directory` will be served to the instance.
void set_directory_served(OptionalDirectory directory) {
served_directories_ |= directory_bitmask(directory);
}
// Returns the capability and directory name for `directory`.
static base::StringPiece GetDirectoryName(OptionalDirectory directory);
// Serves `directory` as `offer` in the instance's subtree as a read-only or
// a read-write (if `writeable`) directory.
void ServeDirectory(OptionalDirectory directory,
std::unique_ptr<vfs::internal::Directory> fs_directory,
bool writeable); bool writeable);
// Offers the directory `directory` from `void`.
void OfferDirectoryFromVoid(OptionalDirectory directory);
// Offers the read-only directory capability named `name` from the parent. // Offers the read-only directory capability named `name` from the parent.
void OfferDirectoryFromParent(base::StringPiece name); void OfferDirectoryFromParent(base::StringPiece name);
@@ -153,6 +197,10 @@ class InstanceBuilder {
const std::string name_; const std::string name_;
raw_ptr<vfs::PseudoDir> instance_dir_; raw_ptr<vfs::PseudoDir> instance_dir_;
base::CommandLine args_; base::CommandLine args_;
// A bitfield of `directory_bitmask()` values indicating which optional
// directories are being served to the instance.
uint32_t served_directories_ = 0;
std::vector<fuchsia::component::decl::Offer> dynamic_offers_; std::vector<fuchsia::component::decl::Offer> dynamic_offers_;
fidl::InterfaceRequest<fuchsia::web::Debug> debug_request_; fidl::InterfaceRequest<fuchsia::web::Debug> debug_request_;
}; };
@@ -197,7 +245,7 @@ InstanceBuilder::InstanceBuilder(fuchsia::component::Realm& realm,
InstanceBuilder::~InstanceBuilder() { InstanceBuilder::~InstanceBuilder() {
if (instance_dir_) { if (instance_dir_) {
DestroyChildDirectory(GetWebInstancesCollectionDir(), name_); DestroyInstanceDirectory(GetWebInstancesCollectionDir(), name_);
} }
} }
@@ -222,7 +270,7 @@ void InstanceBuilder::ServeRootSslCertificates() {
void InstanceBuilder::ServeDataDirectory( void InstanceBuilder::ServeDataDirectory(
fidl::InterfaceHandle<fuchsia::io::Directory> data_directory) { fidl::InterfaceHandle<fuchsia::io::Directory> data_directory) {
DCHECK(instance_dir_); DCHECK(instance_dir_);
ServeDirectory("data", ServeDirectory(OptionalDirectory::kData,
std::make_unique<vfs::RemoteDir>(std::move(data_directory)), std::make_unique<vfs::RemoteDir>(std::move(data_directory)),
/*writeable=*/true); /*writeable=*/true);
} }
@@ -244,7 +292,8 @@ zx_status_t InstanceBuilder::ServeContentDirectories(
} }
} }
ServeDirectory("content-directories", std::move(content_dirs), ServeDirectory(OptionalDirectory::kContentDirectories,
std::move(content_dirs),
/*writeable=*/false); /*writeable=*/false);
return ZX_OK; return ZX_OK;
} }
@@ -253,13 +302,14 @@ void InstanceBuilder::ServeCdmDataDirectory(
fidl::InterfaceHandle<fuchsia::io::Directory> cdm_data_directory) { fidl::InterfaceHandle<fuchsia::io::Directory> cdm_data_directory) {
DCHECK(instance_dir_); DCHECK(instance_dir_);
ServeDirectory( ServeDirectory(
"cdm_data", OptionalDirectory::kCdmData,
std::make_unique<vfs::RemoteDir>(std::move(cdm_data_directory)), std::make_unique<vfs::RemoteDir>(std::move(cdm_data_directory)),
/*writeable=*/true); /*writeable=*/true);
} }
void InstanceBuilder::ServeTmpDirectory(fuchsia::io::DirectoryHandle tmp_dir) { void InstanceBuilder::ServeTmpDirectory(fuchsia::io::DirectoryHandle tmp_dir) {
ServeDirectory("tmp", std::make_unique<vfs::RemoteDir>(std::move(tmp_dir)), ServeDirectory(OptionalDirectory::kTmp,
std::make_unique<vfs::RemoteDir>(std::move(tmp_dir)),
/*writeable=*/true); /*writeable=*/true);
} }
@@ -272,9 +322,20 @@ Instance InstanceBuilder::Build(
fidl::InterfaceRequest<fuchsia::io::Directory> services_request) { fidl::InterfaceRequest<fuchsia::io::Directory> services_request) {
ServeCommandLine(); ServeCommandLine();
// Create dynamic offers from `void` for any optional directories
// expected by web_instance.cm that are not being provided by the invoker.
OfferMissingDirectoriesFromVoid();
fcdecl::Child child_decl; fcdecl::Child child_decl;
child_decl.set_name(name_); child_decl.set_name(name_);
child_decl.set_url(kWebInstanceComponentUrl); // TODO(crbug.com/1010222): Make kWebInstanceComponentUrl a relative
// component URL and remove this workaround.
// TODO(crbug.com/1395054): Better yet, replace the with_webui component with
// direct routing of the resources from web_engine_shell.
child_decl.set_url(
base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kWithWebui)
? kWebInstanceWithWebUiComponentUrl
: kWebInstanceComponentUrl);
child_decl.set_startup(fcdecl::StartupMode::LAZY); child_decl.set_startup(fcdecl::StartupMode::LAZY);
::fuchsia::component::CreateChildArgs create_child_args; ::fuchsia::component::CreateChildArgs create_child_args;
@@ -335,17 +396,48 @@ void InstanceBuilder::ServeCommandLine() {
})); }));
ZX_DCHECK(status == ZX_OK, status); ZX_DCHECK(status == ZX_OK, status);
ServeDirectory("command-line-config", std::move(config_dir), ServeDirectory(OptionalDirectory::kCommandLineConfig, std::move(config_dir),
/*writeable=*/false); /*writeable=*/false);
} }
void InstanceBuilder::OfferMissingDirectoriesFromVoid() {
for (auto directory = OptionalDirectory::kFirst;
directory != OptionalDirectory::kCount;
directory =
static_cast<OptionalDirectory>(static_cast<int>(directory) + 1)) {
if (!is_directory_served(directory)) {
OfferDirectoryFromVoid(directory);
}
}
}
// static
base::StringPiece InstanceBuilder::GetDirectoryName(
OptionalDirectory directory) {
static constexpr auto kNames =
base::MakeFixedFlatMap<OptionalDirectory, base::StringPiece>({
{OptionalDirectory::kCdmData, "cdm_data"},
{OptionalDirectory::kCommandLineConfig, "command-line-config"},
{OptionalDirectory::kContentDirectories, "content-directories"},
{OptionalDirectory::kData, "data"},
{OptionalDirectory::kTmp, "tmp"},
});
static_assert(kNames.size() == static_cast<int>(OptionalDirectory::kCount));
return kNames.at(directory);
}
void InstanceBuilder::ServeDirectory( void InstanceBuilder::ServeDirectory(
base::StringPiece name, OptionalDirectory directory,
std::unique_ptr<vfs::internal::Directory> directory, std::unique_ptr<vfs::internal::Directory> fs_directory,
bool writeable) { bool writeable) {
DCHECK(instance_dir_); DCHECK(instance_dir_);
DCHECK(!is_directory_served(directory));
set_directory_served(directory);
const auto name = GetDirectoryName(directory);
zx_status_t status = zx_status_t status =
instance_dir_->AddEntry(std::string(name), std::move(directory)); instance_dir_->AddEntry(std::string(name), std::move(fs_directory));
ZX_DCHECK(status == ZX_OK, status); ZX_DCHECK(status == ZX_OK, status);
dynamic_offers_.push_back(fcdecl::Offer::WithDirectory( dynamic_offers_.push_back(fcdecl::Offer::WithDirectory(
@@ -360,6 +452,22 @@ void InstanceBuilder::ServeDirectory(
.set_availability(fcdecl::Availability::REQUIRED)))); .set_availability(fcdecl::Availability::REQUIRED))));
} }
void InstanceBuilder::OfferDirectoryFromVoid(OptionalDirectory directory) {
DCHECK(!is_directory_served(directory));
// TODO(fxbug.dev/121722): Enable this once dynamic offer-from-void is
// supported in Fuchsia.
#if 0
const auto name = GetDirectoryName(directory);
dynamic_offers_.push_back(fcdecl::Offer::WithDirectory(
std::move(fcdecl::OfferDirectory()
.set_source(fcdecl::Ref::WithVoidType({}))
.set_target_name(std::string(name))
.set_dependency_type(fcdecl::DependencyType::STRONG)
.set_availability(fcdecl::Availability::OPTIONAL))));
#endif
}
void InstanceBuilder::OfferDirectoryFromParent(base::StringPiece name) { void InstanceBuilder::OfferDirectoryFromParent(base::StringPiece name) {
DCHECK(instance_dir_); DCHECK(instance_dir_);
dynamic_offers_.push_back(fcdecl::Offer::WithDirectory( dynamic_offers_.push_back(fcdecl::Offer::WithDirectory(
@@ -446,7 +554,7 @@ WebInstanceHost::WebInstanceHost() {
WebInstanceHost::~WebInstanceHost() { WebInstanceHost::~WebInstanceHost() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(instances_.empty()); Uninitialize();
} }
zx_status_t WebInstanceHost::CreateInstanceForContextWithCopiedArgs( zx_status_t WebInstanceHost::CreateInstanceForContextWithCopiedArgs(
@@ -530,28 +638,29 @@ void WebInstanceHost::Initialize() {
void WebInstanceHost::Uninitialize() { void WebInstanceHost::Uninitialize() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(instances_.empty());
// Destroy all child instances and each one's outgoing directory subtree.
auto* const instances_dir = GetWebInstancesCollectionDir();
for (auto& [id, binder_ptr] : instances_) {
const std::string name(InstanceNameFromId(id));
if (realm_) {
DestroyInstance(*realm_, name);
}
DestroyInstanceDirectory(instances_dir, name);
binder_ptr.Unbind();
}
instances_.clear();
realm_.Unbind(); realm_.Unbind();
// Note: the entry in the outgoing directory for the top-level instances dir // Note: the entry in the outgoing directory for the top-level instances dir
// is leaked in case multiple hosts are active in the same process. // is leaked in support of having multiple hosts active in a single process.
} }
void WebInstanceHost::OnRealmError(zx_status_t status) { void WebInstanceHost::OnRealmError(zx_status_t status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ZX_LOG(ERROR, status) << "RealmBuilder channel error"; ZX_LOG(ERROR, status) << "RealmBuilder channel error";
// Disconnect from all children and remove their directories.
auto* const instances_dir = GetWebInstancesCollectionDir();
for (auto& [id, binder_ptr] : instances_) {
DestroyChildDirectory(instances_dir, InstanceNameFromId(id));
binder_ptr.Unbind();
}
instances_.clear();
// Go back to the initial state.
Uninitialize(); Uninitialize();
} }
@@ -561,10 +670,10 @@ void WebInstanceHost::OnComponentBinderClosed(const base::GUID& id,
// Destroy the child instance. // Destroy the child instance.
const std::string name(InstanceNameFromId(id)); const std::string name(InstanceNameFromId(id));
DestroyChild(*realm_, name); DestroyInstance(*realm_, name);
// Drop the directory subtree for the child instance. // Drop the directory subtree for the child instance.
DestroyChildDirectory(GetWebInstancesCollectionDir(), name); DestroyInstanceDirectory(GetWebInstancesCollectionDir(), name);
// Drop the hold on the instance's Binder. Note: destroying the InterfacePtr // Drop the hold on the instance's Binder. Note: destroying the InterfacePtr
// here also deletes the lambda into which `id` was bound, so `id` must not // here also deletes the lambda into which `id` was bound, so `id` must not

@@ -77,8 +77,8 @@ class WebInstanceHost {
// Connects to the fuchsia.component/Realm protocol. // Connects to the fuchsia.component/Realm protocol.
void Initialize(); void Initialize();
// Unbinds from the fuchsia.component/Realm protocol. May only be called once // Destroys all child instances and associated resources and unbinds from the
// all web_instances have terminated. // fuchsia.component/Realm protocol.
void Uninitialize(); void Uninitialize();
// Error handler for the channel to RealmBuilder. // Error handler for the channel to RealmBuilder.

@@ -56,7 +56,6 @@
"fuchsia.memorypressure.Provider", "fuchsia.memorypressure.Provider",
"fuchsia.process.Launcher", "fuchsia.process.Launcher",
"fuchsia.sysmem.Allocator", "fuchsia.sysmem.Allocator",
"fuchsia.tracing.provider.Registry",
], ],
from: "parent", from: "parent",
to: "#web_instances", to: "#web_instances",
@@ -78,12 +77,6 @@
from: "parent", from: "parent",
to: "#web_instances", to: "#web_instances",
availability: "optional", availability: "optional",
source_availability: "unknown",
},
{
storage: "cache",
from: "parent",
to: "#web_instances",
}, },
], ],
} }

@@ -434,6 +434,8 @@ void AppendDynamicServices(fuchsia::web::ContextFeatureFlags features,
"fuchsia.media.AudioDeviceEnumerator"}, "fuchsia.media.AudioDeviceEnumerator"},
{ContextFeatureFlags::AUDIO, ContextFeatureFlags::AUDIO, {ContextFeatureFlags::AUDIO, ContextFeatureFlags::AUDIO,
"fuchsia.media.SessionAudioConsumerFactory"}, "fuchsia.media.SessionAudioConsumerFactory"},
{ContextFeatureFlags::VULKAN, ContextFeatureFlags::VULKAN,
"fuchsia.tracing.provider.Registry"},
{ContextFeatureFlags::VULKAN, ContextFeatureFlags::VULKAN, {ContextFeatureFlags::VULKAN, ContextFeatureFlags::VULKAN,
"fuchsia.vulkan.loader.Loader"}, "fuchsia.vulkan.loader.Loader"},
{ContextFeatureFlags::HARDWARE_VIDEO_DECODER, {ContextFeatureFlags::HARDWARE_VIDEO_DECODER,

@@ -146,7 +146,6 @@ std::vector<std::string> GetRequiredServicesForConfig(
"fuchsia.settings.Display", // Used if preferred theme is DEFAULT. "fuchsia.settings.Display", // Used if preferred theme is DEFAULT.
"fuchsia.sysmem.Allocator", "fuchsia.sysmem.Allocator",
"fuchsia.tracing.perfetto.ProducerConnector", "fuchsia.tracing.perfetto.ProducerConnector",
"fuchsia.tracing.provider.Registry",
"fuchsia.ui.scenic.Scenic"}; "fuchsia.ui.scenic.Scenic"};
// TODO(crbug.com/1209031): Provide these conditionally, once corresponding // TODO(crbug.com/1209031): Provide these conditionally, once corresponding
@@ -186,6 +185,7 @@ std::vector<std::string> GetRequiredServicesForConfig(
if ((features & fuchsia::web::ContextFeatureFlags::VULKAN) == if ((features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
fuchsia::web::ContextFeatureFlags::VULKAN) { fuchsia::web::ContextFeatureFlags::VULKAN) {
services.emplace_back("fuchsia.tracing.provider.Registry");
services.emplace_back("fuchsia.vulkan.loader.Loader"); services.emplace_back("fuchsia.vulkan.loader.Loader");
} }