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