blob: 58a0fdd667ce10b57ea83bec4eb89f1e78642242 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fuchsia/runners/cast/cast_runner.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <fuchsia/web/cpp/fidl.h>
#include <memory>
#include <string>
#include <utility>
#include "base/fuchsia/file_utils.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "url/gurl.h"
namespace {
bool AreCastComponentParamsValid(
const CastComponent::CastComponentParams& params) {
if (params.app_config.IsEmpty())
return false;
if (!params.api_bindings_client->HasBindings())
return false;
if (!params.rewrite_rules.has_value())
return false;
return true;
}
// Creates a CreateContextParams object which can be used as a basis
// for starting isolated Runners.
fuchsia::web::CreateContextParams BuildCreateContextParamsForIsolatedRunners(
const fuchsia::web::CreateContextParams& create_context_params) {
fuchsia::web::CreateContextParams output;
// Isolated contexts receive only a limited set of features.
output.set_features(
fuchsia::web::ContextFeatureFlags::AUDIO |
fuchsia::web::ContextFeatureFlags::VULKAN |
fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER);
if (create_context_params.has_user_agent_product()) {
output.set_user_agent_product(create_context_params.user_agent_product());
}
if (create_context_params.has_user_agent_version()) {
output.set_user_agent_version(create_context_params.user_agent_version());
}
if (create_context_params.has_remote_debugging_port()) {
output.set_remote_debugging_port(
create_context_params.remote_debugging_port());
}
return output;
}
} // namespace
CastRunner::CastRunner(sys::OutgoingDirectory* outgoing_directory,
fuchsia::web::CreateContextParams create_context_params)
: WebContentRunner(outgoing_directory,
base::BindOnce(&CastRunner::CreateCastRunnerWebContext,
base::Unretained(this))),
create_context_params_(std::move(create_context_params)),
common_create_context_params_(
BuildCreateContextParamsForIsolatedRunners(create_context_params_)) {}
CastRunner::CastRunner(OnDestructionCallback on_destruction_callback,
fuchsia::web::ContextPtr context)
: WebContentRunner(std::move(context)),
on_destruction_callback_(std::move(on_destruction_callback)) {}
CastRunner::~CastRunner() = default;
fuchsia::web::ContextPtr CastRunner::CreateCastRunnerWebContext() {
return WebContentRunner::CreateWebContext(std::move(create_context_params_));
}
void CastRunner::StartComponent(
fuchsia::sys::Package package,
fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController>
controller_request) {
// Verify that |package| specifies a Cast URI, and pull the app-Id from it.
constexpr char kCastPresentationUrlScheme[] = "cast";
constexpr char kCastSecurePresentationUrlScheme[] = "casts";
GURL cast_url(package.resolved_url);
if (!cast_url.is_valid() ||
(!cast_url.SchemeIs(kCastPresentationUrlScheme) &&
!cast_url.SchemeIs(kCastSecurePresentationUrlScheme)) ||
cast_url.GetContent().empty()) {
LOG(ERROR) << "Rejected invalid URL: " << cast_url;
return;
}
// The application configuration is obtained asynchronously via the
// per-component ApplicationConfigManager. The pointer to that service must be
// kept live until the request completes or CastRunner is deleted.
auto pending_component =
std::make_unique<CastComponent::CastComponentParams>();
pending_component->startup_context =
std::make_unique<base::fuchsia::StartupContext>(std::move(startup_info));
pending_component->agent_manager = std::make_unique<cr_fuchsia::AgentManager>(
pending_component->startup_context->component_context()->svc().get());
pending_component->controller_request = std::move(controller_request);
// Get binding details from the Agent.
fidl::InterfaceHandle<chromium::cast::ApiBindings> api_bindings_client;
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl, api_bindings_client.NewRequest());
pending_component->api_bindings_client = std::make_unique<ApiBindingsClient>(
std::move(api_bindings_client),
base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this),
base::Unretained(pending_component.get())),
base::BindOnce(&CastRunner::CancelComponentLaunch, base::Unretained(this),
base::Unretained(pending_component.get())));
// Get UrlRequestRewriteRulesProvider from the Agent.
fidl::InterfaceHandle<chromium::cast::UrlRequestRewriteRulesProvider>
url_request_rules_provider;
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl, url_request_rules_provider.NewRequest());
pending_component->rewrite_rules_provider = url_request_rules_provider.Bind();
pending_component->rewrite_rules_provider.set_error_handler(
[this, pending_component = pending_component.get()](zx_status_t status) {
ZX_LOG(ERROR, status) << "UrlRequestRewriteRulesProvider disconnected.";
CancelComponentLaunch(pending_component);
});
pending_component->rewrite_rules_provider->GetUrlRequestRewriteRules(
[this, pending_component = pending_component.get()](
std::vector<fuchsia::web::UrlRequestRewriteRule> rewrite_rules) {
pending_component->rewrite_rules =
base::Optional<std::vector<fuchsia::web::UrlRequestRewriteRule>>(
std::move(rewrite_rules));
MaybeStartComponent(pending_component);
});
// Request the configuration for the specified application.
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl, pending_component->app_config_manager.NewRequest());
pending_component->app_config_manager.set_error_handler(
[this, pending_component = pending_component.get()](zx_status_t status) {
ZX_LOG(ERROR, status) << "ApplicationConfigManager disconnected.";
CancelComponentLaunch(pending_component);
});
const std::string cast_app_id(cast_url.GetContent());
pending_component->app_config_manager->GetConfig(
cast_app_id, [this, pending_component = pending_component.get()](
chromium::cast::ApplicationConfig app_config) {
GetConfigCallback(pending_component, std::move(app_config));
});
pending_components_.emplace(std::move(pending_component));
}
void CastRunner::DestroyComponent(WebComponent* component) {
WebContentRunner::DestroyComponent(component);
if (on_destruction_callback_) {
// |this| may be deleted and should not be used after this line.
std::move(on_destruction_callback_).Run(this);
return;
}
}
const char CastRunner::kAgentComponentUrl[] =
"fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx";
void CastRunner::GetConfigCallback(
CastComponent::CastComponentParams* pending_component,
chromium::cast::ApplicationConfig app_config) {
auto it = pending_components_.find(pending_component);
DCHECK(it != pending_components_.end());
if (app_config.IsEmpty()) {
pending_components_.erase(it);
DLOG(WARNING) << "No application config was found.";
return;
}
if (!app_config.has_web_url()) {
pending_components_.erase(it);
DLOG(WARNING) << "Only web-based applications are supported.";
return;
}
pending_component->app_config = std::move(app_config);
MaybeStartComponent(pending_component);
}
void CastRunner::MaybeStartComponent(
CastComponent::CastComponentParams* pending_component_params) {
if (!AreCastComponentParamsValid(*pending_component_params))
return;
// The runner which will host the newly created CastComponent.
CastRunner* component_owner = this;
if (pending_component_params->app_config
.has_content_directories_for_isolated_application()) {
// Create a isolated, isolated CastRunner instance which will own the
// CastComponent.
component_owner =
CreateChildRunnerForIsolatedComponent(pending_component_params);
}
component_owner->CreateAndRegisterCastComponent(
std::move(*pending_component_params));
pending_components_.erase(pending_component_params);
}
void CastRunner::CancelComponentLaunch(
CastComponent::CastComponentParams* params) {
size_t count = pending_components_.erase(params);
DCHECK_EQ(count, 1u);
}
void CastRunner::CreateAndRegisterCastComponent(
CastComponent::CastComponentParams params) {
GURL app_url = GURL(params.app_config.web_url());
auto cast_component =
std::make_unique<CastComponent>(this, std::move(params));
cast_component->StartComponent();
cast_component->LoadUrl(std::move(app_url),
std::vector<fuchsia::net::http::Header>());
RegisterComponent(std::move(cast_component));
}
CastRunner* CastRunner::CreateChildRunnerForIsolatedComponent(
CastComponent::CastComponentParams* params) {
// Construct the CreateContextParams in order to create a new Context.
// Some common parameters must be inherited from
// |common_create_context_params_|.
fuchsia::web::CreateContextParams isolated_context_params;
zx_status_t status =
common_create_context_params_.Clone(&isolated_context_params);
ZX_CHECK(status == ZX_OK, status) << "clone";
isolated_context_params.set_service_directory(base::fuchsia::OpenDirectory(
base::FilePath(base::fuchsia::kServiceDirectoryPath)));
isolated_context_params.set_content_directories(
std::move(*params->app_config
.mutable_content_directories_for_isolated_application()));
std::unique_ptr<CastRunner> cast_runner(
new CastRunner(base::BindOnce(&CastRunner::OnChildRunnerDestroyed,
base::Unretained(this)),
CreateWebContext(std::move(isolated_context_params))));
// If test code is listening for Component creation events, then wire up the
// isolated CastRunner to signal component creation events.
if (web_component_created_callback_for_test()) {
cast_runner->SetWebComponentCreatedCallbackForTest(
web_component_created_callback_for_test());
}
CastRunner* cast_runner_ptr = cast_runner.get();
isolated_runners_.insert(std::move(cast_runner));
return cast_runner_ptr;
}
void CastRunner::OnChildRunnerDestroyed(CastRunner* runner) {
auto runner_iterator = isolated_runners_.find(runner);
DCHECK(runner_iterator != isolated_runners_.end());
isolated_runners_.erase(runner_iterator);
}
size_t CastRunner::GetChildCastRunnerCountForTest() {
return isolated_runners_.size();
}