1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "fuchsia/base/message_port.h"
6
7 #include <stdint.h>
8
9 #include <lib/fidl/cpp/binding.h>
10 #include <lib/fit/function.h>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "base/bind.h"
17 #include "base/check.h"
18 #include "base/containers/circular_deque.h"
19 #include "base/fuchsia/fuchsia_logging.h"
20 #include "base/macros.h"
21 #include "fuchsia/base/mem_buffer_util.h"
22
23 namespace cr_fuchsia {
24 namespace {
25
26 using BlinkMessage = blink::WebMessagePort::Message;
27
28 // Converts a fuchsia::web::WebMessage to a BlinkMessage.
29 // An empty result indicates that conversion was successful.
30 // Data validation errors are returned as a FrameError.
BlinkMessageFromFidl(fuchsia::web::WebMessage fidl_message,BlinkMessage * blink_message)31 base::Optional<fuchsia::web::FrameError> BlinkMessageFromFidl(
32 fuchsia::web::WebMessage fidl_message,
33 BlinkMessage* blink_message) {
34 if (!fidl_message.has_data()) {
35 return fuchsia::web::FrameError::NO_DATA_IN_MESSAGE;
36 }
37
38 base::string16 data_utf16;
39 if (!cr_fuchsia::ReadUTF8FromVMOAsUTF16(fidl_message.data(), &data_utf16)) {
40 return fuchsia::web::FrameError::BUFFER_NOT_UTF8;
41 }
42 blink_message->data = data_utf16;
43
44 if (fidl_message.has_outgoing_transfer() &&
45 fidl_message.has_incoming_transfer()) {
46 DLOG(WARNING) << "WebMessage may only have incoming or outgoing transfer.";
47 return fuchsia::web::FrameError::INTERNAL_ERROR;
48 }
49 if (fidl_message.has_outgoing_transfer()) {
50 for (fuchsia::web::OutgoingTransferable& transferrable :
51 *fidl_message.mutable_outgoing_transfer()) {
52 if (!transferrable.is_message_port())
53 return fuchsia::web::FrameError::INTERNAL_ERROR;
54 blink_message->ports.push_back(
55 BlinkMessagePortFromFidl(std::move(transferrable.message_port())));
56 }
57 } else if (fidl_message.has_incoming_transfer()) {
58 for (fuchsia::web::IncomingTransferable& transferrable :
59 *fidl_message.mutable_incoming_transfer()) {
60 if (!transferrable.is_message_port())
61 return fuchsia::web::FrameError::INTERNAL_ERROR;
62 blink_message->ports.push_back(
63 BlinkMessagePortFromFidl(std::move(transferrable.message_port())));
64 }
65 }
66
67 return base::nullopt;
68 }
69
70 // Defines a MessagePortAdapter, which translates and routes messages between a
71 // FIDL MessagePort and a blink::WebMessagePort. Every MessagePortAdapter has
72 // exactly one FIDL MessagePort and one blink::WebMessagePort.
73 //
74 // MessagePortAdapter instances are self-managed; they destroy themselves when
75 // the connection is terminated from either the Blink or FIDL side.
76 class MessagePortAdapter : public blink::WebMessagePort::MessageReceiver {
77 protected:
MessagePortAdapter(blink::WebMessagePort blink_port)78 explicit MessagePortAdapter(blink::WebMessagePort blink_port)
79 : blink_port_(std::move(blink_port)) {
80 blink_port_.SetReceiver(this, base::ThreadTaskRunnerHandle::Get());
81 }
82
83 ~MessagePortAdapter() override = default;
84
85 // Deletes |this|, implicitly disconnecting the FIDL and Blink ports.
Destroy()86 void Destroy() { delete this; }
87
88 // Sends a message to |blink_port_|.
SendBlinkMessage(BlinkMessage message)89 void SendBlinkMessage(BlinkMessage message) {
90 CHECK(blink_port_.PostMessage(std::move(message)));
91 }
92
93 // Called when a Blink message was received through |blink_port_|.
94 virtual void DeliverMessageToFidl() = 0;
95
96 // Returns the next messagefrom Blink, or an empty value if there
97 // are no more messages in the incoming queue.
GetNextBlinkMessage()98 base::Optional<fuchsia::web::WebMessage> GetNextBlinkMessage() {
99 if (message_queue_.empty())
100 return base::nullopt;
101
102 return std::move(message_queue_.front());
103 }
104
OnDeliverMessageToFidlComplete()105 void OnDeliverMessageToFidlComplete() {
106 DCHECK(!message_queue_.empty());
107 message_queue_.pop_front();
108 }
109
110 private:
111 // blink::WebMessagePort::MessageReceiver implementation:
OnMessage(BlinkMessage message)112 bool OnMessage(BlinkMessage message) override {
113 base::Optional<fuchsia::web::WebMessage> message_converted =
114 FidlWebMessageFromBlink(std::move(message),
115 TransferableHostType::kLocal);
116 if (!message_converted) {
117 DLOG(ERROR) << "Couldn't decode WebMessage from blink::WebMessagePort.";
118 Destroy();
119 return false;
120 }
121 message_queue_.emplace_back(std::move(*message_converted));
122
123 // Start draining the queue if it was empty beforehand.
124 if (message_queue_.size() == 1u)
125 DeliverMessageToFidl();
126
127 return true;
128 }
129
130 // blink::WebMessagePort::MessageReceiver implementation:
OnPipeError()131 void OnPipeError() override { Destroy(); }
132
133 base::circular_deque<fuchsia::web::WebMessage> message_queue_;
134 blink::WebMessagePort blink_port_;
135
136 DISALLOW_COPY_AND_ASSIGN(MessagePortAdapter);
137 };
138
139 // Binds a handle to a remote MessagePort to a blink::WebMessagePort.
140 class FidlMessagePortClientAdapter : public MessagePortAdapter {
141 public:
FidlMessagePortClientAdapter(blink::WebMessagePort blink_port,fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_port)142 FidlMessagePortClientAdapter(
143 blink::WebMessagePort blink_port,
144 fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_port)
145 : MessagePortAdapter(std::move(blink_port)), port_(fidl_port.Bind()) {
146 ReadMessageFromFidl();
147
148 port_.set_error_handler([this](zx_status_t status) {
149 ZX_LOG_IF(ERROR,
150 status != ZX_ERR_PEER_CLOSED && status != ZX_ERR_CANCELED,
151 status)
152 << " MessagePort disconnected.";
153 Destroy();
154 });
155 }
156
NewRequest()157 fidl::InterfaceRequest<fuchsia::web::MessagePort> NewRequest() {
158 return port_.NewRequest();
159 }
160
161 private:
162 ~FidlMessagePortClientAdapter() override = default;
163
ReadMessageFromFidl()164 void ReadMessageFromFidl() {
165 port_->ReceiveMessage(fit::bind_member(
166 this, &FidlMessagePortClientAdapter::OnMessageReceived));
167 }
168
OnMessageReceived(fuchsia::web::WebMessage message)169 void OnMessageReceived(fuchsia::web::WebMessage message) {
170 BlinkMessage blink_message;
171 base::Optional<fuchsia::web::FrameError> result =
172 BlinkMessageFromFidl(std::move(message), &blink_message);
173 if (result) {
174 LOG(WARNING) << "Received bad message, error: "
175 << static_cast<int32_t>(*result);
176 Destroy();
177 return;
178 }
179
180 SendBlinkMessage(std::move(blink_message));
181
182 ReadMessageFromFidl();
183 }
184
OnMessagePosted(fuchsia::web::MessagePort_PostMessage_Result result)185 void OnMessagePosted(fuchsia::web::MessagePort_PostMessage_Result result) {
186 if (result.is_err()) {
187 LOG(ERROR) << "PostMessage failed, reason: "
188 << static_cast<int32_t>(result.err());
189 Destroy();
190 return;
191 }
192
193 DeliverMessageToFidl();
194 }
195
196 // cr_fuchsia::MessagePortAdapter implementation.
DeliverMessageToFidl()197 void DeliverMessageToFidl() override {
198 base::Optional<fuchsia::web::WebMessage> message = GetNextBlinkMessage();
199 if (!message)
200 return;
201
202 port_->PostMessage(
203 std::move(*message),
204 fit::bind_member(this, &FidlMessagePortClientAdapter::OnMessagePosted));
205
206 OnDeliverMessageToFidlComplete();
207 }
208
209 fuchsia::web::MessagePortPtr port_;
210
211 DISALLOW_COPY_AND_ASSIGN(FidlMessagePortClientAdapter);
212 };
213
214 // Binds a MessagePort FIDL service from a blink::WebMessagePort.
215 class FidlMessagePortServerAdapter : public fuchsia::web::MessagePort,
216 public MessagePortAdapter {
217 public:
FidlMessagePortServerAdapter(blink::WebMessagePort blink_port)218 explicit FidlMessagePortServerAdapter(blink::WebMessagePort blink_port)
219 : cr_fuchsia::MessagePortAdapter(std::move(blink_port)), binding_(this) {
220 binding_.set_error_handler([this](zx_status_t status) {
221 ZX_LOG_IF(ERROR,
222 status != ZX_ERR_PEER_CLOSED && status != ZX_ERR_CANCELED,
223 status)
224 << " MessagePort disconnected.";
225 Destroy();
226 });
227 }
228
FidlMessagePortServerAdapter(blink::WebMessagePort blink_port,fidl::InterfaceRequest<fuchsia::web::MessagePort> request)229 FidlMessagePortServerAdapter(
230 blink::WebMessagePort blink_port,
231 fidl::InterfaceRequest<fuchsia::web::MessagePort> request)
232 : FidlMessagePortServerAdapter(std::move(blink_port)) {
233 binding_.Bind(std::move(request));
234 }
235
NewBinding()236 fidl::InterfaceHandle<fuchsia::web::MessagePort> NewBinding() {
237 return binding_.NewBinding();
238 }
239
240 private:
241 ~FidlMessagePortServerAdapter() override = default;
242
243 // cr_fuchsia::MessagePortAdapter implementation.
DeliverMessageToFidl()244 void DeliverMessageToFidl() override {
245 // Do nothing if the client hasn't requested a read, or if there's nothing
246 // to read.
247 if (!pending_receive_message_callback_)
248 return;
249
250 base::Optional<fuchsia::web::WebMessage> message = GetNextBlinkMessage();
251 if (!message)
252 return;
253
254 pending_receive_message_callback_(std::move(*message));
255 pending_receive_message_callback_ = {};
256 OnDeliverMessageToFidlComplete();
257 }
258
259 // fuchsia::web::MessagePort implementation.
PostMessage(fuchsia::web::WebMessage message,PostMessageCallback callback)260 void PostMessage(fuchsia::web::WebMessage message,
261 PostMessageCallback callback) override {
262 BlinkMessage blink_message;
263 base::Optional<fuchsia::web::FrameError> status =
264 BlinkMessageFromFidl(std::move(message), &blink_message);
265
266 if (status) {
267 LOG(ERROR) << "Error when reading message from FIDL: "
268 << static_cast<int32_t>(*status);
269 Destroy();
270 return;
271 }
272
273 SendBlinkMessage(std::move(blink_message));
274 fuchsia::web::MessagePort_PostMessage_Result result;
275 result.set_response(fuchsia::web::MessagePort_PostMessage_Response());
276 callback(std::move(result));
277 }
278
ReceiveMessage(ReceiveMessageCallback callback)279 void ReceiveMessage(ReceiveMessageCallback callback) override {
280 if (pending_receive_message_callback_) {
281 LOG(WARNING)
282 << "ReceiveMessage called multiple times without acknowledgement.";
283 Destroy();
284 return;
285 }
286 pending_receive_message_callback_ = std::move(callback);
287 DeliverMessageToFidl();
288 }
289
290 PostMessageCallback post_message_ack_;
291 ReceiveMessageCallback pending_receive_message_callback_;
292 fidl::Binding<fuchsia::web::MessagePort> binding_;
293
294 DISALLOW_COPY_AND_ASSIGN(FidlMessagePortServerAdapter);
295 };
296
297 fidl::InterfaceRequest<fuchsia::web::MessagePort>
RemoteFidlMessagePortFromBlink(blink::WebMessagePort blink_port)298 RemoteFidlMessagePortFromBlink(blink::WebMessagePort blink_port) {
299 fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_handle;
300 auto request = fidl_handle.NewRequest();
301 new FidlMessagePortClientAdapter(std::move(blink_port),
302 std::move(fidl_handle));
303 return request;
304 }
305
306 } // namespace
307
308 // Methods for constructing MessagePortAdapters for various port types and
309 // origins. The adapters manage their own lifetimes and will self-delete when
310 // either endpoint of their channels are disconnected.
311
BlinkMessagePortFromFidl(fidl::InterfaceRequest<fuchsia::web::MessagePort> fidl_port)312 blink::WebMessagePort BlinkMessagePortFromFidl(
313 fidl::InterfaceRequest<fuchsia::web::MessagePort> fidl_port) {
314 auto port_pair = blink::WebMessagePort::CreatePair();
315 new FidlMessagePortServerAdapter(std::move(port_pair.first),
316 std::move(fidl_port));
317 return std::move(port_pair.second);
318 }
319
BlinkMessagePortFromFidl(fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_port)320 blink::WebMessagePort BlinkMessagePortFromFidl(
321 fidl::InterfaceHandle<fuchsia::web::MessagePort> fidl_port) {
322 auto port_pair = blink::WebMessagePort::CreatePair();
323 new FidlMessagePortClientAdapter(std::move(port_pair.first),
324 std::move(fidl_port));
325 return std::move(port_pair.second);
326 }
327
FidlMessagePortFromBlink(blink::WebMessagePort blink_port)328 fidl::InterfaceHandle<fuchsia::web::MessagePort> FidlMessagePortFromBlink(
329 blink::WebMessagePort blink_port) {
330 auto* adapter = new FidlMessagePortServerAdapter(std::move(blink_port));
331 return adapter->NewBinding();
332 }
333
FidlWebMessageFromBlink(BlinkMessage blink_message,TransferableHostType port_type)334 base::Optional<fuchsia::web::WebMessage> FidlWebMessageFromBlink(
335 BlinkMessage blink_message,
336 TransferableHostType port_type) {
337 fuchsia::web::WebMessage fidl_message;
338
339 if (!blink_message.ports.empty()) {
340 switch (port_type) {
341 case TransferableHostType::kLocal:
342 for (blink::WebMessagePort& port : blink_message.ports) {
343 fuchsia::web::IncomingTransferable incoming;
344 incoming.set_message_port(FidlMessagePortFromBlink(std::move(port)));
345 fidl_message.mutable_incoming_transfer()->push_back(
346 std::move(incoming));
347 }
348 break;
349 case TransferableHostType::kRemote:
350 for (blink::WebMessagePort& port : blink_message.ports) {
351 fuchsia::web::OutgoingTransferable outgoing;
352 outgoing.set_message_port(
353 RemoteFidlMessagePortFromBlink(std::move(port)));
354 fidl_message.mutable_outgoing_transfer()->push_back(
355 std::move(outgoing));
356 }
357 break;
358 }
359 blink_message.ports.clear();
360 }
361
362 base::string16 data_utf16 = std::move(blink_message.data);
363 std::string data_utf8;
364 if (!base::UTF16ToUTF8(data_utf16.data(), data_utf16.size(), &data_utf8))
365 return base::nullopt;
366
367 base::STLClearObject(&data_utf16);
368
369 constexpr char kBufferVmoName[] = "cr-web-message-from-blink";
370 fuchsia::mem::Buffer data_buffer =
371 cr_fuchsia::MemBufferFromString(data_utf8, kBufferVmoName);
372 if (!data_buffer.vmo)
373 return base::nullopt;
374
375 fidl_message.set_data(std::move(data_buffer));
376 return fidl_message;
377 }
378
379 } // namespace cr_fuchsia
380