michael@0: /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MessagePort.h" michael@0: michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/dom/MessagePortBinding.h" michael@0: #include "nsIDOMEvent.h" michael@0: michael@0: #include "SharedWorker.h" michael@0: #include "WorkerPrivate.h" michael@0: #include "WorkerRunnable.h" michael@0: michael@0: using mozilla::dom::EventHandlerNonNull; michael@0: using mozilla::dom::MessagePortBase; michael@0: using mozilla::dom::Optional; michael@0: using mozilla::dom::Sequence; michael@0: using namespace mozilla; michael@0: michael@0: USING_WORKERS_NAMESPACE michael@0: michael@0: namespace { michael@0: michael@0: class DelayedEventRunnable MOZ_FINAL : public WorkerRunnable michael@0: { michael@0: nsRefPtr mMessagePort; michael@0: nsTArray> mEvents; michael@0: michael@0: public: michael@0: DelayedEventRunnable(WorkerPrivate* aWorkerPrivate, michael@0: TargetAndBusyBehavior aBehavior, michael@0: MessagePort* aMessagePort, michael@0: nsTArray>& aEvents) michael@0: : WorkerRunnable(aWorkerPrivate, aBehavior), mMessagePort(aMessagePort) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aMessagePort); michael@0: MOZ_ASSERT(aEvents.Length()); michael@0: michael@0: mEvents.SwapElements(aEvents); michael@0: } michael@0: michael@0: bool michael@0: WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate); michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker, michael@0: uint64_t aSerial) michael@0: : MessagePortBase(aWindow), mSharedWorker(aSharedWorker), michael@0: mWorkerPrivate(nullptr), mSerial(aSerial), mStarted(false) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: MOZ_ASSERT(aSharedWorker); michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: MessagePort::MessagePort(WorkerPrivate* aWorkerPrivate, uint64_t aSerial) michael@0: : mWorkerPrivate(aWorkerPrivate), mSerial(aSerial), mStarted(false) michael@0: { michael@0: aWorkerPrivate->AssertIsOnWorkerThread(); michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: MessagePort::~MessagePort() michael@0: { michael@0: Close(); michael@0: } michael@0: michael@0: void michael@0: MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle aMessage, michael@0: const Optional>& aTransferable, michael@0: ErrorResult& aRv) michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: if (IsClosed()) { michael@0: aRv = NS_ERROR_DOM_INVALID_STATE_ERR; michael@0: return; michael@0: } michael@0: michael@0: if (mSharedWorker) { michael@0: mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv); michael@0: } michael@0: else { michael@0: mWorkerPrivate->PostMessageToParentMessagePort(aCx, Serial(), aMessage, michael@0: aTransferable, aRv); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MessagePort::Start() michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: if (IsClosed()) { michael@0: NS_WARNING("Called start() after calling close()!"); michael@0: return; michael@0: } michael@0: michael@0: if (mStarted) { michael@0: return; michael@0: } michael@0: michael@0: mStarted = true; michael@0: michael@0: if (!mQueuedEvents.IsEmpty()) { michael@0: WorkerPrivate* workerPrivate; michael@0: WorkerRunnable::TargetAndBusyBehavior behavior; michael@0: michael@0: if (mWorkerPrivate) { michael@0: workerPrivate = mWorkerPrivate; michael@0: behavior = WorkerRunnable::WorkerThreadModifyBusyCount; michael@0: } michael@0: else { michael@0: workerPrivate = mSharedWorker->GetWorkerPrivate(); michael@0: MOZ_ASSERT(workerPrivate); michael@0: michael@0: behavior = WorkerRunnable::ParentThreadUnchangedBusyCount; michael@0: } michael@0: michael@0: nsRefPtr runnable = michael@0: new DelayedEventRunnable(workerPrivate, behavior, this, mQueuedEvents); michael@0: runnable->Dispatch(nullptr); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MessagePort::Close() michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: if (!IsClosed()) { michael@0: CloseInternal(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MessagePort::QueueEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: AssertCorrectThread(); michael@0: MOZ_ASSERT(aEvent); michael@0: MOZ_ASSERT(!IsClosed()); michael@0: MOZ_ASSERT(!mStarted); michael@0: michael@0: mQueuedEvents.AppendElement(aEvent); michael@0: } michael@0: michael@0: EventHandlerNonNull* michael@0: MessagePort::GetOnmessage() michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: return NS_IsMainThread() ? GetEventHandler(nsGkAtoms::onmessage, EmptyString()) michael@0: : GetEventHandler(nullptr, NS_LITERAL_STRING("message")); michael@0: } michael@0: michael@0: void michael@0: MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: if (NS_IsMainThread()) { michael@0: SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback); michael@0: } michael@0: else { michael@0: SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback); michael@0: } michael@0: michael@0: Start(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: MessagePort::Clone() michael@0: { michael@0: NS_WARNING("Haven't implemented structured clone for these ports yet!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: MessagePort::CloseInternal() michael@0: { michael@0: AssertCorrectThread(); michael@0: MOZ_ASSERT(!IsClosed()); michael@0: MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty()); michael@0: michael@0: NS_WARN_IF_FALSE(mStarted, "Called close() before start()!"); michael@0: michael@0: if (!mStarted) { michael@0: mQueuedEvents.Clear(); michael@0: } michael@0: michael@0: mSharedWorker = nullptr; michael@0: mWorkerPrivate = nullptr; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: MessagePort::AssertCorrectThread() const michael@0: { michael@0: if (IsClosed()) { michael@0: return; // Can't assert anything if we nulled out our pointers. michael@0: } michael@0: michael@0: MOZ_ASSERT((mSharedWorker || mWorkerPrivate) && michael@0: !(mSharedWorker && mWorkerPrivate)); michael@0: michael@0: if (mSharedWorker) { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: else { michael@0: mWorkerPrivate->AssertIsOnWorkerThread(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorker) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEvents) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort, michael@0: DOMEventTargetHelper) michael@0: tmp->Close(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: JSObject* michael@0: MessagePort::WrapObject(JSContext* aCx) michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: return MessagePortBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: nsresult michael@0: MessagePort::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: AssertCorrectThread(); michael@0: michael@0: nsIDOMEvent*& event = aVisitor.mDOMEvent; michael@0: michael@0: if (event) { michael@0: bool preventDispatch = false; michael@0: michael@0: if (IsClosed()) { michael@0: preventDispatch = true; michael@0: } else if (NS_IsMainThread() && mSharedWorker->IsSuspended()) { michael@0: mSharedWorker->QueueEvent(event); michael@0: preventDispatch = true; michael@0: } else if (!mStarted) { michael@0: QueueEvent(event); michael@0: preventDispatch = true; michael@0: } michael@0: michael@0: if (preventDispatch) { michael@0: aVisitor.mCanHandle = false; michael@0: aVisitor.mParentTarget = nullptr; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return DOMEventTargetHelper::PreHandleEvent(aVisitor); michael@0: } michael@0: michael@0: bool michael@0: DelayedEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) michael@0: { michael@0: MOZ_ASSERT(mMessagePort); michael@0: mMessagePort->AssertCorrectThread(); michael@0: MOZ_ASSERT(mEvents.Length()); michael@0: michael@0: bool ignored; michael@0: for (uint32_t i = 0; i < mEvents.Length(); i++) { michael@0: mMessagePort->DispatchEvent(mEvents[i], &ignored); michael@0: } michael@0: michael@0: return true; michael@0: }