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/command_line.h"
#include "base/ranges/algorithm.h"
using ::component_testing::ChildRef;
using ::component_testing::Directory;
@@ -22,24 +23,61 @@ using ::component_testing::Route;
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,
base::StringPiece child_name,
const base::CommandLine& command_line) {
const std::string child_name_str(child_name);
auto context_provider_decl = realm_builder.GetComponentDecl(child_name_str);
for (auto& entry : *context_provider_decl.mutable_program()
->mutable_info()
->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;
}
}
auto child_component_decl = realm_builder.GetComponentDecl(child_name_str);
AppendCommandLineArgumentsToProgram(*child_component_decl.mutable_program(),
command_line);
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,

@@ -24,6 +24,12 @@ void AppendCommandLineArguments(
base::StringPiece child_name,
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
// 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

@@ -39,9 +39,13 @@ fuchsia_component("web_engine_shell_component") {
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
manifest = "web_engine_shell.cmx"
manifest = "web_engine_shell_for_web_instance_host.cml"
data_deps = [ ":web_engine_shell_exec" ]
}
@@ -52,7 +56,7 @@ fuchsia_package("web_engine_shell_pkg") {
package_name = "web_engine_shell"
deps = [
":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",
"//base",
"//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.web:fuchsia.web_hlcpp",
"//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/sys_component_cpp_testing",
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
"//third_party/widevine/cdm:buildflags",
"//url",

@@ -12,8 +12,7 @@ To build and run WebEngine Shell, execute the following commands:
$ autoninja -C $OUTDIR web_engine_shell
$ $OUTDIR/bin/deploy_web_engine_shell --fuchsia-out-dir $FUCHSIA_OUTDIR
$ cd $FUCHSIA
$ fx shell
$ run fuchsia-pkg://fuchsia.com/web_engine_shell#meta/web_engine_shell.cmx --remote-debugging-port=1234 http://www.example.com
$ ffx test run fuchsia-pkg://fuchsia.com/web_engine_shell#meta/web_engine_shell.cm -- --remote-debugging-port=1234 http://www.example.com
```
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:
```
$ 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
// found in the LICENSE file.
#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/ui/policy/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.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/service_directory.h>
#include <lib/ui/scenic/cpp/view_token_pair.h>
#include <lib/vfs/cpp/pseudo_file.h>
#include <iostream>
#include <utility>
#include "base/base_paths.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/fuchsia/file_utils.h"
@@ -27,9 +32,10 @@
#include "base/values.h"
#include "build/build_config.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/webinstance_host/web_instance_host.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 "url/gurl.h"
@@ -38,6 +44,9 @@ namespace {
constexpr char kHeadlessSwitch[] = "headless";
constexpr char kEnableProtectedMediaIdentifier[] =
"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 kEnableWebInstanceTmp[] = "enable-web-instance-tmp";
@@ -69,17 +78,51 @@ GURL GetUrlFromArgs(const base::CommandLine::StringVector& args) {
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
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);
// Parse the command line arguments and set up logging.
CHECK(base::CommandLine::Init(argc, argv));
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
CHECK(InitLoggingFromCommandLineDefaultingToStderrForTest( // IN-TEST
command_line));
const bool is_run_from_launcher = command_line->HasSwitch(kFromLauncher);
const bool use_context_provider = !command_line->HasSwitch(kUseWebInstance);
if (!is_run_from_launcher && !use_context_provider) {
return RelaunchForWebInstanceHost(*command_line);
}
absl::optional<uint16_t> remote_debugging_port =
GetRemoteDebuggingPort(*command_line);
@@ -91,7 +134,6 @@ int main(int argc, char** argv) {
const bool is_headless = command_line->HasSwitch(kHeadlessSwitch);
const bool enable_protected_media_identifier_access =
command_line->HasSwitch(kEnableProtectedMediaIdentifier);
const bool use_context_provider = !command_line->HasSwitch(kUseWebInstance);
const bool enable_web_instance_tmp =
command_line->HasSwitch(kEnableWebInstanceTmp);
const bool with_webui = command_line->HasSwitch(switches::kWithWebui);
@@ -173,12 +215,9 @@ int main(int argc, char** argv) {
base::RunLoop run_loop;
// Create the browser |context|.
fuchsia::web::ContextPtr context;
// Keep alive in run_loop scope.
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;
if (use_context_provider) {
@@ -189,7 +228,7 @@ int main(int argc, char** argv) {
web_context_provider->Create(std::move(create_context_params),
context.NewRequest());
} else {
web_instance_host = std::make_unique<WebInstanceHostV1>();
web_instance_host = std::make_unique<WebInstanceHost>();
if (enable_web_instance_tmp) {
const zx_status_t status = fdio_open(
"/tmp",
@@ -290,6 +329,8 @@ int main(int argc, char** argv) {
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
// Engine interfaces are dropped.
run_loop.Run();

@@ -10,38 +10,36 @@
// to function correctly.
"//build/config/fuchsia/test/chromium_test_facet.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",
],
program: {
binary: "web_engine_shell_exec",
},
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
// command line at runtime.
{
protocol: "fuchsia.web.ContextProvider",
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",
},
// 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: [
"fuchsia.buildinfo.Provider",
@@ -93,10 +91,103 @@
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: {
"fuchsia.test.deprecated-allowed-packages": [
"web_engine",
"web_engine_with_webui",
],
"fuchsia.test": {
"deprecated-allowed-packages": [
"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") {
# TODO(fxbug.dev/100944): Add appropriate visibility when fixed.
deps = [
":web_instance_component",
":web_instance_component_cfv1",
":webui_resources",
]

@@ -77,6 +77,13 @@
path: "/config/command-line",
availability: "optional",
},
// Temporary directory specified by WebInstanceHost.set_tmp_dir.
{
directory: "tmp",
path: "/tmp",
rights: [ "rw*" ],
availability: "optional",
},
{
// Required capabilities for all configurations.
protocol: [
@@ -155,10 +162,5 @@
],
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.h",
]
public = [ "web_instance_host.h" ]
public = [
"web_instance_host.h",
"web_instance_host_constants.h",
]
deps = [
"//base:base_static",
"//build:chromecast_buildflags",

@@ -20,6 +20,7 @@
#include <vector>
#include "base/check.h"
#include "base/containers/fixed_flat_map.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "base/logging.h"
@@ -32,6 +33,7 @@
#include "base/types/expected.h"
#include "components/fuchsia_component_support/serialize_arguments.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"
namespace {
@@ -43,6 +45,10 @@ namespace fcdecl = ::fuchsia::component::decl;
constexpr char kWebInstanceComponentUrl[] =
"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.
constexpr char kCollectionName[] = "web_instances";
@@ -58,7 +64,8 @@ std::string InstanceNameFromId(const base::GUID& id) {
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(
fcdecl::ChildRef{.name = name, .collection = kCollectionName},
[](::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,
const std::string& name) {
void DestroyInstanceDirectory(vfs::PseudoDir* instances_dir,
const std::string& name) {
zx_status_t status = instances_dir->RemoveEntry(name);
ZX_DCHECK(status == ZX_OK, status);
}
@@ -138,13 +145,50 @@ class InstanceBuilder {
// directory.
void ServeCommandLine();
// Serves `directory` as `name` in the instance's subtree as a read-only or
// a read-write (if `writeable`) directory. `name` is both the name of the
// directory and the name of the capability expected by the instance.
void ServeDirectory(base::StringPiece name,
std::unique_ptr<vfs::internal::Directory> directory,
// Adds offers from `void` for any offered directories that are not being
// served for the invoker.
void OfferMissingDirectoriesFromVoid();
// 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);
// Offers the directory `directory` from `void`.
void OfferDirectoryFromVoid(OptionalDirectory directory);
// Offers the read-only directory capability named `name` from the parent.
void OfferDirectoryFromParent(base::StringPiece name);
@@ -153,6 +197,10 @@ class InstanceBuilder {
const std::string name_;
raw_ptr<vfs::PseudoDir> instance_dir_;
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_;
fidl::InterfaceRequest<fuchsia::web::Debug> debug_request_;
};
@@ -197,7 +245,7 @@ InstanceBuilder::InstanceBuilder(fuchsia::component::Realm& realm,
InstanceBuilder::~InstanceBuilder() {
if (instance_dir_) {
DestroyChildDirectory(GetWebInstancesCollectionDir(), name_);
DestroyInstanceDirectory(GetWebInstancesCollectionDir(), name_);
}
}
@@ -222,7 +270,7 @@ void InstanceBuilder::ServeRootSslCertificates() {
void InstanceBuilder::ServeDataDirectory(
fidl::InterfaceHandle<fuchsia::io::Directory> data_directory) {
DCHECK(instance_dir_);
ServeDirectory("data",
ServeDirectory(OptionalDirectory::kData,
std::make_unique<vfs::RemoteDir>(std::move(data_directory)),
/*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);
return ZX_OK;
}
@@ -253,13 +302,14 @@ void InstanceBuilder::ServeCdmDataDirectory(
fidl::InterfaceHandle<fuchsia::io::Directory> cdm_data_directory) {
DCHECK(instance_dir_);
ServeDirectory(
"cdm_data",
OptionalDirectory::kCdmData,
std::make_unique<vfs::RemoteDir>(std::move(cdm_data_directory)),
/*writeable=*/true);
}
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);
}
@@ -272,9 +322,20 @@ Instance InstanceBuilder::Build(
fidl::InterfaceRequest<fuchsia::io::Directory> services_request) {
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;
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);
::fuchsia::component::CreateChildArgs create_child_args;
@@ -335,17 +396,48 @@ void InstanceBuilder::ServeCommandLine() {
}));
ZX_DCHECK(status == ZX_OK, status);
ServeDirectory("command-line-config", std::move(config_dir),
ServeDirectory(OptionalDirectory::kCommandLineConfig, std::move(config_dir),
/*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(
base::StringPiece name,
std::unique_ptr<vfs::internal::Directory> directory,
OptionalDirectory directory,
std::unique_ptr<vfs::internal::Directory> fs_directory,
bool writeable) {
DCHECK(instance_dir_);
DCHECK(!is_directory_served(directory));
set_directory_served(directory);
const auto name = GetDirectoryName(directory);
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);
dynamic_offers_.push_back(fcdecl::Offer::WithDirectory(
@@ -360,6 +452,22 @@ void InstanceBuilder::ServeDirectory(
.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) {
DCHECK(instance_dir_);
dynamic_offers_.push_back(fcdecl::Offer::WithDirectory(
@@ -446,7 +554,7 @@ WebInstanceHost::WebInstanceHost() {
WebInstanceHost::~WebInstanceHost() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(instances_.empty());
Uninitialize();
}
zx_status_t WebInstanceHost::CreateInstanceForContextWithCopiedArgs(
@@ -530,28 +638,29 @@ void WebInstanceHost::Initialize() {
void WebInstanceHost::Uninitialize() {
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();
// 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) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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();
}
@@ -561,10 +670,10 @@ void WebInstanceHost::OnComponentBinderClosed(const base::GUID& id,
// Destroy the child instance.
const std::string name(InstanceNameFromId(id));
DestroyChild(*realm_, name);
DestroyInstance(*realm_, name);
// 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
// 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.
void Initialize();
// Unbinds from the fuchsia.component/Realm protocol. May only be called once
// all web_instances have terminated.
// Destroys all child instances and associated resources and unbinds from the
// fuchsia.component/Realm protocol.
void Uninitialize();
// Error handler for the channel to RealmBuilder.

@@ -56,7 +56,6 @@
"fuchsia.memorypressure.Provider",
"fuchsia.process.Launcher",
"fuchsia.sysmem.Allocator",
"fuchsia.tracing.provider.Registry",
],
from: "parent",
to: "#web_instances",
@@ -78,12 +77,6 @@
from: "parent",
to: "#web_instances",
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"},
{ContextFeatureFlags::AUDIO, ContextFeatureFlags::AUDIO,
"fuchsia.media.SessionAudioConsumerFactory"},
{ContextFeatureFlags::VULKAN, ContextFeatureFlags::VULKAN,
"fuchsia.tracing.provider.Registry"},
{ContextFeatureFlags::VULKAN, ContextFeatureFlags::VULKAN,
"fuchsia.vulkan.loader.Loader"},
{ContextFeatureFlags::HARDWARE_VIDEO_DECODER,

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