Commit 60626592 authored by Wez's avatar Wez Committed by Commit Bot

[fuchsia] Implement fuchsia.web.FrameHost meta-component in CastRunner.

CastRunner now supports a pseudo app-Id, "fuchsia.web.FrameHost", which
creates a meta-component that publishes the fuchsia.web.FrameHost API.

Requests made through this API are routed to the CastRunner's "main"
web.Context, allowing it to be shared with selected peer components on
the system.

Bug: b/170476111, 1120914, 1144102
Change-Id: I34d8cfbd22ed8bdf975f2a36d0bde0d508bba92b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2476216
Commit-Queue: Wez <wez@chromium.org>
Reviewed-by: default avatarDavid Dorwin <ddorwin@chromium.org>
Auto-Submit: Wez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822590}
parent 3fb4876c
......@@ -43,7 +43,7 @@ class BASE_EXPORT StartupContext {
const sys::ServiceDirectory* svc() const {
return component_context_->svc().get();
}
const sys::OutgoingDirectory* outgoing() const {
sys::OutgoingDirectory* outgoing() const {
return component_context_->outgoing().get();
}
......
......@@ -36,7 +36,7 @@ fidl::InterfaceHandle<fuchsia::io::Directory> StartWebEngineForTestsInternal(
if (!is_for_logging_test) {
// Clone stderr from the current process to WebEngine and ask it to
// redirects all logs to stderr.
// redirect all logs to stderr.
launch_info.err = fuchsia::sys::FileDescriptor::New();
launch_info.err->type0 = PA_FD;
zx_status_t status = fdio_fd_clone(
......
......@@ -70,6 +70,48 @@ bool IsPermissionGrantedInAppConfig(
// Ephemeral remote debugging port used by child contexts.
const uint16_t kEphemeralRemoteDebuggingPort = 0;
// TODO(crbug.com/1120914): Remove this once Component Framework v2 can be
// used to route fuchsia.web.FrameHost capabilities cleanly.
class FrameHostComponent : public fuchsia::sys::ComponentController {
public:
// Creates a FrameHostComponent with lifetime managed by |controller_request|.
static void Start(fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController>
controller_request,
fuchsia::web::FrameHost* frame_host_impl) {
new FrameHostComponent(std::move(startup_info),
std::move(controller_request), frame_host_impl);
}
private:
FrameHostComponent(fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController>
controller_request,
fuchsia::web::FrameHost* frame_host_impl)
: startup_context_(std::move(startup_info)),
frame_host_binding_(startup_context_.outgoing(), frame_host_impl) {
startup_context_.ServeOutgoingDirectory();
binding_.Bind(std::move(controller_request));
binding_.set_error_handler([this](zx_status_t) { Kill(); });
}
~FrameHostComponent() final = default;
// fuchsia::sys::ComponentController interface.
void Kill() final { delete this; }
void Detach() final {
binding_.Close(ZX_ERR_NOT_SUPPORTED);
delete this;
}
base::fuchsia::StartupContext startup_context_;
const base::fuchsia::ScopedServiceBinding<fuchsia::web::FrameHost>
frame_host_binding_;
fidl::Binding<fuchsia::sys::ComponentController> binding_{this};
};
// Application URL for the pseudo-component providing fuchsia.web.FrameHost.
constexpr char kFrameHostComponentName[] = "cast:fuchsia.web.FrameHost";
} // namespace
CastRunner::CastRunner(bool is_headless)
......@@ -123,16 +165,22 @@ void CastRunner::StartComponent(
return;
}
// TODO(crbug.com/1120914): Remove this once Component Framework v2 can be
// used to route fuchsia.web.FrameHost capabilities cleanly.
if (enable_frame_host_component_ &&
(cast_url.spec() == kFrameHostComponentName)) {
FrameHostComponent::Start(std::move(startup_info),
std::move(controller_request),
main_context_.get());
return;
}
pending_components_.emplace(std::make_unique<PendingCastComponent>(
this,
std::make_unique<base::fuchsia::StartupContext>(std::move(startup_info)),
std::move(controller_request), cast_url.GetContent()));
}
fuchsia::web::FrameHost* CastRunner::main_context_frame_host() const {
return main_context_.get();
}
void CastRunner::LaunchPendingComponent(PendingCastComponent* pending_component,
CastComponent::Params params) {
// Save the list of CORS exemptions so that they can be used in Context
......@@ -367,10 +415,17 @@ void CastRunner::OnCameraServiceRequest(
void CastRunner::OnMetricsRecorderServiceRequest(
fidl::InterfaceRequest<fuchsia::legacymetrics::MetricsRecorder> request) {
// TODO(https://crbug.com/1065707): Remove this hack once Runners are using
// TODO(crbug.com/1120914): Allow for the FrameHostComponent being created
// before any Cast components are launched in the |main_context_|.
WebComponent* any_component = main_context_->GetAnyComponent();
if (!any_component) {
LOG(WARNING) << "Ignoring MetricsRecorder request.";
return;
}
// TODO(crbug.com/1065707): Remove this hack once Runners are using
// Component Framework v2.
CastComponent* component =
reinterpret_cast<CastComponent*>(main_context_->GetAnyComponent());
CastComponent* component = reinterpret_cast<CastComponent*>(any_component);
DCHECK(component);
component->startup_context()->svc()->Connect(std::move(request));
......
......@@ -52,9 +52,11 @@ class CastRunner : public fuchsia::sys::Runner,
fidl::InterfaceRequest<fuchsia::sys::ComponentController>
controller_request) final;
// Returns a fuchsia.web.FrameHost interface to the main web.Context used to
// host non-isolated Cast applications.
fuchsia::web::FrameHost* main_context_frame_host() const;
// Enables the special component that provides the fuchsia.web.FrameHost API,
// hosted using the same WebEngine instance as the main web.Context.
void set_enable_frame_host_component() {
enable_frame_host_component_ = true;
}
// Disables use of the VULKAN feature when creating Contexts. Must be set
// before calling StartComponent().
......@@ -123,6 +125,9 @@ class CastRunner : public fuchsia::sys::Runner,
base::UniquePtrComparator>
pending_components_;
// True if this Runner should offer the fuchsia.web.FrameHost component.
bool enable_frame_host_component_ = false;
// List of HTTP headers to exempt from CORS checks.
std::vector<std::vector<uint8_t>> cors_exempt_headers_;
......
......@@ -60,6 +60,8 @@ constexpr char kTestServerRoot[] = "fuchsia/runners/cast/testdata";
constexpr char kDummyAgentUrl[] =
"fuchsia-pkg://fuchsia.com/dummy_agent#meta/dummy_agent.cmx";
constexpr char kEnableFrameHostComponent[] = "enable-frame-host-component";
class FakeUrlRequestRewriteRulesProvider
: public chromium::cast::UrlRequestRewriteRulesProvider {
public:
......@@ -199,10 +201,16 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase {
base::OnceClosure on_delete_;
};
enum CastRunnerFeatures {
kCastRunnerFeaturesNone = 0,
kCastRunnerFeaturesHeadless = 1,
kCastRunnerFeaturesVulkan = 1 << 1,
kCastRunnerFeaturesFrameHost = 1 << 2
};
sys::ServiceDirectory StartCastRunner(
fidl::InterfaceHandle<fuchsia::io::Directory> web_engine_host_directory,
bool enable_headless,
bool enable_vulkan,
CastRunnerFeatures runner_features,
fidl::InterfaceRequest<fuchsia::sys::ComponentController>
component_controller_request) {
fuchsia::sys::LaunchInfo launch_info;
......@@ -218,10 +226,14 @@ sys::ServiceDirectory StartCastRunner(
ZX_CHECK(status == ZX_OK, status);
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitchASCII("enable-logging", "stderr");
if (enable_headless)
if (runner_features & kCastRunnerFeaturesHeadless)
command_line.AppendSwitch(kForceHeadlessForTestsSwitch);
if (!enable_vulkan)
if (!(runner_features & kCastRunnerFeaturesVulkan))
command_line.AppendSwitch(kDisableVulkanForTestsSwitch);
if (runner_features & kCastRunnerFeaturesFrameHost)
command_line.AppendSwitch(kEnableFrameHostComponent);
launch_info.arguments.emplace(std::vector<std::string>(
command_line.argv().begin() + 1, command_line.argv().end()));
......@@ -249,8 +261,8 @@ sys::ServiceDirectory StartCastRunner(
class CastRunnerIntegrationTest : public testing::Test {
public:
CastRunnerIntegrationTest()
: CastRunnerIntegrationTest(/*enable_headless=*/false,
/*enable_vulkan=*/false) {}
: CastRunnerIntegrationTest(kCastRunnerFeaturesNone) {}
CastRunnerIntegrationTest(const CastRunnerIntegrationTest&) = delete;
CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) =
delete;
......@@ -261,7 +273,7 @@ class CastRunnerIntegrationTest : public testing::Test {
}
protected:
explicit CastRunnerIntegrationTest(bool enable_headless, bool enable_vulkan)
explicit CastRunnerIntegrationTest(CastRunnerFeatures runner_features)
: app_config_manager_binding_(&component_services_,
&app_config_manager_) {
StartAndPublishWebEngine();
......@@ -272,8 +284,8 @@ class CastRunnerIntegrationTest : public testing::Test {
::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
incoming_services.NewRequest().TakeChannel());
sys::ServiceDirectory cast_runner_services =
StartCastRunner(std::move(incoming_services), enable_headless,
enable_vulkan, cast_runner_controller_.NewRequest());
StartCastRunner(std::move(incoming_services), runner_features,
cast_runner_controller_.NewRequest());
// Connect to the CastRunner's fuchsia.sys.Runner interface.
cast_runner_ = cast_runner_services.Connect<fuchsia::sys::Runner>();
......@@ -465,10 +477,12 @@ class CastRunnerIntegrationTest : public testing::Test {
void ShutdownComponent() {
DCHECK(component_controller_);
if (component_state_) {
base::RunLoop run_loop;
component_state_->set_on_delete(run_loop.QuitClosure());
component_controller_.Unbind();
run_loop.Run();
}
component_controller_ = nullptr;
}
......@@ -887,8 +901,7 @@ TEST_F(CastRunnerIntegrationTest, CameraAccessAfterComponentShutdown) {
class HeadlessCastRunnerIntegrationTest : public CastRunnerIntegrationTest {
public:
HeadlessCastRunnerIntegrationTest()
: CastRunnerIntegrationTest(/*enable_headless=*/true,
/*enable_vulkan=*/false) {}
: CastRunnerIntegrationTest(kCastRunnerFeaturesHeadless) {}
};
// A basic integration test ensuring a basic cast request launches the right
......@@ -1053,6 +1066,34 @@ TEST_F(CastRunnerIntegrationTest,
EXPECT_EQ(ExecuteJavaScript("document.title"), "absent");
}
class CastRunnerFrameHostIntegrationTest : public CastRunnerIntegrationTest {
public:
CastRunnerFrameHostIntegrationTest()
: CastRunnerIntegrationTest(kCastRunnerFeaturesFrameHost) {}
};
// Verifies that the CastRunner offers a fuchsia.web.FrameHost service.
// TODO(crbug.com/1144102): Clean up config-data vs command-line flags handling
// and add a not-enabled test here.
TEST_F(CastRunnerFrameHostIntegrationTest, FrameHostComponent) {
constexpr char kFrameHostComponentName[] = "cast:fuchsia.web.FrameHost";
StartCastComponent(kFrameHostComponentName);
// Connect to the fuchsia.web.FrameHost service and create a Frame.
auto frame_host =
component_services_client_->Connect<fuchsia::web::FrameHost>();
fuchsia::web::FramePtr frame;
frame_host->CreateFrameWithParams(fuchsia::web::CreateFrameParams(),
frame.NewRequest());
// Verify that a response is received for a LoadUrl() request to the frame.
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
const GURL url = test_server_.GetURL(kBlankAppUrl);
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
controller.get(), fuchsia::web::LoadUrlParams(), url.spec()));
}
#if defined(ARCH_CPU_ARM_FAMILY)
// TODO(crbug.com/1058247): Support Vulkan in tests on ARM64.
#define MAYBE_VulkanCastRunnerIntegrationTest \
......@@ -1064,8 +1105,7 @@ TEST_F(CastRunnerIntegrationTest,
class MAYBE_VulkanCastRunnerIntegrationTest : public CastRunnerIntegrationTest {
public:
MAYBE_VulkanCastRunnerIntegrationTest()
: CastRunnerIntegrationTest(/*enable_headless=*/false,
/*enable_vulkan=*/true) {}
: CastRunnerIntegrationTest(kCastRunnerFeaturesVulkan) {}
};
TEST_F(MAYBE_VulkanCastRunnerIntegrationTest,
......
......@@ -28,29 +28,17 @@ constexpr char kCrashProductName[] = "FuchsiaCastRunner";
constexpr char kComponentUrl[] =
"fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx";
bool IsHeadless() {
constexpr char kHeadlessConfigKey[] = "headless";
// Config-data key for launching Cast content without using Scenic.
constexpr char kHeadlessConfigKey[] = "headless";
// In tests headless mode can be enabled with a command-line flag.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
kForceHeadlessForTestsSwitch)) {
return true;
}
// Config-data key to enable the fuchsia.web.FrameHost provider component.
constexpr char kFrameHostConfigKey[] = "enable-frame-host-component";
// Returns the value of |config_key| or false if it is not set.
bool GetConfigBool(base::StringPiece config_key) {
const base::Optional<base::Value>& config = cr_fuchsia::LoadPackageConfig();
if (config)
return config->FindBoolPath(kHeadlessConfigKey).value_or(false);
return false;
}
bool AllowMainContextSharing() {
constexpr char kAllowMainContextSharing[] = "enable-main-context-sharing";
const base::Optional<base::Value>& config = cr_fuchsia::LoadPackageConfig();
if (config)
return config->FindBoolPath(kAllowMainContextSharing).value_or(false);
return config->FindBoolPath(config_key).value_or(false);
return false;
}
......@@ -63,8 +51,9 @@ int main(int argc, char** argv) {
kCrashProductName);
base::CommandLine::Init(argc, argv);
CHECK(cr_fuchsia::InitLoggingFromCommandLine(
*base::CommandLine::ForCurrentProcess()))
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
CHECK(cr_fuchsia::InitLoggingFromCommandLine(*command_line))
<< "Failed to initialize logging.";
cr_fuchsia::RegisterFuchsiaDirScheme();
......@@ -73,22 +62,23 @@ int main(int argc, char** argv) {
base::ComponentContextForProcess()->outgoing().get();
// Publish the fuchsia.web.Runner implementation for Cast applications.
CastRunner runner(IsHeadless());
const bool enable_headless =
command_line->HasSwitch(kForceHeadlessForTestsSwitch) ||
GetConfigBool(kHeadlessConfigKey);
CastRunner runner(enable_headless);
base::fuchsia::ScopedServiceBinding<fuchsia::sys::Runner> binding(
outgoing_directory, &runner);
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
kDisableVulkanForTestsSwitch)) {
if (command_line->HasSwitch(kDisableVulkanForTestsSwitch)) {
runner.set_disable_vulkan_for_test(); // IN-TEST
}
// Optionally publish the fuchsia.web.FrameHost service, to allow the Cast
// application web.Context to be shared by other components.
base::Optional<base::fuchsia::ScopedServiceBinding<fuchsia::web::FrameHost>>
frame_host_binding;
if (AllowMainContextSharing()) {
frame_host_binding.emplace(outgoing_directory,
runner.main_context_frame_host());
// Optionally enable a pseudo-component providing the fuchsia.web.FrameHost
// service, to allow the Cast application web.Context to be shared by other
// components.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kFrameHostConfigKey) ||
GetConfigBool(kFrameHostConfigKey)) {
runner.set_enable_frame_host_component();
}
outgoing_directory->ServeFromStartupInfo();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment