MessagePort.cpp (24753B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "MessagePort.h" 8 9 #include "MessageEvent.h" 10 #include "MessagePortChild.h" 11 #include "mozilla/ScopeExit.h" 12 #include "mozilla/dom/BlobBinding.h" 13 #include "mozilla/dom/Event.h" 14 #include "mozilla/dom/File.h" 15 #include "mozilla/dom/MessageChannel.h" 16 #include "mozilla/dom/MessageEventBinding.h" 17 #include "mozilla/dom/MessagePortBinding.h" 18 #include "mozilla/dom/MessagePortChild.h" 19 #include "mozilla/dom/PMessagePort.h" 20 #include "mozilla/dom/RefMessageBodyService.h" 21 #include "mozilla/dom/RootedDictionary.h" 22 #include "mozilla/dom/ScriptSettings.h" 23 #include "mozilla/dom/SharedMessageBody.h" 24 #include "mozilla/dom/StructuredCloneTags.h" 25 #include "mozilla/dom/WorkerCommon.h" 26 #include "mozilla/dom/WorkerRef.h" 27 #include "mozilla/dom/WorkerScope.h" 28 #include "mozilla/ipc/BackgroundChild.h" 29 #include "mozilla/ipc/PBackgroundChild.h" 30 #include "nsContentUtils.h" 31 #include "nsGlobalWindowInner.h" 32 #include "nsPresContext.h" 33 34 #ifdef XP_WIN 35 # undef PostMessage 36 #endif 37 38 namespace mozilla::dom { 39 40 void UniqueMessagePortId::ForceClose() { 41 if (!mIdentifier.neutered()) { 42 MessagePort::ForceClose(mIdentifier); 43 mIdentifier.neutered() = true; 44 } 45 } 46 47 class PostMessageRunnable final : public CancelableRunnable { 48 friend class MessagePort; 49 50 public: 51 PostMessageRunnable(MessagePort* aPort, SharedMessageBody* aData) 52 : CancelableRunnable("dom::PostMessageRunnable"), 53 mPort(aPort), 54 mData(aData) { 55 MOZ_ASSERT(aPort); 56 MOZ_ASSERT(aData); 57 } 58 59 NS_IMETHOD 60 Run() override { 61 NS_ASSERT_OWNINGTHREAD(Runnable); 62 63 // The port can be cycle collected while this runnable is pending in 64 // the event queue. 65 if (!mPort) { 66 return NS_OK; 67 } 68 69 MOZ_ASSERT(mPort->mPostMessageRunnable == this); 70 71 DispatchMessage(); 72 73 // We must check if we were waiting for this message in order to shutdown 74 // the port. 75 mPort->UpdateMustKeepAlive(); 76 77 mPort->mPostMessageRunnable = nullptr; 78 mPort->Dispatch(); 79 80 return NS_OK; 81 } 82 83 nsresult Cancel() override { 84 NS_ASSERT_OWNINGTHREAD(Runnable); 85 86 mPort = nullptr; 87 mData = nullptr; 88 return NS_OK; 89 } 90 91 private: 92 void DispatchMessage() const { 93 NS_ASSERT_OWNINGTHREAD(Runnable); 94 95 if (NS_FAILED(mPort->CheckCurrentGlobalCorrectness())) { 96 return; 97 } 98 99 nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject(); 100 101 AutoJSAPI jsapi; 102 if (!globalObject || !jsapi.Init(globalObject)) { 103 NS_WARNING("Failed to initialize AutoJSAPI object."); 104 return; 105 } 106 107 JSContext* cx = jsapi.cx(); 108 109 IgnoredErrorResult rv; 110 JS::Rooted<JS::Value> value(cx); 111 112 mData->Read(cx, &value, mPort->mRefMessageBodyService, 113 SharedMessageBody::ReadMethod::StealRefMessageBody, rv); 114 115 if (NS_WARN_IF(rv.Failed())) { 116 JS_ClearPendingException(cx); 117 mPort->DispatchError(); 118 return; 119 } 120 121 // Create the event 122 RefPtr<MessageEvent> event = new MessageEvent(mPort, nullptr, nullptr); 123 124 Sequence<OwningNonNull<MessagePort>> ports; 125 if (!mData->TakeTransferredPortsAsSequence(ports)) { 126 mPort->DispatchError(); 127 return; 128 } 129 130 event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo, 131 Cancelable::eNo, value, u""_ns, u""_ns, nullptr, 132 ports); 133 event->SetTrusted(true); 134 135 mPort->DispatchEvent(*event); 136 } 137 138 private: 139 ~PostMessageRunnable() = default; 140 141 RefPtr<MessagePort> mPort; 142 RefPtr<SharedMessageBody> mData; 143 }; 144 145 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort) 146 147 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort, 148 DOMEventTargetHelper) 149 if (tmp->mPostMessageRunnable) { 150 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort); 151 } 152 153 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort); 154 155 tmp->CloseForced(); 156 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 157 158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort, 159 DOMEventTargetHelper) 160 if (tmp->mPostMessageRunnable) { 161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort); 162 } 163 164 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort); 165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 166 167 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePort) 168 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 169 170 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper) 171 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper) 172 173 MessagePort::MessagePort(nsIGlobalObject* aGlobal, State aState) 174 : DOMEventTargetHelper(aGlobal), 175 mRefMessageBodyService(RefMessageBodyService::GetOrCreate()), 176 mState(aState), 177 mMessageQueueEnabled(false), 178 mIsKeptAlive(false), 179 mHasBeenTransferredOrClosed(false) { 180 MOZ_ASSERT(aGlobal); 181 182 mIdentifier = MakeUnique<MessagePortIdentifier>(); 183 mIdentifier->neutered() = true; 184 mIdentifier->sequenceId() = 0; 185 } 186 187 MessagePort::~MessagePort() { 188 CloseForced(); 189 MOZ_ASSERT(!mActor); 190 if (mActor) { 191 mActor->SetPort(nullptr); 192 mActor = nullptr; 193 } 194 MOZ_ASSERT(!mWorkerRef); 195 } 196 197 /* static */ 198 already_AddRefed<MessagePort> MessagePort::Create(nsIGlobalObject* aGlobal, 199 const nsID& aUUID, 200 const nsID& aDestinationUUID, 201 ErrorResult& aRv) { 202 MOZ_ASSERT(aGlobal); 203 204 RefPtr<MessagePort> mp = 205 new MessagePort(aGlobal, eStateInitializingUnshippedEntangled); 206 mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */, 207 false /* Neutered */, aRv); 208 return mp.forget(); 209 } 210 211 /* static */ 212 already_AddRefed<MessagePort> MessagePort::Create( 213 nsIGlobalObject* aGlobal, UniqueMessagePortId& aIdentifier, 214 ErrorResult& aRv) { 215 MOZ_ASSERT(aGlobal); 216 217 RefPtr<MessagePort> mp = new MessagePort(aGlobal, eStateEntangling); 218 mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(), 219 aIdentifier.sequenceId(), aIdentifier.neutered(), aRv); 220 aIdentifier.neutered() = true; 221 return mp.forget(); 222 } 223 224 void MessagePort::UnshippedEntangle(RefPtr<MessagePort>& aEntangledPort) { 225 MOZ_DIAGNOSTIC_ASSERT(aEntangledPort); 226 MOZ_DIAGNOSTIC_ASSERT(!mUnshippedEntangledPort); 227 228 if (mState == eStateInitializingUnshippedEntangled) { 229 mUnshippedEntangledPort = aEntangledPort; 230 mState = eStateUnshippedEntangled; 231 } else { 232 MOZ_ASSERT_UNREACHABLE("Should not have been called."); 233 } 234 } 235 236 void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID, 237 uint32_t aSequenceID, bool aNeutered, 238 ErrorResult& aRv) { 239 MOZ_ASSERT(mIdentifier); 240 mIdentifier->uuid() = aUUID; 241 mIdentifier->destinationUuid() = aDestinationUUID; 242 mIdentifier->sequenceId() = aSequenceID; 243 244 if (aNeutered) { 245 // If this port is neutered we don't want to keep it alive artificially nor 246 // we want to add listeners or WorkerRefs. 247 mState = eStateDisentangled; 248 return; 249 } 250 251 if (mState == eStateEntangling) { 252 if (!ConnectToPBackground()) { 253 aRv.Throw(NS_ERROR_FAILURE); 254 return; 255 } 256 } else { 257 MOZ_ASSERT(mState == eStateInitializingUnshippedEntangled); 258 } 259 260 // The port has to keep itself alive until it's entangled. 261 UpdateMustKeepAlive(); 262 263 if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) { 264 RefPtr<MessagePort> self = this; 265 266 // When the callback is executed, we cannot process messages anymore because 267 // we cannot dispatch new runnables. Let's force a Close(). 268 RefPtr<StrongWorkerRef> strongWorkerRef = StrongWorkerRef::Create( 269 workerPrivate, "MessagePort", [self]() { self->CloseForced(); }); 270 if (NS_WARN_IF(!strongWorkerRef)) { 271 // The worker is shutting down. 272 CloseForced(); 273 aRv.Throw(NS_ERROR_FAILURE); 274 return; 275 } 276 277 MOZ_ASSERT(!mWorkerRef); 278 mWorkerRef = std::move(strongWorkerRef); 279 } 280 } 281 282 JSObject* MessagePort::WrapObject(JSContext* aCx, 283 JS::Handle<JSObject*> aGivenProto) { 284 return MessagePort_Binding::Wrap(aCx, this, aGivenProto); 285 } 286 287 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, 288 const Sequence<JSObject*>& aTransferable, 289 ErrorResult& aRv) { 290 // We *must* clone the data here, or the JS::Value could be modified 291 // by script 292 293 // Here we want to check if the transerable object list contains 294 // this port. 295 for (uint32_t i = 0; i < aTransferable.Length(); ++i) { 296 JS::Rooted<JSObject*> object(aCx, aTransferable[i]); 297 if (!object) { 298 continue; 299 } 300 301 MessagePort* port = nullptr; 302 nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port); 303 if (NS_SUCCEEDED(rv) && port == this) { 304 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); 305 return; 306 } 307 } 308 309 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); 310 311 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable, 312 &transferable); 313 if (NS_WARN_IF(aRv.Failed())) { 314 return; 315 } 316 317 Maybe<nsID> agentClusterId; 318 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal(); 319 if (global) { 320 agentClusterId = global->GetAgentClusterId(); 321 } 322 323 RefPtr<SharedMessageBody> data = new SharedMessageBody( 324 StructuredCloneHolder::TransferringSupported, agentClusterId); 325 326 data->Write(aCx, aMessage, transferable, mIdentifier->uuid(), 327 mRefMessageBodyService, aRv); 328 329 if (NS_WARN_IF(aRv.Failed())) { 330 return; 331 } 332 333 // This message has to be ignored. 334 if (mState > eStateEntangled) { 335 return; 336 } 337 338 if (mState == eStateInitializingUnshippedEntangled) { 339 MOZ_ASSERT_UNREACHABLE( 340 "Should be eStateUnshippedEntangled or eStateDisentangled by now."); 341 return; 342 } 343 344 // If we are unshipped we are connected to the other port on the same thread. 345 if (mState == eStateUnshippedEntangled) { 346 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort); 347 mUnshippedEntangledPort->mMessages.AppendElement(data); 348 mUnshippedEntangledPort->Dispatch(); 349 return; 350 } 351 352 // Not entangled yet, but already closed/disentangled. 353 if (mState == eStateEntanglingForDisentangle || 354 mState == eStateEntanglingForClose) { 355 return; 356 } 357 358 RemoveDocFromBFCache(); 359 360 // Not entangled yet. 361 if (mState == eStateEntangling) { 362 mMessagesForTheOtherPort.AppendElement(data); 363 return; 364 } 365 366 MOZ_ASSERT(mActor); 367 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); 368 369 AutoTArray<RefPtr<SharedMessageBody>, 1> array; 370 array.AppendElement(data); 371 372 AutoTArray<MessageData, 1> messages; 373 // note: `messages` will borrow the underlying buffer, but this is okay 374 // because reverse destruction order means `messages` will be destroyed prior 375 // to `array`/`data`. 376 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), array, 377 messages); 378 mActor->SendPostMessages(messages); 379 } 380 381 void MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, 382 const StructuredSerializeOptions& aOptions, 383 ErrorResult& aRv) { 384 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv); 385 } 386 387 void MessagePort::Start() { 388 if (mMessageQueueEnabled) { 389 return; 390 } 391 392 mMessageQueueEnabled = true; 393 Dispatch(); 394 } 395 396 void MessagePort::Dispatch() { 397 if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) { 398 return; 399 } 400 401 switch (mState) { 402 case eStateInitializingUnshippedEntangled: 403 MOZ_ASSERT_UNREACHABLE( 404 "Should be eStateUnshippedEntangled or eStateDisentangled by now."); 405 break; 406 407 case eStateUnshippedEntangled: 408 // Everything is fine here. We have messages because the other 409 // port populates our queue directly. 410 break; 411 412 case eStateEntangling: 413 // Everything is fine here as well. We have messages because the other 414 // port populated our queue directly when we were in the 415 // eStateUnshippedEntangled state. 416 break; 417 418 case eStateEntanglingForDisentangle: 419 // Here we don't want to ship messages because these messages must be 420 // delivered by the cloned version of this one. They will be sent in the 421 // SendDisentangle(). 422 return; 423 424 case eStateEntanglingForClose: 425 // We still want to deliver messages if we are closing. These messages 426 // are here from the previous eStateUnshippedEntangled state. 427 break; 428 429 case eStateEntangled: 430 // This port is up and running. 431 break; 432 433 case eStateDisentangling: 434 // If we are in the process to disentangle the port, we cannot dispatch 435 // messages. They will be sent to the cloned version of this port via 436 // SendDisentangle(); 437 return; 438 439 case eStateDisentangled: 440 MOZ_CRASH("This cannot happen."); 441 // It cannot happen because Disentangle should take off all the pending 442 // messages. 443 break; 444 445 case eStateDisentangledForClose: 446 // If we are here is because the port has been closed. We can still 447 // process the pending messages. 448 break; 449 } 450 451 RefPtr<SharedMessageBody> data = mMessages.ElementAt(0); 452 mMessages.RemoveElementAt(0); 453 454 mPostMessageRunnable = new PostMessageRunnable(this, data); 455 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable)); 456 } 457 458 void MessagePort::Close() { 459 mHasBeenTransferredOrClosed = true; 460 CloseInternal(true /* aSoftly */); 461 } 462 463 void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); } 464 465 void MessagePort::CloseInternal(bool aSoftly) { 466 // If we have some messages to send but we don't want a 'soft' close, we have 467 // to flush them now. 468 if (!aSoftly) { 469 mMessages.Clear(); 470 } 471 472 // Let's inform the RefMessageBodyService that any our shared messages are 473 // now invalid. 474 mRefMessageBodyService->ForgetPort(mIdentifier->uuid()); 475 476 if (mState == eStateInitializingUnshippedEntangled) { 477 // We can end up here if we failed to create our WorkerRef. Our Create 478 // method will end up returning an error and MessageChannel will not bother 479 // creating our counterpart port or calling UnshippedEntangle (and we 480 // assert on this). 481 mState = eStateDisentangledForClose; 482 483 UpdateMustKeepAlive(); 484 return; 485 } 486 487 if (mState == eStateUnshippedEntangled) { 488 MOZ_DIAGNOSTIC_ASSERT(mUnshippedEntangledPort); 489 490 // This avoids loops. 491 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort); 492 493 mState = eStateDisentangledForClose; 494 port->CloseInternal(aSoftly); 495 496 UpdateMustKeepAlive(); 497 return; 498 } 499 500 // Not entangled yet, we have to wait. 501 if (mState == eStateEntangling) { 502 mState = eStateEntanglingForClose; 503 return; 504 } 505 506 // Not entangled but already cloned or closed 507 if (mState == eStateEntanglingForDisentangle || 508 mState == eStateEntanglingForClose) { 509 return; 510 } 511 512 // Maybe we were already closing the port but softly. In this case we call 513 // UpdateMustKeepAlive() to consider the empty pending message queue. 514 if (mState == eStateDisentangledForClose && !aSoftly) { 515 UpdateMustKeepAlive(); 516 return; 517 } 518 519 if (mState > eStateEntangled) { 520 return; 521 } 522 523 // We don't care about stopping the sending of messages because from now all 524 // the incoming messages will be ignored. 525 mState = eStateDisentangledForClose; 526 527 MOZ_ASSERT(mActor); 528 529 mActor->SendClose(); 530 mActor->SetPort(nullptr); 531 mActor = nullptr; 532 533 UpdateMustKeepAlive(); 534 } 535 536 EventHandlerNonNull* MessagePort::GetOnmessage() { 537 return GetEventHandler(nsGkAtoms::onmessage); 538 } 539 540 void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) { 541 SetEventHandler(nsGkAtoms::onmessage, aCallback); 542 543 // When using onmessage, the call to start() is implied. 544 Start(); 545 } 546 547 // This method is called when the PMessagePortChild actor is entangled to 548 // another actor. It receives a list of messages to be dispatch. It can be that 549 // we were waiting for this entangling step in order to disentangle the port or 550 // to close it. 551 void MessagePort::Entangled(nsTArray<MessageData>& aMessages) { 552 MOZ_ASSERT(mState == eStateEntangling || 553 mState == eStateEntanglingForDisentangle || 554 mState == eStateEntanglingForClose); 555 556 State oldState = mState; 557 mState = eStateEntangled; 558 559 // If we have pending messages, these have to be sent. 560 if (!mMessagesForTheOtherPort.IsEmpty()) { 561 { 562 nsTArray<MessageData> messages; 563 SharedMessageBody::FromSharedToMessagesChild( 564 mActor->Manager(), mMessagesForTheOtherPort, messages); 565 mActor->SendPostMessages(messages); 566 } 567 // Because `messages` borrow the underlying JSStructuredCloneData buffers, 568 // only clear after `messages` have gone out of scope. 569 mMessagesForTheOtherPort.Clear(); 570 } 571 572 // We must convert the messages into SharedMessageBodys to avoid leaks. 573 FallibleTArray<RefPtr<SharedMessageBody>> data; 574 if (NS_WARN_IF( 575 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) { 576 DispatchError(); 577 return; 578 } 579 580 // If the next step is to close the port, we do it ignoring the received 581 // messages. 582 if (oldState == eStateEntanglingForClose) { 583 CloseForced(); 584 return; 585 } 586 587 mMessages.AppendElements(data); 588 589 // We were waiting for the entangling callback in order to disentangle this 590 // port immediately after. 591 if (oldState == eStateEntanglingForDisentangle) { 592 StartDisentangling(); 593 return; 594 } 595 596 Dispatch(); 597 } 598 599 void MessagePort::StartDisentangling() { 600 MOZ_ASSERT(mActor); 601 MOZ_ASSERT(mState == eStateEntangled); 602 603 mState = eStateDisentangling; 604 605 // Sending this message we communicate to the parent actor that we don't want 606 // to receive any new messages. It is possible that a message has been 607 // already sent but not received yet. So we have to collect all of them and 608 // we send them in the SendDispatch() request. 609 mActor->SendStopSendingData(); 610 } 611 612 void MessagePort::MessagesReceived(nsTArray<MessageData>& aMessages) { 613 MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling || 614 // This last step can happen only if Close() has been called 615 // manually. At this point SendClose() is sent but we can still 616 // receive something until the Closing request is processed. 617 mState == eStateDisentangledForClose); 618 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); 619 620 RemoveDocFromBFCache(); 621 622 FallibleTArray<RefPtr<SharedMessageBody>> data; 623 if (NS_WARN_IF( 624 !SharedMessageBody::FromMessagesToSharedChild(aMessages, data))) { 625 DispatchError(); 626 return; 627 } 628 629 mMessages.AppendElements(data); 630 631 if (mState == eStateEntangled) { 632 Dispatch(); 633 } 634 } 635 636 void MessagePort::StopSendingDataConfirmed() { 637 MOZ_ASSERT(mState == eStateDisentangling); 638 MOZ_ASSERT(mActor); 639 640 Disentangle(); 641 } 642 643 void MessagePort::Disentangle() { 644 MOZ_ASSERT(mState == eStateDisentangling); 645 MOZ_ASSERT(mActor); 646 647 mState = eStateDisentangled; 648 649 { 650 nsTArray<MessageData> messages; 651 SharedMessageBody::FromSharedToMessagesChild(mActor->Manager(), mMessages, 652 messages); 653 mActor->SendDisentangle(messages); 654 } 655 656 // Let's inform the RefMessageBodyService that any our shared messages are 657 // now invalid. 658 mRefMessageBodyService->ForgetPort(mIdentifier->uuid()); 659 660 // Only clear mMessages after the MessageData instances have gone out of scope 661 // because they borrow mMessages' underlying JSStructuredCloneDatas. 662 mMessages.Clear(); 663 664 mActor->SetPort(nullptr); 665 mActor = nullptr; 666 667 UpdateMustKeepAlive(); 668 } 669 670 void MessagePort::CloneAndDisentangle(UniqueMessagePortId& aIdentifier) { 671 MOZ_ASSERT(mIdentifier); 672 MOZ_ASSERT(!mHasBeenTransferredOrClosed); 673 674 mHasBeenTransferredOrClosed = true; 675 676 // We can clone a port that has already been transfered. In this case, on the 677 // otherside will have a neutered port. Here we set neutered to true so that 678 // we are safe in case a early return. 679 aIdentifier.neutered() = true; 680 681 if (mState > eStateEntangled) { 682 return; 683 } 684 685 // We already have a 'next step'. We have to consider this port as already 686 // cloned/closed/disentangled. 687 if (mState == eStateEntanglingForDisentangle || 688 mState == eStateEntanglingForClose) { 689 return; 690 } 691 692 aIdentifier.uuid() = mIdentifier->uuid(); 693 aIdentifier.destinationUuid() = mIdentifier->destinationUuid(); 694 aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1; 695 aIdentifier.neutered() = false; 696 697 // We have to entangle first. 698 if (mState == eStateUnshippedEntangled) { 699 MOZ_ASSERT(mUnshippedEntangledPort); 700 MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); 701 702 RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort); 703 704 // Disconnect the entangled port and connect it to PBackground. 705 if (!port->ConnectToPBackground()) { 706 // We are probably shutting down. We cannot proceed. 707 mState = eStateDisentangled; 708 UpdateMustKeepAlive(); 709 return; 710 } 711 712 // In this case, we don't need to be connected to the PBackground service. 713 if (mMessages.IsEmpty()) { 714 aIdentifier.sequenceId() = mIdentifier->sequenceId(); 715 716 mState = eStateDisentangled; 717 UpdateMustKeepAlive(); 718 return; 719 } 720 721 // Register this component to PBackground. 722 if (!ConnectToPBackground()) { 723 // We are probably shutting down. We cannot proceed. 724 return; 725 } 726 727 mState = eStateEntanglingForDisentangle; 728 return; 729 } 730 731 // Not entangled yet, we have to wait. 732 if (mState == eStateEntangling) { 733 mState = eStateEntanglingForDisentangle; 734 return; 735 } 736 737 MOZ_ASSERT(mState == eStateEntangled); 738 StartDisentangling(); 739 } 740 741 void MessagePort::Closed() { 742 if (mState >= eStateDisentangled) { 743 return; 744 } 745 746 mState = eStateDisentangledForClose; 747 748 if (mActor) { 749 mActor->SetPort(nullptr); 750 mActor = nullptr; 751 } 752 753 UpdateMustKeepAlive(); 754 } 755 756 bool MessagePort::ConnectToPBackground() { 757 RefPtr<MessagePort> self = this; 758 auto raii = MakeScopeExit([self] { 759 self->mState = eStateDisentangled; 760 self->UpdateMustKeepAlive(); 761 }); 762 763 mozilla::ipc::PBackgroundChild* actorChild = 764 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 765 if (NS_WARN_IF(!actorChild)) { 766 return false; 767 } 768 769 PMessagePortChild* actor = actorChild->SendPMessagePortConstructor( 770 mIdentifier->uuid(), mIdentifier->destinationUuid(), 771 mIdentifier->sequenceId()); 772 if (NS_WARN_IF(!actor)) { 773 return false; 774 } 775 776 mActor = static_cast<MessagePortChild*>(actor); 777 MOZ_ASSERT(mActor); 778 779 mActor->SetPort(this); 780 mState = eStateEntangling; 781 782 raii.release(); 783 return true; 784 } 785 786 void MessagePort::UpdateMustKeepAlive() { 787 if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) { 788 mIsKeptAlive = false; 789 790 // The DTOR of this WorkerRef will release the worker for us. 791 mWorkerRef = nullptr; 792 793 Release(); 794 return; 795 } 796 797 if (mState < eStateDisentangled && !mIsKeptAlive) { 798 mIsKeptAlive = true; 799 AddRef(); 800 } 801 } 802 803 void MessagePort::DisconnectFromOwner() { 804 CloseForced(); 805 DOMEventTargetHelper::DisconnectFromOwner(); 806 } 807 808 void MessagePort::RemoveDocFromBFCache() { 809 if (!NS_IsMainThread()) { 810 return; 811 } 812 813 if (nsPIDOMWindowInner* window = GetOwnerWindow()) { 814 window->RemoveFromBFCacheSync(); 815 } 816 } 817 818 /* static */ 819 void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) { 820 mozilla::ipc::PBackgroundChild* actorChild = 821 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 822 if (NS_WARN_IF(!actorChild)) { 823 // We are shutting down this process. This port will be leaked. 824 return; 825 } 826 827 (void)actorChild->SendMessagePortForceClose(aIdentifier.uuid(), 828 aIdentifier.destinationUuid(), 829 aIdentifier.sequenceId()); 830 } 831 832 void MessagePort::DispatchError() { 833 nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject(); 834 835 AutoJSAPI jsapi; 836 if (!globalObject || !jsapi.Init(globalObject)) { 837 NS_WARNING("Failed to initialize AutoJSAPI object."); 838 return; 839 } 840 841 RootedDictionary<MessageEventInit> init(jsapi.cx()); 842 init.mBubbles = false; 843 init.mCancelable = false; 844 845 RefPtr<Event> event = 846 MessageEvent::Constructor(this, u"messageerror"_ns, init); 847 event->SetTrusted(true); 848 849 DispatchEvent(*event); 850 } 851 852 } // namespace mozilla::dom