| // 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 "third_party/blink/public/common/messaging/web_message_port.h" |
| |
| #include "base/memory/ptr_util.h" |
| #include "third_party/blink/public/common/messaging/message_port_channel.h" |
| #include "third_party/blink/public/common/messaging/string_message_codec.h" |
| #include "third_party/blink/public/common/messaging/transferable_message.h" |
| #include "third_party/blink/public/common/messaging/transferable_message_mojom_traits.h" |
| #include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h" |
| |
| namespace blink { |
| |
| WebMessagePort::Message::Message() = default; |
| WebMessagePort::Message::Message(Message&&) = default; |
| WebMessagePort::Message& WebMessagePort::Message::operator=(Message&&) = |
| default; |
| WebMessagePort::Message::~Message() = default; |
| |
| WebMessagePort::Message::Message(const base::string16& data) : data(data) {} |
| |
| WebMessagePort::Message::Message(std::vector<WebMessagePort> ports) |
| : ports(std::move(ports)) {} |
| |
| WebMessagePort::Message::Message(WebMessagePort&& port) { |
| ports.emplace_back(std::move(port)); |
| } |
| |
| WebMessagePort::Message::Message(const base::string16& data, |
| std::vector<WebMessagePort> ports) |
| : data(data), ports(std::move(ports)) {} |
| |
| WebMessagePort::Message::Message(const base::string16& data, |
| WebMessagePort port) |
| : data(data) { |
| ports.emplace_back(std::move(port)); |
| } |
| |
| WebMessagePort::MessageReceiver::MessageReceiver() = default; |
| WebMessagePort::MessageReceiver::~MessageReceiver() = default; |
| |
| bool WebMessagePort::MessageReceiver::OnMessage(Message) { |
| return false; |
| } |
| |
| WebMessagePort::WebMessagePort() = default; |
| |
| WebMessagePort::WebMessagePort(WebMessagePort&& other) { |
| Take(std::move(other)); |
| } |
| |
| WebMessagePort& WebMessagePort::operator=(WebMessagePort&& other) { |
| CloseIfNecessary(); |
| Take(std::move(other)); |
| return *this; |
| } |
| |
| WebMessagePort::~WebMessagePort() { |
| CloseIfNecessary(); |
| } |
| |
| // static |
| std::pair<WebMessagePort, WebMessagePort> WebMessagePort::CreatePair() { |
| MessagePortDescriptorPair port_pair; |
| return std::make_pair(WebMessagePort(port_pair.TakePort0()), |
| WebMessagePort(port_pair.TakePort1())); |
| } |
| |
| void WebMessagePort::SetReceiver( |
| MessageReceiver* receiver, |
| scoped_refptr<base::SequencedTaskRunner> runner) { |
| DCHECK(receiver); |
| DCHECK(runner.get()); |
| |
| DCHECK(port_.IsValid()); |
| DCHECK(!connector_); |
| DCHECK(!is_closed_); |
| DCHECK(!is_errored_); |
| DCHECK(is_transferable_); |
| |
| is_transferable_ = false; |
| receiver_ = receiver; |
| connector_ = std::make_unique<mojo::Connector>( |
| port_.TakeHandleToEntangleWithEmbedder(), |
| mojo::Connector::SINGLE_THREADED_SEND, std::move(runner)); |
| connector_->set_incoming_receiver(this); |
| connector_->set_connection_error_handler( |
| base::BindOnce(&WebMessagePort::OnPipeError, base::Unretained(this))); |
| } |
| |
| void WebMessagePort::ClearReceiver() { |
| if (!connector_) |
| return; |
| port_.GiveDisentangledHandle(connector_->PassMessagePipe()); |
| connector_.reset(); |
| receiver_ = nullptr; |
| } |
| |
| base::SequencedTaskRunner* WebMessagePort::GetTaskRunner() const { |
| if (!connector_) |
| return nullptr; |
| return connector_->task_runner(); |
| } |
| |
| MessagePortDescriptor WebMessagePort::PassPort() { |
| DCHECK(is_transferable_); |
| |
| // Clear the receiver, which takes the handle out of the connector if it |
| // exists, and puts it back in |port_|. |
| ClearReceiver(); |
| MessagePortDescriptor port = std::move(port_); |
| Reset(); |
| return port; |
| } |
| |
| WebMessagePort::WebMessagePort(MessagePortDescriptor&& port) |
| : port_(std::move(port)), is_closed_(false), is_transferable_(true) { |
| DCHECK(port_.IsValid()); |
| } |
| |
| bool WebMessagePort::CanPostMessage() const { |
| return connector_ && connector_->is_valid() && !is_closed_ && !is_errored_ && |
| receiver_; |
| } |
| |
| bool WebMessagePort::PostMessage(Message&& message) { |
| if (!CanPostMessage()) |
| return false; |
| |
| // Extract the underlying handles for transport in a |
| // blink::TransferableMessage. |
| std::vector<MessagePortDescriptor> ports; |
| for (auto& port : message.ports) { |
| // We should not be trying to send ourselves in a message. Mojo prevents |
| // this at a deeper level, but we can also check here. |
| DCHECK_NE(this, &port); |
| |
| ports.emplace_back(port.PassPort()); |
| } |
| |
| // Build the message. |
| // TODO(chrisha): Finally kill off MessagePortChannel, once |
| // MessagePortDescriptor more thoroughly plays that role. |
| blink::TransferableMessage transferable_message; |
| transferable_message.owned_encoded_message = |
| blink::EncodeStringMessage(message.data); |
| transferable_message.encoded_message = |
| transferable_message.owned_encoded_message; |
| transferable_message.ports = |
| blink::MessagePortChannel::CreateFromHandles(std::move(ports)); |
| |
| // TODO(chrisha): Notify the instrumentation delegate of a message being sent! |
| |
| // Send via Mojo. The message should never be malformed so should always be |
| // accepted. |
| mojo::Message mojo_message = |
| blink::mojom::TransferableMessage::SerializeAsMessage( |
| &transferable_message); |
| CHECK(connector_->Accept(&mojo_message)); |
| |
| return true; |
| } |
| |
| bool WebMessagePort::IsValid() const { |
| if (connector_) |
| return connector_->is_valid(); |
| return port_.IsValid(); |
| } |
| |
| void WebMessagePort::Close() { |
| CloseIfNecessary(); |
| } |
| |
| void WebMessagePort::Reset() { |
| CloseIfNecessary(); |
| is_closed_ = true; |
| is_errored_ = false; |
| is_transferable_ = false; |
| } |
| |
| void WebMessagePort::Take(WebMessagePort&& other) { |
| port_ = std::move(other.port_); |
| connector_ = std::move(other.connector_); |
| is_closed_ = std::exchange(other.is_closed_, true); |
| is_errored_ = std::exchange(other.is_errored_, false); |
| is_transferable_ = std::exchange(other.is_transferable_, false); |
| receiver_ = std::exchange(other.receiver_, nullptr); |
| } |
| |
| void WebMessagePort::OnPipeError() { |
| DCHECK(!is_transferable_); |
| if (is_errored_) |
| return; |
| is_errored_ = true; |
| if (receiver_) |
| receiver_->OnPipeError(); |
| } |
| |
| void WebMessagePort::CloseIfNecessary() { |
| if (is_closed_) |
| return; |
| is_closed_ = true; |
| ClearReceiver(); |
| port_.Reset(); |
| } |
| |
| bool WebMessagePort::Accept(mojo::Message* mojo_message) { |
| DCHECK(receiver_); |
| DCHECK(!is_transferable_); |
| |
| // Deserialize the message. |
| blink::TransferableMessage transferable_message; |
| if (!blink::mojom::TransferableMessage::DeserializeFromMessage( |
| std::move(*mojo_message), &transferable_message)) { |
| return false; |
| } |
| |
| // Decode the string portion of the message. |
| Message message; |
| if (!blink::DecodeStringMessage(transferable_message.encoded_message, |
| &message.data)) { |
| return false; |
| } |
| |
| // Convert raw handles to MessagePorts. |
| // TODO(chrisha): Kill off MessagePortChannel entirely! |
| auto handles = |
| blink::MessagePortChannel::ReleaseHandles(transferable_message.ports); |
| for (auto& handle : handles) { |
| message.ports.emplace_back(WebMessagePort(std::move(handle))); |
| } |
| |
| // Pass the message on to the receiver. |
| return receiver_->OnMessage(std::move(message)); |
| } |
| |
| } // namespace blink |