/* -*- 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 "SharedWorker.h" #include "nsPIDOMWindow.h" #include "mozilla/EventDispatcher.h" #include "mozilla/Preferences.h" #include "mozilla/dom/MessagePort.h" #include "mozilla/dom/SharedWorkerBinding.h" #include "mozilla/dom/WorkerBinding.h" #include "mozilla/Telemetry.h" #include "nsContentUtils.h" #include "nsIClassInfoImpl.h" #include "nsIDOMEvent.h" #include "RuntimeService.h" #include "WorkerPrivate.h" #ifdef XP_WIN #undef PostMessage #endif using mozilla::dom::Optional; using mozilla::dom::Sequence; using mozilla::dom::MessagePort; using namespace mozilla; using namespace mozilla::dom; SharedWorker::SharedWorker(nsPIDOMWindowInner* aWindow, WorkerPrivate* aWorkerPrivate, MessagePort* aMessagePort) : DOMEventTargetHelper(aWindow) , mWorkerPrivate(aWorkerPrivate) , mMessagePort(aMessagePort) , mFrozen(false) { AssertIsOnMainThread(); MOZ_ASSERT(aWorkerPrivate); MOZ_ASSERT(aMessagePort); } SharedWorker::~SharedWorker() { AssertIsOnMainThread(); } // static already_AddRefed SharedWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL, const StringOrWorkerOptions& aOptions, ErrorResult& aRv) { AssertIsOnMainThread(); workerinternals::RuntimeService* rts = workerinternals::RuntimeService::GetOrCreateService(); if (!rts) { aRv = NS_ERROR_NOT_AVAILABLE; return nullptr; } nsAutoString name; if (aOptions.IsString()) { name = aOptions.GetAsString(); } else { MOZ_ASSERT(aOptions.IsWorkerOptions()); name = aOptions.GetAsWorkerOptions().mName; } RefPtr sharedWorker; nsresult rv = rts->CreateSharedWorker(aGlobal, aScriptURL, name, getter_AddRefs(sharedWorker)); if (NS_FAILED(rv)) { aRv = rv; return nullptr; } return sharedWorker.forget(); } MessagePort* SharedWorker::Port() { AssertIsOnMainThread(); return mMessagePort; } void SharedWorker::Freeze() { AssertIsOnMainThread(); MOZ_ASSERT(!IsFrozen()); mFrozen = true; } void SharedWorker::Thaw() { AssertIsOnMainThread(); MOZ_ASSERT(IsFrozen()); mFrozen = false; if (!mFrozenEvents.IsEmpty()) { nsTArray> events; mFrozenEvents.SwapElements(events); for (uint32_t index = 0; index < events.Length(); index++) { nsCOMPtr& event = events[index]; MOZ_ASSERT(event); nsCOMPtr target; if (NS_SUCCEEDED(event->GetTarget(getter_AddRefs(target)))) { bool ignored; if (NS_FAILED(target->DispatchEvent(event, &ignored))) { NS_WARNING("Failed to dispatch event!"); } } else { NS_WARNING("Failed to get target!"); } } } } void SharedWorker::QueueEvent(nsIDOMEvent* aEvent) { AssertIsOnMainThread(); MOZ_ASSERT(aEvent); MOZ_ASSERT(IsFrozen()); mFrozenEvents.AppendElement(aEvent); } void SharedWorker::Close() { AssertIsOnMainThread(); if (mMessagePort) { mMessagePort->Close(); } } void SharedWorker::PostMessage(JSContext* aCx, JS::Handle aMessage, const Sequence& aTransferable, ErrorResult& aRv) { AssertIsOnMainThread(); MOZ_ASSERT(mWorkerPrivate); MOZ_ASSERT(mMessagePort); mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv); } NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SharedWorker) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrozenEvents) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrozenEvents) NS_IMPL_CYCLE_COLLECTION_UNLINK_END JSObject* SharedWorker::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { AssertIsOnMainThread(); return SharedWorkerBinding::Wrap(aCx, this, aGivenProto); } nsresult SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnMainThread(); if (IsFrozen()) { nsCOMPtr event = aVisitor.mDOMEvent; if (!event) { event = EventDispatcher::CreateEvent(aVisitor.mEvent->mOriginalTarget, aVisitor.mPresContext, aVisitor.mEvent, EmptyString()); } QueueEvent(event); aVisitor.mCanHandle = false; aVisitor.SetParentTarget(nullptr, false); return NS_OK; } return DOMEventTargetHelper::GetEventTargetParent(aVisitor); }