Backed out changeset ccfddcbccdac (bug 1204775) for sharedworker bustage

This commit is contained in:
Wes Kocher 2015-09-15 11:08:09 -07:00
parent fb4dc678e9
commit 3a14d28fdc
25 changed files with 967 additions and 253 deletions

View File

@ -114,7 +114,8 @@ PostMessageEvent::Run()
false /*cancelable */, messageData, mCallerOrigin,
EmptyString(), mSource);
nsTArray<nsRefPtr<MessagePort>> ports = TakeTransferredPorts();
nsTArray<nsRefPtr<MessagePortBase>> ports;
TakeTransferredPorts(ports);
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
ports));

View File

@ -20,6 +20,9 @@ class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class MessagePortBase;
class MessagePortIdentifier;
/**
* Class used to represent events generated by calls to Window.postMessage,
* which asynchronously creates and dispatches events.

View File

@ -1072,14 +1072,16 @@ StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
}
{
MessagePort* port = nullptr;
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
if (NS_SUCCEEDED(rv)) {
// We use aExtraData to store the index of this new port identifier.
*aExtraData = mPortIdentifiers.Length();
MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
port->CloneAndDisentangle(*identifier);
if (!port->CloneAndDisentangle(*identifier)) {
return false;
}
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
*aOwnership = JS::SCTAG_TMO_CUSTOM;

View File

@ -7,7 +7,6 @@
#define mozilla_dom_StructuredCloneHelper_h
#include "js/StructuredClone.h"
#include "mozilla/Move.h"
#include "nsAutoPtr.h"
#include "nsISupports.h"
#include "nsTArray.h"
@ -112,7 +111,7 @@ protected:
};
class BlobImpl;
class MessagePort;
class MessagePortBase;
class MessagePortIdentifier;
class StructuredCloneHelper : public StructuredCloneHelperInternal
@ -192,10 +191,11 @@ public:
// This must be called if the transferring has ports generated by Read().
// MessagePorts are not thread-safe and they must be retrieved in the thread
// where they are created.
nsTArray<nsRefPtr<MessagePort>>&& TakeTransferredPorts()
void TakeTransferredPorts(nsTArray<nsRefPtr<MessagePortBase>>& aPorts)
{
MOZ_ASSERT(mSupportsTransferring);
return Move(mTransferredPorts);
MOZ_ASSERT(aPorts.IsEmpty());
aPorts.SwapElements(mTransferredPorts);
}
nsTArray<MessagePortIdentifier>& PortIdentifiers()
@ -291,7 +291,7 @@ protected:
// This array contains the ports once we've finished the reading. It's
// generated from the mPortIdentifiers array.
nsTArray<nsRefPtr<MessagePort>> mTransferredPorts;
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
// This array contains the identifiers of the MessagePorts. Based on these we
// are able to reconnect the new transferred ports with the other

View File

@ -739,6 +739,11 @@ DOMInterfaces = {
'headerFile': 'MediaRecorder.h',
},
'MessagePort': {
'nativeType': 'mozilla::dom::MessagePortBase',
'headerFile': 'mozilla/dom/MessagePort.h',
},
'MimeType': {
'headerFile' : 'nsMimeTypeArray.h',
'nativeType': 'nsMimeType',

View File

@ -167,7 +167,7 @@ MessageEvent::Constructor(EventTarget* aEventTarget,
}
if (aParam.mPorts.WasPassed() && !aParam.mPorts.Value().IsNull()) {
nsTArray<nsRefPtr<MessagePort>> ports;
nsTArray<nsRefPtr<MessagePortBase>> ports;
for (uint32_t i = 0, len = aParam.mPorts.Value().Value().Length(); i < len; ++i) {
ports.AppendElement(aParam.mPorts.Value().Value()[i].get());
}

View File

@ -17,6 +17,7 @@ namespace dom {
struct MessageEventInit;
class MessagePort;
class MessagePortBase;
class MessagePortList;
class OwningWindowProxyOrMessagePortOrClient;
@ -93,7 +94,7 @@ private:
nsString mOrigin;
nsString mLastEventId;
nsCOMPtr<nsIDOMWindow> mWindowSource;
nsRefPtr<MessagePort> mPortSource;
nsRefPtr<MessagePortBase> mPortSource;
nsRefPtr<workers::ServiceWorkerClient> mClientSource;
nsRefPtr<MessagePortList> mPorts;
};

View File

@ -49,12 +49,7 @@ MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
{
// window can be null in workers.
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
return Constructor(window, aRv);
}
/* static */ already_AddRefed<MessageChannel>
MessageChannel::Constructor(nsPIDOMWindow* aWindow, ErrorResult& aRv)
{
nsID portUUID1;
aRv = nsContentUtils::GenerateUUIDInPlace(portUUID1);
if (aRv.Failed()) {
@ -67,14 +62,14 @@ MessageChannel::Constructor(nsPIDOMWindow* aWindow, ErrorResult& aRv)
return nullptr;
}
nsRefPtr<MessageChannel> channel = new MessageChannel(aWindow);
nsRefPtr<MessageChannel> channel = new MessageChannel(window);
channel->mPort1 = MessagePort::Create(aWindow, portUUID1, portUUID2, aRv);
channel->mPort1 = MessagePort::Create(window, portUUID1, portUUID2, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
channel->mPort2 = MessagePort::Create(aWindow, portUUID2, portUUID1, aRv);
channel->mPort2 = MessagePort::Create(window, portUUID2, portUUID1, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}

View File

@ -40,9 +40,6 @@ public:
static already_AddRefed<MessageChannel>
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
static already_AddRefed<MessageChannel>
Constructor(nsPIDOMWindow* aWindow, ErrorResult& aRv);
MessagePort*
Port1() const
{

View File

@ -140,7 +140,8 @@ public:
event->SetTrusted(true);
event->SetSource(mPort);
nsTArray<nsRefPtr<MessagePort>> ports = mData->TakeTransferredPorts();
nsTArray<nsRefPtr<MessagePortBase>> ports;
mData->TakeTransferredPorts(ports);
nsRefPtr<MessagePortList> portList =
new MessagePortList(static_cast<dom::Event*>(event.get()),
@ -170,10 +171,19 @@ private:
NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
{
}
MessagePortBase::MessagePortBase()
{
}
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
DOMEventTargetHelper)
MessagePortBase)
if (tmp->mDispatchRunnable) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
}
@ -184,7 +194,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
DOMEventTargetHelper)
MessagePortBase)
if (tmp->mDispatchRunnable) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
}
@ -195,10 +205,10 @@ 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_INTERFACE_MAP_END_INHERITING(MessagePortBase)
NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(MessagePort, MessagePortBase)
NS_IMPL_RELEASE_INHERITED(MessagePort, MessagePortBase)
namespace {
@ -277,7 +287,7 @@ NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
} // namespace
MessagePort::MessagePort(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
: MessagePortBase(aWindow)
, mInnerID(0)
, mMessageQueueEnabled(false)
, mIsKeptAlive(false)
@ -354,7 +364,16 @@ MessagePort::Initialize(const nsID& aUUID,
// The port has to keep itself alive until it's entangled.
UpdateMustKeepAlive();
if (!NS_IsMainThread()) {
if (NS_IsMainThread()) {
MOZ_ASSERT(GetOwner());
MOZ_ASSERT(GetOwner()->IsInnerWindow());
mInnerID = GetOwner()->WindowID();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "inner-window-destroyed", false);
}
} else {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
MOZ_ASSERT(!mWorkerFeature);
@ -367,15 +386,6 @@ MessagePort::Initialize(const nsID& aUUID,
}
mWorkerFeature = Move(feature);
} else if (GetOwner()) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(GetOwner()->IsInnerWindow());
mInnerID = GetOwner()->WindowID();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "inner-window-destroyed", false);
}
}
}
@ -404,7 +414,7 @@ MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
continue;
}
MessagePort* port = nullptr;
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, &value.toObject(), port);
if (NS_FAILED(rv)) {
continue;
@ -510,6 +520,11 @@ MessagePort::Dispatch()
void
MessagePort::Close()
{
// Not entangled yet, but already closed.
if (mNextStep != eNextStepNone) {
return;
}
if (mState == eStateUnshippedEntangled) {
MOZ_ASSERT(mUnshippedEntangledPort);
@ -525,7 +540,7 @@ MessagePort::Close()
}
// Not entangled yet, we have to wait.
if (mState == eStateEntangling) {
if (mState < eStateEntangling) {
mNextStep = eNextStepClose;
return;
}
@ -684,7 +699,7 @@ MessagePort::Disentangle()
UpdateMustKeepAlive();
}
void
bool
MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
{
MOZ_ASSERT(mIdentifier);
@ -695,13 +710,13 @@ MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
aIdentifier.neutered() = true;
if (mState > eStateEntangled) {
return;
return true;
}
// We already have a 'next step'. We have to consider this port as already
// cloned/closed/disentangled.
if (mNextStep != eNextStepNone) {
return;
return true;
}
aIdentifier.uuid() = mIdentifier->uuid();
@ -724,23 +739,24 @@ MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
mState = eStateDisentangled;
UpdateMustKeepAlive();
return;
return true;
}
// Register this component to PBackground.
ConnectToPBackground();
mNextStep = eNextStepDisentangle;
return;
return true;
}
// Not entangled yet, we have to wait.
if (mState < eStateEntangled) {
mNextStep = eNextStepDisentangle;
return;
return true;
}
StartDisentangling();
return true;
}
void

View File

@ -31,7 +31,41 @@ namespace workers {
class WorkerFeature;
} // namespace workers
class MessagePort final : public DOMEventTargetHelper
class MessagePortBase : public DOMEventTargetHelper
{
protected:
explicit MessagePortBase(nsPIDOMWindow* aWindow);
MessagePortBase();
public:
virtual void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv) = 0;
virtual void
Start() = 0;
virtual void
Close() = 0;
// The 'message' event handler has to call |Start()| method, so we
// cannot use IMPL_EVENT_HANDLER macro here.
virtual EventHandlerNonNull*
GetOnmessage() = 0;
virtual void
SetOnmessage(EventHandlerNonNull* aCallback) = 0;
// Duplicate this message port. This method is used by the Structured Clone
// Algorithm and populates a MessagePortIdentifier object with the information
// useful to create new MessagePort.
virtual bool
CloneAndDisentangle(MessagePortIdentifier& aIdentifier) = 0;
};
class MessagePort final : public MessagePortBase
, public nsIIPCBackgroundChildCreateCallback
, public nsIObserver
{
@ -42,7 +76,7 @@ public:
NS_DECL_NSIOBSERVER
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
DOMEventTargetHelper)
MessagePortBase)
static already_AddRefed<MessagePort>
Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
@ -58,24 +92,24 @@ public:
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
virtual void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
ErrorResult& aRv) override;
void Start();
virtual void Start() override;
void Close();
virtual void Close() override;
EventHandlerNonNull* GetOnmessage();
virtual EventHandlerNonNull* GetOnmessage() override;
void SetOnmessage(EventHandlerNonNull* aCallback);
virtual void SetOnmessage(EventHandlerNonNull* aCallback) override;
// Non WebIDL methods
void UnshippedEntangle(MessagePort* aEntangledPort);
void CloneAndDisentangle(MessagePortIdentifier& aIdentifier);
virtual bool CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
// These methods are useful for MessagePortChild

View File

@ -29,7 +29,7 @@ public:
public:
MessagePortList(nsISupports* aOwner,
const nsTArray<nsRefPtr<MessagePort>>& aPorts)
const nsTArray<nsRefPtr<MessagePortBase>>& aPorts)
: mOwner(aOwner)
, mPorts(aPorts)
{
@ -50,13 +50,13 @@ public:
return mPorts.Length();
}
MessagePort*
MessagePortBase*
Item(uint32_t aIndex)
{
return mPorts.SafeElementAt(aIndex);
}
MessagePort*
MessagePortBase*
IndexedGetter(uint32_t aIndex, bool &aFound)
{
aFound = aIndex < mPorts.Length();
@ -68,7 +68,7 @@ public:
public:
nsCOMPtr<nsISupports> mOwner;
nsTArray<nsRefPtr<MessagePort>> mPorts;
nsTArray<nsRefPtr<MessagePortBase>> mPorts;
};
} // namespace dom

318
dom/workers/MessagePort.cpp Normal file
View File

@ -0,0 +1,318 @@
/* -*- 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 "mozilla/EventDispatcher.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsIDOMEvent.h"
#include "SharedWorker.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
using mozilla::dom::EventHandlerNonNull;
using mozilla::dom::MessagePortBase;
using mozilla::dom::MessagePortIdentifier;
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
using mozilla::dom::AutoNoJSAPI;
using namespace mozilla;
USING_WORKERS_NAMESPACE
namespace {
class DelayedEventRunnable final : public WorkerRunnable
{
nsRefPtr<mozilla::dom::workers::MessagePort> mMessagePort;
nsTArray<nsCOMPtr<nsIDOMEvent>> mEvents;
public:
DelayedEventRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior,
mozilla::dom::workers::MessagePort* aMessagePort,
nsTArray<nsCOMPtr<nsIDOMEvent>>& aEvents)
: WorkerRunnable(aWorkerPrivate, aBehavior), mMessagePort(aMessagePort)
{
AssertIsOnMainThread();
MOZ_ASSERT(aMessagePort);
MOZ_ASSERT(aEvents.Length());
mEvents.SwapElements(aEvents);
}
bool PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
if (mBehavior == WorkerThreadModifyBusyCount) {
return aWorkerPrivate->ModifyBusyCount(aCx, true);
}
return true;
}
void PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aDispatchResult)
{
if (!aDispatchResult) {
if (mBehavior == WorkerThreadModifyBusyCount) {
aWorkerPrivate->ModifyBusyCount(aCx, false);
}
if (aCx) {
JS_ReportPendingException(aCx);
}
}
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
};
} // namespace
BEGIN_WORKERS_NAMESPACE
MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
uint64_t aSerial)
: MessagePortBase(aWindow), mSharedWorker(aSharedWorker),
mWorkerPrivate(nullptr), mSerial(aSerial), mStarted(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
}
MessagePort::MessagePort(WorkerPrivate* aWorkerPrivate, uint64_t aSerial)
: mWorkerPrivate(aWorkerPrivate), mSerial(aSerial), mStarted(false)
{
aWorkerPrivate->AssertIsOnWorkerThread();
}
MessagePort::~MessagePort()
{
Close();
}
void
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
AssertCorrectThread();
if (IsClosed()) {
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
return;
}
if (mSharedWorker) {
mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv);
}
else {
mWorkerPrivate->PostMessageToParentMessagePort(aCx, Serial(), aMessage,
aTransferable, aRv);
}
}
void
MessagePort::Start()
{
AssertCorrectThread();
if (IsClosed()) {
NS_WARNING("Called start() after calling close()!");
return;
}
if (mStarted) {
return;
}
mStarted = true;
if (!mQueuedEvents.IsEmpty()) {
WorkerPrivate* workerPrivate;
WorkerRunnable::TargetAndBusyBehavior behavior;
if (mWorkerPrivate) {
workerPrivate = mWorkerPrivate;
behavior = WorkerRunnable::WorkerThreadModifyBusyCount;
}
else {
workerPrivate = mSharedWorker->GetWorkerPrivate();
MOZ_ASSERT(workerPrivate);
behavior = WorkerRunnable::ParentThreadUnchangedBusyCount;
}
nsRefPtr<DelayedEventRunnable> runnable =
new DelayedEventRunnable(workerPrivate, behavior, this, mQueuedEvents);
runnable->Dispatch(nullptr);
}
}
void
MessagePort::Close()
{
AssertCorrectThread();
if (!IsClosed()) {
CloseInternal();
}
}
void
MessagePort::QueueEvent(nsIDOMEvent* aEvent)
{
AssertCorrectThread();
MOZ_ASSERT(aEvent);
MOZ_ASSERT(!IsClosed());
MOZ_ASSERT(!mStarted);
mQueuedEvents.AppendElement(aEvent);
}
EventHandlerNonNull*
MessagePort::GetOnmessage()
{
AssertCorrectThread();
return NS_IsMainThread() ? GetEventHandler(nsGkAtoms::onmessage, EmptyString())
: GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
}
void
MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
{
AssertCorrectThread();
if (NS_IsMainThread()) {
SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
}
else {
SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
}
Start();
}
bool
MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
{
NS_WARNING("Haven't implemented structured clone for these ports yet!");
return false;
}
void
MessagePort::CloseInternal()
{
AssertCorrectThread();
MOZ_ASSERT(!IsClosed());
MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
if (!mStarted) {
mQueuedEvents.Clear();
}
mSharedWorker = nullptr;
mWorkerPrivate = nullptr;
}
#ifdef DEBUG
void
MessagePort::AssertCorrectThread() const
{
if (IsClosed()) {
return; // Can't assert anything if we nulled out our pointers.
}
MOZ_ASSERT((mSharedWorker || mWorkerPrivate) &&
!(mSharedWorker && mWorkerPrivate));
if (mSharedWorker) {
AssertIsOnMainThread();
}
else {
mWorkerPrivate->AssertIsOnWorkerThread();
}
}
#endif
NS_IMPL_ADDREF_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorker)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
DOMEventTargetHelper)
tmp->Close();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
JSObject*
MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
AssertCorrectThread();
return MessagePortBinding::Wrap(aCx, this, aGivenProto);
}
nsresult
MessagePort::PreHandleEvent(EventChainPreVisitor& aVisitor)
{
AssertCorrectThread();
nsIDOMEvent*& event = aVisitor.mDOMEvent;
if (event) {
bool preventDispatch = false;
if (IsClosed()) {
preventDispatch = true;
} else if (NS_IsMainThread() && mSharedWorker->IsFrozen()) {
mSharedWorker->QueueEvent(event);
preventDispatch = true;
} else if (!mStarted) {
QueueEvent(event);
preventDispatch = true;
}
if (preventDispatch) {
aVisitor.mCanHandle = false;
aVisitor.mParentTarget = nullptr;
return NS_OK;
}
}
return DOMEventTargetHelper::PreHandleEvent(aVisitor);
}
END_WORKERS_NAMESPACE
bool
DelayedEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(mMessagePort);
mMessagePort->AssertCorrectThread();
MOZ_ASSERT(mEvents.Length());
AutoNoJSAPI nojsapi;
bool ignored;
for (uint32_t i = 0; i < mEvents.Length(); i++) {
mMessagePort->DispatchEvent(mEvents[i], &ignored);
}
return true;
}

112
dom/workers/MessagePort.h Normal file
View File

@ -0,0 +1,112 @@
/* -*- 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/. */
#ifndef mozilla_dom_workers_messageport_h_
#define mozilla_dom_workers_messageport_h_
#include "mozilla/dom/workers/Workers.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/MessagePort.h"
class nsIDOMEvent;
class nsPIDOMWindow;
namespace mozilla {
class EventChainPreVisitor;
} // namespace mozilla
BEGIN_WORKERS_NAMESPACE
class SharedWorker;
class WorkerPrivate;
class MessagePort final : public mozilla::dom::MessagePortBase
{
friend class SharedWorker;
friend class WorkerPrivate;
typedef mozilla::ErrorResult ErrorResult;
nsRefPtr<SharedWorker> mSharedWorker;
WorkerPrivate* mWorkerPrivate;
nsTArray<nsCOMPtr<nsIDOMEvent>> mQueuedEvents;
uint64_t mSerial;
bool mStarted;
public:
static bool
PrefEnabled();
virtual void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv) override;
virtual void
Start() override;
virtual void
Close() override;
uint64_t
Serial() const
{
return mSerial;
}
void
QueueEvent(nsIDOMEvent* aEvent);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, DOMEventTargetHelper)
virtual EventHandlerNonNull*
GetOnmessage() override;
virtual void
SetOnmessage(EventHandlerNonNull* aCallback) override;
virtual bool
CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
bool
IsClosed() const
{
return !mSharedWorker && !mWorkerPrivate;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual nsresult
PreHandleEvent(EventChainPreVisitor& aVisitor) override;
#ifdef DEBUG
void
AssertCorrectThread() const;
#else
void
AssertCorrectThread() const { }
#endif
private:
// This class can only be created by SharedWorker or WorkerPrivate.
MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
uint64_t aSerial);
MessagePort(WorkerPrivate* aWorkerPrivate, uint64_t aSerial);
// This class is reference-counted and will be destroyed from Release().
~MessagePort();
void
CloseInternal();
};
END_WORKERS_NAMESPACE
#endif // mozilla_dom_workers_messageport_h_

View File

@ -34,7 +34,6 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/EventTargetBinding.h"
#include "mozilla/dom/MessageChannel.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/ScriptSettings.h"
@ -2494,8 +2493,8 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
nsCOMPtr<nsPIDOMWindow> window = aLoadInfo->mWindow;
bool created = false;
ErrorResult rv;
if (!workerPrivate) {
ErrorResult rv;
workerPrivate =
WorkerPrivate::Constructor(aCx, aScriptURL, false,
aType, aName, aLoadInfo, rv);
@ -2509,18 +2508,9 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
}
// We don't actually care about this MessageChannel, but we use it to 'steal'
// its 2 connected ports.
nsRefPtr<MessageChannel> channel = MessageChannel::Constructor(window, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
nsRefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate);
nsRefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate,
channel->Port1());
if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker,
channel->Port2())) {
if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
NS_WARNING("Worker is unreachable, this shouldn't happen!");
sharedWorker->Close();
return NS_ERROR_FAILURE;

View File

@ -138,7 +138,8 @@ private:
return NS_ERROR_FAILURE;
}
nsTArray<nsRefPtr<MessagePort>> ports = TakeTransferredPorts();
nsTArray<nsRefPtr<MessagePortBase>> ports;
TakeTransferredPorts(ports);
nsRefPtr<MessagePortList> portList =
new MessagePortList(static_cast<dom::Event*>(event.get()),

View File

@ -11,7 +11,6 @@
#include "nsIInputStream.h"
#include "nsILineInputStream.h"
#include "nsIObserverService.h"
#include "nsIOutputStream.h"
#include "nsISafeOutputStream.h"
#include "MainThreadUtils.h"

View File

@ -10,12 +10,12 @@
#include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/SharedWorkerBinding.h"
#include "nsContentUtils.h"
#include "nsIClassInfoImpl.h"
#include "nsIDOMEvent.h"
#include "MessagePort.h"
#include "RuntimeService.h"
#include "WorkerPrivate.h"
@ -26,14 +26,16 @@ using namespace mozilla;
USING_WORKERS_NAMESPACE
SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
WorkerPrivate* aWorkerPrivate,
MessagePort* aMessagePort)
: DOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate)
, mMessagePort(aMessagePort)
, mFrozen(false)
WorkerPrivate* aWorkerPrivate)
: DOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate),
mFrozen(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
mSerial = aWorkerPrivate->NextMessagePortSerial();
mMessagePort = new MessagePort(aWindow, this, mSerial);
}
SharedWorker::~SharedWorker()
@ -74,11 +76,13 @@ SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
return sharedWorker.forget();
}
MessagePort*
already_AddRefed<mozilla::dom::workers::MessagePort>
SharedWorker::Port()
{
AssertIsOnMainThread();
return mMessagePort;
nsRefPtr<MessagePort> messagePort = mMessagePort;
return messagePort.forget();
}
void
@ -153,7 +157,8 @@ SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
MOZ_ASSERT(mWorkerPrivate);
MOZ_ASSERT(mMessagePort);
mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
mWorkerPrivate->PostMessageToMessagePort(aCx, mMessagePort->Serial(),
aMessage, aTransferable, aRv);
}
void

View File

@ -10,6 +10,7 @@
#include "Workers.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/workers/bindings/MessagePort.h"
#include "mozilla/DOMEventTargetHelper.h"
class nsIDOMEvent;
@ -17,19 +18,17 @@ class nsPIDOMWindow;
namespace mozilla {
class EventChainPreVisitor;
namespace dom {
class MessagePort;
}
} // namespace mozilla
BEGIN_WORKERS_NAMESPACE
class MessagePort;
class RuntimeService;
class WorkerPrivate;
class SharedWorker final : public DOMEventTargetHelper
{
friend class MessagePort;
friend class RuntimeService;
typedef mozilla::ErrorResult ErrorResult;
@ -38,6 +37,7 @@ class SharedWorker final : public DOMEventTargetHelper
nsRefPtr<WorkerPrivate> mWorkerPrivate;
nsRefPtr<MessagePort> mMessagePort;
nsTArray<nsCOMPtr<nsIDOMEvent>> mFrozenEvents;
uint64_t mSerial;
bool mFrozen;
public:
@ -46,9 +46,15 @@ public:
const nsAString& aScriptURL, const Optional<nsAString>& aName,
ErrorResult& aRv);
MessagePort*
already_AddRefed<mozilla::dom::workers::MessagePort>
Port();
uint64_t
Serial() const
{
return mSerial;
}
bool
IsFrozen() const
{
@ -87,8 +93,7 @@ public:
private:
// This class can only be created from the RuntimeService.
SharedWorker(nsPIDOMWindow* aWindow,
WorkerPrivate* aWorkerPrivate,
MessagePort* aMessagePort);
WorkerPrivate* aWorkerPrivate);
// This class is reference-counted and will be destroyed from Release().
~SharedWorker();

View File

@ -54,7 +54,6 @@
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/MessagePortList.h"
#include "mozilla/dom/PMessagePort.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/ScriptSettings.h"
@ -89,6 +88,7 @@
#include "nsThreadManager.h"
#endif
#include "MessagePort.h"
#include "Navigator.h"
#include "Principal.h"
#include "RuntimeService.h"
@ -594,15 +594,21 @@ private:
class MessageEventRunnable final : public WorkerRunnable
, public StructuredCloneHelper
{
uint64_t mMessagePortSerial;
bool mToMessagePort;
// This is only used for messages dispatched to a service worker.
nsAutoPtr<ServiceWorkerClientInfo> mEventSource;
public:
MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior)
TargetAndBusyBehavior aBehavior,
bool aToMessagePort, uint64_t aMessagePortSerial)
: WorkerRunnable(aWorkerPrivate, aBehavior)
, StructuredCloneHelper(CloningSupported, TransferringSupported,
SameProcessDifferentThread)
, mMessagePortSerial(aMessagePortSerial)
, mToMessagePort(aToMessagePort)
{
}
@ -648,7 +654,8 @@ public:
return false;
}
nsTArray<nsRefPtr<MessagePort>> ports = TakeTransferredPorts();
nsTArray<nsRefPtr<MessagePortBase>> ports;
TakeTransferredPorts(ports);
event->SetTrusted(true);
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
@ -664,6 +671,8 @@ private:
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker());
if (mBehavior == ParentThreadUnchangedBusyCount) {
// Don't fire this event if the JS object has been disconnected from the
// private object.
@ -671,6 +680,13 @@ private:
return true;
}
if (mToMessagePort) {
return
aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
mMessagePortSerial,
*this);
}
if (aWorkerPrivate->IsFrozen()) {
aWorkerPrivate->QueueRunnable(this);
return true;
@ -684,6 +700,16 @@ private:
MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
if (mToMessagePort) {
nsRefPtr<workers::MessagePort> port =
aWorkerPrivate->GetMessagePort(mMessagePortSerial);
if (!port) {
// Must have been closed already.
return true;
}
return DispatchDOMEvent(aCx, aWorkerPrivate, port, false);
}
return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
false);
}
@ -1495,19 +1521,18 @@ public:
class MessagePortRunnable final : public WorkerRunnable
{
MessagePortIdentifier mPortIdentifier;
uint64_t mMessagePortSerial;
bool mConnect;
public:
MessagePortRunnable(WorkerPrivate* aWorkerPrivate,
MessagePort* aPort)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
{
MOZ_ASSERT(aPort);
// In order to move the port from one thread to another one, we have to
// close and disentangle it. The output will be a MessagePortIdentifier that
// will be used to recreate a new MessagePort on the other thread.
aPort->CloneAndDisentangle(mPortIdentifier);
}
uint64_t aMessagePortSerial,
bool aConnect)
: WorkerRunnable(aWorkerPrivate, aConnect ?
WorkerThreadModifyBusyCount :
WorkerThreadUnchangedBusyCount),
mMessagePortSerial(aMessagePortSerial), mConnect(aConnect)
{ }
private:
~MessagePortRunnable()
@ -1516,7 +1541,12 @@ private:
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
if (mConnect) {
return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial);
}
aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial);
return true;
}
};
@ -2092,7 +2122,8 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
mParent(aParent), mScriptURL(aScriptURL),
mSharedWorkerName(aSharedWorkerName), mLoadingWorkerScript(false),
mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
mBusyCount(0), mMessagePortSerial(0),
mParentStatus(Pending), mParentFrozen(false),
mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
mWorkerType(aWorkerType),
mCreationTimeStamp(TimeStamp::Now()),
@ -2456,29 +2487,55 @@ WorkerPrivateParent<Derived>::Freeze(JSContext* aCx, nsPIDOMWindow* aWindow)
// Shared workers are only frozen if all of their owning documents are
// frozen. It can happen that mSharedWorkers is empty but this thread has
// not been unregistered yet.
if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
if ((IsSharedWorker() || IsServiceWorker()) && mSharedWorkers.Count()) {
AssertIsOnMainThread();
bool allFrozen = false;
struct Closure
{
nsPIDOMWindow* mWindow;
bool mAllFrozen;
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
// Calling Freeze() may change the refcount, ensure that the worker
// outlives this call.
nsRefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
mSharedWorkers[i]->Freeze();
} else {
MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
aWindow));
if (!mSharedWorkers[i]->IsFrozen()) {
allFrozen = false;
}
explicit Closure(nsPIDOMWindow* aWindow)
: mWindow(aWindow), mAllFrozen(true)
{
AssertIsOnMainThread();
// aWindow may be null here.
}
}
if (!allFrozen || mParentFrozen) {
static PLDHashOperator
Freeze(const uint64_t& aKey,
SharedWorker* aSharedWorker,
void* aClosure)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
MOZ_ASSERT(aClosure);
auto closure = static_cast<Closure*>(aClosure);
if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
// Calling Freeze() may change the refcount, ensure that the worker
// outlives this call.
nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
aSharedWorker->Freeze();
} else {
MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
!SameCOMIdentity(aSharedWorker->GetOwner(),
closure->mWindow));
if (!aSharedWorker->IsFrozen()) {
closure->mAllFrozen = false;
}
}
return PL_DHASH_NEXT;
}
};
Closure closure(aWindow);
mSharedWorkers.EnumerateRead(Closure::Freeze, &closure);
if (!closure.mAllFrozen || mParentFrozen) {
return true;
}
}
@ -2519,30 +2576,56 @@ WorkerPrivateParent<Derived>::Thaw(JSContext* aCx, nsPIDOMWindow* aWindow)
// Shared workers are resumed if any of their owning documents are thawed.
// It can happen that mSharedWorkers is empty but this thread has not been
// unregistered yet.
if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
if ((IsSharedWorker() || IsServiceWorker()) && mSharedWorkers.Count()) {
AssertIsOnMainThread();
bool anyRunning = false;
struct Closure
{
nsPIDOMWindow* mWindow;
bool mAnyRunning;
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
// Calling Thaw() may change the refcount, ensure that the worker
// outlives this call.
nsRefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
mSharedWorkers[i]->Thaw();
anyRunning = true;
} else {
MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
aWindow));
if (!mSharedWorkers[i]->IsFrozen()) {
anyRunning = true;
}
explicit Closure(nsPIDOMWindow* aWindow)
: mWindow(aWindow), mAnyRunning(false)
{
AssertIsOnMainThread();
// aWindow may be null here.
}
}
if (!anyRunning || !mParentFrozen) {
static PLDHashOperator
Thaw(const uint64_t& aKey,
SharedWorker* aSharedWorker,
void* aClosure)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
MOZ_ASSERT(aClosure);
auto closure = static_cast<Closure*>(aClosure);
if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
// Calling Thaw() may change the refcount, ensure that the worker
// outlives this call.
nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
aSharedWorker->Thaw();
closure->mAnyRunning = true;
} else {
MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
!SameCOMIdentity(aSharedWorker->GetOwner(),
closure->mWindow));
if (!aSharedWorker->IsFrozen()) {
closure->mAnyRunning = true;
}
}
return PL_DHASH_NEXT;
}
};
Closure closure(aWindow);
mSharedWorkers.EnumerateRead(Closure::Thaw, &closure);
if (!closure.mAnyRunning || !mParentFrozen) {
return true;
}
}
@ -2679,6 +2762,8 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
bool aToMessagePort,
uint64_t aMessagePortSerial,
ServiceWorkerClientInfo* aClientInfo,
ErrorResult& aRv)
{
@ -2712,7 +2797,8 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
nsRefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(ParentAsWorkerPrivate(),
WorkerRunnable::WorkerThreadModifyBusyCount);
WorkerRunnable::WorkerThreadModifyBusyCount,
aToMessagePort, aMessagePortSerial);
runnable->Write(aCx, aMessage, transferable, aRv);
if (NS_WARN_IF(aRv.Failed())) {
@ -2735,7 +2821,88 @@ WorkerPrivateParent<Derived>::PostMessageToServiceWorker(
ErrorResult& aRv)
{
AssertIsOnMainThread();
PostMessageInternal(aCx, aMessage, aTransferable, aClientInfo.forget(), aRv);
PostMessageInternal(aCx, aMessage, aTransferable, false, 0,
aClientInfo.forget(), aRv);
}
template <class Derived>
void
WorkerPrivateParent<Derived>::PostMessageToMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnMainThread();
PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
nullptr, aRv);
}
template <class Derived>
bool
WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
JSContext* aCx, uint64_t aMessagePortSerial,
StructuredCloneHelper& aHelper)
{
AssertIsOnMainThread();
SharedWorker* sharedWorker;
if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
// SharedWorker has already been unregistered?
return true;
}
nsRefPtr<MessagePort> port = sharedWorker->Port();
NS_ASSERTION(port, "SharedWorkers always have a port!");
if (port->IsClosed()) {
return true;
}
nsCOMPtr<nsISupports> parent = do_QueryInterface(port->GetParentObject());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
return false;
}
JSContext* cx = jsapi.cx();
ErrorResult rv;
JS::Rooted<JS::Value> data(cx);
aHelper.Read(parent, cx, &data, rv);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(cx, rv.StealNSResult());
return false;
}
nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
EmptyString(), EmptyString(), nullptr);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(cx, rv.StealNSResult());
return false;
}
nsTArray<nsRefPtr<MessagePortBase>> ports;
aHelper.TakeTransferredPorts(ports);
event->SetTrusted(true);
event->SetPorts(new MessagePortList(port, ports));
nsCOMPtr<nsIDOMEvent> domEvent;
CallQueryInterface(event.get(), getter_AddRefs(domEvent));
NS_ASSERTION(domEvent, "This should never fail!");
bool ignored;
rv = port->DispatchEvent(domEvent, &ignored);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(cx, rv.StealNSResult());
return false;
}
return true;
}
template <class Derived>
@ -2923,27 +3090,27 @@ WorkerPrivate::OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline)
template <class Derived>
bool
WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
SharedWorker* aSharedWorker,
MessagePort* aPort)
SharedWorker* aSharedWorker)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial()));
if (IsSharedWorker()) {
nsRefPtr<MessagePortRunnable> runnable =
new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
true);
if (!runnable->Dispatch(aCx)) {
return false;
}
}
mSharedWorkers.AppendElement(aSharedWorker);
mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker);
// If there were other SharedWorker objects attached to this worker then they
// may all have been frozen and this worker would need to be thawed.
if (mSharedWorkers.Length() > 1 && !Thaw(aCx, nullptr)) {
if (mSharedWorkers.Count() > 1 && !Thaw(aCx, nullptr)) {
return false;
}
@ -2959,14 +3126,21 @@ WorkerPrivateParent<Derived>::UnregisterSharedWorker(
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial()));
mSharedWorkers.RemoveElement(aSharedWorker);
nsRefPtr<MessagePortRunnable> runnable =
new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
false);
if (!runnable->Dispatch(aCx)) {
JS_ReportPendingException(aCx);
}
mSharedWorkers.Remove(aSharedWorker->Serial());
// If there are still SharedWorker objects attached to this worker then they
// may all be frozen and this worker would need to be frozen. Otherwise,
// if that was the last SharedWorker then it's time to cancel this worker.
if (!mSharedWorkers.IsEmpty()) {
if (mSharedWorkers.Count()) {
if (!Freeze(aCx, nullptr)) {
JS_ReportPendingException(aCx);
}
@ -3104,13 +3278,29 @@ WorkerPrivateParent<Derived>::GetAllSharedWorkers(
AssertIsOnMainThread();
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
struct Helper
{
static PLDHashOperator
Collect(const uint64_t& aKey,
SharedWorker* aSharedWorker,
void* aClosure)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
MOZ_ASSERT(aClosure);
auto array = static_cast<nsTArray<nsRefPtr<SharedWorker>>*>(aClosure);
array->AppendElement(aSharedWorker);
return PL_DHASH_NEXT;
}
};
if (!aSharedWorkers.IsEmpty()) {
aSharedWorkers.Clear();
}
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
aSharedWorkers.AppendElement(mSharedWorkers[i]);
}
mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers);
}
template <class Derived>
@ -3122,19 +3312,47 @@ WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
MOZ_ASSERT(aWindow);
nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers;
struct Closure
{
nsPIDOMWindow* mWindow;
nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers;
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
if (mSharedWorkers[i]->GetOwner() == aWindow) {
sharedWorkers.AppendElement(mSharedWorkers[i]);
} else {
MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
aWindow));
explicit Closure(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
}
}
for (uint32_t index = 0; index < sharedWorkers.Length(); index++) {
sharedWorkers[index]->Close();
static PLDHashOperator
Collect(const uint64_t& aKey,
SharedWorker* aSharedWorker,
void* aClosure)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
MOZ_ASSERT(aClosure);
auto closure = static_cast<Closure*>(aClosure);
MOZ_ASSERT(closure->mWindow);
if (aSharedWorker->GetOwner() == closure->mWindow) {
closure->mSharedWorkers.AppendElement(aSharedWorker);
} else {
MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(),
closure->mWindow));
}
return PL_DHASH_NEXT;
}
};
Closure closure(aWindow);
mSharedWorkers.EnumerateRead(Closure::Collect, &closure);
for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) {
closure.mSharedWorkers[index]->Close();
}
}
@ -4370,6 +4588,9 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
}
}
// Clear away our MessagePorts.
mWorkerPorts.Clear();
// Unroot the globals
mScope = nullptr;
mDebuggerScope = nullptr;
@ -5307,6 +5528,8 @@ WorkerPrivate::PostMessageToParentInternal(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
bool aToMessagePort,
uint64_t aMessagePortSerial,
ErrorResult& aRv)
{
AssertIsOnWorkerThread();
@ -5331,7 +5554,8 @@ WorkerPrivate::PostMessageToParentInternal(
nsRefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(this,
WorkerRunnable::ParentThreadUnchangedBusyCount);
WorkerRunnable::ParentThreadUnchangedBusyCount,
aToMessagePort, aMessagePortSerial);
runnable->Write(aCx, aMessage, transferable, aRv);
if (NS_WARN_IF(aRv.Failed())) {
@ -5343,6 +5567,26 @@ WorkerPrivate::PostMessageToParentInternal(
}
}
void
WorkerPrivate::PostMessageToParentMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnWorkerThread();
if (!mWorkerPorts.GetWeak(aMessagePortSerial)) {
// This port has been closed from the main thread. There's no point in
// sending this message so just bail.
return;
}
PostMessageToParentInternal(aCx, aMessage, aTransferable, true,
aMessagePortSerial, aRv);
}
void
WorkerPrivate::EnterDebuggerEventLoop()
{
@ -6200,23 +6444,19 @@ WorkerPrivate::EndCTypesCall()
}
bool
WorkerPrivate::ConnectMessagePort(JSContext* aCx,
MessagePortIdentifier& aIdentifier)
WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
{
AssertIsOnWorkerThread();
NS_ASSERTION(!mWorkerPorts.GetWeak(aMessagePortSerial),
"Already have this port registered!");
WorkerGlobalScope* globalScope = GlobalScope();
JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
MOZ_ASSERT(jsGlobal);
// This MessagePortIdentifier is used to create a new port, still connected
// with the other one, but in the worker thread.
ErrorResult rv;
nsRefPtr<MessagePort> port = MessagePort::Create(nullptr, aIdentifier, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
nsRefPtr<MessagePort> port = new MessagePort(this, aMessagePortSerial);
GlobalObject globalObject(aCx, jsGlobal);
if (globalObject.Failed()) {
@ -6228,27 +6468,51 @@ WorkerPrivate::ConnectMessagePort(JSContext* aCx,
init.mCancelable = false;
init.mSource.SetValue().SetAsMessagePort() = port;
ErrorResult rv;
nsRefPtr<MessageEvent> event =
MessageEvent::Constructor(globalObject,
NS_LITERAL_STRING("connect"), init, rv);
event->SetTrusted(true);
nsTArray<nsRefPtr<MessagePort>> ports;
nsTArray<nsRefPtr<MessagePortBase>> ports;
ports.AppendElement(port);
nsRefPtr<MessagePortList> portList =
new MessagePortList(static_cast<nsIDOMEventTarget*>(globalScope), ports);
event->SetPorts(portList);
mWorkerPorts.Put(aMessagePortSerial, port);
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
nsEventStatus dummy = nsEventStatus_eIgnore;
globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
return true;
}
void
WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial)
{
AssertIsOnWorkerThread();
mWorkerPorts.Remove(aMessagePortSerial);
}
workers::MessagePort*
WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial)
{
AssertIsOnWorkerThread();
nsRefPtr<MessagePort> port;
if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) {
return port;
}
return nullptr;
}
WorkerGlobalScope*
WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
{

View File

@ -50,8 +50,6 @@ struct RuntimeStats;
namespace mozilla {
namespace dom {
class Function;
class MessagePort;
class MessagePortIdentifier;
class StructuredCloneHelper;
} // namespace dom
namespace ipc {
@ -66,6 +64,7 @@ class ReportDebuggerErrorRunnable;
BEGIN_WORKERS_NAMESPACE
class AutoSyncLoopHolder;
class MessagePort;
class SharedWorker;
class ServiceWorkerClientInfo;
class WorkerControlRunnable;
@ -178,9 +177,10 @@ private:
// Only touched on the parent thread (currently this is always the main
// thread as SharedWorkers are always top-level).
nsTArray<SharedWorker*> mSharedWorkers;
nsDataHashtable<nsUint64HashKey, SharedWorker*> mSharedWorkers;
uint64_t mBusyCount;
uint64_t mMessagePortSerial;
Status mParentStatus;
bool mParentFrozen;
bool mIsChromeWorker;
@ -226,6 +226,7 @@ private:
void
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
bool aToMessagePort, uint64_t aMessagePortSerial,
ServiceWorkerClientInfo* aClientInfo,
ErrorResult& aRv);
@ -327,7 +328,7 @@ public:
const Optional<Sequence<JS::Value> >& aTransferable,
ErrorResult& aRv)
{
PostMessageInternal(aCx, aMessage, aTransferable, nullptr, aRv);
PostMessageInternal(aCx, aMessage, aTransferable, false, 0, nullptr, aRv);
}
void
@ -336,6 +337,19 @@ public:
nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
ErrorResult& aRv);
void
PostMessageToMessagePort(JSContext* aCx,
uint64_t aMessagePortSerial,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value> >& aTransferable,
ErrorResult& aRv);
bool
DispatchMessageEventToMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
StructuredCloneHelper& aHelper);
void
UpdateRuntimeOptions(JSContext* aCx,
const JS::RuntimeOptions& aRuntimeOptions);
@ -365,8 +379,7 @@ public:
OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline);
bool
RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker,
MessagePort* aPort);
RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
void
UnregisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
@ -745,6 +758,13 @@ public:
return mSharedWorkerName;
}
uint64_t
NextMessagePortSerial()
{
AssertIsOnMainThread();
return mMessagePortSerial++;
}
bool
IsStorageAllowed() const
{
@ -932,6 +952,8 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
nsRefPtr<MemoryReporter> mMemoryReporter;
nsRefPtrHashtable<nsUint64HashKey, MessagePort> mWorkerPorts;
// fired on the main thread if the worker script fails to load
nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
@ -1054,12 +1076,13 @@ public:
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
PostMessageToParentInternal(aCx, aMessage, aTransferable, false, 0, aRv);
}
void
PostMessageToParentMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
@ -1216,7 +1239,13 @@ public:
}
bool
ConnectMessagePort(JSContext* aCx, MessagePortIdentifier& aIdentifier);
ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial);
void
DisconnectMessagePort(uint64_t aMessagePortSerial);
MessagePort*
GetMessagePort(uint64_t aMessagePortSerial);
WorkerGlobalScope*
GetOrCreateGlobalScope(JSContext* aCx);
@ -1416,6 +1445,8 @@ private:
PostMessageToParentInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
bool aToMessagePort,
uint64_t aMessagePortSerial,
ErrorResult& aRv);
void

View File

@ -29,6 +29,7 @@ EXPORTS.mozilla.dom.workers.bindings += [
'DataStoreCursor.h',
'FileReaderSync.h',
'Location.h',
'MessagePort.h',
'Navigator.h',
'Performance.h',
'ServiceWorker.h',
@ -55,6 +56,7 @@ UNIFIED_SOURCES += [
'DataStoreCursor.cpp',
'FileReaderSync.cpp',
'Location.cpp',
'MessagePort.cpp',
'Navigator.cpp',
'Performance.cpp',
'Principal.cpp',

View File

@ -110,7 +110,6 @@ support-files =
sharedworker_performance_user_timing.js
referrer.sjs
performance_observer.html
sharedWorker_ports.js
[test_404.html]
[test_atob.html]
@ -222,4 +221,3 @@ skip-if = buildapp == 'b2g' || e10s
skip-if = (os == "win") || (os == "mac") || toolkit == 'android' || e10s #bug 798220
[test_xhrAbort.html]
[test_referrer.html]
[test_sharedWorker_ports.html]

View File

@ -1,23 +0,0 @@
var port;
onconnect = function(evt) {
evt.source.postMessage({ type: "connected" });
if (!port) {
port = evt.source;
evt.source.onmessage = function(evtFromPort) {
port.postMessage({type: "status",
test: "Port from the main-thread!" == evtFromPort.data,
msg: "The message is coming from the main-thread"});
port.postMessage({type: "status",
test: (evtFromPort.ports.length == 1),
msg: "1 port transferred"});
evtFromPort.ports[0].onmessage = function(evtFromPort2) {
port.postMessage({type: "status",
test: (evtFromPort2.data.type == "connected"),
msg: "The original message received" });
port.postMessage({type: "finish"});
}
}
}
}

View File

@ -1,42 +0,0 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for MessagePort and SharedWorkers</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
</head>
<body>
<script class="testbody" type="text/javascript">
SpecialPowers.pushPrefEnv({ set: [["dom.workers.sharedWorkers.enabled", true]]},
function() {
var sw1 = new SharedWorker('sharedWorker_ports.js');
sw1.port.onmessage = function(event) {
if (event.data.type == "connected") {
ok(true, "The SharedWorker is alive.");
var sw2 = new SharedWorker('sharedWorker_ports.js');
sw1.port.postMessage("Port from the main-thread!", [sw2.port]);
return;
}
if (event.data.type == "status") {
ok(event.data.test, event.data.msg);
return;
}
if (event.data.type == "finish") {
SimpleTest.finish();
}
}
});
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>