blob: 32c636b9d1b0848aac3f7a5b115851b5673b1cf0 [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 <memory>
#include <string>
#include <utility>
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/startup_context.h"
#include "base/logging.h"
#include "fuchsia/base/agent_manager.h"
#include "fuchsia/runners/cast/cast_component.h"
#include "url/gurl.h"
CastRunner::CastRunner(base::fuchsia::ServiceDirectory* service_directory,
fuchsia::web::ContextPtr context)
: WebContentRunner(service_directory, std::move(context)) {}
CastRunner::~CastRunner() = default;
struct CastRunner::PendingComponent {
chromium::cast::ApplicationConfigManagerPtr app_config_manager;
std::unique_ptr<base::fuchsia::StartupContext> startup_context;
std::unique_ptr<cr_fuchsia::AgentManager> agent_manager;
std::unique_ptr<ApiBindingsClient> bindings_manager;
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request;
chromium::cast::ApplicationConfig app_config;
};
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 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<PendingComponent>();
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->incoming_services());
pending_component->controller_request = std::move(controller_request);
// 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.";
GetConfigCallback(pending_component,
chromium::cast::ApplicationConfig());
});
// 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->bindings_manager = std::make_unique<ApiBindingsClient>(
std::move(api_bindings_client),
base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this),
base::Unretained(pending_component.get())));
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));
}
const char CastRunner::kAgentComponentUrl[] =
"fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx";
void CastRunner::GetConfigCallback(
PendingComponent* pending_component,
chromium::cast::ApplicationConfig app_config) {
// Ideally the PendingComponent would be move()d out of |pending_components_|
// here, but that requires extract(), which isn't available until C++17.
// Instead find |pending_component| and move() the individual fields out
// before erase()ing it.
auto it = pending_components_.find(pending_component);
DCHECK(it != pending_components_.end());
// If no configuration was returned then ignore the request.
if (!app_config.has_web_url()) {
pending_components_.erase(it);
DLOG(WARNING) << "No ApplicationConfig was found.";
return;
}
pending_component->app_config = std::move(app_config);
MaybeStartComponent(pending_component);
}
void CastRunner::MaybeStartComponent(PendingComponent* pending_component) {
// Called after the completion of GetConfigCallback() or
// ApiBindingsClient::OnBindingsReceived().
if (pending_component->app_config.IsEmpty() ||
!pending_component->bindings_manager->HasBindings()) {
// Don't proceed unless both the application config and API bindings are
// received.
return;
}
// Create a component based on the returned configuration, and pass it the
// fields stashed in PendingComponent.
GURL cast_app_url(pending_component->app_config.web_url());
auto component = std::make_unique<CastComponent>(
this, std::move(pending_component->app_config),
std::move(pending_component->bindings_manager),
std::move(pending_component->startup_context),
std::move(pending_component->controller_request),
std::move(pending_component->agent_manager));
pending_components_.erase(pending_component);
component->LoadUrl(std::move(cast_app_url));
RegisterComponent(std::move(component));
}