Commit f9dea821 authored by Kevin Marshall's avatar Kevin Marshall Committed by Commit Bot

[Fuchsia] Add ApiBindingsClient to CastRunner.

Implements a component which receives API bindings from the Agent
and injects them into the current Cast application, and connects ports
pushed from the Cast application to the Agent.

Modifies CastComponent to only trigger NamedMessagePortConnector port
injection once a page is fully loaded.

Bug: 953958
Change-Id: I11f7a64bf5b0db4bfd341ea8b6b1ffb03c5b2ffb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1601563
Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
Reviewed-by: default avatarFabrice de Gans-Riberi <fdegans@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660633}
parent ad953fc1
...@@ -32,6 +32,8 @@ source_set("common") { ...@@ -32,6 +32,8 @@ source_set("common") {
source_set("cast_runner_core") { source_set("cast_runner_core") {
sources = [ sources = [
"cast/api_bindings_client.cc",
"cast/api_bindings_client.h",
"cast/cast_channel_bindings.cc", "cast/cast_channel_bindings.cc",
"cast/cast_channel_bindings.h", "cast/cast_channel_bindings.h",
"cast/cast_component.cc", "cast/cast_component.cc",
...@@ -103,6 +105,8 @@ source_set("cast_runner_test_core") { ...@@ -103,6 +105,8 @@ source_set("cast_runner_test_core") {
"cast/fake_application_config_manager.h", "cast/fake_application_config_manager.h",
"cast/fake_queryable_data.cc", "cast/fake_queryable_data.cc",
"cast/fake_queryable_data.h", "cast/fake_queryable_data.h",
"cast/test_api_bindings.cc",
"cast/test_api_bindings.h",
] ]
deps = [ deps = [
":cast_runner_core", ":cast_runner_core",
...@@ -140,6 +144,7 @@ test("cast_runner_integration_tests") { ...@@ -140,6 +144,7 @@ test("cast_runner_integration_tests") {
test("cast_runner_browsertests") { test("cast_runner_browsertests") {
sources = [ sources = [
"cast/api_bindings_client_browsertest.cc",
"cast/cast_channel_bindings_browsertest.cc", "cast/cast_channel_bindings_browsertest.cc",
"cast/named_message_port_connector_browsertest.cc", "cast/named_message_port_connector_browsertest.cc",
"cast/not_implemented_api_bindings_browsertest.cc", "cast/not_implemented_api_bindings_browsertest.cc",
......
// Copyright 2019 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/api_bindings_client.h"
#include <utility>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/strings/string_piece.h"
namespace {
uint64_t kBindingsIdStart = 0xFF0000;
} // namespace
ApiBindingsClient::ApiBindingsClient(
fidl::InterfaceHandle<chromium::cast::ApiBindings> bindings_service,
base::OnceClosure on_bindings_received_callback)
: bindings_service_(bindings_service.Bind()),
on_bindings_received_callback_(std::move(on_bindings_received_callback)) {
DCHECK(bindings_service_);
DCHECK(on_bindings_received_callback_);
bindings_service_->GetAll(
fit::bind_member(this, &ApiBindingsClient::OnBindingsReceived));
bindings_service_.set_error_handler([this](zx_status_t status) mutable {
ZX_LOG_IF(ERROR, status != ZX_OK, status)
<< "ApiBindings disconnected before bindings were read.";
if (!bindings_) {
// The Agent disconnected before sending a bindings response,
// so it's possible that the Agent doesn't yet implement the ApiBindings
// service. Populate the bindings with an empty set so initialization may
// continue.
// TODO(crbug.com/953958): Remove this fallback once the Agent implements
// ApiBindings.
LOG(WARNING)
<< "Couldn't receive bindings from Agent, proceeding anyway.";
OnBindingsReceived({});
}
});
}
void ApiBindingsClient::OnBindingsReceived(
std::vector<chromium::cast::ApiBinding> bindings) {
DCHECK(on_bindings_received_callback_);
bindings_ = std::move(bindings);
std::move(on_bindings_received_callback_).Run();
}
void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame,
NamedMessagePortConnector* connector,
base::OnceClosure on_error_callback) {
DCHECK(!frame_) << "AttachToFrame() was called twice.";
DCHECK(frame);
DCHECK(connector);
DCHECK(bindings_)
<< "AttachToFrame() was called before bindings were received.";
connector_ = connector;
frame_ = frame;
bindings_service_.set_error_handler([on_error_callback =
std::move(on_error_callback)](
zx_status_t status) mutable {
ZX_LOG_IF(ERROR, status != ZX_OK, status) << "ApiBindings disconnected.";
std::move(on_error_callback).Run();
});
connector_->RegisterDefaultHandler(base::BindRepeating(
&ApiBindingsClient::OnPortConnected, base::Unretained(this)));
// Enumerate and inject all scripts in |bindings|.
uint64_t bindings_id = kBindingsIdStart;
for (chromium::cast::ApiBinding& entry : *bindings_) {
frame_->AddBeforeLoadJavaScript(
bindings_id++, {"*"}, std::move(*entry.mutable_before_load_script()),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
CHECK(result.is_response()) << "JavaScript injection error: "
<< static_cast<uint32_t>(result.err());
});
}
}
ApiBindingsClient::~ApiBindingsClient() {
if (connector_ && frame_) {
connector_->RegisterDefaultHandler({});
// Remove all injected scripts using their automatically enumerated IDs.
for (uint64_t i = 0; i < bindings_->size(); ++i)
frame_->RemoveBeforeLoadJavaScript(kBindingsIdStart + i);
}
}
void ApiBindingsClient::OnPortConnected(
base::StringPiece port_name,
fidl::InterfaceHandle<fuchsia::web::MessagePort> port) {
if (bindings_service_)
bindings_service_->Connect(port_name.as_string(), std::move(port));
}
bool ApiBindingsClient::HasBindings() const {
return bindings_.has_value();
}
// Copyright 2019 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.
#ifndef FUCHSIA_RUNNERS_CAST_API_BINDINGS_CLIENT_H_
#define FUCHSIA_RUNNERS_CAST_API_BINDINGS_CLIENT_H_
#include <fuchsia/web/cpp/fidl.h>
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
#include "fuchsia/runners/cast/named_message_port_connector.h"
// Injects scripts received from the ApiBindings service, and provides connected
// ports to the Agent.
class ApiBindingsClient {
public:
// Reads bindings definitions from |bindings_service_| at construction time.
// Invokes |on_bindings_received_callback| once the definitions are received,
// after which the client may invoke AttachToFrame().
ApiBindingsClient(
fidl::InterfaceHandle<chromium::cast::ApiBindings> bindings_service,
base::OnceClosure on_bindings_received_callback);
~ApiBindingsClient();
// Injects APIs and handles channel connections on |frame|.
// |on_error_closure|: Invoked in the event of an unrecoverable error (e.g.
// lost connection to the Agent). The callback must
// remain valid for the entire lifetime of |this|.
void AttachToFrame(fuchsia::web::Frame* frame,
NamedMessagePortConnector* connector,
base::OnceClosure on_error_callback);
// Indicates that bindings were successfully received from
// |bindings_service_|.
bool HasBindings() const;
private:
// Called when ApiBindings::GetAll() has responded.
void OnBindingsReceived(std::vector<chromium::cast::ApiBinding> bindings);
// Called when |connector_| has connected a port.
void OnPortConnected(base::StringPiece port_name,
fidl::InterfaceHandle<fuchsia::web::MessagePort> port);
base::Optional<std::vector<chromium::cast::ApiBinding>> bindings_;
fuchsia::web::Frame* frame_ = nullptr;
NamedMessagePortConnector* connector_ = nullptr;
chromium::cast::ApiBindingsPtr bindings_service_;
base::OnceClosure on_bindings_received_callback_;
DISALLOW_COPY_AND_ASSIGN(ApiBindingsClient);
};
#endif // FUCHSIA_RUNNERS_CAST_API_BINDINGS_CLIENT_H_
// 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/web/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include "base/test/bind_test_util.h"
#include "base/test/test_timeouts.h"
#include "fuchsia/base/fit_adapter.h"
#include "fuchsia/base/frame_test_util.h"
#include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/base/result_receiver.h"
#include "fuchsia/base/test_navigation_listener.h"
#include "fuchsia/engine/test/web_engine_browser_test.h"
#include "fuchsia/runners/cast/api_bindings_client.h"
#include "fuchsia/runners/cast/test_api_bindings.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest {
public:
ApiBindingsClientTest()
: api_service_binding_(&api_service_),
run_timeout_(TestTimeouts::action_timeout(),
base::MakeExpectedNotRunClosure(FROM_HERE)) {
set_test_server_root(base::FilePath("fuchsia/runners/cast/testdata"));
}
~ApiBindingsClientTest() override = default;
protected:
void StartClient() {
base::ScopedAllowBlockingForTesting allow_blocking;
// Get the bindings from |api_service_|.
base::RunLoop run_loop;
client_ = std::make_unique<ApiBindingsClient>(
api_service_binding_.NewBinding(), run_loop.QuitClosure());
EXPECT_FALSE(client_->HasBindings());
run_loop.Run();
EXPECT_TRUE(client_->HasBindings());
frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_);
frame_->GetNavigationController(controller_.NewRequest());
connector_ = std::make_unique<NamedMessagePortConnector>(frame_.get());
client_->AttachToFrame(frame_.get(), connector_.get(),
base::MakeExpectedNotRunClosure(FROM_HERE));
}
void SetUpOnMainThread() override {
cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
}
void TearDownOnMainThread() override {
// Destroy |client_| before the MessageLoop is destroyed.
client_.reset();
}
fuchsia::web::FramePtr frame_;
std::unique_ptr<NamedMessagePortConnector> connector_;
TestApiBindings api_service_;
fidl::Binding<chromium::cast::ApiBindings> api_service_binding_;
std::unique_ptr<ApiBindingsClient> client_;
cr_fuchsia::TestNavigationListener navigation_listener_;
fuchsia::web::NavigationControllerPtr controller_;
private:
const base::RunLoop::ScopedRunTimeoutForTest run_timeout_;
DISALLOW_COPY_AND_ASSIGN(ApiBindingsClientTest);
};
IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) {
std::vector<chromium::cast::ApiBinding> binding_list;
chromium::cast::ApiBinding echo_binding;
echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString(
"window.echo = cast.__platform__.PortConnector.bind('echoService');"));
binding_list.emplace_back(std::move(echo_binding));
api_service_.set_bindings(std::move(binding_list));
StartClient();
const GURL test_url = embedded_test_server()->GetURL("/echo.html");
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
controller_.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
navigation_listener_.RunUntilUrlEquals(test_url);
connector_->OnPageLoad();
fuchsia::web::MessagePortPtr port =
api_service_.RunUntilMessagePortReceived("echoService").Bind();
fuchsia::web::WebMessage message;
message.set_data(cr_fuchsia::MemBufferFromString("ping"));
port->PostMessage(std::move(message),
[](fuchsia::web::MessagePort_PostMessage_Result result) {
EXPECT_TRUE(result.is_response());
});
base::RunLoop response_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> response(
response_loop.QuitClosure());
port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(response.GetReceiveCallback()));
response_loop.Run();
std::string response_string;
EXPECT_TRUE(
cr_fuchsia::StringFromMemBuffer(response->data(), &response_string));
EXPECT_EQ("ack ping", response_string);
}
} // namespace
...@@ -26,18 +26,15 @@ const char kMessagePortName[] = "cast.__platform__.channel"; ...@@ -26,18 +26,15 @@ const char kMessagePortName[] = "cast.__platform__.channel";
CastChannelBindings::CastChannelBindings( CastChannelBindings::CastChannelBindings(
fuchsia::web::Frame* frame, fuchsia::web::Frame* frame,
NamedMessagePortConnector* connector, NamedMessagePortConnector* connector,
chromium::cast::CastChannelPtr channel_consumer, chromium::cast::CastChannelPtr channel_consumer)
base::OnceClosure on_error_closure)
: frame_(frame), : frame_(frame),
connector_(connector), connector_(connector),
on_error_closure_(std::move(on_error_closure)),
channel_consumer_(std::move(channel_consumer)) { channel_consumer_(std::move(channel_consumer)) {
DCHECK(connector_); DCHECK(connector_);
DCHECK(frame_); DCHECK(frame_);
channel_consumer_.set_error_handler([this](zx_status_t status) mutable { channel_consumer_.set_error_handler([](zx_status_t status) mutable {
ZX_LOG(ERROR, status) << " Agent disconnected"; ZX_LOG(ERROR, status) << "Cast Channel FIDL client disconnected";
std::move(on_error_closure_).Run();
}); });
connector->Register( connector->Register(
......
...@@ -23,15 +23,11 @@ class CastChannelBindings { ...@@ -23,15 +23,11 @@ class CastChannelBindings {
// |frame|: The frame to be provided with a CastChannel. // |frame|: The frame to be provided with a CastChannel.
// |connector|: The NamedMessagePortConnector to use for establishing // |connector|: The NamedMessagePortConnector to use for establishing
// transport. // transport.
// |on_error_closure|: Invoked in the event of an unrecoverable error (e.g.
// lost connection to the Agent). The callback must
// remain valid for the entire lifetime of |this|.
// |channel_consumer|: A FIDL service which receives opened Cast Channels. // |channel_consumer|: A FIDL service which receives opened Cast Channels.
// Both |frame| and |connector| must outlive |this|. // Both |frame| and |connector| must outlive |this|.
CastChannelBindings(fuchsia::web::Frame* frame, CastChannelBindings(fuchsia::web::Frame* frame,
NamedMessagePortConnector* connector, NamedMessagePortConnector* connector,
chromium::cast::CastChannelPtr channel_consumer, chromium::cast::CastChannelPtr channel_consumer);
base::OnceClosure on_error_closure);
~CastChannelBindings(); ~CastChannelBindings();
private: private:
...@@ -65,8 +61,6 @@ class CastChannelBindings { ...@@ -65,8 +61,6 @@ class CastChannelBindings {
fuchsia::mem::Buffer bindings_script_; fuchsia::mem::Buffer bindings_script_;
base::OnceClosure on_error_closure_;
// The service which will receive connected Cast Channels. // The service which will receive connected Cast Channels.
chromium::cast::CastChannelPtr channel_consumer_; chromium::cast::CastChannelPtr channel_consumer_;
......
...@@ -129,8 +129,7 @@ IN_PROC_BROWSER_TEST_F(CastChannelBindingsTest, CastChannelBufferedInput) { ...@@ -129,8 +129,7 @@ IN_PROC_BROWSER_TEST_F(CastChannelBindingsTest, CastChannelBufferedInput) {
frame_->GetNavigationController(controller.NewRequest()); frame_->GetNavigationController(controller.NewRequest());
CastChannelBindings cast_channel_instance( CastChannelBindings cast_channel_instance(
frame_.get(), connector_.get(), receiver_binding_.NewBinding().Bind(), frame_.get(), connector_.get(), receiver_binding_.NewBinding().Bind());
base::MakeExpectedNotRunClosure(FROM_HERE));
// Verify that CastChannelBindings can properly handle message, connect, // Verify that CastChannelBindings can properly handle message, connect,
// disconnect, and MessagePort disconnection events. // disconnect, and MessagePort disconnection events.
...@@ -156,8 +155,7 @@ IN_PROC_BROWSER_TEST_F(CastChannelBindingsTest, CastChannelReconnect) { ...@@ -156,8 +155,7 @@ IN_PROC_BROWSER_TEST_F(CastChannelBindingsTest, CastChannelReconnect) {
frame_->GetNavigationController(controller.NewRequest()); frame_->GetNavigationController(controller.NewRequest());
CastChannelBindings cast_channel_instance( CastChannelBindings cast_channel_instance(
frame_.get(), connector_.get(), receiver_binding_.NewBinding().Bind(), frame_.get(), connector_.get(), receiver_binding_.NewBinding().Bind());
base::MakeExpectedNotRunClosure(FROM_HERE));
// Verify that CastChannelBindings can properly handle message, connect, // Verify that CastChannelBindings can properly handle message, connect,
// disconnect, and MessagePort disconnection events. // disconnect, and MessagePort disconnection events.
......
...@@ -41,6 +41,7 @@ TouchInputPolicy TouchInputPolicyFromApplicationConfig( ...@@ -41,6 +41,7 @@ TouchInputPolicy TouchInputPolicyFromApplicationConfig(
CastComponent::CastComponent( CastComponent::CastComponent(
CastRunner* runner, CastRunner* runner,
chromium::cast::ApplicationConfig application_config, chromium::cast::ApplicationConfig application_config,
std::unique_ptr<ApiBindingsClient> api_bindings_client,
std::unique_ptr<base::fuchsia::StartupContext> context, std::unique_ptr<base::fuchsia::StartupContext> context,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> fidl::InterfaceRequest<fuchsia::sys::ComponentController>
controller_request, controller_request,
...@@ -51,6 +52,7 @@ CastComponent::CastComponent( ...@@ -51,6 +52,7 @@ CastComponent::CastComponent(
touch_input_policy_( touch_input_policy_(
TouchInputPolicyFromApplicationConfig(application_config_)), TouchInputPolicyFromApplicationConfig(application_config_)),
connector_(frame()), connector_(frame()),
api_bindings_client_(std::move(api_bindings_client)),
navigation_listener_binding_(this) { navigation_listener_binding_(this) {
base::AutoReset<bool> constructor_active_reset(&constructor_active_, true); base::AutoReset<bool> constructor_active_reset(&constructor_active_, true);
...@@ -59,6 +61,11 @@ CastComponent::CastComponent( ...@@ -59,6 +61,11 @@ CastComponent::CastComponent(
frame()->SetNavigationEventListener( frame()->SetNavigationEventListener(
navigation_listener_binding_.NewBinding()); navigation_listener_binding_.NewBinding());
api_bindings_client_->AttachToFrame(
frame(), &connector_,
base::BindOnce(&CastComponent::DestroyComponent, base::Unretained(this),
kBindingsFailureExitCode,
fuchsia::sys::TerminationReason::INTERNAL_ERROR));
} }
CastComponent::~CastComponent() = default; CastComponent::~CastComponent() = default;
...@@ -96,10 +103,7 @@ void CastComponent::InitializeCastPlatformBindings() { ...@@ -96,10 +103,7 @@ void CastComponent::InitializeCastPlatformBindings() {
cast_channel_ = std::make_unique<CastChannelBindings>( cast_channel_ = std::make_unique<CastChannelBindings>(
frame(), &connector_, frame(), &connector_,
agent_manager_->ConnectToAgentService<chromium::cast::CastChannel>( agent_manager_->ConnectToAgentService<chromium::cast::CastChannel>(
CastRunner::kAgentComponentUrl), CastRunner::kAgentComponentUrl));
base::BindOnce(&CastComponent::DestroyComponent, base::Unretained(this),
kBindingsFailureExitCode,
fuchsia::sys::TerminationReason::INTERNAL_ERROR));
queryable_data_ = std::make_unique<QueryableDataBindings>( queryable_data_ = std::make_unique<QueryableDataBindings>(
frame(), frame(),
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/fuchsia/service_directory.h" #include "base/fuchsia/service_directory.h"
#include "fuchsia/base/agent_manager.h" #include "fuchsia/base/agent_manager.h"
#include "fuchsia/runners/cast/api_bindings_client.h"
#include "fuchsia/runners/cast/cast_channel_bindings.h" #include "fuchsia/runners/cast/cast_channel_bindings.h"
#include "fuchsia/runners/cast/named_message_port_connector.h" #include "fuchsia/runners/cast/named_message_port_connector.h"
#include "fuchsia/runners/cast/queryable_data_bindings.h" #include "fuchsia/runners/cast/queryable_data_bindings.h"
...@@ -24,6 +25,7 @@ class CastComponent : public WebComponent, ...@@ -24,6 +25,7 @@ class CastComponent : public WebComponent,
public: public:
CastComponent(CastRunner* runner, CastComponent(CastRunner* runner,
chromium::cast::ApplicationConfig application_config, chromium::cast::ApplicationConfig application_config,
std::unique_ptr<ApiBindingsClient> bindings_manager,
std::unique_ptr<base::fuchsia::StartupContext> startup_context, std::unique_ptr<base::fuchsia::StartupContext> startup_context,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> fidl::InterfaceRequest<fuchsia::sys::ComponentController>
controller_request, controller_request,
...@@ -31,6 +33,7 @@ class CastComponent : public WebComponent, ...@@ -31,6 +33,7 @@ class CastComponent : public WebComponent,
~CastComponent() override; ~CastComponent() override;
private: private:
// TODO(crbug.com/953958): Remove this.
void InitializeCastPlatformBindings(); void InitializeCastPlatformBindings();
// WebComponent overrides. // WebComponent overrides.
...@@ -52,6 +55,7 @@ class CastComponent : public WebComponent, ...@@ -52,6 +55,7 @@ class CastComponent : public WebComponent,
std::unique_ptr<CastChannelBindings> cast_channel_; std::unique_ptr<CastChannelBindings> cast_channel_;
std::unique_ptr<QueryableDataBindings> queryable_data_; std::unique_ptr<QueryableDataBindings> queryable_data_;
std::unique_ptr<TouchInputBindings> touch_input_; std::unique_ptr<TouchInputBindings> touch_input_;
std::unique_ptr<ApiBindingsClient> api_bindings_client_;
fidl::Binding<fuchsia::web::NavigationEventListener> fidl::Binding<fuchsia::web::NavigationEventListener>
navigation_listener_binding_; navigation_listener_binding_;
......
...@@ -26,7 +26,9 @@ struct CastRunner::PendingComponent { ...@@ -26,7 +26,9 @@ struct CastRunner::PendingComponent {
chromium::cast::ApplicationConfigManagerPtr app_config_manager; chromium::cast::ApplicationConfigManagerPtr app_config_manager;
std::unique_ptr<base::fuchsia::StartupContext> startup_context; std::unique_ptr<base::fuchsia::StartupContext> startup_context;
std::unique_ptr<cr_fuchsia::AgentManager> agent_manager; std::unique_ptr<cr_fuchsia::AgentManager> agent_manager;
std::unique_ptr<ApiBindingsClient> bindings_manager;
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request; fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request;
chromium::cast::ApplicationConfig app_config;
}; };
void CastRunner::StartComponent( void CastRunner::StartComponent(
...@@ -67,6 +69,15 @@ void CastRunner::StartComponent( ...@@ -67,6 +69,15 @@ void CastRunner::StartComponent(
chromium::cast::ApplicationConfig()); 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()); const std::string cast_app_id(cast_url.GetContent());
pending_component->app_config_manager->GetConfig( pending_component->app_config_manager->GetConfig(
cast_app_id, [this, pending_component = pending_component.get()]( cast_app_id, [this, pending_component = pending_component.get()](
...@@ -97,15 +108,31 @@ void CastRunner::GetConfigCallback( ...@@ -97,15 +108,31 @@ void CastRunner::GetConfigCallback(
return; 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 // Create a component based on the returned configuration, and pass it the
// fields stashed in PendingComponent. // fields stashed in PendingComponent.
GURL cast_app_url(app_config.web_url()); GURL cast_app_url(pending_component->app_config.web_url());
auto component = std::make_unique<CastComponent>( auto component = std::make_unique<CastComponent>(
this, std::move(app_config), this, std::move(pending_component->app_config),
std::move(pending_component->bindings_manager),
std::move(pending_component->startup_context), std::move(pending_component->startup_context),
std::move(pending_component->controller_request), std::move(pending_component->controller_request),
std::move(pending_component->agent_manager)); std::move(pending_component->agent_manager));
pending_components_.erase(it); pending_components_.erase(pending_component);
component->LoadUrl(std::move(cast_app_url)); component->LoadUrl(std::move(cast_app_url));
RegisterComponent(std::move(component)); RegisterComponent(std::move(component));
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
...@@ -37,6 +38,11 @@ class CastRunner : public WebContentRunner { ...@@ -37,6 +38,11 @@ class CastRunner : public WebContentRunner {
void GetConfigCallback(PendingComponent* pending_component, void GetConfigCallback(PendingComponent* pending_component,
chromium::cast::ApplicationConfig app_config); chromium::cast::ApplicationConfig app_config);
void GetBindingsCallback(PendingComponent* pending_component,
std::vector<chromium::cast::ApiBinding> bindings);
// Starts a component once all configuration data is available.
void MaybeStartComponent(PendingComponent* pending_component);
// Holds StartComponent() requests while the ApplicationConfig is being // Holds StartComponent() requests while the ApplicationConfig is being
// fetched from the ApplicationConfigManager. // fetched from the ApplicationConfigManager.
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "fuchsia/base/result_receiver.h" #include "fuchsia/base/result_receiver.h"
#include "fuchsia/runners/cast/cast_runner.h" #include "fuchsia/runners/cast/cast_runner.h"
#include "fuchsia/runners/cast/fake_application_config_manager.h" #include "fuchsia/runners/cast/fake_application_config_manager.h"
#include "fuchsia/runners/cast/test_api_bindings.h"
#include "fuchsia/runners/common/web_component.h" #include "fuchsia/runners/common/web_component.h"
#include "fuchsia/runners/common/web_content_runner.h" #include "fuchsia/runners/common/web_content_runner.h"
#include "net/test/embedded_test_server/default_handlers.h" #include "net/test/embedded_test_server/default_handlers.h"
...@@ -78,10 +79,16 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { ...@@ -78,10 +79,16 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase {
public: public:
FakeComponentState( FakeComponentState(
base::StringPiece component_url, base::StringPiece component_url,
chromium::cast::ApplicationConfigManager* app_config_manager) chromium::cast::ApplicationConfigManager* app_config_manager,
chromium::cast::ApiBindings* bindings_manager)
: ComponentStateBase(component_url), : ComponentStateBase(component_url),
app_config_binding_(service_directory(), app_config_manager), app_config_binding_(service_directory(), app_config_manager),
cast_channel_(std::make_unique<FakeCastChannel>(service_directory())) {} cast_channel_(std::make_unique<FakeCastChannel>(service_directory())) {
if (bindings_manager)
bindings_manager_binding_ = std::make_unique<
base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>>(
service_directory(), bindings_manager);
}
~FakeComponentState() override { ~FakeComponentState() override {
if (on_delete_) if (on_delete_)
std::move(on_delete_).Run(); std::move(on_delete_).Run();
...@@ -98,6 +105,9 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { ...@@ -98,6 +105,9 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase {
const base::fuchsia::ScopedServiceBinding< const base::fuchsia::ScopedServiceBinding<
chromium::cast::ApplicationConfigManager> chromium::cast::ApplicationConfigManager>
app_config_binding_; app_config_binding_;
std::unique_ptr<
base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>>
bindings_manager_binding_;
std::unique_ptr<FakeCastChannel> cast_channel_; std::unique_ptr<FakeCastChannel> cast_channel_;
base::OnceClosure on_delete_; base::OnceClosure on_delete_;
...@@ -196,7 +206,8 @@ class CastRunnerIntegrationTest : public testing::Test { ...@@ -196,7 +206,8 @@ class CastRunnerIntegrationTest : public testing::Test {
std::unique_ptr<cr_fuchsia::AgentImpl::ComponentStateBase> OnComponentConnect( std::unique_ptr<cr_fuchsia::AgentImpl::ComponentStateBase> OnComponentConnect(
base::StringPiece component_url) { base::StringPiece component_url) {
auto component_state = std::make_unique<FakeComponentState>( auto component_state = std::make_unique<FakeComponentState>(
component_url, &app_config_manager_); component_url, &app_config_manager_,
(provide_api_bindings_ ? &api_bindings_ : nullptr));
component_state_ = component_state.get(); component_state_ = component_state.get();
return component_state; return component_state;
} }
...@@ -208,6 +219,11 @@ class CastRunnerIntegrationTest : public testing::Test { ...@@ -208,6 +219,11 @@ class CastRunnerIntegrationTest : public testing::Test {
// Returns fake Cast application information to the CastRunner. // Returns fake Cast application information to the CastRunner.
FakeApplicationConfigManager app_config_manager_; FakeApplicationConfigManager app_config_manager_;
TestApiBindings api_bindings_;
// If set, publishes an ApiBindings service from the Agent.
bool provide_api_bindings_ = true;
// Incoming service directory, ComponentContext and per-component state. // Incoming service directory, ComponentContext and per-component state.
std::unique_ptr<base::fuchsia::ServiceDirectory> component_services_; std::unique_ptr<base::fuchsia::ServiceDirectory> component_services_;
std::unique_ptr<cr_fuchsia::FakeComponentContext> component_context_; std::unique_ptr<cr_fuchsia::FakeComponentContext> component_context_;
...@@ -270,6 +286,56 @@ TEST_F(CastRunnerIntegrationTest, BasicRequest) { ...@@ -270,6 +286,56 @@ TEST_F(CastRunnerIntegrationTest, BasicRequest) {
run_loop.Run(); run_loop.Run();
} }
// Ensures that the runner will continue to work during the transitional period
// when the Agent does not supply an ApiBindings.
// TODO(crbug.com/953958): Remove this.
TEST_F(CastRunnerIntegrationTest, NoApiBindings) {
provide_api_bindings_ = false;
const char kBlankAppId[] = "00000000";
const char kBlankAppPath[] = "/defaultresponse";
app_config_manager_.AddAppMapping(kBlankAppId,
test_server_.GetURL(kBlankAppPath));
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId));
component_controller.set_error_handler(&ComponentErrorHandler);
// Access the NavigationController from the WebComponent. The test will hang
// here if no WebComponent was created.
fuchsia::web::NavigationControllerPtr nav_controller;
{
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<WebComponent*> web_component(
run_loop.QuitClosure());
cast_runner_->GetWebComponentForTest(web_component.GetReceiveCallback());
run_loop.Run();
ASSERT_NE(*web_component, nullptr);
(*web_component)
->frame()
->GetNavigationController(nav_controller.NewRequest());
}
// Ensure the NavigationState has the expected URL.
{
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> nav_entry(
run_loop.QuitClosure());
nav_controller->GetVisibleEntry(
cr_fuchsia::CallbackToFitFunction(nav_entry.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(nav_entry->has_url());
EXPECT_EQ(nav_entry->url(), test_server_.GetURL(kBlankAppPath).spec());
}
// Verify that the component is torn down when |component_controller| is
// unbound.
base::RunLoop run_loop;
component_state_->set_on_delete(run_loop.QuitClosure());
component_controller.Unbind();
run_loop.Run();
}
TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) { TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) {
// Launch the a component with an invalid Cast app Id. // Launch the a component with an invalid Cast app Id.
fuchsia::sys::ComponentControllerPtr component_controller = fuchsia::sys::ComponentControllerPtr component_controller =
...@@ -353,37 +419,6 @@ TEST_F(CastRunnerIntegrationTest, CastChannel) { ...@@ -353,37 +419,6 @@ TEST_F(CastRunnerIntegrationTest, CastChannel) {
run_loop.Run(); run_loop.Run();
} }
TEST_F(CastRunnerIntegrationTest, CastChannelConsumerDropped) {
const char kCastChannelAppId[] = "00000001";
const char kCastChannelAppPath[] = "/cast_channel.html";
app_config_manager_.AddAppMapping(kCastChannelAppId,
test_server_.GetURL(kCastChannelAppPath));
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
// Spin the message loop to handle creation of the component state.
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(component_state_);
// Disconnecting the CastChannel should trigger the component to teardown,
// resulting in our ComponentControllerPtr being dropped, and the component
// state held by the agent being deleted.
component_state_->set_on_delete(base::MakeExpectedRunClosure(FROM_HERE));
component_state_->ClearCastChannel();
base::RunLoop run_loop;
component_controller.set_error_handler([&run_loop](zx_status_t status) {
EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
run_loop.Quit();
});
run_loop.Run();
EXPECT_FALSE(component_controller.is_bound());
// Give the fake agent an opportunity to perform teardown cleanup.
base::RunLoop().RunUntilIdle();
}
TEST_F(CastRunnerIntegrationTest, CastChannelComponentControllerDropped) { TEST_F(CastRunnerIntegrationTest, CastChannelComponentControllerDropped) {
const char kCastChannelAppId[] = "00000001"; const char kCastChannelAppId[] = "00000001";
const char kCastChannelAppPath[] = "/cast_channel.html"; const char kCastChannelAppPath[] = "/cast_channel.html";
......
...@@ -57,6 +57,11 @@ NamedMessagePortConnector::~NamedMessagePortConnector() { ...@@ -57,6 +57,11 @@ NamedMessagePortConnector::~NamedMessagePortConnector() {
DCHECK(port_connected_handlers_.empty()); DCHECK(port_connected_handlers_.empty());
} }
void NamedMessagePortConnector::RegisterDefaultHandler(
DefaultPortConnectedCallback handler) {
default_handler_ = std::move(handler);
}
void NamedMessagePortConnector::Register(const std::string& port_name, void NamedMessagePortConnector::Register(const std::string& port_name,
PortConnectedCallback handler) { PortConnectedCallback handler) {
DCHECK(handler); DCHECK(handler);
...@@ -106,13 +111,6 @@ void NamedMessagePortConnector::OnConnectRequest( ...@@ -106,13 +111,6 @@ void NamedMessagePortConnector::OnConnectRequest(
return; return;
} }
if (port_connected_handlers_.find(port_name) ==
port_connected_handlers_.end()) {
LOG(ERROR) << "No registration for port: " << port_name;
control_port_.Unbind();
return;
}
if (message.incoming_transfer().size() != 1) { if (message.incoming_transfer().size() != 1) {
LOG(ERROR) << "Expected one Transferable, got " LOG(ERROR) << "Expected one Transferable, got "
<< message.incoming_transfer().size() << " instead."; << message.incoming_transfer().size() << " instead.";
...@@ -127,8 +125,24 @@ void NamedMessagePortConnector::OnConnectRequest( ...@@ -127,8 +125,24 @@ void NamedMessagePortConnector::OnConnectRequest(
control_port_.Unbind(); control_port_.Unbind();
return; return;
} }
port_connected_handlers_[port_name].Run(
std::move(transferable.message_port()));
ReceiveNextConnectRequest(); if (default_handler_ && port_connected_handlers_.find(port_name) ==
port_connected_handlers_.end()) {
default_handler_.Run(port_name, std::move(transferable.message_port()));
} else {
// TODO(crbug.com/953958): Deprecated, remove this once all APIs are
// migrated.
if (port_connected_handlers_.find(port_name) ==
port_connected_handlers_.end()) {
LOG(ERROR) << "No registration for port: " << port_name;
control_port_.Unbind();
return;
}
port_connected_handlers_[port_name].Run(
std::move(transferable.message_port()));
ReceiveNextConnectRequest();
}
} }
...@@ -17,18 +17,31 @@ ...@@ -17,18 +17,31 @@
// or more services registered by the caller. // or more services registered by the caller.
class NamedMessagePortConnector { class NamedMessagePortConnector {
public: public:
using DefaultPortConnectedCallback = base::RepeatingCallback<void(
base::StringPiece,
fidl::InterfaceHandle<fuchsia::web::MessagePort>)>;
// TODO(crbug.com/953958): Deprecated, remove this.
using PortConnectedCallback = base::RepeatingCallback<void( using PortConnectedCallback = base::RepeatingCallback<void(
fidl::InterfaceHandle<fuchsia::web::MessagePort>)>; fidl::InterfaceHandle<fuchsia::web::MessagePort>)>;
explicit NamedMessagePortConnector(fuchsia::web::Frame* frame); explicit NamedMessagePortConnector(fuchsia::web::Frame* frame);
~NamedMessagePortConnector(); ~NamedMessagePortConnector();
// Sets the handler that is called for connected ports which aren't
// registered in advance.
// TODO(crbug.com/953958): Rename this to Register() when the transition is
// complete.
void RegisterDefaultHandler(DefaultPortConnectedCallback handler);
// Registers a |handler| which will receive MessagePorts originating from // Registers a |handler| which will receive MessagePorts originating from
// |frame_|'s web content. |port_name| is a non-empty, alphanumeric string // |frame_|'s web content. |port_name| is a non-empty, alphanumeric string
// shared with the native backends. // shared with the native backends.
// TODO(crbug.com/953958): Remove this method.
void Register(const std::string& port_name, PortConnectedCallback handler); void Register(const std::string& port_name, PortConnectedCallback handler);
// Unregisters a handler. // Unregisters a handler.
// TODO(crbug.com/953958): Remove this method.
void Unregister(const std::string& port_name); void Unregister(const std::string& port_name);
// Invoked by the caller after every |frame_| page load. // Invoked by the caller after every |frame_| page load.
...@@ -43,7 +56,14 @@ class NamedMessagePortConnector { ...@@ -43,7 +56,14 @@ class NamedMessagePortConnector {
void OnConnectRequest(fuchsia::web::WebMessage message); void OnConnectRequest(fuchsia::web::WebMessage message);
fuchsia::web::Frame* const frame_; fuchsia::web::Frame* const frame_;
// Invoked for ports which weren't previously Register()'ed.
DefaultPortConnectedCallback default_handler_;
// Deprecated.
// TODO(crbug.com/953958): Remove this.
std::map<std::string, PortConnectedCallback> port_connected_handlers_; std::map<std::string, PortConnectedCallback> port_connected_handlers_;
fuchsia::web::MessagePortPtr control_port_; fuchsia::web::MessagePortPtr control_port_;
DISALLOW_COPY_AND_ASSIGN(NamedMessagePortConnector); DISALLOW_COPY_AND_ASSIGN(NamedMessagePortConnector);
......
// Copyright 2019 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/test_api_bindings.h"
#include "base/run_loop.h"
TestApiBindings::TestApiBindings() = default;
TestApiBindings::~TestApiBindings() = default;
fidl::InterfaceHandle<::fuchsia::web::MessagePort>
TestApiBindings::RunUntilMessagePortReceived(base::StringPiece port_name) {
while (ports_.find(port_name.as_string()) == ports_.end()) {
base::RunLoop run_loop;
port_received_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
fidl::InterfaceHandle<::fuchsia::web::MessagePort> port =
std::move(ports_[port_name.as_string()]);
ports_.erase(port_name.as_string());
return port;
}
void TestApiBindings::GetAll(GetAllCallback callback) {
callback(std::move(bindings_));
}
void TestApiBindings::Connect(
std::string port_name,
fidl::InterfaceHandle<::fuchsia::web::MessagePort> message_port) {
ports_[port_name] = std::move(message_port);
if (port_received_closure_)
std::move(port_received_closure_).Run();
}
// Copyright 2019 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.
#ifndef FUCHSIA_RUNNERS_CAST_TEST_API_BINDINGS_H_
#define FUCHSIA_RUNNERS_CAST_TEST_API_BINDINGS_H_
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
// Simple implementation of the ApiBindings service, for use by tests.
class TestApiBindings : public chromium::cast::ApiBindings {
public:
TestApiBindings();
~TestApiBindings() override;
// Spins a RunLoop until a port named |port_name| is received.
fidl::InterfaceHandle<::fuchsia::web::MessagePort>
RunUntilMessagePortReceived(base::StringPiece port_name);
// Sets the list of bindings which will be returned by GetAll().
void set_bindings(std::vector<chromium::cast::ApiBinding> bindings) {
bindings_ = std::move(bindings);
}
private:
// chromium::cast::ApiBindingsManager implementation.
void GetAll(GetAllCallback callback) override;
void Connect(
std::string channel_id,
fidl::InterfaceHandle<::fuchsia::web::MessagePort> message_port) override;
std::map<std::string, fidl::InterfaceHandle<::fuchsia::web::MessagePort>>
ports_;
std::vector<chromium::cast::ApiBinding> bindings_;
base::OnceClosure port_received_closure_;
DISALLOW_COPY_AND_ASSIGN(TestApiBindings);
};
#endif // FUCHSIA_RUNNERS_CAST_TEST_API_BINDINGS_H_
<!DOCTYPE html>
<html>
<head><title>echo</title></head>
<body>
<script>
// Use the "echo" port that was bound to the scripting context by the
// browsertest.
echo.onmessage = function(msg) {
echo.postMessage('ack ' + msg.data);
}
</script>
</body>
</html>
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