blob: bdebecf348d8d828c71e67530a9832d4e6acc53f [file] [log] [blame]
// 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 "chromecast/bindings/bindings_manager_cast.h"
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chromecast/bindings/grit/resources.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() : cast_web_contents_(nullptr) {
// NamedMessagePortConnector binding will be injected into page first.
AddBinding(kNamedMessagePortConnectorBindingsId,
ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_PORT_CONNECTOR_JS));
}
BindingsManagerCast::~BindingsManagerCast() = default;
void BindingsManagerCast::AddBinding(base::StringPiece binding_name,
base::StringPiece binding_script) {
bindings_by_id_[binding_name.as_string()] = binding_script.as_string();
}
void BindingsManagerCast::AttachToPage(
chromecast::CastWebContents* cast_web_contents) {
DCHECK(!cast_web_contents_) << "AttachToPage() was called twice.";
DCHECK(cast_web_contents);
cast_web_contents_ = cast_web_contents;
CastWebContents::Observer::Observe(cast_web_contents_);
for (const auto& binding : bindings_by_id_) {
LOG(INFO) << "Register bindings for page. bindingId: " << binding.first;
cast_web_contents_->AddBeforeLoadJavaScript(
binding.first /* binding ID */, {"*"}, binding.second /* binding JS */);
}
}
void BindingsManagerCast::OnPageStateChanged(
CastWebContents* cast_web_contents) {
auto page_state = cast_web_contents->page_state();
switch (page_state) {
case CastWebContents::PageState::IDLE:
case CastWebContents::PageState::LOADING:
case CastWebContents::PageState::CLOSED:
return;
case CastWebContents::PageState::DESTROYED:
case CastWebContents::PageState::ERROR:
blink_port_.Reset();
CastWebContents::Observer::Observe(nullptr);
cast_web_contents_ = nullptr;
return;
case CastWebContents::PageState::LOADED:
OnPageLoaded();
return;
}
}
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));
}
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