/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MessagePort.h" #include "MessageEvent.h" #include "MessagePortChild.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/File.h" #include "mozilla/dom/MessageChannel.h" #include "mozilla/dom/MessageEventBinding.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/MessagePortChild.h" #include "mozilla/dom/PMessagePort.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerScope.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/MessagePortTimelineMarker.h" #include "mozilla/TimelineConsumers.h" #include "mozilla/TimelineMarker.h" #include "mozilla/Unused.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsPresContext.h" #include "ScriptSettings.h" #include "SharedMessagePortMessage.h" #include "nsIBFCacheEntry.h" #include "nsIDocument.h" #include "nsIDOMFileList.h" #include "nsIPresShell.h" #include "nsISupportsPrimitives.h" #include "nsServiceManagerUtils.h" #ifdef XP_WIN #undef PostMessage #endif using namespace mozilla::dom::workers; namespace mozilla { namespace dom { class PostMessageRunnable final : public CancelableRunnable { friend class MessagePort; public: PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData) : mPort(aPort) , mData(aData) { MOZ_ASSERT(aPort); MOZ_ASSERT(aData); } NS_IMETHOD Run() override { NS_ASSERT_OWNINGTHREAD(Runnable); // The port can be cycle collected while this runnable is pending in // the event queue. if (!mPort) { return NS_OK; } MOZ_ASSERT(mPort->mPostMessageRunnable == this); nsresult rv = DispatchMessage(); // We must check if we were waiting for this message in order to shutdown // the port. mPort->UpdateMustKeepAlive(); mPort->mPostMessageRunnable = nullptr; mPort->Dispatch(); return rv; } nsresult Cancel() override { NS_ASSERT_OWNINGTHREAD(Runnable); mPort = nullptr; mData = nullptr; return NS_OK; } private: nsresult DispatchMessage() const { NS_ASSERT_OWNINGTHREAD(Runnable); nsCOMPtr globalObject = mPort->GetParentObject(); AutoJSAPI jsapi; if (!globalObject || !jsapi.Init(globalObject)) { NS_WARNING("Failed to initialize AutoJSAPI object."); return NS_ERROR_FAILURE; } JSContext* cx = jsapi.cx(); ErrorResult rv; JS::Rooted value(cx); UniquePtr start; UniquePtr end; RefPtr timelines = TimelineConsumers::Get(); bool isTimelineRecording = timelines && !timelines->IsEmpty(); if (isTimelineRecording) { start = MakeUnique( ProfileTimelineMessagePortOperationType::DeserializeData, MarkerTracingType::START); } mData->Read(cx, &value, rv); if (isTimelineRecording) { end = MakeUnique( ProfileTimelineMessagePortOperationType::DeserializeData, MarkerTracingType::END); timelines->AddMarkerForAllObservedDocShells(start); timelines->AddMarkerForAllObservedDocShells(end); } if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } // Create the event nsCOMPtr eventTarget = do_QueryInterface(mPort->GetOwner()); RefPtr event = new MessageEvent(eventTarget, nullptr, nullptr); Sequence> ports; if (!mData->TakeTransferredPortsAsSequence(ports)) { return NS_ERROR_OUT_OF_MEMORY; } event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"), false /* non-bubbling */, false /* cancelable */, value, EmptyString(), EmptyString(), nullptr, ports); event->SetTrusted(true); bool dummy; mPort->DispatchEvent(static_cast(event.get()), &dummy); return NS_OK; } private: ~PostMessageRunnable() {} RefPtr mPort; RefPtr mData; }; NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort, DOMEventTargetHelper) if (tmp->mPostMessageRunnable) { NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort); } NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages); NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort); NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort, DOMEventTargetHelper) if (tmp->mPostMessageRunnable) { NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort) NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback) NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper) namespace { class MessagePortWorkerHolder final : public workers::WorkerHolder { MessagePort* mPort; public: explicit MessagePortWorkerHolder(MessagePort* aPort) : mPort(aPort) { MOZ_ASSERT(aPort); MOZ_COUNT_CTOR(MessagePortWorkerHolder); } virtual bool Notify(workers::Status aStatus) override { if (aStatus > Running) { // We cannot process messages anymore because we cannot dispatch new // runnables. Let's force a Close(). mPort->CloseForced(); } return true; } private: ~MessagePortWorkerHolder() { MOZ_COUNT_DTOR(MessagePortWorkerHolder); } }; class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback { public: NS_DECL_ISUPPORTS static void ForceClose(const MessagePortIdentifier& aIdentifier) { PBackgroundChild* actor = mozilla::ipc::BackgroundChild::GetForCurrentThread(); if (actor) { Unused << actor->SendMessagePortForceClose(aIdentifier.uuid(), aIdentifier.destinationUuid(), aIdentifier.sequenceId()); return; } RefPtr helper = new ForceCloseHelper(aIdentifier); if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) { MOZ_CRASH(); } } private: explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier) : mIdentifier(aIdentifier) {} ~ForceCloseHelper() {} void ActorFailed() override { MOZ_CRASH("Failed to create a PBackgroundChild actor!"); } void ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override { ForceClose(mIdentifier); } const MessagePortIdentifier mIdentifier; }; NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback) } // namespace MessagePort::MessagePort(nsIGlobalObject* aGlobal) : DOMEventTargetHelper(aGlobal) , mInnerID(0) , mMessageQueueEnabled(false) , mIsKeptAlive(false) { MOZ_ASSERT(aGlobal); mIdentifier = new MessagePortIdentifier(); mIdentifier->neutered() = true; mIdentifier->sequenceId() = 0; } MessagePort::~MessagePort() { CloseForced(); MOZ_ASSERT(!mWorkerHolder); } /* static */ already_AddRefed MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID, const nsID& aDestinationUUID, ErrorResult& aRv) { MOZ_ASSERT(aGlobal); RefPtr mp = new MessagePort(aGlobal); mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */, false /* Neutered */, eStateUnshippedEntangled, aRv); return mp.forget(); } /* static */ already_AddRefed MessagePort::Create(nsIGlobalObject* aGlobal, const MessagePortIdentifier& aIdentifier, ErrorResult& aRv) { MOZ_ASSERT(aGlobal); RefPtr mp = new MessagePort(aGlobal); mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(), aIdentifier.sequenceId(), aIdentifier.neutered(), eStateEntangling, aRv); return mp.forget(); } void MessagePort::UnshippedEntangle(MessagePort* aEntangledPort) { MOZ_ASSERT(aEntangledPort); MOZ_ASSERT(!mUnshippedEntangledPort); mUnshippedEntangledPort = aEntangledPort; } void MessagePort::Initialize(const nsID& aUUID, const nsID& aDestinationUUID, uint32_t aSequenceID, bool mNeutered, State aState, ErrorResult& aRv) { MOZ_ASSERT(mIdentifier); mIdentifier->uuid() = aUUID; mIdentifier->destinationUuid() = aDestinationUUID; mIdentifier->sequenceId() = aSequenceID; mState = aState; if (mNeutered) { // If this port is neutered we don't want to keep it alive artificially nor // we want to add listeners or workerWorkerHolders. mState = eStateDisentangled; return; } if (mState == eStateEntangling) { ConnectToPBackground(); } else { MOZ_ASSERT(mState == eStateUnshippedEntangled); } // The port has to keep itself alive until it's entangled. UpdateMustKeepAlive(); if (!NS_IsMainThread()) { WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); MOZ_ASSERT(!mWorkerHolder); nsAutoPtr workerHolder(new MessagePortWorkerHolder(this)); if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Closing))) { aRv.Throw(NS_ERROR_FAILURE); return; } mWorkerHolder = Move(workerHolder); } else if (GetOwner()) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(GetOwner()->IsInnerWindow()); mInnerID = GetOwner()->WindowID(); nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "inner-window-destroyed", false); } } } JSObject* MessagePort::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return MessagePortBinding::Wrap(aCx, this, aGivenProto); } void MessagePort::PostMessage(JSContext* aCx, JS::Handle aMessage, const Sequence& aTransferable, ErrorResult& aRv) { // We *must* clone the data here, or the JS::Value could be modified // by script // Here we want to check if the transerable object list contains // this port. for (uint32_t i = 0; i < aTransferable.Length(); ++i) { JSObject* object = aTransferable[i]; if (!object) { continue; } MessagePort* port = nullptr; nsresult rv = UNWRAP_OBJECT(MessagePort, object, port); if (NS_SUCCEEDED(rv) && port == this) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } } JS::Rooted transferable(aCx, JS::UndefinedValue()); aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable, &transferable); if (NS_WARN_IF(aRv.Failed())) { return; } RefPtr data = new SharedMessagePortMessage(); UniquePtr start; UniquePtr end; RefPtr timelines = TimelineConsumers::Get(); bool isTimelineRecording = timelines && !timelines->IsEmpty(); if (isTimelineRecording) { start = MakeUnique( ProfileTimelineMessagePortOperationType::SerializeData, MarkerTracingType::START); } data->Write(aCx, aMessage, transferable, aRv); if (isTimelineRecording) { end = MakeUnique( ProfileTimelineMessagePortOperationType::SerializeData, MarkerTracingType::END); timelines->AddMarkerForAllObservedDocShells(start); timelines->AddMarkerForAllObservedDocShells(end); } if (NS_WARN_IF(aRv.Failed())) { return; } // This message has to be ignored. if (mState > eStateEntangled) { return; } // If we are unshipped we are connected to the other port on the same thread. if (mState == eStateUnshippedEntangled) { MOZ_ASSERT(mUnshippedEntangledPort); mUnshippedEntangledPort->mMessages.AppendElement(data); mUnshippedEntangledPort->Dispatch(); return; } // Not entangled yet, but already closed/disentangled. if (mState == eStateEntanglingForDisentangle || mState == eStateEntanglingForClose) { return; } RemoveDocFromBFCache(); // Not entangled yet. if (mState == eStateEntangling) { mMessagesForTheOtherPort.AppendElement(data); return; } MOZ_ASSERT(mActor); MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); AutoTArray, 1> array; array.AppendElement(data); AutoTArray messages; // note: `messages` will borrow the underlying buffer, but this is okay // because reverse destruction order means `messages` will be destroyed prior // to `array`/`data`. SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages); mActor->SendPostMessages(messages); } void MessagePort::Start() { if (mMessageQueueEnabled) { return; } mMessageQueueEnabled = true; Dispatch(); } void MessagePort::Dispatch() { if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) { return; } switch (mState) { case eStateUnshippedEntangled: // Everything is fine here. We have messages because the other // port populates our queue directly. break; case eStateEntangling: // Everything is fine here as well. We have messages because the other // port populated our queue directly when we were in the // eStateUnshippedEntangled state. break; case eStateEntanglingForDisentangle: // Here we don't want to ship messages because these messages must be // delivered by the cloned version of this one. They will be sent in the // SendDisentangle(). return; case eStateEntanglingForClose: // We still want to deliver messages if we are closing. These messages // are here from the previous eStateUnshippedEntangled state. break; case eStateEntangled: // This port is up and running. break; case eStateDisentangling: // If we are in the process to disentangle the port, we cannot dispatch // messages. They will be sent to the cloned version of this port via // SendDisentangle(); return; case eStateDisentangled: MOZ_CRASH("This cannot happen."); // It cannot happen because Disentangle should take off all the pending // messages. break; case eStateDisentangledForClose: // If we are here is because the port has been closed. We can still // process the pending messages. break; } RefPtr data = mMessages.ElementAt(0); mMessages.RemoveElementAt(0); mPostMessageRunnable = new PostMessageRunnable(this, data); nsCOMPtr global = GetOwnerGlobal(); if (NS_IsMainThread() && global) { MOZ_ALWAYS_SUCCEEDS(global->Dispatch("MessagePortMessage", TaskCategory::Other, do_AddRef(mPostMessageRunnable))); return; } MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable)); } void MessagePort::Close() { CloseInternal(true /* aSoftly */); } void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); } void MessagePort::CloseInternal(bool aSoftly) { // If we have some messages to send but we don't want a 'soft' close, we have // to flush them now. if (!aSoftly) { mMessages.Clear(); } if (mState == eStateUnshippedEntangled) { MOZ_ASSERT(mUnshippedEntangledPort); // This avoids loops. RefPtr port = Move(mUnshippedEntangledPort); MOZ_ASSERT(mUnshippedEntangledPort == nullptr); mState = eStateDisentangledForClose; port->CloseInternal(aSoftly); UpdateMustKeepAlive(); return; } // Not entangled yet, we have to wait. if (mState == eStateEntangling) { mState = eStateEntanglingForClose; return; } // Not entangled but already cloned or closed if (mState == eStateEntanglingForDisentangle || mState == eStateEntanglingForClose) { return; } // Maybe we were already closing the port but softly. In this case we call // UpdateMustKeepAlive() to consider the empty pending message queue. if (mState == eStateDisentangledForClose && !aSoftly) { UpdateMustKeepAlive(); return; } if (mState > eStateEntangled) { return; } // We don't care about stopping the sending of messages because from now all // the incoming messages will be ignored. mState = eStateDisentangledForClose; MOZ_ASSERT(mActor); mActor->SendClose(); mActor->SetPort(nullptr); mActor = nullptr; UpdateMustKeepAlive(); } EventHandlerNonNull* MessagePort::GetOnmessage() { if (NS_IsMainThread()) { return GetEventHandler(nsGkAtoms::onmessage, EmptyString()); } return GetEventHandler(nullptr, NS_LITERAL_STRING("message")); } void MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) { if (NS_IsMainThread()) { SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback); } else { SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback); } // When using onmessage, the call to start() is implied. Start(); } // This method is called when the PMessagePortChild actor is entangled to // another actor. It receives a list of messages to be dispatch. It can be that // we were waiting for this entangling step in order to disentangle the port or // to close it. void MessagePort::Entangled(nsTArray& aMessages) { MOZ_ASSERT(mState == eStateEntangling || mState == eStateEntanglingForDisentangle || mState == eStateEntanglingForClose); State oldState = mState; mState = eStateEntangled; // If we have pending messages, these have to be sent. if (!mMessagesForTheOtherPort.IsEmpty()) { { nsTArray messages; SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessagesForTheOtherPort, messages); mActor->SendPostMessages(messages); } // Because `messages` borrow the underlying JSStructuredCloneData buffers, // only clear after `messages` have gone out of scope. mMessagesForTheOtherPort.Clear(); } // We must convert the messages into SharedMessagePortMessages to avoid leaks. FallibleTArray> data; if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages, data))) { // OOM, we cannot continue. return; } // If the next step is to close the port, we do it ignoring the received // messages. if (oldState == eStateEntanglingForClose) { CloseForced(); return; } mMessages.AppendElements(data); // We were waiting for the entangling callback in order to disentangle this // port immediately after. if (oldState == eStateEntanglingForDisentangle) { StartDisentangling(); return; } Dispatch(); } void MessagePort::StartDisentangling() { MOZ_ASSERT(mActor); MOZ_ASSERT(mState == eStateEntangled); mState = eStateDisentangling; // Sending this message we communicate to the parent actor that we don't want // to receive any new messages. It is possible that a message has been // already sent but not received yet. So we have to collect all of them and // we send them in the SendDispatch() request. mActor->SendStopSendingData(); } void MessagePort::MessagesReceived(nsTArray& aMessages) { MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling || // This last step can happen only if Close() has been called // manually. At this point SendClose() is sent but we can still // receive something until the Closing request is processed. mState == eStateDisentangledForClose); MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); RemoveDocFromBFCache(); FallibleTArray> data; if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages, data))) { // OOM, We cannot continue. return; } mMessages.AppendElements(data); if (mState == eStateEntangled) { Dispatch(); } } void MessagePort::StopSendingDataConfirmed() { MOZ_ASSERT(mState == eStateDisentangling); MOZ_ASSERT(mActor); Disentangle(); } void MessagePort::Disentangle() { MOZ_ASSERT(mState == eStateDisentangling); MOZ_ASSERT(mActor); mState = eStateDisentangled; { nsTArray messages; SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages, messages); mActor->SendDisentangle(messages); } // Only clear mMessages after the ClonedMessageData instances have gone out of // scope because they borrow mMessages' underlying JSStructuredCloneDatas. mMessages.Clear(); mActor->SetPort(nullptr); mActor = nullptr; UpdateMustKeepAlive(); } void MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier) { MOZ_ASSERT(mIdentifier); // We can clone a port that has already been transfered. In this case, on the // otherside will have a neutered port. Here we set neutered to true so that // we are safe in case a early return. aIdentifier.neutered() = true; if (mState > eStateEntangled) { return; } // We already have a 'next step'. We have to consider this port as already // cloned/closed/disentangled. if (mState == eStateEntanglingForDisentangle || mState == eStateEntanglingForClose) { return; } aIdentifier.uuid() = mIdentifier->uuid(); aIdentifier.destinationUuid() = mIdentifier->destinationUuid(); aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1; aIdentifier.neutered() = false; // We have to entangle first. if (mState == eStateUnshippedEntangled) { MOZ_ASSERT(mUnshippedEntangledPort); MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); // Disconnect the entangled port and connect it to PBackground. mUnshippedEntangledPort->ConnectToPBackground(); mUnshippedEntangledPort = nullptr; // In this case, we don't need to be connected to the PBackground service. if (mMessages.IsEmpty()) { aIdentifier.sequenceId() = mIdentifier->sequenceId(); mState = eStateDisentangled; UpdateMustKeepAlive(); return; } // Register this component to PBackground. ConnectToPBackground(); mState = eStateEntanglingForDisentangle; return; } // Not entangled yet, we have to wait. if (mState == eStateEntangling) { mState = eStateEntanglingForDisentangle; return; } MOZ_ASSERT(mState == eStateEntangled); StartDisentangling(); } void MessagePort::Closed() { if (mState >= eStateDisentangled) { return; } mState = eStateDisentangledForClose; if (mActor) { mActor->SetPort(nullptr); mActor = nullptr; } UpdateMustKeepAlive(); } void MessagePort::ConnectToPBackground() { mState = eStateEntangling; PBackgroundChild* actor = mozilla::ipc::BackgroundChild::GetForCurrentThread(); if (actor) { ActorCreated(actor); } else { if (NS_WARN_IF( !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) { MOZ_CRASH(); } } } void MessagePort::ActorFailed() { MOZ_CRASH("Failed to create a PBackgroundChild actor!"); } void MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor) { MOZ_ASSERT(aActor); MOZ_ASSERT(!mActor); MOZ_ASSERT(mIdentifier); MOZ_ASSERT(mState == eStateEntangling || mState == eStateEntanglingForDisentangle || mState == eStateEntanglingForClose); PMessagePortChild* actor = aActor->SendPMessagePortConstructor(mIdentifier->uuid(), mIdentifier->destinationUuid(), mIdentifier->sequenceId()); mActor = static_cast(actor); MOZ_ASSERT(mActor); mActor->SetPort(this); } void MessagePort::UpdateMustKeepAlive() { if (mState >= eStateDisentangled && mMessages.IsEmpty() && mIsKeptAlive) { mIsKeptAlive = false; // The DTOR of this WorkerHolder will release the worker for us. mWorkerHolder = nullptr; if (NS_IsMainThread()) { nsCOMPtr obs = do_GetService("@mozilla.org/observer-service;1"); if (obs) { obs->RemoveObserver(this, "inner-window-destroyed"); } } Release(); return; } if (mState < eStateDisentangled && !mIsKeptAlive) { mIsKeptAlive = true; AddRef(); } } NS_IMETHODIMP MessagePort::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { MOZ_ASSERT(NS_IsMainThread()); if (strcmp(aTopic, "inner-window-destroyed")) { return NS_OK; } // If the window id destroyed we have to release the reference that we are // keeping. if (!mIsKeptAlive) { return NS_OK; } nsCOMPtr wrapper = do_QueryInterface(aSubject); NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); uint64_t innerID; nsresult rv = wrapper->GetData(&innerID); NS_ENSURE_SUCCESS(rv, rv); if (innerID == mInnerID) { CloseForced(); } return NS_OK; } void MessagePort::RemoveDocFromBFCache() { if (!NS_IsMainThread()) { return; } nsPIDOMWindowInner* window = GetOwner(); if (!window) { return; } nsIDocument* doc = window->GetExtantDoc(); if (!doc) { return; } nsCOMPtr bfCacheEntry = doc->GetBFCacheEntry(); if (!bfCacheEntry) { return; } bfCacheEntry->RemoveFromBFCacheSync(); } /* static */ void MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier) { ForceCloseHelper::ForceClose(aIdentifier); } } // namespace dom } // namespace mozilla