1 // Copyright 2014 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 "third_party/blink/renderer/modules/presentation/presentation_connection.h"
6 
7 #include <memory>
8 #include "third_party/blink/public/platform/task_type.h"
9 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
10 #include "third_party/blink/renderer/core/dom/document.h"
11 #include "third_party/blink/renderer/core/dom/events/event.h"
12 #include "third_party/blink/renderer/core/events/message_event.h"
13 #include "third_party/blink/renderer/core/fileapi/file_error.h"
14 #include "third_party/blink/renderer/core/fileapi/file_reader_loader.h"
15 #include "third_party/blink/renderer/core/fileapi/file_reader_loader_client.h"
16 #include "third_party/blink/renderer/core/frame/deprecation.h"
17 #include "third_party/blink/renderer/core/frame/local_frame.h"
18 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
19 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
20 #include "third_party/blink/renderer/modules/event_target_modules.h"
21 #include "third_party/blink/renderer/modules/presentation/presentation.h"
22 #include "third_party/blink/renderer/modules/presentation/presentation_connection_available_event.h"
23 #include "third_party/blink/renderer/modules/presentation/presentation_connection_close_event.h"
24 #include "third_party/blink/renderer/modules/presentation/presentation_controller.h"
25 #include "third_party/blink/renderer/modules/presentation/presentation_receiver.h"
26 #include "third_party/blink/renderer/modules/presentation/presentation_request.h"
27 #include "third_party/blink/renderer/platform/heap/heap.h"
28 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
29 #include "third_party/blink/renderer/platform/wtf/assertions.h"
30 #include "third_party/blink/renderer/platform/wtf/functional.h"
31 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
32 
33 namespace blink {
34 
35 namespace {
36 
MakeBinaryMessage(const DOMArrayBuffer * buffer)37 mojom::blink::PresentationConnectionMessagePtr MakeBinaryMessage(
38     const DOMArrayBuffer* buffer) {
39   // Mutating the data field on the message instead of passing in an already
40   // populated Vector into message constructor is more efficient since the
41   // latter does not support moves.
42   auto message = mojom::blink::PresentationConnectionMessage::NewData(
43       WTF::Vector<uint8_t>());
44   WTF::Vector<uint8_t>& data = message->get_data();
45   data.Append(static_cast<const uint8_t*>(buffer->Data()),
46               base::checked_cast<wtf_size_t>(buffer->ByteLengthAsSizeT()));
47   return message;
48 }
49 
MakeTextMessage(const String & text)50 mojom::blink::PresentationConnectionMessagePtr MakeTextMessage(
51     const String& text) {
52   return mojom::blink::PresentationConnectionMessage::NewMessage(text);
53 }
54 
ConnectionStateToString(mojom::blink::PresentationConnectionState state)55 const AtomicString& ConnectionStateToString(
56     mojom::blink::PresentationConnectionState state) {
57   DEFINE_STATIC_LOCAL(const AtomicString, connecting_value, ("connecting"));
58   DEFINE_STATIC_LOCAL(const AtomicString, connected_value, ("connected"));
59   DEFINE_STATIC_LOCAL(const AtomicString, closed_value, ("closed"));
60   DEFINE_STATIC_LOCAL(const AtomicString, terminated_value, ("terminated"));
61 
62   switch (state) {
63     case mojom::blink::PresentationConnectionState::CONNECTING:
64       return connecting_value;
65     case mojom::blink::PresentationConnectionState::CONNECTED:
66       return connected_value;
67     case mojom::blink::PresentationConnectionState::CLOSED:
68       return closed_value;
69     case mojom::blink::PresentationConnectionState::TERMINATED:
70       return terminated_value;
71   }
72 
73   NOTREACHED();
74   return terminated_value;
75 }
76 
ConnectionCloseReasonToString(mojom::blink::PresentationConnectionCloseReason reason)77 const AtomicString& ConnectionCloseReasonToString(
78     mojom::blink::PresentationConnectionCloseReason reason) {
79   DEFINE_STATIC_LOCAL(const AtomicString, error_value, ("error"));
80   DEFINE_STATIC_LOCAL(const AtomicString, closed_value, ("closed"));
81   DEFINE_STATIC_LOCAL(const AtomicString, went_away_value, ("wentaway"));
82 
83   switch (reason) {
84     case mojom::blink::PresentationConnectionCloseReason::CONNECTION_ERROR:
85       return error_value;
86     case mojom::blink::PresentationConnectionCloseReason::CLOSED:
87       return closed_value;
88     case mojom::blink::PresentationConnectionCloseReason::WENT_AWAY:
89       return went_away_value;
90   }
91 
92   NOTREACHED();
93   return error_value;
94 }
95 
ThrowPresentationDisconnectedError(ExceptionState & exception_state)96 void ThrowPresentationDisconnectedError(ExceptionState& exception_state) {
97   exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
98                                     "Presentation connection is disconnected.");
99 }
100 
101 }  // namespace
102 
103 class PresentationConnection::Message final
104     : public GarbageCollected<PresentationConnection::Message> {
105  public:
Message(const String & text)106   Message(const String& text) : type(kMessageTypeText), text(text) {}
107 
Message(DOMArrayBuffer * array_buffer)108   Message(DOMArrayBuffer* array_buffer)
109       : type(kMessageTypeArrayBuffer), array_buffer(array_buffer) {}
110 
Message(scoped_refptr<BlobDataHandle> blob_data_handle)111   Message(scoped_refptr<BlobDataHandle> blob_data_handle)
112       : type(kMessageTypeBlob), blob_data_handle(std::move(blob_data_handle)) {}
113 
Trace(Visitor * visitor)114   void Trace(Visitor* visitor) { visitor->Trace(array_buffer); }
115 
116   MessageType type;
117   String text;
118   Member<DOMArrayBuffer> array_buffer;
119   scoped_refptr<BlobDataHandle> blob_data_handle;
120 };
121 
122 class PresentationConnection::BlobLoader final
123     : public GarbageCollected<PresentationConnection::BlobLoader>,
124       public FileReaderLoaderClient {
125  public:
BlobLoader(scoped_refptr<BlobDataHandle> blob_data_handle,PresentationConnection * presentation_connection,scoped_refptr<base::SingleThreadTaskRunner> task_runner)126   BlobLoader(scoped_refptr<BlobDataHandle> blob_data_handle,
127              PresentationConnection* presentation_connection,
128              scoped_refptr<base::SingleThreadTaskRunner> task_runner)
129       : presentation_connection_(presentation_connection),
130         loader_(std::make_unique<FileReaderLoader>(
131             FileReaderLoader::kReadAsArrayBuffer,
132             this,
133             std::move(task_runner))) {
134     loader_->Start(std::move(blob_data_handle));
135   }
136   ~BlobLoader() override = default;
137 
138   // FileReaderLoaderClient functions.
DidStartLoading()139   void DidStartLoading() override {}
DidReceiveData()140   void DidReceiveData() override {}
DidFinishLoading()141   void DidFinishLoading() override {
142     presentation_connection_->DidFinishLoadingBlob(
143         loader_->ArrayBufferResult());
144   }
DidFail(FileErrorCode error_code)145   void DidFail(FileErrorCode error_code) override {
146     presentation_connection_->DidFailLoadingBlob(error_code);
147   }
148 
Cancel()149   void Cancel() { loader_->Cancel(); }
150 
Trace(Visitor * visitor)151   void Trace(Visitor* visitor) { visitor->Trace(presentation_connection_); }
152 
153  private:
154   Member<PresentationConnection> presentation_connection_;
155   std::unique_ptr<FileReaderLoader> loader_;
156 };
157 
PresentationConnection(LocalFrame & frame,const String & id,const KURL & url)158 PresentationConnection::PresentationConnection(LocalFrame& frame,
159                                                const String& id,
160                                                const KURL& url)
161     : ExecutionContextLifecycleStateObserver(frame.GetDocument()),
162       id_(id),
163       url_(url),
164       state_(mojom::blink::PresentationConnectionState::CONNECTING),
165       binary_type_(kBinaryTypeArrayBuffer),
166       file_reading_task_runner_(frame.GetTaskRunner(TaskType::kFileReading)) {
167   UpdateStateIfNeeded();
168 }
169 
~PresentationConnection()170 PresentationConnection::~PresentationConnection() {
171   DCHECK(!blob_loader_);
172 }
173 
OnMessage(mojom::blink::PresentationConnectionMessagePtr message)174 void PresentationConnection::OnMessage(
175     mojom::blink::PresentationConnectionMessagePtr message) {
176   if (message->is_data()) {
177     const auto& data = message->get_data();
178     DidReceiveBinaryMessage(&data.front(), data.size());
179   } else {
180     DidReceiveTextMessage(message->get_message());
181   }
182 }
183 
DidChangeState(mojom::blink::PresentationConnectionState state)184 void PresentationConnection::DidChangeState(
185     mojom::blink::PresentationConnectionState state) {
186   // Closed state is handled in |DidClose()|.
187   DCHECK_NE(mojom::blink::PresentationConnectionState::CLOSED, state);
188 
189   if (state_ == state)
190     return;
191 
192   state_ = state;
193 
194   switch (state_) {
195     case mojom::blink::PresentationConnectionState::CONNECTING:
196       return;
197     case mojom::blink::PresentationConnectionState::CONNECTED:
198       DispatchStateChangeEvent(Event::Create(event_type_names::kConnect));
199       return;
200     case mojom::blink::PresentationConnectionState::CLOSED:
201       return;
202     case mojom::blink::PresentationConnectionState::TERMINATED:
203       DispatchStateChangeEvent(Event::Create(event_type_names::kTerminate));
204       return;
205   }
206   NOTREACHED();
207 }
208 
DidClose(mojom::blink::PresentationConnectionCloseReason reason)209 void PresentationConnection::DidClose(
210     mojom::blink::PresentationConnectionCloseReason reason) {
211   DidClose(reason, /* message */ String());
212 }
213 
214 // static
Take(ScriptPromiseResolver * resolver,const mojom::blink::PresentationInfo & presentation_info,PresentationRequest * request)215 ControllerPresentationConnection* ControllerPresentationConnection::Take(
216     ScriptPromiseResolver* resolver,
217     const mojom::blink::PresentationInfo& presentation_info,
218     PresentationRequest* request) {
219   DCHECK(resolver);
220   DCHECK(request);
221 
222   Document* document = Document::From(resolver->GetExecutionContext());
223   if (!document->GetFrame())
224     return nullptr;
225 
226   PresentationController* controller =
227       PresentationController::From(*document->GetFrame());
228   if (!controller)
229     return nullptr;
230 
231   return Take(controller, presentation_info, request);
232 }
233 
234 // static
Take(PresentationController * controller,const mojom::blink::PresentationInfo & presentation_info,PresentationRequest * request)235 ControllerPresentationConnection* ControllerPresentationConnection::Take(
236     PresentationController* controller,
237     const mojom::blink::PresentationInfo& presentation_info,
238     PresentationRequest* request) {
239   DCHECK(controller);
240   DCHECK(request);
241 
242   auto* connection = MakeGarbageCollected<ControllerPresentationConnection>(
243       *controller->GetFrame(), controller, presentation_info.id,
244       presentation_info.url);
245   controller->RegisterConnection(connection);
246 
247   // Fire onconnectionavailable event asynchronously.
248   auto* event = PresentationConnectionAvailableEvent::Create(
249       event_type_names::kConnectionavailable, connection);
250   request->GetExecutionContext()
251       ->GetTaskRunner(TaskType::kPresentation)
252       ->PostTask(FROM_HERE,
253                  WTF::Bind(&PresentationConnection::DispatchEventAsync,
254                            WrapPersistent(request), WrapPersistent(event)));
255 
256   return connection;
257 }
258 
ControllerPresentationConnection(LocalFrame & frame,PresentationController * controller,const String & id,const KURL & url)259 ControllerPresentationConnection::ControllerPresentationConnection(
260     LocalFrame& frame,
261     PresentationController* controller,
262     const String& id,
263     const KURL& url)
264     : PresentationConnection(frame, id, url), controller_(controller) {}
265 
~ControllerPresentationConnection()266 ControllerPresentationConnection::~ControllerPresentationConnection() {}
267 
Trace(Visitor * visitor)268 void ControllerPresentationConnection::Trace(Visitor* visitor) {
269   visitor->Trace(controller_);
270   PresentationConnection::Trace(visitor);
271 }
272 
Init(mojo::PendingRemote<mojom::blink::PresentationConnection> connection_remote,mojo::PendingReceiver<mojom::blink::PresentationConnection> connection_receiver)273 void ControllerPresentationConnection::Init(
274     mojo::PendingRemote<mojom::blink::PresentationConnection> connection_remote,
275     mojo::PendingReceiver<mojom::blink::PresentationConnection>
276         connection_receiver) {
277   // Note that it is possible for the binding to be already bound here, because
278   // the ControllerPresentationConnection object could be reused when
279   // reconnecting in the same frame. In this case the existing connections are
280   // discarded.
281   if (connection_receiver_.is_bound()) {
282     connection_receiver_.reset();
283     target_connection_.reset();
284   }
285 
286   DidChangeState(mojom::blink::PresentationConnectionState::CONNECTING);
287   target_connection_.Bind(std::move(connection_remote));
288   connection_receiver_.Bind(
289       std::move(connection_receiver),
290       GetExecutionContext()->GetTaskRunner(blink::TaskType::kPresentation));
291 }
292 
CloseInternal()293 void ControllerPresentationConnection::CloseInternal() {
294   auto& service = controller_->GetPresentationService();
295   if (service)
296     service->CloseConnection(url_, id_);
297 }
298 
TerminateInternal()299 void ControllerPresentationConnection::TerminateInternal() {
300   auto& service = controller_->GetPresentationService();
301   if (service)
302     service->Terminate(url_, id_);
303 }
304 
305 // static
Take(PresentationReceiver * receiver,const mojom::blink::PresentationInfo & presentation_info,mojo::PendingRemote<mojom::blink::PresentationConnection> controller_connection,mojo::PendingReceiver<mojom::blink::PresentationConnection> receiver_connection_receiver)306 ReceiverPresentationConnection* ReceiverPresentationConnection::Take(
307     PresentationReceiver* receiver,
308     const mojom::blink::PresentationInfo& presentation_info,
309     mojo::PendingRemote<mojom::blink::PresentationConnection>
310         controller_connection,
311     mojo::PendingReceiver<mojom::blink::PresentationConnection>
312         receiver_connection_receiver) {
313   DCHECK(receiver);
314 
315   ReceiverPresentationConnection* connection =
316       MakeGarbageCollected<ReceiverPresentationConnection>(
317           *receiver->GetFrame(), receiver, presentation_info.id,
318           presentation_info.url);
319   connection->Init(std::move(controller_connection),
320                    std::move(receiver_connection_receiver));
321 
322   receiver->RegisterConnection(connection);
323   return connection;
324 }
325 
ReceiverPresentationConnection(LocalFrame & frame,PresentationReceiver * receiver,const String & id,const KURL & url)326 ReceiverPresentationConnection::ReceiverPresentationConnection(
327     LocalFrame& frame,
328     PresentationReceiver* receiver,
329     const String& id,
330     const KURL& url)
331     : PresentationConnection(frame, id, url), receiver_(receiver) {}
332 
333 ReceiverPresentationConnection::~ReceiverPresentationConnection() = default;
334 
Init(mojo::PendingRemote<mojom::blink::PresentationConnection> controller_connection_remote,mojo::PendingReceiver<mojom::blink::PresentationConnection> receiver_connection_receiver)335 void ReceiverPresentationConnection::Init(
336     mojo::PendingRemote<mojom::blink::PresentationConnection>
337         controller_connection_remote,
338     mojo::PendingReceiver<mojom::blink::PresentationConnection>
339         receiver_connection_receiver) {
340   target_connection_.Bind(std::move(controller_connection_remote));
341   connection_receiver_.Bind(
342       std::move(receiver_connection_receiver),
343       GetExecutionContext()->GetTaskRunner(blink::TaskType::kPresentation));
344 
345   target_connection_->DidChangeState(
346       mojom::blink::PresentationConnectionState::CONNECTED);
347   DidChangeState(mojom::blink::PresentationConnectionState::CONNECTED);
348 }
349 
DidChangeState(mojom::blink::PresentationConnectionState state)350 void ReceiverPresentationConnection::DidChangeState(
351     mojom::blink::PresentationConnectionState state) {
352   PresentationConnection::DidChangeState(state);
353 }
354 
DidClose(mojom::blink::PresentationConnectionCloseReason reason)355 void ReceiverPresentationConnection::DidClose(
356     mojom::blink::PresentationConnectionCloseReason reason) {
357   PresentationConnection::DidClose(reason);
358   receiver_->RemoveConnection(this);
359 }
360 
CloseInternal()361 void ReceiverPresentationConnection::CloseInternal() {
362   // No-op
363 }
364 
TerminateInternal()365 void ReceiverPresentationConnection::TerminateInternal() {
366   // This will close the receiver window. Change the state to TERMINATED now
367   // since ReceiverPresentationConnection won't get a state change notification.
368   if (state_ == mojom::blink::PresentationConnectionState::TERMINATED)
369     return;
370 
371   receiver_->Terminate();
372 
373   state_ = mojom::blink::PresentationConnectionState::TERMINATED;
374   if (target_connection_)
375     target_connection_->DidChangeState(state_);
376 }
377 
Trace(Visitor * visitor)378 void ReceiverPresentationConnection::Trace(Visitor* visitor) {
379   visitor->Trace(receiver_);
380   PresentationConnection::Trace(visitor);
381 }
382 
InterfaceName() const383 const AtomicString& PresentationConnection::InterfaceName() const {
384   return event_target_names::kPresentationConnection;
385 }
386 
GetExecutionContext() const387 ExecutionContext* PresentationConnection::GetExecutionContext() const {
388   return ExecutionContextLifecycleObserver::GetExecutionContext();
389 }
390 
AddedEventListener(const AtomicString & event_type,RegisteredEventListener & registered_listener)391 void PresentationConnection::AddedEventListener(
392     const AtomicString& event_type,
393     RegisteredEventListener& registered_listener) {
394   EventTargetWithInlineData::AddedEventListener(event_type,
395                                                 registered_listener);
396   if (event_type == event_type_names::kConnect) {
397     UseCounter::Count(GetExecutionContext(),
398                       WebFeature::kPresentationConnectionConnectEventListener);
399   } else if (event_type == event_type_names::kClose) {
400     UseCounter::Count(GetExecutionContext(),
401                       WebFeature::kPresentationConnectionCloseEventListener);
402   } else if (event_type == event_type_names::kTerminate) {
403     UseCounter::Count(
404         GetExecutionContext(),
405         WebFeature::kPresentationConnectionTerminateEventListener);
406   } else if (event_type == event_type_names::kMessage) {
407     UseCounter::Count(GetExecutionContext(),
408                       WebFeature::kPresentationConnectionMessageEventListener);
409   }
410 }
411 
ContextDestroyed()412 void PresentationConnection::ContextDestroyed() {
413   CloseConnection();
414 }
415 
ContextLifecycleStateChanged(mojom::FrameLifecycleState state)416 void PresentationConnection::ContextLifecycleStateChanged(
417     mojom::FrameLifecycleState state) {
418   if (state == mojom::FrameLifecycleState::kFrozen ||
419       state == mojom::FrameLifecycleState::kFrozenAutoResumeMedia) {
420     CloseConnection();
421   }
422 }
423 
CloseConnection()424 void PresentationConnection::CloseConnection() {
425   DoClose(mojom::blink::PresentationConnectionCloseReason::WENT_AWAY);
426   target_connection_.reset();
427   connection_receiver_.reset();
428 }
429 
Trace(Visitor * visitor)430 void PresentationConnection::Trace(Visitor* visitor) {
431   visitor->Trace(blob_loader_);
432   visitor->Trace(messages_);
433   EventTargetWithInlineData::Trace(visitor);
434   ExecutionContextLifecycleStateObserver::Trace(visitor);
435 }
436 
state() const437 const AtomicString& PresentationConnection::state() const {
438   return ConnectionStateToString(state_);
439 }
440 
send(const String & message,ExceptionState & exception_state)441 void PresentationConnection::send(const String& message,
442                                   ExceptionState& exception_state) {
443   if (!CanSendMessage(exception_state))
444     return;
445 
446   messages_.push_back(MakeGarbageCollected<Message>(message));
447   HandleMessageQueue();
448 }
449 
send(DOMArrayBuffer * array_buffer,ExceptionState & exception_state)450 void PresentationConnection::send(DOMArrayBuffer* array_buffer,
451                                   ExceptionState& exception_state) {
452   DCHECK(array_buffer);
453   if (!CanSendMessage(exception_state))
454     return;
455   if (!base::CheckedNumeric<wtf_size_t>(array_buffer->ByteLengthAsSizeT())
456            .IsValid()) {
457     static_assert(
458         4294967295 == std::numeric_limits<wtf_size_t>::max(),
459         "Change the error message below if this static_assert fails.");
460     exception_state.ThrowRangeError(
461         "ArrayBuffer size exceeds the maximum supported size (4294967295)");
462     return;
463   }
464 
465   messages_.push_back(MakeGarbageCollected<Message>(array_buffer));
466   HandleMessageQueue();
467 }
468 
send(NotShared<DOMArrayBufferView> array_buffer_view,ExceptionState & exception_state)469 void PresentationConnection::send(
470     NotShared<DOMArrayBufferView> array_buffer_view,
471     ExceptionState& exception_state) {
472   DCHECK(array_buffer_view);
473   if (!CanSendMessage(exception_state))
474     return;
475   if (!base::CheckedNumeric<wtf_size_t>(
476            array_buffer_view.View()->byteLengthAsSizeT())
477            .IsValid()) {
478     static_assert(
479         4294967295 == std::numeric_limits<wtf_size_t>::max(),
480         "Change the error message below if this static_assert fails.");
481     exception_state.ThrowRangeError(
482         "ArrayBuffer size exceeds the maximum supported size (4294967295)");
483     return;
484   }
485 
486   messages_.push_back(
487       MakeGarbageCollected<Message>(array_buffer_view.View()->buffer()));
488   HandleMessageQueue();
489 }
490 
send(Blob * data,ExceptionState & exception_state)491 void PresentationConnection::send(Blob* data, ExceptionState& exception_state) {
492   DCHECK(data);
493   if (!CanSendMessage(exception_state))
494     return;
495 
496   messages_.push_back(MakeGarbageCollected<Message>(data->GetBlobDataHandle()));
497   HandleMessageQueue();
498 }
499 
DoClose(mojom::blink::PresentationConnectionCloseReason reason)500 void PresentationConnection::DoClose(
501     mojom::blink::PresentationConnectionCloseReason reason) {
502   if (state_ != mojom::blink::PresentationConnectionState::CONNECTING &&
503       state_ != mojom::blink::PresentationConnectionState::CONNECTED) {
504     return;
505   }
506 
507   if (target_connection_)
508     target_connection_->DidClose(reason);
509 
510   DidClose(reason);
511   CloseInternal();
512   TearDown();
513 }
514 
CanSendMessage(ExceptionState & exception_state)515 bool PresentationConnection::CanSendMessage(ExceptionState& exception_state) {
516   if (state_ != mojom::blink::PresentationConnectionState::CONNECTED) {
517     ThrowPresentationDisconnectedError(exception_state);
518     return false;
519   }
520 
521   return !!target_connection_;
522 }
523 
HandleMessageQueue()524 void PresentationConnection::HandleMessageQueue() {
525   if (!target_connection_)
526     return;
527 
528   while (!messages_.IsEmpty() && !blob_loader_) {
529     Message* message = messages_.front().Get();
530     switch (message->type) {
531       case kMessageTypeText:
532         SendMessageToTargetConnection(MakeTextMessage(message->text));
533         messages_.pop_front();
534         break;
535       case kMessageTypeArrayBuffer:
536         SendMessageToTargetConnection(MakeBinaryMessage(message->array_buffer));
537         messages_.pop_front();
538         break;
539       case kMessageTypeBlob:
540         DCHECK(!blob_loader_);
541         blob_loader_ = MakeGarbageCollected<BlobLoader>(
542             message->blob_data_handle, this, file_reading_task_runner_);
543         break;
544     }
545   }
546 }
547 
binaryType() const548 String PresentationConnection::binaryType() const {
549   switch (binary_type_) {
550     case kBinaryTypeBlob:
551       return "blob";
552     case kBinaryTypeArrayBuffer:
553       return "arraybuffer";
554   }
555   NOTREACHED();
556   return String();
557 }
558 
setBinaryType(const String & binary_type)559 void PresentationConnection::setBinaryType(const String& binary_type) {
560   if (binary_type == "blob") {
561     binary_type_ = kBinaryTypeBlob;
562     return;
563   }
564   if (binary_type == "arraybuffer") {
565     binary_type_ = kBinaryTypeArrayBuffer;
566     return;
567   }
568   NOTREACHED();
569 }
570 
SendMessageToTargetConnection(mojom::blink::PresentationConnectionMessagePtr message)571 void PresentationConnection::SendMessageToTargetConnection(
572     mojom::blink::PresentationConnectionMessagePtr message) {
573   if (target_connection_)
574     target_connection_->OnMessage(std::move(message));
575 }
576 
DidReceiveTextMessage(const WebString & message)577 void PresentationConnection::DidReceiveTextMessage(const WebString& message) {
578   if (state_ != mojom::blink::PresentationConnectionState::CONNECTED)
579     return;
580 
581   DispatchEvent(*MessageEvent::Create(message));
582 }
583 
DidReceiveBinaryMessage(const uint8_t * data,uint32_t length)584 void PresentationConnection::DidReceiveBinaryMessage(const uint8_t* data,
585                                                      uint32_t length) {
586   if (state_ != mojom::blink::PresentationConnectionState::CONNECTED)
587     return;
588 
589   switch (binary_type_) {
590     case kBinaryTypeBlob: {
591       auto blob_data = std::make_unique<BlobData>();
592       blob_data->AppendBytes(data, length);
593       auto* blob = MakeGarbageCollected<Blob>(
594           BlobDataHandle::Create(std::move(blob_data), length));
595       DispatchEvent(*MessageEvent::Create(blob));
596       return;
597     }
598     case kBinaryTypeArrayBuffer:
599       DOMArrayBuffer* buffer = DOMArrayBuffer::Create(data, length);
600       DispatchEvent(*MessageEvent::Create(buffer));
601       return;
602   }
603   NOTREACHED();
604 }
605 
GetState() const606 mojom::blink::PresentationConnectionState PresentationConnection::GetState()
607     const {
608   return state_;
609 }
610 
close()611 void PresentationConnection::close() {
612   DoClose(mojom::blink::PresentationConnectionCloseReason::CLOSED);
613 }
614 
terminate()615 void PresentationConnection::terminate() {
616   if (state_ != mojom::blink::PresentationConnectionState::CONNECTED)
617     return;
618 
619   TerminateInternal();
620   TearDown();
621 }
622 
Matches(const String & id,const KURL & url) const623 bool PresentationConnection::Matches(const String& id, const KURL& url) const {
624   return url_ == url && id_ == id;
625 }
626 
DidClose(mojom::blink::PresentationConnectionCloseReason reason,const String & message)627 void PresentationConnection::DidClose(
628     mojom::blink::PresentationConnectionCloseReason reason,
629     const String& message) {
630   if (state_ == mojom::blink::PresentationConnectionState::CLOSED ||
631       state_ == mojom::blink::PresentationConnectionState::TERMINATED) {
632     return;
633   }
634 
635   state_ = mojom::blink::PresentationConnectionState::CLOSED;
636   DispatchStateChangeEvent(PresentationConnectionCloseEvent::Create(
637       event_type_names::kClose, ConnectionCloseReasonToString(reason),
638       message));
639 }
640 
DidFinishLoadingBlob(DOMArrayBuffer * buffer)641 void PresentationConnection::DidFinishLoadingBlob(DOMArrayBuffer* buffer) {
642   DCHECK(!messages_.IsEmpty());
643   DCHECK_EQ(messages_.front()->type, kMessageTypeBlob);
644   DCHECK(buffer);
645   if (!base::CheckedNumeric<wtf_size_t>(buffer->ByteLengthAsSizeT())
646            .IsValid()) {
647     // TODO(crbug.com/1036565): generate error message? The problem is that the
648     // content of {buffer} is copied into a WTF::Vector, but a DOMArrayBuffer
649     // has a bigger maximum size than a WTF::Vector. Ignore the current failed
650     // blob item and continue with next items.
651     messages_.pop_front();
652     blob_loader_.Clear();
653     HandleMessageQueue();
654   }
655   // Send the loaded blob immediately here and continue processing the queue.
656   SendMessageToTargetConnection(MakeBinaryMessage(buffer));
657 
658   messages_.pop_front();
659   blob_loader_.Clear();
660   HandleMessageQueue();
661 }
662 
DidFailLoadingBlob(FileErrorCode error_code)663 void PresentationConnection::DidFailLoadingBlob(FileErrorCode error_code) {
664   DCHECK(!messages_.IsEmpty());
665   DCHECK_EQ(messages_.front()->type, kMessageTypeBlob);
666   // TODO(crbug.com/1036565): generate error message?
667   // Ignore the current failed blob item and continue with next items.
668   messages_.pop_front();
669   blob_loader_.Clear();
670   HandleMessageQueue();
671 }
672 
DispatchStateChangeEvent(Event * event)673 void PresentationConnection::DispatchStateChangeEvent(Event* event) {
674   GetExecutionContext()
675       ->GetTaskRunner(TaskType::kPresentation)
676       ->PostTask(FROM_HERE,
677                  WTF::Bind(&PresentationConnection::DispatchEventAsync,
678                            WrapPersistent(this), WrapPersistent(event)));
679 }
680 
681 // static
DispatchEventAsync(EventTarget * target,Event * event)682 void PresentationConnection::DispatchEventAsync(EventTarget* target,
683                                                 Event* event) {
684   DCHECK(target);
685   DCHECK(event);
686   target->DispatchEvent(*event);
687 }
688 
TearDown()689 void PresentationConnection::TearDown() {
690   // Cancel current Blob loading if any.
691   if (blob_loader_) {
692     blob_loader_->Cancel();
693     blob_loader_.Clear();
694   }
695   messages_.clear();
696 }
697 
698 }  // namespace blink
699