Commit 49fc0673 authored by Kevin Marshall's avatar Kevin Marshall Committed by Commit Bot

[bindings] Integrate NamedMessagePortConnector into //chromecast.

Replaces port connection logic in BindingsManagerCast
with the NamedMessagePortConnector component.

Bug: 1104369
Change-Id: Ifbf4f3414cf0bf44e7041b5084c24f3edbdac84e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2404744
Auto-Submit: Kevin Marshall <kmarshall@chromium.org>
Commit-Queue: Sergey Volk <servolk@chromium.org>
Reviewed-by: default avatarSergey Volk <servolk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808673}
parent bb86fc81
......@@ -502,7 +502,7 @@ cast_executable("cast_shell") {
repack("cast_shell_pak") {
sources = [
"$root_gen_dir/chromecast/app/shell_resources.pak",
"$root_gen_dir/chromecast/bindings/bindings_resources.pak",
"$root_gen_dir/components/cast/named_message_port_connector/named_message_port_connector_resources.pak",
"$root_gen_dir/content/app/resources/content_resources_100_percent.pak",
"$root_gen_dir/content/content_resources.pak",
"$root_gen_dir/content/dev_ui_content_resources.pak",
......@@ -521,7 +521,7 @@ repack("cast_shell_pak") {
deps = [
"//chromecast/app:resources",
"//chromecast/bindings:bindings_resources_grit",
"//components/cast/named_message_port_connector:resources",
"//content:content_resources",
"//content:dev_ui_content_resources",
"//content/app/resources",
......
......@@ -8,14 +8,15 @@ if (is_fuchsia) {
import("//build/config/fuchsia/generate_runner_scripts.gni")
}
# TODO(crbug.com/1104369): Deprecated. Delete after off-tree clients
# are migrated to use NamedMessagePortConnector directly.
source_set("named_message_port_connector_resources") {
data = [
"resources/named_message_port_connector.js",
"${target_gen_dir}/bindings_resources.pak",
]
data_deps = [ ":bindings_resources" ]
deps = [ ":bindings_resources" ]
}
# TODO(crbug.com/1104369): Deprecated. Delete after off-tree clients
# are migrated to use NamedMessagePortConnector directly.
grit("bindings_resources") {
source = "bindings_resources.grd"
outputs = [
......@@ -55,19 +56,19 @@ if (is_linux || is_chromeos || is_android) {
sources = [
"bindings_manager_cast.cc",
"bindings_manager_cast.h",
"named_message_port_connector_cast.cc",
"named_message_port_connector_cast.h",
]
deps = [
":bindings_manager",
":bindings_resources",
"//base",
"//chromecast/browser:public",
"//components/cast/api_bindings:manager",
"//components/cast/named_message_port_connector",
"//components/on_load_script_injector/browser",
"//mojo/public/cpp/system",
"//third_party/blink/public/common",
]
data_deps = [ ":named_message_port_connector_resources" ]
}
source_set("browsertests_cast") {
......@@ -84,9 +85,9 @@ if (is_linux || is_chromeos || is_android) {
"//chromecast/base",
"//chromecast/base:chromecast_switches",
"//chromecast/bindings:bindings_manager_cast",
"//chromecast/bindings:named_message_port_connector_resources",
"//chromecast/browser:browser",
"//chromecast/browser:public",
"//components/cast/named_message_port_connector:resources",
"//content/public/browser",
"//content/test:test_support",
"//net:test_support",
......
include_rules = [
"+chromecast/browser",
"+components/cast/api_bindings",
"+components/cast/named_message_port_connector",
"+components/on_load_script_injector/browser",
"+content/public/common",
"+content/public/test",
......
......@@ -9,36 +9,23 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chromecast/bindings/grit/resources.h"
#include "chromecast/bindings/named_message_port_connector_cast.h"
#include "components/on_load_script_injector/browser/on_load_script_injector_host.h"
#include "mojo/public/cpp/bindings/connector.h"
#include "ui/base/resource/resource_bundle.h"
namespace chromecast {
namespace bindings {
namespace {
const char kNamedMessagePortConnectorBindingsId[] =
"NAMED_MESSAGE_PORT_CONNECTOR";
const char kControlPortConnectMessage[] = "cast.master.connect";
} // namespace
BindingsManagerCast::BindingsManagerCast(
chromecast::CastWebContents* cast_web_contents)
: cast_web_contents_(cast_web_contents) {
DCHECK(cast_web_contents_);
// TODO(crbug.com/1103058): Remove this when NamedMessagePortConnector is
// added as a Component.
CastWebContents::Observer::Observe(cast_web_contents_);
// NamedMessagePortConnector binding will be injected into page first.
AddBinding(kNamedMessagePortConnectorBindingsId,
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_PORT_CONNECTOR_JS));
port_connector_ =
std::make_unique<NamedMessagePortConnectorCast>(cast_web_contents_, this);
port_connector_->RegisterPortHandler(base::BindRepeating(
&BindingsManagerCast::OnPortConnected, base::Unretained(this)));
}
BindingsManagerCast::~BindingsManagerCast() = default;
......@@ -49,29 +36,6 @@ void BindingsManagerCast::AddBinding(base::StringPiece binding_name,
binding_name.as_string(), binding_script);
}
void BindingsManagerCast::OnPageLoaded() {
DCHECK(cast_web_contents_)
<< "Received PageLoaded event while not observing a page";
// Unbind platform-side MessagePort connector.
blink_port_.Reset();
// Create a blink::WebMessagePort, this is the way Chromium implements HTML5
// MessagePorts.
auto port_pair = blink::WebMessagePort::CreatePair();
blink_port_ = std::move(port_pair.first);
blink_port_.SetReceiver(this, base::ThreadTaskRunnerHandle::Get());
// Post the other end of the pipe to the page so that we can receive messages
// over |content_port|. |named_message_port_connector.js| will receive this
// through an onmessage event.
std::vector<blink::WebMessagePort> message_ports;
message_ports.push_back(std::move(port_pair.second));
cast_web_contents_->PostMessageToMainFrame("*", kControlPortConnectMessage,
std::move(message_ports));
}
void BindingsManagerCast::OnPageStateChanged(
CastWebContents* cast_web_contents) {
auto page_state = cast_web_contents->page_state();
......@@ -79,50 +43,21 @@ void BindingsManagerCast::OnPageStateChanged(
switch (page_state) {
case CastWebContents::PageState::LOADING:
cast_web_contents_->InjectScriptsIntoMainFrame();
return;
break;
case CastWebContents::PageState::DESTROYED:
case CastWebContents::PageState::ERROR:
blink_port_.Reset();
CastWebContents::Observer::Observe(nullptr);
cast_web_contents_ = nullptr;
return;
port_connector_.reset();
break;
case CastWebContents::PageState::LOADED:
OnPageLoaded();
return;
port_connector_->OnPageLoaded();
break;
case CastWebContents::PageState::IDLE:
case CastWebContents::PageState::CLOSED:
return;
break;
}
}
bool BindingsManagerCast::OnMessage(blink::WebMessagePort::Message message) {
// Receive MessagePort and forward ports to their corresponding
// binding handlers.
// One and only one MessagePort should be sent to here.
if (message.ports.empty())
LOG(ERROR) << "blink::WebMessagePort::Message contains no ports.";
DCHECK_EQ(1u, message.ports.size())
<< "Only one control port should be provided";
blink::WebMessagePort message_port = std::move(message.ports[0]);
message.ports.clear();
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;
}
// Route the port to corresponding binding backend.
OnPortConnected(binding_id, std::move(message_port));
return true;
}
void BindingsManagerCast::OnPipeError() {
LOG(INFO) << "NamedMessagePortConnector control port disconnected";
blink_port_.Reset();
}
} // namespace bindings
} // namespace chromecast
......@@ -5,19 +5,15 @@
#ifndef CHROMECAST_BINDINGS_BINDINGS_MANAGER_CAST_H_
#define CHROMECAST_BINDINGS_BINDINGS_MANAGER_CAST_H_
#include <map>
#include <string>
#include "base/callback.h"
#include "chromecast/bindings/bindings_manager.h"
#include "chromecast/browser/cast_web_contents.h"
#include "mojo/public/cpp/bindings/connector.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "components/cast/api_bindings/manager.h"
namespace chromecast {
namespace bindings {
class NamedMessagePortConnectorCast;
// Implements the CastOS BindingsManager.
class BindingsManagerCast : public BindingsManager,
public CastWebContents::Observer,
......@@ -26,12 +22,8 @@ class BindingsManagerCast : public BindingsManager,
explicit BindingsManagerCast(chromecast::CastWebContents* cast_web_contents);
~BindingsManagerCast() override;
// The document and its statically-declared subresources are loaded.
// BindingsManagerCast will inject all registered bindings at this time.
// BindingsManagerCast will post a message that conveys an end of MessagePort
// to the loaded page, so that the NamedMessagePort binding could utilize the
// port to communicate with the native part.
void OnPageLoaded();
BindingsManagerCast(const BindingsManagerCast&) = delete;
void operator=(const BindingsManagerCast&) = delete;
// BindingsManager implementation.
void AddBinding(base::StringPiece binding_name,
......@@ -41,16 +33,8 @@ class BindingsManagerCast : public BindingsManager,
// CastWebContents::Observer implementation.
void OnPageStateChanged(CastWebContents* cast_web_contents) override;
// blink::WebMessagePort::MessageReceiver implementation:
bool OnMessage(blink::WebMessagePort::Message message) override;
void OnPipeError() override;
chromecast::CastWebContents* cast_web_contents_;
// Receives messages from JS.
blink::WebMessagePort blink_port_;
DISALLOW_COPY_AND_ASSIGN(BindingsManagerCast);
std::unique_ptr<NamedMessagePortConnectorCast> port_connector_;
};
} // namespace bindings
......
......@@ -112,7 +112,7 @@ class TitleChangeObserver : public CastWebContents::Observer {
// Test class for communicating with connector.html.
class TestBindingBackend : public blink::WebMessagePort::MessageReceiver {
public:
TestBindingBackend(bindings::BindingsManager* bindings_manager)
TestBindingBackend(bindings::BindingsManagerCast* bindings_manager)
: bindings_manager_(bindings_manager) {
constexpr char kPortName[] = "hello";
bindings_manager_->RegisterPortHandler(
......
......@@ -9,7 +9,7 @@
<translations />
<release seq="1">
<includes>
<include name="IDR_PORT_CONNECTOR_JS" file="resources\named_message_port_connector.js" type="BINDATA" />
<include name="IDR_PORT_CONNECTOR_JS" file="../../components/cast/named_message_port_connector/named_message_port_connector.js" type="BINDATA" />
</includes>
</release>
</grit>
// 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 "chromecast/bindings/named_message_port_connector_cast.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chromecast/browser/cast_web_contents.h"
#include "components/cast/api_bindings/manager.h"
#include "components/cast/named_message_port_connector/grit/named_message_port_connector_resources.h"
#include "ui/base/resource/resource_bundle.h"
namespace chromecast {
namespace bindings {
namespace {
const char kNamedMessagePortConnectorBindingsId[] =
"NAMED_MESSAGE_PORT_CONNECTOR";
} // namespace
NamedMessagePortConnectorCast::NamedMessagePortConnectorCast(
chromecast::CastWebContents* cast_web_contents,
cast_api_bindings::Manager* bindings_manager)
: cast_web_contents_(cast_web_contents),
bindings_manager_(bindings_manager) {
DCHECK(cast_web_contents_);
DCHECK(bindings_manager_);
// Register the port connection JS script for early injection.
std::string bindings_script_string =
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_PORT_CONNECTOR_JS);
DCHECK(!bindings_script_string.empty())
<< "NamedMessagePortConnector resources not loaded.";
bindings_manager_->AddBinding(kNamedMessagePortConnectorBindingsId,
bindings_script_string);
}
NamedMessagePortConnectorCast::~NamedMessagePortConnectorCast() = default;
void NamedMessagePortConnectorCast::OnPageLoaded() {
// Send the port connection message to the page once it is loaded.
blink::WebMessagePort::Message connect_message = GetConnectMessage();
cast_web_contents_->PostMessageToMainFrame(
"*", base::UTF16ToUTF8(connect_message.data),
std::move(connect_message.ports));
}
} // namespace bindings
} // namespace chromecast
// 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 CHROMECAST_BINDINGS_NAMED_MESSAGE_PORT_CONNECTOR_CAST_H_
#define CHROMECAST_BINDINGS_NAMED_MESSAGE_PORT_CONNECTOR_CAST_H_
#include "components/cast/named_message_port_connector/named_message_port_connector.h"
namespace cast_api_bindings {
class Manager;
}
namespace chromecast {
class CastWebContents;
namespace bindings {
// Injects and connects to NamedMessagePortConnector services into documents
// hosted by |cast_web_contents|.
class NamedMessagePortConnectorCast
: public cast_api_bindings::NamedMessagePortConnector {
public:
// |cast_web_contents|: The CastWebContents which will receive port connection
// services.
// |bindings_manager|: The BindingsManager instance that handles script
// injection on |cast_web_contents|.
// Both arguments must outlive |this|.
NamedMessagePortConnectorCast(chromecast::CastWebContents* cast_web_contents,
cast_api_bindings::Manager* bindings_manager);
~NamedMessagePortConnectorCast() override;
NamedMessagePortConnectorCast(const NamedMessagePortConnectorCast&) = delete;
void operator=(const NamedMessagePortConnectorCast&) = delete;
// Sends a connection message to |cast_web_contents_|.
// Should be invoked when |cast_web_contents| has finished loading a page.
void OnPageLoaded();
private:
chromecast::CastWebContents* cast_web_contents_;
cast_api_bindings::Manager* bindings_manager_;
};
} // namespace bindings
} // namespace chromecast
#endif // CHROMECAST_BINDINGS_NAMED_MESSAGE_PORT_CONNECTOR_CAST_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)
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) {
var 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 (var i in e.ports)
e.ports[i].close()
return;
}
this.controlPort_ = e.ports[0]
for (var 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);
}
}();
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