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

Reland "[bindings] Define cross-platform NamedMessagePortConnector impl."

This is a reland of 13c126e3

Addresses the size increase by removing the dep on //ui/base.
All uses of ui::ResourceBundle in CastRunner are replaced with disk
reads.

Original change's description:
> [bindings] Define cross-platform NamedMessagePortConnector impl.
>
> Refactors NamedMessagePortConnector into a platform-agnostic component,
> which can be reused across FIDL, in-process, and testing contexts.
> It uses Blink messages and message ports as a common basis for
> message and port transport.
>
> * Implements some missing portions of the Blink/FIDL MessagePort
>   conversion layer.
> * Adds a Fuchsia implementation of NMPC.
>
> Bug: 1104369
> Change-Id: I01e86c38963bfb114a7466c6f73b143e5302ad1e
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2293110
> Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
> Reviewed-by: Jochen Eisinger <jochen@chromium.org>
> Reviewed-by: David Dorwin <ddorwin@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#806684}

Bug: 1104369
Change-Id: I77dc7679030e0f0aa61ad4dba2050ce2cb6e22d2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2417492Reviewed-by: default avatarKevin Marshall <kmarshall@chromium.org>
Reviewed-by: default avatarJochen Eisinger <jochen@chromium.org>
Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808393}
parent 55776f79
# Copyright 2020 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.
import("//tools/grit/grit_rule.gni")
source_set("named_message_port_connector") {
sources = [
"named_message_port_connector.cc",
"named_message_port_connector.h",
]
deps = [
"//base",
"//components/cast:export",
]
public_deps = [
":resources",
"//third_party/blink/public/common",
]
}
grit("resources") {
source = "named_message_port_connector_resources.grd"
outputs = [
"grit/named_message_port_connector_resources.h",
"named_message_port_connector_resources.pak",
]
# Allow GRIT to assign IDs using its default set of base IDs.
resource_ids = ""
}
include_rules = [
"+third_party/blink/public/common/messaging",
"+ui/base/resource",
]
// Copyright 2020 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 "components/cast/named_message_port_connector/named_message_port_connector.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
namespace cast_api_bindings {
NamedMessagePortConnector::NamedMessagePortConnector() = default;
NamedMessagePortConnector::~NamedMessagePortConnector() = default;
void NamedMessagePortConnector::RegisterPortHandler(
PortConnectedCallback handler) {
handler_ = std::move(handler);
}
// Receives the MessagePort and forwards ports to their corresponding binding
// handlers.
bool NamedMessagePortConnector::OnMessage(
blink::WebMessagePort::Message message) {
if (message.ports.size() != 1) {
DLOG(FATAL) << "Only one control port should be provided";
return false;
}
// Read the port ID.
base::string16 data_utf16 = std::move(message.data);
std::string binding_id;
if (!base::UTF16ToUTF8(data_utf16.data(), data_utf16.size(), &binding_id))
return false;
return handler_.Run(binding_id, std::move(message.ports[0]));
}
blink::WebMessagePort::Message NamedMessagePortConnector::GetConnectMessage() {
constexpr char kControlPortConnectMessage[] = "cast.master.connect";
// Pass the control message port into the page as an HTML5 MessageChannel
// message.
auto port_pair = blink::WebMessagePort::CreatePair();
control_port_ = std::move(port_pair.first);
control_port_.SetReceiver(this, base::ThreadTaskRunnerHandle::Get());
blink::WebMessagePort::Message connect_message;
connect_message.data = base::UTF8ToUTF16(kControlPortConnectMessage);
connect_message.ports.push_back(std::move(port_pair.second));
return connect_message;
}
} // namespace cast_api_bindings
// Copyright 2020 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 COMPONENTS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_NAMED_MESSAGE_PORT_CONNECTOR_H_
#define COMPONENTS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_NAMED_MESSAGE_PORT_CONNECTOR_H_
#include "base/callback.h"
#include "base/strings/string_piece.h"
#include "third_party/blink/public/common/messaging/web_message_port.h"
namespace cast_api_bindings {
// Injects an API into |frame| through which it can connect MessagePorts to one
// or more services registered by the caller.
// Platform specific details, such as how the script resources are injected, and
// how the connection message is posted to the page, are delegated to the
// caller.
// TODO(crbug.com/1126571): Migrate off Blink::WebMessagePort to a
// platform-agnostic MessagePort abstraction.
class NamedMessagePortConnector
: public blink::WebMessagePort::MessageReceiver {
public:
// Signature of callback to be invoked when a port is connected.
// The callback should return true if the connection request was valid.
using PortConnectedCallback =
base::RepeatingCallback<bool(base::StringPiece, blink::WebMessagePort)>;
NamedMessagePortConnector();
~NamedMessagePortConnector() override;
NamedMessagePortConnector(const NamedMessagePortConnector&) = delete;
NamedMessagePortConnector& operator=(const NamedMessagePortConnector&) =
delete;
// Sets the callback which will be invoked when a port is connected.
void RegisterPortHandler(PortConnectedCallback handler);
// Returns a connection message which should be posted to the page on
// every navigation.
// Calling this method will drop any preexisting connections made to the page.
blink::WebMessagePort::Message GetConnectMessage();
private:
// blink::WebMessagePort::MessageReceiver implementation:
bool OnMessage(blink::WebMessagePort::Message message) override;
PortConnectedCallback handler_;
blink::WebMessagePort control_port_;
};
} // namespace cast_api_bindings
#endif // COMPONENTS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_NAMED_MESSAGE_PORT_CONNECTOR_H_
// 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.
'use strict';
if (!cast) {
// eslint-disable-next-line no-var
var cast = new Object;
}
if (!cast.__platform__) {
cast.__platform__ = new Object;
}
// Creates named HTML5 MessagePorts that are connected to native code.
cast.__platform__.PortConnector = new class {
constructor() {
this.controlPort_ = null;
// A map of ports waiting to be published to the controlPort_, keyed by
// string IDs.
this.pendingPorts_ = {};
this.listener = this.onMessageEvent.bind(this);
window.addEventListener(
'message', this.listener,
true // Let the listener handle events before they hit the DOM tree.
);
}
// Returns a MessagePort whose channel will be passed to the native code.
// The channel can be used immediately after construction. Outgoing messages
// will be automatically buffered until the connection is established.
bind(id) {
const channel = new MessageChannel();
if (this.controlPort_) {
this.sendPort(id, channel.port2);
} else {
this.pendingPorts_[id] = channel.port2;
}
return channel.port1;
}
sendPort(portId, port) {
this.controlPort_.postMessage(portId, [port]);
}
// Receives a control port from native code.
onMessageEvent(e) {
// Only process window.onmessage events which are intended for this class.
if (e.data != 'cast.master.connect') {
return;
}
if (e.ports.length != 1) {
console.error(
'Expected only one MessagePort, got ' + e.ports.length + ' instead.');
for (const i in e.ports) {
e.ports[i].close();
}
return;
}
this.controlPort_ = e.ports[0];
for (const portId in this.pendingPorts_) {
this.sendPort(portId, this.pendingPorts_[portId]);
}
this.pendingPorts_ = null;
e.stopPropagation();
// No need to receive more onmessage events.
window.removeEventListener('message', this.listener);
}
}
();
<?xml version="1.0" encoding="utf-8"?>
<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
<outputs>
<output filename="grit/named_message_port_connector_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="named_message_port_connector_resources.pak" type="data_package" />
</outputs>
<release seq="1">
<includes>
<include name="IDR_PORT_CONNECTOR_JS" file="named_message_port_connector.js" type="BINDATA" />
</includes>
</release>
</grit>
...@@ -20,10 +20,4 @@ component("browser") { ...@@ -20,10 +20,4 @@ component("browser") {
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//third_party/blink/public/common", "//third_party/blink/public/common",
] ]
visibility = [
"//chromecast/bindings:bindings_manager_cast",
"//chromecast/browser:browser",
"//fuchsia/engine:web_engine_core",
]
} }
...@@ -21,9 +21,4 @@ component("renderer") { ...@@ -21,9 +21,4 @@ component("renderer") {
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//third_party/blink/public/common", "//third_party/blink/public/common",
] ]
visibility = [
"//chromecast/renderer/*",
"//fuchsia/engine:web_engine_core",
]
} }
...@@ -24,6 +24,9 @@ namespace { ...@@ -24,6 +24,9 @@ namespace {
using BlinkMessage = blink::WebMessagePort::Message; using BlinkMessage = blink::WebMessagePort::Message;
// Converts a fuchsia::web::WebMessage to a BlinkMessage.
// An empty result indicates that conversion was successful.
// Data validation errors are returned as a FrameError.
base::Optional<fuchsia::web::FrameError> BlinkMessageFromFidl( base::Optional<fuchsia::web::FrameError> BlinkMessageFromFidl(
fuchsia::web::WebMessage fidl_message, fuchsia::web::WebMessage fidl_message,
BlinkMessage* blink_message) { BlinkMessage* blink_message) {
...@@ -50,39 +53,15 @@ base::Optional<fuchsia::web::FrameError> BlinkMessageFromFidl( ...@@ -50,39 +53,15 @@ base::Optional<fuchsia::web::FrameError> BlinkMessageFromFidl(
blink_message->ports.push_back( blink_message->ports.push_back(
BlinkMessagePortFromFidl(std::move(transferrable.message_port()))); BlinkMessagePortFromFidl(std::move(transferrable.message_port())));
} }
} } else if (fidl_message.has_incoming_transfer()) {
for (fuchsia::web::IncomingTransferable& incoming :
return base::nullopt; *fidl_message.mutable_incoming_transfer()) {
} blink_message->ports.push_back(
BlinkMessagePortFromFidl(std::move(incoming.message_port())));
base::Optional<fuchsia::web::WebMessage> FidlWebMessageFromBlink(
BlinkMessage blink_message) {
fuchsia::web::WebMessage fidl_message;
if (!blink_message.ports.empty()) {
std::vector<fuchsia::web::IncomingTransferable> transferables;
for (blink::WebMessagePort& port : blink_message.ports) {
fuchsia::web::IncomingTransferable incoming;
incoming.set_message_port(FidlMessagePortFromBlink(std::move(port)));
transferables.push_back(std::move(incoming));
} }
fidl_message.set_incoming_transfer(std::move(transferables));
blink_message.ports.clear();
} }
base::string16 data_utf16 = std::move(blink_message.data); return base::nullopt;
std::string data_utf8;
if (!base::UTF16ToUTF8(data_utf16.data(), data_utf16.size(), &data_utf8))
return base::nullopt;
base::STLClearObject(&data_utf16);
fuchsia::mem::Buffer data =
cr_fuchsia::MemBufferFromString(data_utf8, "cr-web-message-from-blink");
if (!data.vmo)
return base::nullopt;
fidl_message.set_data(std::move(data));
return fidl_message;
} }
// Defines a MessagePortAdapter, which translates and routes messages between a // Defines a MessagePortAdapter, which translates and routes messages between a
...@@ -129,7 +108,8 @@ class MessagePortAdapter : public blink::WebMessagePort::MessageReceiver { ...@@ -129,7 +108,8 @@ class MessagePortAdapter : public blink::WebMessagePort::MessageReceiver {
// blink::WebMessagePort::MessageReceiver implementation: // blink::WebMessagePort::MessageReceiver implementation:
bool OnMessage(BlinkMessage message) override { bool OnMessage(BlinkMessage message) override {
base::Optional<fuchsia::web::WebMessage> message_converted = base::Optional<fuchsia::web::WebMessage> message_converted =
FidlWebMessageFromBlink(std::move(message)); FidlWebMessageFromBlink(std::move(message),
TransferableHostType::kLocal);
if (!message_converted) { if (!message_converted) {
DLOG(ERROR) << "Couldn't decode WebMessage from blink::WebMessagePort."; DLOG(ERROR) << "Couldn't decode WebMessage from blink::WebMessagePort.";
Destroy(); Destroy();
...@@ -171,6 +151,10 @@ class FidlMessagePortClientAdapter : public MessagePortAdapter { ...@@ -171,6 +151,10 @@ class FidlMessagePortClientAdapter : public MessagePortAdapter {
}); });
} }
fidl::InterfaceRequest<fuchsia::web::MessagePort> NewRequest() {
return port_.NewRequest();
}
private: private:
~FidlMessagePortClientAdapter() override = default; ~FidlMessagePortClientAdapter() override = default;
...@@ -307,12 +291,24 @@ class FidlMessagePortServerAdapter : public fuchsia::web::MessagePort, ...@@ -307,12 +291,24 @@ class FidlMessagePortServerAdapter : public fuchsia::web::MessagePort,
DISALLOW_COPY_AND_ASSIGN(FidlMessagePortServerAdapter); DISALLOW_COPY_AND_ASSIGN(FidlMessagePortServerAdapter);
}; };
fidl::InterfaceRequest<fuchsia::web::MessagePort>
RemoteFidlMessagePortFromBlink(blink::WebMessagePort blink_port) {
fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_handle;
auto request = fidl_handle.NewRequest();
new FidlMessagePortClientAdapter(std::move(blink_port),
std::move(fidl_handle));
return request;
}
} // namespace } // namespace
// Methods for constructing MessagePortAdapters for various port types and
// origins. The adapters manage their own lifetimes and will self-delete when
// either endpoint of their channels are disconnected.
blink::WebMessagePort BlinkMessagePortFromFidl( blink::WebMessagePort BlinkMessagePortFromFidl(
fidl::InterfaceRequest<fuchsia::web::MessagePort> fidl_port) { fidl::InterfaceRequest<fuchsia::web::MessagePort> fidl_port) {
auto port_pair = blink::WebMessagePort::CreatePair(); auto port_pair = blink::WebMessagePort::CreatePair();
// The adapter cleans itself up when either of the associated ports is closed.
new FidlMessagePortServerAdapter(std::move(port_pair.first), new FidlMessagePortServerAdapter(std::move(port_pair.first),
std::move(fidl_port)); std::move(fidl_port));
return std::move(port_pair.second); return std::move(port_pair.second);
...@@ -321,7 +317,6 @@ blink::WebMessagePort BlinkMessagePortFromFidl( ...@@ -321,7 +317,6 @@ blink::WebMessagePort BlinkMessagePortFromFidl(
blink::WebMessagePort BlinkMessagePortFromFidl( blink::WebMessagePort BlinkMessagePortFromFidl(
fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_port) { fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_port) {
auto port_pair = blink::WebMessagePort::CreatePair(); auto port_pair = blink::WebMessagePort::CreatePair();
// The adapter cleans itself up when either of the associated ports is closed.
new FidlMessagePortClientAdapter(std::move(port_pair.first), new FidlMessagePortClientAdapter(std::move(port_pair.first),
std::move(fidl_port)); std::move(fidl_port));
return std::move(port_pair.second); return std::move(port_pair.second);
...@@ -333,4 +328,49 @@ fidl::InterfaceHandle<fuchsia::web::MessagePort> FidlMessagePortFromBlink( ...@@ -333,4 +328,49 @@ fidl::InterfaceHandle<fuchsia::web::MessagePort> FidlMessagePortFromBlink(
return adapter->NewBinding(); return adapter->NewBinding();
} }
base::Optional<fuchsia::web::WebMessage> FidlWebMessageFromBlink(
BlinkMessage blink_message,
TransferableHostType port_type) {
fuchsia::web::WebMessage fidl_message;
if (!blink_message.ports.empty()) {
switch (port_type) {
case TransferableHostType::kLocal:
for (blink::WebMessagePort& port : blink_message.ports) {
fuchsia::web::IncomingTransferable incoming;
incoming.set_message_port(FidlMessagePortFromBlink(std::move(port)));
fidl_message.mutable_incoming_transfer()->push_back(
std::move(incoming));
}
break;
case TransferableHostType::kRemote:
for (blink::WebMessagePort& port : blink_message.ports) {
fuchsia::web::OutgoingTransferable outgoing;
outgoing.set_message_port(
RemoteFidlMessagePortFromBlink(std::move(port)));
fidl_message.mutable_outgoing_transfer()->push_back(
std::move(outgoing));
}
break;
}
blink_message.ports.clear();
}
base::string16 data_utf16 = std::move(blink_message.data);
std::string data_utf8;
if (!base::UTF16ToUTF8(data_utf16.data(), data_utf16.size(), &data_utf8))
return base::nullopt;
base::STLClearObject(&data_utf16);
constexpr char kBufferVmoName[] = "cr-web-message-from-blink";
fuchsia::mem::Buffer data_buffer =
cr_fuchsia::MemBufferFromString(data_utf8, kBufferVmoName);
if (!data_buffer.vmo)
return base::nullopt;
fidl_message.set_data(std::move(data_buffer));
return fidl_message;
}
} // namespace cr_fuchsia } // namespace cr_fuchsia
...@@ -29,6 +29,21 @@ blink::WebMessagePort BlinkMessagePortFromFidl( ...@@ -29,6 +29,21 @@ blink::WebMessagePort BlinkMessagePortFromFidl(
fidl::InterfaceHandle<fuchsia::web::MessagePort> FidlMessagePortFromBlink( fidl::InterfaceHandle<fuchsia::web::MessagePort> FidlMessagePortFromBlink(
blink::WebMessagePort blink_port); blink::WebMessagePort blink_port);
// Specifies the location of the MessagePort FIDL service that handles messages
// sent over the Transferable.
enum class TransferableHostType {
// The MessagePort FIDL service is hosted in-process.
kLocal,
// The MessagePort FIDL service is hosted remotely.
kRemote,
};
// Converts a BlinkMessage to a fuchsia::web::WebMessage.
base::Optional<fuchsia::web::WebMessage> FidlWebMessageFromBlink(
blink::WebMessagePort::Message blink_message,
TransferableHostType port_type);
} // namespace cr_fuchsia } // namespace cr_fuchsia
#endif // FUCHSIA_BASE_MESSAGE_PORT_H_ #endif // FUCHSIA_BASE_MESSAGE_PORT_H_
...@@ -690,11 +690,9 @@ void FrameImpl::PostMessage(std::string origin, ...@@ -690,11 +690,9 @@ void FrameImpl::PostMessage(std::string origin,
return; return;
} }
// Include outgoing MessagePorts in the message. // Convert and pass along any MessagePorts contained in the message.
std::vector<blink::WebMessagePort> message_ports; std::vector<blink::WebMessagePort> message_ports;
if (message.has_outgoing_transfer()) { if (message.has_outgoing_transfer()) {
// Verify that all the Transferables are valid before we start allocating
// resources to them.
for (const fuchsia::web::OutgoingTransferable& outgoing : for (const fuchsia::web::OutgoingTransferable& outgoing :
message.outgoing_transfer()) { message.outgoing_transfer()) {
if (!outgoing.is_message_port()) { if (!outgoing.is_message_port()) {
......
...@@ -62,16 +62,21 @@ source_set("cast_runner_core") { ...@@ -62,16 +62,21 @@ source_set("cast_runner_core") {
"cast/cast_runner.h", "cast/cast_runner.h",
"cast/cast_streaming.cc", "cast/cast_streaming.cc",
"cast/cast_streaming.h", "cast/cast_streaming.h",
"cast/named_message_port_connector.cc", "cast/named_message_port_connector_fuchsia.cc",
"cast/named_message_port_connector.h", "cast/named_message_port_connector_fuchsia.h",
"cast/pending_cast_component.cc", "cast/pending_cast_component.cc",
"cast/pending_cast_component.h", "cast/pending_cast_component.h",
] ]
data_deps = [ "//chromecast/bindings:named_message_port_connector_resources" ] data = [
data = [ "cast/data" ] "cast/data/receiver.html",
rebase_path(
"//components/cast/named_message_port_connector/named_message_port_connector.js"),
]
deps = [ deps = [
"//base", "//base",
"//components/cast/named_message_port_connector:named_message_port_connector",
"//fuchsia/base", "//fuchsia/base",
"//fuchsia/base:message_port",
"//fuchsia/base:modular", "//fuchsia/base:modular",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.modular", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.modular",
"//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
...@@ -95,7 +100,9 @@ executable("cast_runner_exe") { ...@@ -95,7 +100,9 @@ executable("cast_runner_exe") {
":common", ":common",
"//base", "//base",
"//fuchsia/base", "//fuchsia/base",
"//mojo/core/embedder", # TODO(crbug.com/1126571): Remove when MPP lands.
] ]
data_deps = [ ":cast_runner_core" ]
visibility = [ ":*" ] visibility = [ ":*" ]
} }
...@@ -147,6 +154,7 @@ test("cast_runner_unittests") { ...@@ -147,6 +154,7 @@ test("cast_runner_unittests") {
"//testing/gtest", "//testing/gtest",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web",
] ]
data_deps = [ ":cast_runner_core" ]
} }
test("cast_runner_integration_tests") { test("cast_runner_integration_tests") {
...@@ -158,6 +166,7 @@ test("cast_runner_integration_tests") { ...@@ -158,6 +166,7 @@ test("cast_runner_integration_tests") {
"//base/test:run_all_unittests", "//base/test:run_all_unittests",
"//base/test:test_support", "//base/test:test_support",
"//fuchsia/base:test_support", "//fuchsia/base:test_support",
"//mojo/core/embedder", # TODO(crbug.com/1126571): Remove when MPP lands.
"//net:test_support", "//net:test_support",
"//testing/gtest", "//testing/gtest",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
...@@ -166,6 +175,7 @@ test("cast_runner_integration_tests") { ...@@ -166,6 +175,7 @@ test("cast_runner_integration_tests") {
"//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
] ]
data_deps = [ ":cast_runner_core" ]
package_deps = [ [ package_deps = [ [
"//fuchsia/engine:web_engine", "//fuchsia/engine:web_engine",
"web_engine", "web_engine",
...@@ -175,7 +185,7 @@ test("cast_runner_integration_tests") { ...@@ -175,7 +185,7 @@ test("cast_runner_integration_tests") {
test("cast_runner_browsertests") { test("cast_runner_browsertests") {
sources = [ sources = [
"cast/api_bindings_client_browsertest.cc", "cast/api_bindings_client_browsertest.cc",
"cast/named_message_port_connector_browsertest.cc", "cast/named_message_port_connector_fuchsia_browsertest.cc",
] ]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
data = [ "cast/testdata" ] data = [ "cast/testdata" ]
...@@ -186,6 +196,7 @@ test("cast_runner_browsertests") { ...@@ -186,6 +196,7 @@ test("cast_runner_browsertests") {
"//content/public/browser", "//content/public/browser",
"//content/test:test_support", "//content/test:test_support",
"//fuchsia/base", "//fuchsia/base",
"//fuchsia/base:message_port",
"//fuchsia/base:test_support", "//fuchsia/base:test_support",
"//fuchsia/engine:browsertest_core", "//fuchsia/engine:browsertest_core",
"//testing/gmock", "//testing/gmock",
......
include_rules = [ include_rules = [
"+components/cast/named_message_port_connector",
"+content/public/test", "+content/public/test",
] "+mojo/core/embedder",
\ No newline at end of file ]
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "fuchsia/base/message_port.h"
namespace { namespace {
...@@ -35,7 +36,7 @@ ApiBindingsClient::ApiBindingsClient( ...@@ -35,7 +36,7 @@ ApiBindingsClient::ApiBindingsClient(
ApiBindingsClient::~ApiBindingsClient() { ApiBindingsClient::~ApiBindingsClient() {
if (connector_ && frame_) { if (connector_ && frame_) {
connector_->Register({}); connector_->RegisterPortHandler({});
// Remove all injected scripts using their automatically enumerated IDs. // Remove all injected scripts using their automatically enumerated IDs.
for (uint64_t i = 0; i < bindings_->size(); ++i) for (uint64_t i = 0; i < bindings_->size(); ++i)
...@@ -43,9 +44,10 @@ ApiBindingsClient::~ApiBindingsClient() { ...@@ -43,9 +44,10 @@ ApiBindingsClient::~ApiBindingsClient() {
} }
} }
void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame, void ApiBindingsClient::AttachToFrame(
NamedMessagePortConnector* connector, fuchsia::web::Frame* frame,
base::OnceClosure on_error_callback) { cast_api_bindings::NamedMessagePortConnector* connector,
base::OnceClosure on_error_callback) {
DCHECK(!frame_) << "AttachToFrame() was called twice."; DCHECK(!frame_) << "AttachToFrame() was called twice.";
DCHECK(frame); DCHECK(frame);
DCHECK(connector); DCHECK(connector);
...@@ -62,8 +64,8 @@ void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame, ...@@ -62,8 +64,8 @@ void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame,
std::move(on_error_callback).Run(); std::move(on_error_callback).Run();
}); });
connector_->Register(base::BindRepeating(&ApiBindingsClient::OnPortConnected, connector_->RegisterPortHandler(base::BindRepeating(
base::Unretained(this))); &ApiBindingsClient::OnPortConnected, base::Unretained(this)));
// Enumerate and inject all scripts in |bindings|. // Enumerate and inject all scripts in |bindings|.
uint64_t bindings_id = kBindingsIdStart; uint64_t bindings_id = kBindingsIdStart;
...@@ -87,11 +89,15 @@ bool ApiBindingsClient::HasBindings() const { ...@@ -87,11 +89,15 @@ bool ApiBindingsClient::HasBindings() const {
return bindings_.has_value(); return bindings_.has_value();
} }
void ApiBindingsClient::OnPortConnected( bool ApiBindingsClient::OnPortConnected(base::StringPiece port_name,
base::StringPiece port_name, blink::WebMessagePort port) {
fidl::InterfaceHandle<fuchsia::web::MessagePort> port) { if (!bindings_service_)
if (bindings_service_) return false;
bindings_service_->Connect(port_name.as_string(), std::move(port));
bindings_service_->Connect(
port_name.as_string(),
cr_fuchsia::FidlMessagePortFromBlink(std::move(port)));
return true;
} }
void ApiBindingsClient::OnBindingsReceived( void ApiBindingsClient::OnBindingsReceived(
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "components/cast/named_message_port_connector/named_message_port_connector.h"
#include "fuchsia/fidl/chromium/cast/cpp/fidl.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 // Injects scripts received from the ApiBindings service, and provides connected
// ports to the Agent. // ports to the Agent.
...@@ -31,7 +31,7 @@ class ApiBindingsClient { ...@@ -31,7 +31,7 @@ class ApiBindingsClient {
// lost connection to the Agent). The callback must remain valid for the // lost connection to the Agent). The callback must remain valid for the
// entire lifetime of |this|. // entire lifetime of |this|.
void AttachToFrame(fuchsia::web::Frame* frame, void AttachToFrame(fuchsia::web::Frame* frame,
NamedMessagePortConnector* connector, cast_api_bindings::NamedMessagePortConnector* connector,
base::OnceClosure on_error_callback); base::OnceClosure on_error_callback);
// Indicates that the Frame is no longer live, preventing the API bindings // Indicates that the Frame is no longer live, preventing the API bindings
...@@ -45,17 +45,15 @@ class ApiBindingsClient { ...@@ -45,17 +45,15 @@ class ApiBindingsClient {
// TODO(crbug.com/1082821): Move this method back to private once the Cast // TODO(crbug.com/1082821): Move this method back to private once the Cast
// Streaming Receiver component has been implemented. // Streaming Receiver component has been implemented.
// Called when |connector_| has connected a port. // Called when |connector_| has connected a port.
void OnPortConnected(base::StringPiece port_name, bool OnPortConnected(base::StringPiece port_name, blink::WebMessagePort port);
fidl::InterfaceHandle<fuchsia::web::MessagePort> port);
private: private:
// Called when ApiBindings::GetAll() has responded. // Called when ApiBindings::GetAll() has responded.
void OnBindingsReceived(std::vector<chromium::cast::ApiBinding> bindings); void OnBindingsReceived(std::vector<chromium::cast::ApiBinding> bindings);
base::Optional<std::vector<chromium::cast::ApiBinding>> bindings_; base::Optional<std::vector<chromium::cast::ApiBinding>> bindings_;
fuchsia::web::Frame* frame_ = nullptr; fuchsia::web::Frame* frame_ = nullptr;
NamedMessagePortConnector* connector_ = nullptr; cast_api_bindings::NamedMessagePortConnector* connector_ = nullptr;
chromium::cast::ApiBindingsPtr bindings_service_; chromium::cast::ApiBindingsPtr bindings_service_;
base::OnceClosure on_initialization_complete_; base::OnceClosure on_initialization_complete_;
......
...@@ -5,15 +5,21 @@ ...@@ -5,15 +5,21 @@
#include <fuchsia/web/cpp/fidl.h> #include <fuchsia/web/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h> #include <lib/fidl/cpp/binding.h>
#include "base/barrier_closure.h"
#include "base/base_paths_fuchsia.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "fuchsia/base/fit_adapter.h" #include "fuchsia/base/fit_adapter.h"
#include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/frame_test_util.h"
#include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/base/message_port.h"
#include "fuchsia/base/result_receiver.h" #include "fuchsia/base/result_receiver.h"
#include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/base/test_navigation_listener.h"
#include "fuchsia/engine/test/web_engine_browser_test.h" #include "fuchsia/engine/test/web_engine_browser_test.h"
#include "fuchsia/runners/cast/api_bindings_client.h" #include "fuchsia/runners/cast/api_bindings_client.h"
#include "fuchsia/runners/cast/named_message_port_connector_fuchsia.h"
#include "fuchsia/runners/cast/test_api_bindings.h" #include "fuchsia/runners/cast/test_api_bindings.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -27,6 +33,8 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest { ...@@ -27,6 +33,8 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest {
~ApiBindingsClientTest() override = default; ~ApiBindingsClientTest() override = default;
void SetUp() override { cr_fuchsia::WebEngineBrowserTest::SetUp(); }
protected: protected:
void StartClient() { void StartClient() {
base::ScopedAllowBlockingForTesting allow_blocking; base::ScopedAllowBlockingForTesting allow_blocking;
...@@ -41,7 +49,9 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest { ...@@ -41,7 +49,9 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest {
frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_); frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_);
frame_->GetNavigationController(controller_.NewRequest()); frame_->GetNavigationController(controller_.NewRequest());
connector_ = std::make_unique<NamedMessagePortConnector>(frame_.get()); connector_ =
std::make_unique<NamedMessagePortConnectorFuchsia>(frame_.get());
client_->AttachToFrame(frame_.get(), connector_.get(), client_->AttachToFrame(frame_.get(), connector_.get(),
base::MakeExpectedNotRunClosure(FROM_HERE)); base::MakeExpectedNotRunClosure(FROM_HERE));
} }
...@@ -57,7 +67,7 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest { ...@@ -57,7 +67,7 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest {
} }
fuchsia::web::FramePtr frame_; fuchsia::web::FramePtr frame_;
std::unique_ptr<NamedMessagePortConnector> connector_; std::unique_ptr<NamedMessagePortConnectorFuchsia> connector_;
TestApiBindings api_service_; TestApiBindings api_service_;
fidl::Binding<chromium::cast::ApiBindings> api_service_binding_; fidl::Binding<chromium::cast::ApiBindings> api_service_binding_;
std::unique_ptr<ApiBindingsClient> client_; std::unique_ptr<ApiBindingsClient> client_;
...@@ -67,7 +77,11 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest { ...@@ -67,7 +77,11 @@ class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest {
DISALLOW_COPY_AND_ASSIGN(ApiBindingsClientTest); DISALLOW_COPY_AND_ASSIGN(ApiBindingsClientTest);
}; };
// Tests API registration, injection, and message IPC.
// Registers a port that echoes messages received over a MessagePort back to the
// sender.
IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) { IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) {
// Define the injected bindings.
std::vector<chromium::cast::ApiBinding> binding_list; std::vector<chromium::cast::ApiBinding> binding_list;
chromium::cast::ApiBinding echo_binding; chromium::cast::ApiBinding echo_binding;
echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString( echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString(
...@@ -75,24 +89,41 @@ IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) { ...@@ -75,24 +89,41 @@ IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) {
"test")); "test"));
binding_list.emplace_back(std::move(echo_binding)); binding_list.emplace_back(std::move(echo_binding));
api_service_.set_bindings(std::move(binding_list)); api_service_.set_bindings(std::move(binding_list));
StartClient(); StartClient();
base::RunLoop post_message_responses_loop;
base::RepeatingClosure post_message_response_closure =
base::BarrierClosure(2, post_message_responses_loop.QuitClosure());
// Navigate to a test page that makes use of the injected bindings.
const GURL test_url = embedded_test_server()->GetURL("/echo.html"); const GURL test_url = embedded_test_server()->GetURL("/echo.html");
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
controller_.get(), fuchsia::web::LoadUrlParams(), test_url.spec())); controller_.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
navigation_listener_.RunUntilUrlEquals(test_url); navigation_listener_.RunUntilUrlEquals(test_url);
connector_->OnPageLoad(); frame_->PostMessage("*",
std::move(*cr_fuchsia::FidlWebMessageFromBlink(
fuchsia::web::MessagePortPtr port = connector_->GetConnectMessage(),
api_service_.RunUntilMessagePortReceived("echoService").Bind(); cr_fuchsia::TransferableHostType::kRemote)),
[&post_message_response_closure](
fuchsia::web::Frame_PostMessage_Result result) {
ASSERT_TRUE(result.is_response());
post_message_response_closure.Run();
});
// Connect to the echo service hosted by the page and send a ping to it.
fuchsia::web::WebMessage message; fuchsia::web::WebMessage message;
message.set_data(cr_fuchsia::MemBufferFromString("ping", "ping-msg")); message.set_data(cr_fuchsia::MemBufferFromString("ping", "ping-msg"));
fuchsia::web::MessagePortPtr port =
api_service_.RunUntilMessagePortReceived("echoService").Bind();
port->PostMessage(std::move(message), port->PostMessage(std::move(message),
[](fuchsia::web::MessagePort_PostMessage_Result result) { [&post_message_response_closure](
EXPECT_TRUE(result.is_response()); fuchsia::web::MessagePort_PostMessage_Result result) {
ASSERT_TRUE(result.is_response());
post_message_response_closure.Run();
}); });
// Handle the ping response.
base::RunLoop response_loop; base::RunLoop response_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> response( cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> response(
response_loop.QuitClosure()); response_loop.QuitClosure());
...@@ -104,6 +135,9 @@ IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) { ...@@ -104,6 +135,9 @@ IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) {
EXPECT_TRUE( EXPECT_TRUE(
cr_fuchsia::StringFromMemBuffer(response->data(), &response_string)); cr_fuchsia::StringFromMemBuffer(response->data(), &response_string));
EXPECT_EQ("ack ping", response_string); EXPECT_EQ("ack ping", response_string);
// Ensure that we've received acks for all messages.
post_message_responses_loop.Run();
} }
} // namespace } // namespace
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <lib/fidl/cpp/binding.h> #include <lib/fidl/cpp/binding.h>
#include <lib/ui/scenic/cpp/view_ref_pair.h> #include <lib/ui/scenic/cpp/view_ref_pair.h>
#include <algorithm> #include <algorithm>
#include <string>
#include <utility> #include <utility>
#include "base/auto_reset.h" #include "base/auto_reset.h"
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include "base/task/current_thread.h" #include "base/task/current_thread.h"
#include "fuchsia/base/agent_manager.h" #include "fuchsia/base/agent_manager.h"
#include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/base/message_port.h"
#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" #include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
#include "fuchsia/runners/cast/cast_runner.h" #include "fuchsia/runners/cast/cast_runner.h"
#include "fuchsia/runners/cast/cast_streaming.h" #include "fuchsia/runners/cast/cast_streaming.h"
...@@ -78,7 +80,7 @@ void CastComponent::StartComponent() { ...@@ -78,7 +80,7 @@ void CastComponent::StartComponent() {
WebComponent::StartComponent(); WebComponent::StartComponent();
connector_ = std::make_unique<NamedMessagePortConnector>(frame()); connector_ = std::make_unique<NamedMessagePortConnectorFuchsia>(frame());
url_rewrite_rules_provider_.set_error_handler([this](zx_status_t status) { url_rewrite_rules_provider_.set_error_handler([this](zx_status_t status) {
ZX_LOG_IF(ERROR, status != ZX_OK, status) ZX_LOG_IF(ERROR, status != ZX_OK, status)
...@@ -115,8 +117,9 @@ void CastComponent::StartComponent() { ...@@ -115,8 +117,9 @@ void CastComponent::StartComponent() {
fuchsia::sys::TerminationReason::INTERNAL_ERROR); fuchsia::sys::TerminationReason::INTERNAL_ERROR);
} }
}); });
api_bindings_client_->OnPortConnected(kCastStreamingMessagePortName, api_bindings_client_->OnPortConnected(
std::move(message_port)); kCastStreamingMessagePortName,
cr_fuchsia::BlinkMessagePortFromFidl(std::move(message_port)));
} }
api_bindings_client_->AttachToFrame( api_bindings_client_->AttachToFrame(
...@@ -166,6 +169,7 @@ void CastComponent::DestroyComponent(int64_t exit_code, ...@@ -166,6 +169,7 @@ void CastComponent::DestroyComponent(int64_t exit_code,
// frame() is about to be destroyed, so there is no need to perform cleanup // frame() is about to be destroyed, so there is no need to perform cleanup
// such as removing before-load JavaScripts. // such as removing before-load JavaScripts.
api_bindings_client_->DetachFromFrame(frame()); api_bindings_client_->DetachFromFrame(frame());
connector_->DetachFromFrame();
WebComponent::DestroyComponent(exit_code, reason); WebComponent::DestroyComponent(exit_code, reason);
} }
...@@ -181,8 +185,18 @@ void CastComponent::OnRewriteRulesReceived( ...@@ -181,8 +185,18 @@ void CastComponent::OnRewriteRulesReceived(
void CastComponent::OnNavigationStateChanged( void CastComponent::OnNavigationStateChanged(
fuchsia::web::NavigationState change, fuchsia::web::NavigationState change,
OnNavigationStateChangedCallback callback) { OnNavigationStateChangedCallback callback) {
if (change.has_is_main_document_loaded() && change.is_main_document_loaded()) if (change.has_is_main_document_loaded() &&
connector_->OnPageLoad(); change.is_main_document_loaded()) {
// Send the NamedMessagePortConnector handshake to the page.
frame()->PostMessage("*",
*cr_fuchsia::FidlWebMessageFromBlink(
connector_->GetConnectMessage(),
cr_fuchsia::TransferableHostType::kRemote),
[](fuchsia::web::Frame_PostMessage_Result result) {
DCHECK(result.is_response());
});
}
WebComponent::OnNavigationStateChanged(std::move(change), WebComponent::OnNavigationStateChanged(std::move(change),
std::move(callback)); std::move(callback));
} }
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" #include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
#include "fuchsia/runners/cast/api_bindings_client.h" #include "fuchsia/runners/cast/api_bindings_client.h"
#include "fuchsia/runners/cast/application_controller_impl.h" #include "fuchsia/runners/cast/application_controller_impl.h"
#include "fuchsia/runners/cast/named_message_port_connector.h" #include "fuchsia/runners/cast/named_message_port_connector_fuchsia.h"
#include "fuchsia/runners/common/web_component.h" #include "fuchsia/runners/common/web_component.h"
namespace cr_fuchsia { namespace cr_fuchsia {
...@@ -108,7 +108,7 @@ class CastComponent : public WebComponent, ...@@ -108,7 +108,7 @@ class CastComponent : public WebComponent,
std::vector<fuchsia::web::UrlRequestRewriteRule> initial_url_rewrite_rules_; std::vector<fuchsia::web::UrlRequestRewriteRule> initial_url_rewrite_rules_;
bool constructor_active_ = false; bool constructor_active_ = false;
std::unique_ptr<NamedMessagePortConnector> connector_; std::unique_ptr<NamedMessagePortConnectorFuchsia> connector_;
std::unique_ptr<ApiBindingsClient> api_bindings_client_; std::unique_ptr<ApiBindingsClient> api_bindings_client_;
std::unique_ptr<ApplicationControllerImpl> application_controller_; std::unique_ptr<ApplicationControllerImpl> application_controller_;
chromium::cast::ApplicationContextPtr application_context_; chromium::cast::ApplicationContextPtr application_context_;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "base/base_paths_fuchsia.h" #include "base/base_paths_fuchsia.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/files/file_util.h"
#include "base/fuchsia/file_utils.h" #include "base/fuchsia/file_utils.h"
#include "base/fuchsia/filtered_service_directory.h" #include "base/fuchsia/filtered_service_directory.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
#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/cast/test_api_bindings.h"
#include "mojo/core/embedder/embedder.h"
#include "net/test/embedded_test_server/default_handlers.h" #include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_request.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -224,6 +226,8 @@ class CastRunnerIntegrationTest : public testing::Test { ...@@ -224,6 +226,8 @@ class CastRunnerIntegrationTest : public testing::Test {
CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) = CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) =
delete; delete;
void SetUp() override { mojo::core::Init(); }
void TearDown() override { void TearDown() override {
if (component_controller_) if (component_controller_)
ShutdownComponent(); ShutdownComponent();
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/scoped_service_binding.h"
#include "base/message_loop/message_pump_type.h" #include "base/message_loop/message_pump_type.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/task/single_thread_task_executor.h" #include "base/task/single_thread_task_executor.h"
#include "base/values.h" #include "base/values.h"
...@@ -18,6 +19,7 @@ ...@@ -18,6 +19,7 @@
#include "fuchsia/base/init_logging.h" #include "fuchsia/base/init_logging.h"
#include "fuchsia/base/inspect.h" #include "fuchsia/base/inspect.h"
#include "fuchsia/runners/cast/cast_runner.h" #include "fuchsia/runners/cast/cast_runner.h"
#include "mojo/core/embedder/embedder.h"
namespace { namespace {
...@@ -59,6 +61,8 @@ int main(int argc, char** argv) { ...@@ -59,6 +61,8 @@ int main(int argc, char** argv) {
*base::CommandLine::ForCurrentProcess())) *base::CommandLine::ForCurrentProcess()))
<< "Failed to initialize logging."; << "Failed to initialize logging.";
mojo::core::Init();
cr_fuchsia::RegisterFuchsiaDirScheme(); cr_fuchsia::RegisterFuchsiaDirScheme();
sys::OutgoingDirectory* const outgoing_directory = sys::OutgoingDirectory* const outgoing_directory =
......
// 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/named_message_port_connector.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/runners/cast/cast_platform_bindings_ids.h"
namespace {
const char kBindingsJsPath[] = FILE_PATH_LITERAL(
"chromecast/bindings/resources/named_message_port_connector.js");
const char kControlPortConnectMessage[] = "cast.master.connect";
} // namespace
NamedMessagePortConnector::NamedMessagePortConnector(fuchsia::web::Frame* frame)
: frame_(frame) {
DCHECK(frame_);
// Inject the script providing the connection API into the Frame.
base::FilePath assets_path;
bool path_service_result =
base::PathService::Get(base::DIR_ASSETS, &assets_path);
DCHECK(path_service_result);
fuchsia::mem::Buffer bindings_script_ = cr_fuchsia::MemBufferFromFile(
base::File(assets_path.AppendASCII(kBindingsJsPath),
base::File::FLAG_OPEN | base::File::FLAG_READ));
std::vector<std::string> origins = {"*"};
frame_->AddBeforeLoadJavaScript(
static_cast<uint64_t>(
CastPlatformBindingsId::NAMED_MESSAGE_PORT_CONNECTOR),
std::move(origins),
cr_fuchsia::CloneBuffer(bindings_script_, "cast-bindings-js"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
CHECK(result.is_response()) << "Couldn't inject bindings.";
});
}
NamedMessagePortConnector::~NamedMessagePortConnector() {
if (frame_) {
// Don't attempt to remove before-load JavaScript when being deleted because
// the Frame has disconnected.
frame_->RemoveBeforeLoadJavaScript(static_cast<uint64_t>(
CastPlatformBindingsId::NAMED_MESSAGE_PORT_CONNECTOR));
}
}
void NamedMessagePortConnector::Register(DefaultPortConnectedCallback handler) {
default_handler_ = std::move(handler);
}
void NamedMessagePortConnector::OnPageLoad() {
control_port_.Unbind();
fuchsia::web::WebMessage message;
message.set_data(cr_fuchsia::MemBufferFromString(kControlPortConnectMessage,
"cast-connect-message"));
std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector(1);
outgoing_vector[0].set_message_port(control_port_.NewRequest());
message.set_outgoing_transfer(std::move(outgoing_vector));
frame_->PostMessage("*", std::move(message),
[](fuchsia::web::Frame_PostMessage_Result result) {
DCHECK(result.is_response());
});
ReceiveNextConnectRequest();
}
void NamedMessagePortConnector::ReceiveNextConnectRequest() {
DCHECK(control_port_);
control_port_->ReceiveMessage(
fit::bind_member(this, &NamedMessagePortConnector::OnConnectRequest));
}
void NamedMessagePortConnector::OnConnectRequest(
fuchsia::web::WebMessage message) {
std::string port_name;
if (!message.has_data() ||
!cr_fuchsia::StringFromMemBuffer(message.data(), &port_name)) {
LOG(ERROR) << "Couldn't read from message VMO.";
control_port_.Unbind();
return;
}
size_t num_transfers = message.has_incoming_transfer() ?
message.incoming_transfer().size() : 0U;
if (num_transfers != 1) {
LOG(ERROR) << "Expected one Transferable, got " << num_transfers
<< " instead.";
control_port_.Unbind();
return;
}
fuchsia::web::IncomingTransferable& transferable =
(*message.mutable_incoming_transfer())[0];
if (!transferable.is_message_port()) {
LOG(ERROR) << "Transferable is not a MessagePort.";
control_port_.Unbind();
return;
}
DCHECK(default_handler_);
default_handler_.Run(port_name, std::move(transferable.message_port()));
ReceiveNextConnectRequest();
}
// 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.
#ifndef FUCHSIA_RUNNERS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_H_
#define FUCHSIA_RUNNERS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_H_
#include <fuchsia/web/cpp/fidl.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <map>
#include <string>
#include "base/callback.h"
#include "base/macros.h"
// Injects an API into |frame| through which it can connect MessagePorts to one
// or more services registered by the caller.
class NamedMessagePortConnector {
public:
using DefaultPortConnectedCallback = base::RepeatingCallback<void(
base::StringPiece,
fidl::InterfaceHandle<fuchsia::web::MessagePort>)>;
explicit NamedMessagePortConnector(fuchsia::web::Frame* frame);
~NamedMessagePortConnector();
// Sets the handler that is called for connected ports which aren't
// registered in advance.
void Register(DefaultPortConnectedCallback handler);
// Invoked by the caller after every |frame_| page load.
// Half-connected ports from prior page generations will be discarded.
void OnPageLoad();
private:
// Gets the next port from |control_port_|.
void ReceiveNextConnectRequest();
// Handles a port received from |control_port_|.
void OnConnectRequest(fuchsia::web::WebMessage message);
fuchsia::web::Frame* const frame_;
// Invoked for ports which weren't previously Register()'ed.
DefaultPortConnectedCallback default_handler_;
fuchsia::web::MessagePortPtr control_port_;
DISALLOW_COPY_AND_ASSIGN(NamedMessagePortConnector);
};
#endif // FUCHSIA_RUNNERS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_H_
// Copyright 2020 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/named_message_port_connector_fuchsia.h"
#include <fuchsia/web/cpp/fidl.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "components/cast/named_message_port_connector/grit/named_message_port_connector_resources.h"
#include "fuchsia/base/mem_buffer_util.h"
namespace {
constexpr uint64_t kPortConnectorBindingsId = 1000;
} // namespace
NamedMessagePortConnectorFuchsia::NamedMessagePortConnectorFuchsia(
fuchsia::web::Frame* frame)
: frame_(frame) {
DCHECK(frame_);
base::FilePath port_connector_js_path;
CHECK(base::PathService::Get(base::DIR_ASSETS, &port_connector_js_path));
port_connector_js_path = port_connector_js_path.AppendASCII(
"components/cast/named_message_port_connector/"
"named_message_port_connector.js");
std::string bindings_script_string;
CHECK(
base::ReadFileToString(port_connector_js_path, &bindings_script_string));
DCHECK(!bindings_script_string.empty())
<< "NamedMessagePortConnector resources not loaded.";
// Inject the JS connection API into the Frame.
constexpr char kBindingsScriptVmoName[] = "port-connector-js";
fuchsia::mem::Buffer bindings_script = cr_fuchsia::MemBufferFromString(
std::move(bindings_script_string), kBindingsScriptVmoName);
std::vector<std::string> origins = {"*"};
frame_->AddBeforeLoadJavaScript(
kPortConnectorBindingsId, std::move(origins), std::move(bindings_script),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
CHECK(result.is_response())
<< "Couldn't inject port connector bindings.";
});
}
NamedMessagePortConnectorFuchsia::~NamedMessagePortConnectorFuchsia() {
// Nothing to do if there is no attached Frame.
if (!frame_)
return;
frame_->RemoveBeforeLoadJavaScript(kPortConnectorBindingsId);
}
void NamedMessagePortConnectorFuchsia::DetachFromFrame() {
frame_ = nullptr;
}
// Copyright 2020 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_NAMED_MESSAGE_PORT_CONNECTOR_FUCHSIA_H_
#define FUCHSIA_RUNNERS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_FUCHSIA_H_
#include "components/cast/named_message_port_connector/named_message_port_connector.h"
namespace fuchsia {
namespace web {
class Frame;
}
} // namespace fuchsia
// Publishes NamedMessagePortConnector services to documents loaded in |frame|.
// OnFrameDisconnect() should be called if the FramePtr is torn down before
// |this|.
class NamedMessagePortConnectorFuchsia
: public cast_api_bindings::NamedMessagePortConnector {
public:
explicit NamedMessagePortConnectorFuchsia(fuchsia::web::Frame* frame);
~NamedMessagePortConnectorFuchsia() override;
NamedMessagePortConnectorFuchsia(const NamedMessagePortConnectorFuchsia&) =
delete;
void operator=(const NamedMessagePortConnectorFuchsia&) = delete;
// Called when the peer Frame connection has terminated.
void DetachFromFrame();
private:
fuchsia::web::Frame* frame_ = nullptr;
};
#endif // FUCHSIA_RUNNERS_CAST_NAMED_MESSAGE_PORT_CONNECTOR_FUCHSIA_H_
...@@ -9,38 +9,39 @@ ...@@ -9,38 +9,39 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "fuchsia/base/fit_adapter.h" #include "fuchsia/base/fit_adapter.h"
#include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/frame_test_util.h"
#include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/base/message_port.h"
#include "fuchsia/base/result_receiver.h" #include "fuchsia/base/result_receiver.h"
#include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/base/test_navigation_listener.h"
#include "fuchsia/engine/test/web_engine_browser_test.h" #include "fuchsia/engine/test/web_engine_browser_test.h"
#include "fuchsia/runners/cast/named_message_port_connector.h" #include "fuchsia/runners/cast/named_message_port_connector_fuchsia.h"
#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/url_constants.h" #include "url/url_constants.h"
class NamedMessagePortConnectorTest : public cr_fuchsia::WebEngineBrowserTest { class NamedMessagePortConnectorFuchsiaTest
: public cr_fuchsia::WebEngineBrowserTest {
public: public:
NamedMessagePortConnectorTest() { NamedMessagePortConnectorFuchsiaTest() {
set_test_server_root(base::FilePath("fuchsia/runners/cast/testdata")); set_test_server_root(base::FilePath("fuchsia/runners/cast/testdata"));
navigation_listener_.SetBeforeAckHook( navigation_listener_.SetBeforeAckHook(base::BindRepeating(
base::BindRepeating(&NamedMessagePortConnectorTest::OnBeforeAckHook, &NamedMessagePortConnectorFuchsiaTest::OnBeforeAckHook,
base::Unretained(this))); base::Unretained(this)));
} }
~NamedMessagePortConnectorTest() override = default; ~NamedMessagePortConnectorFuchsiaTest() override = default;
protected: protected:
// BrowserTestBase implementation. // BrowserTestBase implementation.
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread(); cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread();
frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_); frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_);
base::ScopedAllowBlockingForTesting allow_blocking; connector_ =
connector_ = std::make_unique<NamedMessagePortConnector>(frame_.get()); std::make_unique<NamedMessagePortConnectorFuchsia>(frame_.get());
} }
// Intercepts the page load event to trigger the injection of |connector_|'s // Intercepts the page load event to trigger the injection of |connector_|'s
...@@ -50,8 +51,15 @@ class NamedMessagePortConnectorTest : public cr_fuchsia::WebEngineBrowserTest { ...@@ -50,8 +51,15 @@ class NamedMessagePortConnectorTest : public cr_fuchsia::WebEngineBrowserTest {
fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback
callback) { callback) {
if (change.has_is_main_document_loaded() && if (change.has_is_main_document_loaded() &&
change.is_main_document_loaded()) change.is_main_document_loaded()) {
connector_->OnPageLoad(); frame_->PostMessage("*",
std::move(*cr_fuchsia::FidlWebMessageFromBlink(
connector_->GetConnectMessage(),
cr_fuchsia::TransferableHostType::kRemote)),
[](fuchsia::web::Frame_PostMessage_Result result) {
DCHECK(result.is_response());
});
}
// Allow the TestNavigationListener's usual navigation event processing flow // Allow the TestNavigationListener's usual navigation event processing flow
// to continue. // to continue.
...@@ -60,36 +68,42 @@ class NamedMessagePortConnectorTest : public cr_fuchsia::WebEngineBrowserTest { ...@@ -60,36 +68,42 @@ class NamedMessagePortConnectorTest : public cr_fuchsia::WebEngineBrowserTest {
std::unique_ptr<base::RunLoop> navigate_run_loop_; std::unique_ptr<base::RunLoop> navigate_run_loop_;
fuchsia::web::FramePtr frame_; fuchsia::web::FramePtr frame_;
std::unique_ptr<NamedMessagePortConnector> connector_; std::unique_ptr<NamedMessagePortConnectorFuchsia> connector_;
cr_fuchsia::TestNavigationListener navigation_listener_; cr_fuchsia::TestNavigationListener navigation_listener_;
DISALLOW_COPY_AND_ASSIGN(NamedMessagePortConnectorTest); DISALLOW_COPY_AND_ASSIGN(NamedMessagePortConnectorFuchsiaTest);
}; };
IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, EndToEnd) { IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorFuchsiaTest, EndToEnd) {
ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url(embedded_test_server()->GetURL("/connector.html")); GURL test_url(embedded_test_server()->GetURL("/connector.html"));
fuchsia::web::NavigationControllerPtr controller; fuchsia::web::NavigationControllerPtr controller;
frame_->GetNavigationController(controller.NewRequest()); frame_->GetNavigationController(controller.NewRequest());
std::string received_port_name;
fidl::InterfaceHandle<fuchsia::web::MessagePort> received_port; fidl::InterfaceHandle<fuchsia::web::MessagePort> received_port;
base::RunLoop receive_port_run_loop; base::RunLoop receive_port_run_loop;
connector_->Register(base::BindRepeating( connector_->RegisterPortHandler(base::BindRepeating(
[](fidl::InterfaceHandle<fuchsia::web::MessagePort>* received_port, [](std::string* received_port_name,
base::RunLoop* receive_port_run_loop, base::StringPiece name, fidl::InterfaceHandle<fuchsia::web::MessagePort>* received_port,
fidl::InterfaceHandle<fuchsia::web::MessagePort> message_port) { base::RunLoop* receive_port_run_loop, base::StringPiece port_name,
*received_port = std::move(message_port); blink::WebMessagePort port) -> bool {
*received_port_name = port_name.as_string();
*received_port = cr_fuchsia::FidlMessagePortFromBlink(std::move(port));
receive_port_run_loop->Quit(); receive_port_run_loop->Quit();
return true;
}, },
base::Unretained(&received_port), base::Unretained(&received_port_name), base::Unretained(&received_port),
base::Unretained(&receive_port_run_loop))); base::Unretained(&receive_port_run_loop)));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec())); controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
navigation_listener_.RunUntilUrlEquals(test_url); navigation_listener_.RunUntilUrlEquals(test_url);
// The JS code in connector.html should connect to the port "echo".
receive_port_run_loop.Run(); receive_port_run_loop.Run();
EXPECT_EQ(received_port_name, "echo");
fuchsia::web::MessagePortPtr message_port = received_port.Bind(); fuchsia::web::MessagePortPtr message_port = received_port.Bind();
...@@ -130,9 +144,11 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, EndToEnd) { ...@@ -130,9 +144,11 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, EndToEnd) {
} }
} }
// Tests that the NamedMessagePortConnector can receive more than one port over // Tests that the NamedMessagePortConnectorFuchsia can receive more than one
// its lifetime. // port over its lifetime.
IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) { IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorFuchsiaTest, MultiplePorts) {
constexpr size_t kExpectedPortCount = 3;
ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url( GURL test_url(
embedded_test_server()->GetURL("/connector_multiple_ports.html")); embedded_test_server()->GetURL("/connector_multiple_ports.html"));
...@@ -142,15 +158,18 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) { ...@@ -142,15 +158,18 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) {
std::vector<fidl::InterfaceHandle<fuchsia::web::MessagePort>> received_ports; std::vector<fidl::InterfaceHandle<fuchsia::web::MessagePort>> received_ports;
base::RunLoop receive_ports_run_loop; base::RunLoop receive_ports_run_loop;
connector_->Register(base::BindRepeating( connector_->RegisterPortHandler(base::BindRepeating(
[](std::vector<fidl::InterfaceHandle<fuchsia::web::MessagePort>>* [](std::vector<fidl::InterfaceHandle<fuchsia::web::MessagePort>>*
received_ports, received_ports,
base::RunLoop* receive_ports_run_loop, base::StringPiece name, base::RunLoop* receive_ports_run_loop, base::StringPiece,
fidl::InterfaceHandle<fuchsia::web::MessagePort> message_port) { blink::WebMessagePort port) -> bool {
received_ports->push_back(std::move(message_port)); received_ports->push_back(
cr_fuchsia::FidlMessagePortFromBlink(std::move(port)));
if (received_ports->size() == 3) if (received_ports->size() == kExpectedPortCount)
receive_ports_run_loop->Quit(); receive_ports_run_loop->Quit();
return true;
}, },
base::Unretained(&received_ports), base::Unretained(&received_ports),
base::Unretained(&receive_ports_run_loop))); base::Unretained(&receive_ports_run_loop)));
...@@ -158,7 +177,6 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) { ...@@ -158,7 +177,6 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) {
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec())); controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
navigation_listener_.RunUntilUrlEquals(test_url); navigation_listener_.RunUntilUrlEquals(test_url);
receive_ports_run_loop.Run(); receive_ports_run_loop.Run();
for (fidl::InterfaceHandle<fuchsia::web::MessagePort>& message_port : for (fidl::InterfaceHandle<fuchsia::web::MessagePort>& message_port :
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<head><title>bindings</title></head> <head><title>bindings</title></head>
<body> <body>
<script> <script>
var testChannel = cast.__platform__.PortConnector.bind('hello'); var testChannel = cast.__platform__.PortConnector.bind('echo');
testChannel.onmessage = function(msg) { testChannel.onmessage = function(msg) {
testChannel.postMessage('ack ' + msg.data); testChannel.postMessage('ack ' + msg.data);
return true; return true;
......
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