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