@@ -144,7 +144,7 @@ void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
144
144
145
145
namespace {
146
146
147
- void ThrowDataCloneError (Environment* env, Local<String> message) {
147
+ void ThrowDataCloneException (Environment* env, Local<String> message) {
148
148
Local<Value> argv[] = {
149
149
message,
150
150
FIXED_ONE_BYTE_STRING (env->isolate (), " DataCloneError" )
@@ -168,7 +168,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
168
168
: env_(env), context_(context), msg_(m) {}
169
169
170
170
void ThrowDataCloneError (Local<String> message) override {
171
- ThrowDataCloneError (env_, message);
171
+ ThrowDataCloneException (env_, message);
172
172
}
173
173
174
174
Maybe<bool > WriteHostObject (Isolate* isolate, Local<Object> object) override {
@@ -239,7 +239,8 @@ class SerializerDelegate : public ValueSerializer::Delegate {
239
239
Maybe<bool > Message::Serialize (Environment* env,
240
240
Local<Context> context,
241
241
Local<Value> input,
242
- Local<Value> transfer_list_v) {
242
+ Local<Value> transfer_list_v,
243
+ Local<Object> source_port) {
243
244
HandleScope handle_scope (env->isolate ());
244
245
Context::Scope context_scope (context);
245
246
@@ -273,8 +274,23 @@ Maybe<bool> Message::Serialize(Environment* env,
273
274
continue ;
274
275
} else if (env->message_port_constructor_template ()
275
276
->HasInstance (entry)) {
277
+ // Check if the source MessagePort is being transferred.
278
+ if (!source_port.IsEmpty () && entry == source_port) {
279
+ ThrowDataCloneException (
280
+ env,
281
+ FIXED_ONE_BYTE_STRING (env->isolate (),
282
+ " Transfer list contains source port" ));
283
+ return Nothing<bool >();
284
+ }
276
285
MessagePort* port = Unwrap<MessagePort>(entry.As <Object>());
277
- CHECK_NE (port, nullptr );
286
+ if (port == nullptr || port->IsDetached ()) {
287
+ ThrowDataCloneException (
288
+ env,
289
+ FIXED_ONE_BYTE_STRING (
290
+ env->isolate (),
291
+ " MessagePort in transfer list is already detached" ));
292
+ return Nothing<bool >();
293
+ }
278
294
delegate.ports_ .push_back (port);
279
295
continue ;
280
296
}
@@ -410,6 +426,10 @@ uv_async_t* MessagePort::async() {
410
426
return reinterpret_cast <uv_async_t *>(GetHandle ());
411
427
}
412
428
429
+ bool MessagePort::IsDetached () const {
430
+ return data_ == nullptr || IsHandleClosing ();
431
+ }
432
+
413
433
void MessagePort::TriggerAsync () {
414
434
if (IsHandleClosing ()) return ;
415
435
CHECK_EQ (uv_async_send (async ()), 0 );
@@ -552,36 +572,69 @@ std::unique_ptr<MessagePortData> MessagePort::Detach() {
552
572
}
553
573
554
574
555
- void MessagePort::Send (Message&& message) {
556
- Mutex::ScopedLock lock (*data_-> sibling_mutex_ );
557
- if (data_-> sibling_ == nullptr )
558
- return ;
559
- data_-> sibling_ -> AddToIncomingQueue ( std::move (message) );
560
- }
575
+ Maybe< bool > MessagePort::PostMessage (Environment* env,
576
+ Local<Value> message_v,
577
+ Local<Value> transfer_v) {
578
+ Isolate* isolate = env-> isolate () ;
579
+ Local<Object> obj = object (isolate );
580
+ Local<Context> context = obj-> CreationContext ();
561
581
562
- void MessagePort::Send (const FunctionCallbackInfo<Value>& args) {
563
- Environment* env = Environment::GetCurrent (args);
564
- Local<Context> context = object (env->isolate ())->CreationContext ();
565
582
Message msg;
566
- if (msg.Serialize (env, context, args[0 ], args[1 ])
567
- .IsNothing ()) {
568
- return ;
583
+
584
+ // Per spec, we need to both check if transfer list has the source port, and
585
+ // serialize the input message, even if the MessagePort is closed or detached.
586
+
587
+ Maybe<bool > serialization_maybe =
588
+ msg.Serialize (env, context, message_v, transfer_v, obj);
589
+ if (data_ == nullptr ) {
590
+ return serialization_maybe;
591
+ }
592
+ if (serialization_maybe.IsNothing ()) {
593
+ return Nothing<bool >();
594
+ }
595
+
596
+ Mutex::ScopedLock lock (*data_->sibling_mutex_ );
597
+ bool doomed = false ;
598
+
599
+ // Check if the target port is posted to itself.
600
+ if (data_->sibling_ != nullptr ) {
601
+ for (const auto & port_data : msg.message_ports ()) {
602
+ if (data_->sibling_ == port_data.get ()) {
603
+ doomed = true ;
604
+ ProcessEmitWarning (env, " The target port was posted to itself, and "
605
+ " the communication channel was lost" );
606
+ break ;
607
+ }
608
+ }
569
609
}
570
- Send (std::move (msg));
610
+
611
+ if (data_->sibling_ == nullptr || doomed)
612
+ return Just (true );
613
+
614
+ data_->sibling_ ->AddToIncomingQueue (std::move (msg));
615
+ return Just (true );
571
616
}
572
617
573
618
void MessagePort::PostMessage (const FunctionCallbackInfo<Value>& args) {
574
619
Environment* env = Environment::GetCurrent (args);
575
- MessagePort* port;
576
- ASSIGN_OR_RETURN_UNWRAP (&port, args.This ());
577
- if (!port->data_ ) {
578
- return THROW_ERR_CLOSED_MESSAGE_PORT (env);
579
- }
580
620
if (args.Length () == 0 ) {
581
621
return THROW_ERR_MISSING_ARGS (env, " Not enough arguments to "
582
622
" MessagePort.postMessage" );
583
623
}
584
- port->Send (args);
624
+
625
+ MessagePort* port = Unwrap<MessagePort>(args.This ());
626
+ // Even if the backing MessagePort object has already been deleted, we still
627
+ // want to serialize the message to ensure spec-compliant behavior w.r.t.
628
+ // transfers.
629
+ if (port == nullptr ) {
630
+ Message msg;
631
+ Local<Object> obj = args.This ();
632
+ Local<Context> context = obj->CreationContext ();
633
+ USE (msg.Serialize (env, context, args[0 ], args[1 ], obj));
634
+ return ;
635
+ }
636
+
637
+ port->PostMessage (env, args[0 ], args[1 ]);
585
638
}
586
639
587
640
void MessagePort::Start () {
0 commit comments