Bug 643325 - Implement SharedWorker. r=khuey.

--HG--
extra : transplant_source : %B6%DD%5B%B6%D1%2C%9D%A9%FCK%60%E8%8F%0C-xz%91%5E%9E
This commit is contained in:
Ben Turner 2013-06-05 07:04:23 -07:00
parent c802aa00c3
commit 80ebdbabd2
44 changed files with 4167 additions and 840 deletions

View File

@ -135,6 +135,10 @@ const kEventConstructors = {
return e;
},
},
ErrorEvent: { create: function (aName, aProps) {
return new ErrorEvent(aName, aProps);
},
},
ElementReplaceEvent: { create: function (aName, aProps) {
return new ElementReplaceEvent(aName, aProps);
},

View File

@ -1433,12 +1433,7 @@ nsGlobalWindow::FreeInnerObjects()
NotifyDOMWindowDestroyed(this);
// Kill all of the workers for this window.
// We push a cx so that exceptions get reported in the right DOM Window.
{
nsIScriptContext *scx = GetContextInternal();
AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
mozilla::dom::workers::CancelWorkersForWindow(cx, this);
}
mozilla::dom::workers::CancelWorkersForWindow(this);
// Close all offline storages for this window.
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
@ -11240,12 +11235,7 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
DisableGamepadUpdates();
// Suspend all of the workers for this window.
// We push a cx so that exceptions get reported in the right DOM Window.
{
nsIScriptContext *scx = GetContextInternal();
AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
mozilla::dom::workers::SuspendWorkersForWindow(cx, this);
}
mozilla::dom::workers::SuspendWorkersForWindow(this);
TimeStamp now = TimeStamp::Now();
for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
@ -11334,10 +11324,7 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
}
// Resume all of the workers for this window.
// We push a cx so that exceptions get reported in the right DOM Window.
nsIScriptContext *scx = GetContextInternal();
AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
mozilla::dom::workers::ResumeWorkersForWindow(scx, this);
mozilla::dom::workers::ResumeWorkersForWindow(this);
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mTimeRemaining).
@ -12130,4 +12117,3 @@ nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
#undef BEFOREUNLOAD_EVENT
#undef ERROR_EVENT
#undef EVENT

View File

@ -83,6 +83,10 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::Activity',
},
'AbstractWorker': {
'concrete': False
},
'AnimationEvent': {
'nativeType': 'nsDOMAnimationEvent',
},
@ -943,6 +947,12 @@ DOMInterfaces = {
'nativeType': 'nsDOMScrollAreaEvent',
},
'SharedWorker': {
'nativeType': 'mozilla::dom::workers::SharedWorker',
'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
'implicitJSContext': [ 'constructor' ],
},
'SimpleGestureEvent': {
'nativeType': 'nsDOMSimpleGestureEvent',
},
@ -1438,6 +1448,18 @@ DOMInterfaces = {
'workers': True,
},
'WorkerMessagePort': [{
'nativeType': 'mozilla::dom::workers::MessagePort',
'headerFile': 'mozilla/dom/workers/bindings/MessagePort.h',
'implicitJSContext': [ 'postMessage' ],
},
{
'nativeType': 'mozilla::dom::workers::WorkerMessagePort',
'headerFile': 'mozilla/dom/workers/bindings/WorkerMessagePort.h',
'workers': True,
'nativeOwnership': 'worker',
}],
'WorkerNavigator': {
'headerFile': 'mozilla/dom/workers/bindings/Navigator.h',
'workers': True,

View File

@ -184,6 +184,7 @@ var interfaceNamesInGlobalScope =
"DynamicsCompressorNode",
"Element",
"ElementReplaceEvent",
"ErrorEvent",
"Event",
"EventListenerInfo",
"EventSource",

View File

@ -0,0 +1,10 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
[NoInterfaceObject]
interface AbstractWorker {
attribute EventHandler onerror;
};

View File

@ -0,0 +1,21 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
[Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
interface ErrorEvent : Event
{
readonly attribute DOMString message;
readonly attribute DOMString filename;
readonly attribute unsigned long lineno;
readonly attribute unsigned long column;
};
dictionary ErrorEventInit : EventInit
{
DOMString message = "";
DOMString filename = "";
unsigned long lineno = 0;
unsigned long column = 0;
};

View File

@ -0,0 +1,13 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
[PrefControlled,
Constructor(DOMString scriptURL, optional DOMString name)]
interface SharedWorker : EventTarget {
readonly attribute WorkerMessagePort port;
};
SharedWorker implements AbstractWorker;

View File

@ -0,0 +1,18 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
// XXX Remove me soon!
[PrefControlled]
interface WorkerMessagePort : EventTarget {
[Throws]
void postMessage(any message, optional sequence<any> transferable);
void start();
void close();
[SetterThrows=Workers, GetterThrows=Workers]
attribute EventHandler onmessage;
};

View File

@ -16,6 +16,7 @@ PREPROCESSED_WEBIDL_FILES = [
]
WEBIDL_FILES = [
'AbstractWorker.webidl',
'AnalyserNode.webidl',
'AnimationEvent.webidl',
'ArchiveReader.webidl',
@ -366,6 +367,7 @@ WEBIDL_FILES = [
'ScriptProcessorNode.webidl',
'ScrollAreaEvent.webidl',
'SettingsManager.webidl',
'SharedWorker.webidl',
'SimpleGestureEvent.webidl',
'SourceBuffer.webidl',
'SourceBufferList.webidl',
@ -406,6 +408,7 @@ WEBIDL_FILES = [
'WifiOptions.webidl',
'Window.webidl',
'WorkerLocation.webidl',
'WorkerMessagePort.webidl',
'WorkerNavigator.webidl',
'XMLDocument.webidl',
'XMLHttpRequest.webidl',
@ -537,6 +540,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'BlobEvent.webidl',
'DeviceLightEvent.webidl',
'DeviceProximityEvent.webidl',
'ErrorEvent.webidl',
'MediaStreamEvent.webidl',
'MozInterAppMessageEvent.webidl',
'RTCDataChannelEvent.webidl',
@ -551,4 +555,3 @@ if CONFIG['MOZ_GAMEPAD']:
'GamepadEvent.webidl',
]

View File

@ -6,17 +6,19 @@
#ifndef mozilla_dom_workers_dombindinginlines_h__
#define mozilla_dom_workers_dombindinginlines_h__
#include "jsfriendapi.h"
#include "mozilla/dom/JSSlots.h"
#include "mozilla/dom/URLBinding.h"
#include "mozilla/dom/WorkerMessagePortBinding.h"
#include "mozilla/dom/XMLHttpRequestBinding.h"
#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
#include "mozilla/dom/URLBinding.h"
#include "jsfriendapi.h"
BEGIN_WORKERS_NAMESPACE
class URL;
class WorkerMessagePort;
class XMLHttpRequest;
class XMLHttpRequestUpload;
class URL;
namespace {
@ -45,9 +47,10 @@ struct WrapPrototypeTraits
} \
};
SPECIALIZE_PROTO_TRAITS(URL)
SPECIALIZE_PROTO_TRAITS(WorkerMessagePort)
SPECIALIZE_PROTO_TRAITS(XMLHttpRequest)
SPECIALIZE_PROTO_TRAITS(XMLHttpRequestUpload)
SPECIALIZE_PROTO_TRAITS(URL)
#undef SPECIALIZE_PROTO_TRAITS

View File

@ -414,8 +414,10 @@ public:
}
static JSObject*
Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JSAutoStructuredCloneBuffer& aData,
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime)
Create(JSContext* aCx, JS::Handle<JSObject*> aParent,
JSAutoStructuredCloneBuffer& aData,
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
bool aMainRuntime)
{
JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "message"));
if (!type) {
@ -429,16 +431,57 @@ public:
return NULL;
}
JS::Rooted<JSObject*> ports(aCx, JS_NewArrayObject(aCx, 0, nullptr));
if (!ports) {
return NULL;
}
MessageEvent* priv = new MessageEvent(aMainRuntime);
SetJSPrivateSafeish(obj, priv);
InitMessageEventCommon(aCx, obj, priv, type, false, false, NULL, NULL, NULL,
true);
ports, true);
priv->mBuffer.swap(aData);
priv->mClonedObjects.SwapElements(aClonedObjects);
return obj;
}
static JSObject*
Create(JSContext* aCx, JS::Handle<JSObject*> aParent,
JS::Handle<JSString*> aType, bool aBubbles, bool aCancelable,
JS::Handle<JSString*> aData, JS::Handle<JSString*> aOrigin,
JS::Handle<JSObject*> aSource, JS::Handle<JSObject*> aMessagePort,
bool aIsTrusted)
{
JS::Rooted<JSObject*> obj(aCx,
JS_NewObject(aCx, &sClass, nullptr, aParent));
if (!obj) {
return nullptr;
}
JS::Rooted<JSObject*> ports(aCx);
if (aMessagePort) {
JS::Value port = OBJECT_TO_JSVAL(aMessagePort);
ports = JS_NewArrayObject(aCx, 1, &port);
} else {
ports = JS_NewArrayObject(aCx, 0, nullptr);
}
if (!ports) {
return NULL;
}
MessageEvent* priv = new MessageEvent(false);
SetJSPrivateSafeish(obj, priv);
InitMessageEventCommon(aCx, obj, priv, aType, aBubbles, aCancelable, aData,
aOrigin, aSource, ports, aIsTrusted);
return obj;
}
protected:
MessageEvent(bool aMainRuntime)
: mMainRuntime(aMainRuntime)
@ -455,6 +498,7 @@ protected:
SLOT_data = Event::SLOT_COUNT,
SLOT_origin,
SLOT_source,
SLOT_ports,
SLOT_COUNT,
SLOT_FIRST = SLOT_data
@ -479,7 +523,7 @@ private:
InitMessageEventCommon(JSContext* aCx, JSObject* aObj, Event* aEvent,
JSString* aType, bool aBubbles, bool aCancelable,
JSString* aData, JSString* aOrigin, JSObject* aSource,
bool aIsTrusted)
JS::Handle<JSObject*> aMessagePorts, bool aIsTrusted)
{
jsval emptyString = JS_GetEmptyStringValue(aCx);
@ -490,6 +534,7 @@ private:
JS_SetReservedSlot(aObj, SLOT_origin,
aOrigin ? STRING_TO_JSVAL(aOrigin) : emptyString);
JS_SetReservedSlot(aObj, SLOT_source, OBJECT_TO_JSVAL(aSource));
JS_SetReservedSlot(aObj, SLOT_ports, OBJECT_TO_JSVAL(aMessagePorts));
}
static bool
@ -591,7 +636,7 @@ private:
}
InitMessageEventCommon(aCx, obj, event, type, bubbles, cancelable,
data, origin, source, false);
data, origin, source, JS::NullPtr(), false);
return true;
}
};
@ -616,6 +661,8 @@ const JSPropertySpec MessageEvent::sProperties[] = {
JSPROP_ENUMERATE),
JS_PSGS("source", Property<SLOT_source>::Get, GetterOnlyJSNative,
JSPROP_ENUMERATE),
JS_PSGS("ports", Property<SLOT_ports>::Get, GetterOnlyJSNative,
JSPROP_ENUMERATE),
JS_PS_END
};
@ -1049,6 +1096,25 @@ CreateProgressEvent(JSContext* aCx, JS::Handle<JSString*> aType, bool aLengthCom
aTotal);
}
JSObject*
CreateConnectEvent(JSContext* aCx, JS::Handle<JSObject*> aMessagePort)
{
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "connect"));
if (!type) {
return nullptr;
}
JS::Rooted<JSString*> emptyStr(aCx, JS_GetEmptyString(JS_GetRuntime(aCx)));
if (!emptyStr) {
return nullptr;
}
return MessageEvent::Create(aCx, global, type, false, false, emptyStr,
emptyStr, JS::NullPtr(), aMessagePort, true);
}
bool
IsSupportedEventClass(JSObject* aEvent)
{

View File

@ -35,6 +35,9 @@ JSObject*
CreateProgressEvent(JSContext* aCx, JS::Handle<JSString*> aType,
bool aLengthComputable, double aLoaded, double aTotal);
JSObject*
CreateConnectEvent(JSContext* aCx, JS::Handle<JSObject*> aMessagePort);
bool
IsSupportedEventClass(JSObject* aEvent);

View File

@ -6,6 +6,10 @@
#include "FileReaderSync.h"
#include "jsfriendapi.h"
#include "mozilla/Base64.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/FileReaderSyncBinding.h"
#include "nsCExternalHandlerService.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
@ -21,12 +25,9 @@
#include "nsISupportsImpl.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "File.h"
#include "RuntimeService.h"
#include "DOMBindingInlines.h"
#include "mozilla/Base64.h"
#include "mozilla/dom/EncodingUtils.h"
USING_WORKERS_NAMESPACE
using namespace mozilla;
@ -35,6 +36,7 @@ using mozilla::dom::GlobalObject;
NS_IMPL_ADDREF(FileReaderSync)
NS_IMPL_RELEASE(FileReaderSync)
NS_INTERFACE_MAP_BEGIN(FileReaderSync)
NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
NS_INTERFACE_MAP_END
@ -48,6 +50,12 @@ FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
return frs.forget();
}
JSObject*
FileReaderSync::WrapObject(JSContext* aCx, JS::HandleObject aScope)
{
return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
}
JSObject*
FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
JS::Handle<JSObject*> aScopeObj,

View File

@ -10,9 +10,6 @@
#include "Workers.h"
#include "nsICharsetDetectionObserver.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/FileReaderSyncBinding.h"
class nsIInputStream;
class nsIDOMBlob;
@ -39,10 +36,7 @@ public:
static already_AddRefed<FileReaderSync>
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
}
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aScope);
NS_DECL_ISUPPORTS

View File

@ -5,9 +5,6 @@
#include "Location.h"
#include "DOMBindingInlines.h"
#include "nsTraceRefcnt.h"
#include "mozilla/dom/WorkerLocationBinding.h"
BEGIN_WORKERS_NAMESPACE

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

@ -0,0 +1,209 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/dom/WorkerMessagePortBinding.h"
#include "nsDOMEvent.h"
#include "nsEventDispatcher.h"
#include "SharedWorker.h"
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
USING_WORKERS_NAMESPACE
namespace {
class DelayedEventRunnable MOZ_FINAL : public nsIRunnable
{
nsRefPtr<MessagePort> mMessagePort;
nsCOMPtr<nsIDOMEvent> mEvent;
public:
DelayedEventRunnable(MessagePort* aMessagePort,
already_AddRefed<nsIDOMEvent> aEvent)
: mMessagePort(aMessagePort), mEvent(aEvent)
{
AssertIsOnMainThread();
MOZ_ASSERT(aMessagePort);
MOZ_ASSERT(aEvent.get());
}
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
};
} // anonymous namespace
MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
uint64_t aSerial)
: nsDOMEventTargetHelper(aWindow), mSharedWorker(aSharedWorker),
mSerial(aSerial), mStarted(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(aSharedWorker);
}
MessagePort::~MessagePort()
{
AssertIsOnMainThread();
Close();
}
// static
bool
MessagePort::PrefEnabled()
{
AssertIsOnMainThread();
// Currently tied to the SharedWorker preference.
return SharedWorker::PrefEnabled();
}
void
MessagePort::PostMessage(JSContext* aCx, JS::HandleValue aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnMainThread();
if (IsClosed()) {
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
return;
}
mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv);
}
void
MessagePort::Start()
{
AssertIsOnMainThread();
if (IsClosed()) {
NS_WARNING("Called start() after calling close()!");
return;
}
if (mStarted) {
return;
}
mStarted = true;
if (!mQueuedEvents.IsEmpty()) {
for (uint32_t index = 0; index < mQueuedEvents.Length(); index++) {
nsCOMPtr<nsIRunnable> runnable =
new DelayedEventRunnable(this, mQueuedEvents[index].forget());
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
NS_WARNING("Failed to dispatch queued event!");
}
}
mQueuedEvents.Clear();
}
}
void
MessagePort::QueueEvent(nsIDOMEvent* aEvent)
{
AssertIsOnMainThread();
MOZ_ASSERT(aEvent);
MOZ_ASSERT(!IsClosed());
MOZ_ASSERT(!mStarted);
mQueuedEvents.AppendElement(aEvent);
}
void
MessagePort::CloseInternal()
{
AssertIsOnMainThread();
MOZ_ASSERT(!IsClosed());
MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
NS_WARN_IF_FALSE(mStarted, "Called close() before start()!");
if (!mStarted) {
mQueuedEvents.Clear();
}
mSharedWorker = nullptr;
}
NS_IMPL_ADDREF_INHERITED(MessagePort, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MessagePort, nsDOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
nsDOMEventTargetHelper)
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,
nsDOMEventTargetHelper)
tmp->Close();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
JSObject*
MessagePort::WrapObject(JSContext* aCx, JS::HandleObject aScope)
{
AssertIsOnMainThread();
return WorkerMessagePortBinding::Wrap(aCx, aScope, this);
}
nsresult
MessagePort::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
AssertIsOnMainThread();
nsIDOMEvent*& event = aVisitor.mDOMEvent;
if (event) {
bool preventDispatch = false;
if (IsClosed()) {
preventDispatch = true;
} else if (mSharedWorker->IsSuspended()) {
mSharedWorker->QueueEvent(event);
preventDispatch = true;
} else if (!mStarted) {
QueueEvent(event);
preventDispatch = true;
}
if (preventDispatch) {
aVisitor.mCanHandle = false;
aVisitor.mParentTarget = nullptr;
return NS_OK;
}
}
return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
}
NS_IMPL_ISUPPORTS1(DelayedEventRunnable, nsIRunnable)
NS_IMETHODIMP
DelayedEventRunnable::Run()
{
AssertIsOnMainThread();
MOZ_ASSERT(mMessagePort);
MOZ_ASSERT(mEvent);
bool ignored;
nsresult rv = mMessagePort->DispatchEvent(mEvent, &ignored);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}

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

@ -0,0 +1,112 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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 "Workers.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsDOMEventTargetHelper.h"
class nsIDOMEvent;
class nsPIDOMWindow;
BEGIN_WORKERS_NAMESPACE
class SharedWorker;
class MessagePort MOZ_FINAL : public nsDOMEventTargetHelper
{
friend class SharedWorker;
typedef mozilla::ErrorResult ErrorResult;
nsRefPtr<SharedWorker> mSharedWorker;
nsTArray<nsCOMPtr<nsIDOMEvent>> mQueuedEvents;
uint64_t mSerial;
bool mStarted;
public:
static bool
PrefEnabled();
void
PostMessage(JSContext* aCx, JS::HandleValue aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
void
Start();
void
Close()
{
AssertIsOnMainThread();
if (!IsClosed()) {
CloseInternal();
}
}
uint64_t
Serial() const
{
return mSerial;
}
void
QueueEvent(nsIDOMEvent* aEvent);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, nsDOMEventTargetHelper)
EventHandlerNonNull*
GetOnmessage()
{
AssertIsOnMainThread();
return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
}
void
SetOnmessage(EventHandlerNonNull* aCallback)
{
AssertIsOnMainThread();
SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
Start();
}
bool
IsClosed() const
{
AssertIsOnMainThread();
return !mSharedWorker;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
virtual nsresult
PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
private:
// This class can only be created by SharedWorker.
MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
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

@ -5,10 +5,10 @@
#include "Navigator.h"
#include "DOMBindingInlines.h"
#include "RuntimeService.h"
#include "mozilla/dom/WorkerNavigatorBinding.h"
#include "RuntimeService.h"
BEGIN_WORKERS_NAMESPACE
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator)

View File

@ -6,15 +6,19 @@
#include "RuntimeService.h"
#include "nsIChannel.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIDocument.h"
#include "nsIDOMChromeWindow.h"
#include "nsIEffectiveTLDService.h"
#include "nsIObserverService.h"
#include "nsIPlatformCharset.h"
#include "nsIPrincipal.h"
#include "nsIScriptContext.h"
#include "nsIScriptSecurityManager.h"
#include "nsISupportsPriority.h"
#include "nsITimer.h"
#include "nsIURI.h"
#include "nsPIDOMWindow.h"
#include <algorithm>
@ -36,12 +40,14 @@
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsTraceRefcnt.h"
#include "nsXPCOM.h"
#include "nsXPCOMPrivate.h"
#include "OSFileConstants.h"
#include "xpcpublic.h"
#include "Events.h"
#include "SharedWorker.h"
#include "Worker.h"
#include "WorkerPrivate.h"
@ -1030,32 +1036,32 @@ ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid
}
void
CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow)
CancelWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->CancelWorkersForWindow(aCx, aWindow);
runtime->CancelWorkersForWindow(aWindow);
}
}
void
SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow)
SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->SuspendWorkersForWindow(aCx, aWindow);
runtime->SuspendWorkersForWindow(aWindow);
}
}
void
ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow)
ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->ResumeWorkersForWindow(aCx, aWindow);
runtime->ResumeWorkersForWindow(aWindow);
}
}
@ -1205,11 +1211,32 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
}
}
bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
const nsString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
nsCString sharedWorkerScriptSpec;
if (isSharedWorker) {
AssertIsOnMainThread();
nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
NS_ASSERTION(scriptURI, "Null script URI!");
nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
if (NS_FAILED(rv)) {
NS_WARNING("GetSpec failed?!");
xpc::Throw(aCx, rv);
return false;
}
NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
}
const nsCString& domain = aWorkerPrivate->Domain();
WorkerDomainInfo* domainInfo;
bool queued = false;
{
const nsCString& domain = aWorkerPrivate->Domain();
MutexAutoLock lock(mMutex);
if (!mDomainMap.Get(domain, &domainInfo)) {
@ -1220,7 +1247,6 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
mDomainMap.Put(domain, domainInfo);
}
if (domainInfo) {
queued = gMaxWorkersPerDomain &&
domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
!domain.IsEmpty();
@ -1234,12 +1260,16 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
else {
domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
}
}
}
if (!domainInfo) {
JS_ReportOutOfMemory(aCx);
return false;
if (isSharedWorker) {
MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(sharedWorkerScriptSpec));
SharedWorkerInfo* sharedWorkerInfo =
new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
sharedWorkerName);
domainInfo->mSharedWorkerInfos.Put(sharedWorkerScriptSpec,
sharedWorkerInfo);
}
}
// From here on out we must call UnregisterWorker if something fails!
@ -1267,15 +1297,15 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
nsTArray<WorkerPrivate*>* windowArray;
if (!mWindowMap.Get(window, &windowArray)) {
NS_ASSERTION(!parent, "Shouldn't have a parent here!");
windowArray = new nsTArray<WorkerPrivate*>(1);
mWindowMap.Put(window, windowArray);
}
NS_ASSERTION(!windowArray->Contains(aWorkerPrivate),
"Already know about this worker!");
if (!windowArray->Contains(aWorkerPrivate)) {
windowArray->AppendElement(aWorkerPrivate);
} else {
MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
}
}
if (!queued && !ScheduleWorker(aCx, aWorkerPrivate)) {
@ -1295,10 +1325,10 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
AssertIsOnMainThread();
}
WorkerPrivate* queuedWorker = nullptr;
{
const nsCString& domain = aWorkerPrivate->Domain();
WorkerPrivate* queuedWorker = nullptr;
{
MutexAutoLock lock(mMutex);
WorkerDomainInfo* domainInfo;
@ -1322,6 +1352,17 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
}
if (aWorkerPrivate->IsSharedWorker()) {
MatchSharedWorkerInfo match(aWorkerPrivate);
domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
&match);
if (match.mSharedWorkerInfo) {
domainInfo->mSharedWorkerInfos.Remove(
match.mSharedWorkerInfo->mScriptSpec);
}
}
// See if there's a queued worker we can schedule.
if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
!domainInfo->mQueuedWorkers.IsEmpty()) {
@ -1342,23 +1383,39 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
}
}
if (aWorkerPrivate->IsSharedWorker()) {
AssertIsOnMainThread();
nsAutoTArray<nsRefPtr<SharedWorker>, 5> sharedWorkersToNotify;
aWorkerPrivate->GetAllSharedWorkers(sharedWorkersToNotify);
for (uint32_t index = 0; index < sharedWorkersToNotify.Length(); index++) {
MOZ_ASSERT(sharedWorkersToNotify[index]);
sharedWorkersToNotify[index]->NoteDeadWorker(aCx);
}
}
if (parent) {
parent->RemoveChildWorker(aCx, aWorkerPrivate);
}
else if (aWorkerPrivate->IsSharedWorker()) {
mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
}
else {
// May be null.
nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
nsTArray<WorkerPrivate*>* windowArray;
if (!mWindowMap.Get(window, &windowArray)) {
NS_ERROR("Don't have an entry for this window!");
MOZ_ASSERT(false, "Don't have an entry for this window!");
}
NS_ASSERTION(windowArray->Contains(aWorkerPrivate),
"Don't know about this worker!");
windowArray->RemoveElement(aWorkerPrivate);
if (!windowArray->RemoveElement(aWorkerPrivate)) {
MOZ_ASSERT(false, "Worker wasn't in the correct window array!");
}
if (windowArray->IsEmpty()) {
NS_ASSERTION(!queuedWorker, "How can this be?!");
MOZ_ASSERT(!queuedWorker, "queuedWorker should be in this array!");
mWindowMap.Remove(window);
}
}
@ -1794,6 +1851,48 @@ RuntimeService::AddAllTopLevelWorkersToArray(const nsACString& aKey,
return PL_DHASH_NEXT;
}
// static
PLDHashOperator
RuntimeService::RemoveSharedWorkerFromWindowMap(
nsPIDOMWindow* aKey,
nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
void* aUserArg)
{
AssertIsOnMainThread();
MOZ_ASSERT(aData.get());
MOZ_ASSERT(aUserArg);
auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
MOZ_ASSERT(workerPrivate->IsSharedWorker());
if (aData->RemoveElement(workerPrivate)) {
MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
if (aData->IsEmpty()) {
return PL_DHASH_REMOVE;
}
}
return PL_DHASH_NEXT;
}
// static
PLDHashOperator
RuntimeService::FindSharedWorkerInfo(const nsACString& aKey,
SharedWorkerInfo* aData,
void* aUserArg)
{
auto match = static_cast<MatchSharedWorkerInfo*>(aUserArg);
if (aData->mWorkerPrivate == match->mWorkerPrivate) {
match->mSharedWorkerInfo = aData;
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
void
RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow,
nsTArray<WorkerPrivate*>& aWorkers)
@ -1811,59 +1910,169 @@ RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow,
}
void
RuntimeService::CancelWorkersForWindow(JSContext* aCx,
nsPIDOMWindow* aWindow)
RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
nsAutoTArray<WorkerPrivate*, 100> workers;
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
GetWorkersForWindow(aWindow, workers);
if (!workers.IsEmpty()) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
MOZ_ASSERT(sgo);
nsIScriptContext* scx = sgo->GetContext();
AutoPushJSContext cx(scx ?
scx->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->Cancel(aCx)) {
NS_WARNING("Failed to cancel worker!");
WorkerPrivate*& worker = workers[index];
if (worker->IsSharedWorker()) {
worker->CloseSharedWorkersForWindow(aWindow);
} else if (!worker->Cancel(cx)) {
JS_ReportPendingException(cx);
}
}
}
}
void
RuntimeService::SuspendWorkersForWindow(JSContext* aCx,
nsPIDOMWindow* aWindow)
RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
nsAutoTArray<WorkerPrivate*, 100> workers;
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
GetWorkersForWindow(aWindow, workers);
if (!workers.IsEmpty()) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
MOZ_ASSERT(sgo);
nsIScriptContext* scx = sgo->GetContext();
AutoPushJSContext cx(scx ?
scx->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->Suspend(aCx)) {
NS_WARNING("Failed to cancel worker!");
if (!workers[index]->Suspend(cx, aWindow)) {
JS_ReportPendingException(cx);
}
}
}
}
void
RuntimeService::ResumeWorkersForWindow(nsIScriptContext* aCx,
nsPIDOMWindow* aWindow)
RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
nsAutoTArray<WorkerPrivate*, 100> workers;
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
GetWorkersForWindow(aWindow, workers);
if (!workers.IsEmpty()) {
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
MOZ_ASSERT(sgo);
nsIScriptContext* scx = sgo->GetContext();
AutoPushJSContext cx(scx ?
scx->GetNativeContext() :
nsContentUtils::GetSafeJSContext());
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->SynchronizeAndResume(aCx)) {
NS_WARNING("Failed to cancel worker!");
if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) {
JS_ReportPendingException(cx);
}
}
}
}
nsresult
RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
const nsAString& aScriptURL,
const nsAString& aName,
SharedWorker** aSharedWorker)
{
AssertIsOnMainThread();
MOZ_ASSERT(aCx);
MOZ_ASSERT(aWindow);
WorkerPrivate::LoadInfo loadInfo;
nsresult rv = WorkerPrivate::GetLoadInfo(aCx, aWindow, nullptr, aScriptURL,
false, &loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(loadInfo.mResolvedScriptURI);
nsCString scriptSpec;
rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec);
NS_ENSURE_SUCCESS(rv, rv);
WorkerPrivate* workerPrivate = nullptr;
{
MutexAutoLock lock(mMutex);
WorkerDomainInfo* domainInfo;
SharedWorkerInfo* sharedWorkerInfo;
if (mDomainMap.Get(loadInfo.mDomain, &domainInfo) &&
domainInfo->mSharedWorkerInfos.Get(scriptSpec, &sharedWorkerInfo) &&
sharedWorkerInfo->mName == aName) {
workerPrivate = sharedWorkerInfo->mWorkerPrivate;
}
}
bool created = false;
if (!workerPrivate) {
nsRefPtr<WorkerPrivate> newWorkerPrivate =
WorkerPrivate::Create(aCx, JS::NullPtr(), nullptr, aScriptURL, false,
true, aName, &loadInfo);
NS_ENSURE_TRUE(newWorkerPrivate, NS_ERROR_FAILURE);
if (!RegisterWorker(aCx, newWorkerPrivate)) {
NS_WARNING("Failed to register worker!");
return NS_ERROR_FAILURE;
}
created = true;
newWorkerPrivate.forget(&workerPrivate);
}
MOZ_ASSERT(workerPrivate->IsSharedWorker());
nsRefPtr<SharedWorker> sharedWorker =
new SharedWorker(aWindow, workerPrivate);
if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
NS_WARNING("Failed to dispatch to worker!");
return NS_ERROR_FAILURE;
}
// This is normally handled in RegisterWorker, but that wasn't called if the
// worker already existed.
if (!created) {
nsTArray<WorkerPrivate*>* windowArray;
if (!mWindowMap.Get(aWindow, &windowArray)) {
windowArray = new nsTArray<WorkerPrivate*>(1);
mWindowMap.Put(aWindow, windowArray);
}
if (!windowArray->Contains(workerPrivate)) {
windowArray->AppendElement(workerPrivate);
}
}
sharedWorker.forget(aSharedWorker);
return NS_OK;
}
void
RuntimeService::NoteIdleThread(nsIThread* aThread)
{

View File

@ -14,9 +14,11 @@
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include "nsString.h"
#include "nsTArray.h"
@ -27,18 +29,35 @@ class nsPIDOMWindow;
BEGIN_WORKERS_NAMESPACE
class SharedWorker;
class WorkerPrivate;
class RuntimeService MOZ_FINAL : public nsIObserver
{
struct SharedWorkerInfo
{
WorkerPrivate* mWorkerPrivate;
nsCString mScriptSpec;
nsString mName;
SharedWorkerInfo(WorkerPrivate* aWorkerPrivate,
const nsACString& aScriptSpec,
const nsAString& aName)
: mWorkerPrivate(aWorkerPrivate), mScriptSpec(aScriptSpec), mName(aName)
{ }
};
struct WorkerDomainInfo
{
nsCString mDomain;
nsTArray<WorkerPrivate*> mActiveWorkers;
nsTArray<WorkerPrivate*> mQueuedWorkers;
nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos;
uint32_t mChildWorkerCount;
WorkerDomainInfo() : mActiveWorkers(1), mChildWorkerCount(0) { }
WorkerDomainInfo()
: mActiveWorkers(1), mChildWorkerCount(0)
{ }
uint32_t
ActiveWorkerCount() const
@ -53,6 +72,16 @@ class RuntimeService MOZ_FINAL : public nsIObserver
mozilla::TimeStamp mExpirationTime;
};
struct MatchSharedWorkerInfo
{
WorkerPrivate* mWorkerPrivate;
SharedWorkerInfo* mSharedWorkerInfo;
MatchSharedWorkerInfo(WorkerPrivate* aWorkerPrivate)
: mWorkerPrivate(aWorkerPrivate), mSharedWorkerInfo(nullptr)
{ }
};
mozilla::Mutex mMutex;
// Protected by mMutex.
@ -62,7 +91,8 @@ class RuntimeService MOZ_FINAL : public nsIObserver
nsTArray<IdleThreadInfo> mIdleThreadArray;
// *Not* protected by mMutex.
nsClassHashtable<nsPtrHashKey<nsPIDOMWindow>, nsTArray<WorkerPrivate*> > mWindowMap;
nsClassHashtable<nsPtrHashKey<nsPIDOMWindow>,
nsTArray<WorkerPrivate*> > mWindowMap;
// Only used on the main thread.
nsCOMPtr<nsITimer> mIdleThreadTimer;
@ -106,13 +136,18 @@ public:
UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
void
CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
void
SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
void
ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow);
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
nsresult
CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
const nsAString& aScriptURL, const nsAString& aName,
SharedWorker** aSharedWorker);
const nsACString&
GetDetectorName() const
@ -219,6 +254,16 @@ private:
WorkerDomainInfo* aData,
void* aUserArg);
static PLDHashOperator
RemoveSharedWorkerFromWindowMap(nsPIDOMWindow* aKey,
nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
void* aUserArg);
static PLDHashOperator
FindSharedWorkerInfo(const nsACString& aKey,
SharedWorkerInfo* aData,
void* aUserArg);
void
GetWorkersForWindow(nsPIDOMWindow* aWindow,
nsTArray<WorkerPrivate*>& aWorkers);

View File

@ -38,10 +38,12 @@
#define MAX_CONCURRENT_SCRIPTS 1000
BEGIN_WORKERS_NAMESPACE
USING_WORKERS_NAMESPACE
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
namespace {
namespace scriptloader {
static
nsresult
ChannelFromScriptURL(nsIPrincipal* principal,
nsIURI* baseURI,
@ -49,19 +51,85 @@ ChannelFromScriptURL(nsIPrincipal* principal,
nsILoadGroup* loadGroup,
nsIIOService* ios,
nsIScriptSecurityManager* secMan,
const nsString& aScriptURL,
const nsAString& aScriptURL,
bool aIsWorkerScript,
nsIChannel** aChannel);
nsIChannel** aChannel)
{
AssertIsOnMainThread();
} // namespace scriptloader
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
aScriptURL, parentDoc,
baseURI);
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
END_WORKERS_NAMESPACE
// If we're part of a document then check the content load policy.
if (parentDoc) {
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
principal, parentDoc,
NS_LITERAL_CSTRING("text/javascript"),
nullptr, &shouldLoad,
nsContentUtils::GetContentPolicy(),
secMan);
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
return rv = NS_ERROR_CONTENT_BLOCKED;
}
return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
}
}
USING_WORKERS_NAMESPACE
// If this script loader is being used to make a new worker then we need
// to do a same-origin check. Otherwise we need to clear the load with the
// security manager.
if (aIsWorkerScript) {
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
using mozilla::dom::Throw;
// We pass true as the 3rd argument to checkMayLoad here.
// This allows workers in sandboxed documents to load data URLs
// (and other URLs that inherit their principal from their
// creator.)
rv = principal->CheckMayLoad(uri, false, true);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
else {
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
namespace {
// Get Content Security Policy from parent document to pass into channel.
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = principal->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannelPolicy> channelPolicy;
if (csp) {
channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = channelPolicy->SetContentSecurityPolicy(csp);
NS_ENSURE_SUCCESS(rv, rv);
rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
NS_ENSURE_SUCCESS(rv, rv);
}
uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
flags, channelPolicy);
NS_ENSURE_SUCCESS(rv, rv);
channel.forget(aChannel);
return rv;
}
class ScriptLoaderRunnable;
@ -275,7 +343,7 @@ public:
nsCOMPtr<nsIChannel> channel;
if (mIsWorkerScript) {
// May be null.
channel = mWorkerPrivate->GetChannel();
channel = mWorkerPrivate->ForgetWorkerChannel();
}
// All of these can potentially be null, but that should be ok. We'll either
@ -295,9 +363,8 @@ public:
nsresult& rv = loadInfo.mLoadResult;
if (!channel) {
rv = scriptloader::ChannelFromScriptURL(principal, baseURI, parentDoc,
loadGroup, ios, secMan,
loadInfo.mURL, mIsWorkerScript,
rv = ChannelFromScriptURL(principal, baseURI, parentDoc, loadGroup, ios,
secMan, loadInfo.mURL, mIsWorkerScript,
getter_AddRefs(channel));
if (NS_FAILED(rv)) {
return rv;
@ -481,6 +548,12 @@ public:
void
ExecuteFinishedScripts()
{
AssertIsOnMainThread();
if (mIsWorkerScript) {
mWorkerPrivate->WorkerScriptLoaded();
}
uint32_t firstIndex = UINT32_MAX;
uint32_t lastIndex = UINT32_MAX;
@ -544,14 +617,14 @@ class ChannelGetterRunnable MOZ_FINAL : public nsRunnable
{
WorkerPrivate* mParentWorker;
uint32_t mSyncQueueKey;
const nsString& mScriptURL;
const nsAString& mScriptURL;
nsIChannel** mChannel;
nsresult mResult;
public:
ChannelGetterRunnable(WorkerPrivate* aParentWorker,
uint32_t aSyncQueueKey,
const nsString& aScriptURL,
const nsAString& aScriptURL,
nsIChannel** aChannel)
: mParentWorker(aParentWorker), mSyncQueueKey(aSyncQueueKey),
mScriptURL(aScriptURL), mChannel(aChannel), mResult(NS_ERROR_FAILURE)
@ -723,98 +796,11 @@ BEGIN_WORKERS_NAMESPACE
namespace scriptloader {
// static
nsresult
ChannelFromScriptURL(nsIPrincipal* principal,
nsIURI* baseURI,
nsIDocument* parentDoc,
nsILoadGroup* loadGroup,
nsIIOService* ios,
nsIScriptSecurityManager* secMan,
const nsString& aScriptURL,
bool aIsWorkerScript,
nsIChannel** aChannel)
{
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
aScriptURL, parentDoc,
baseURI);
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
// If we're part of a document then check the content load policy.
if (parentDoc) {
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
principal, parentDoc,
NS_LITERAL_CSTRING("text/javascript"),
nullptr, &shouldLoad,
nsContentUtils::GetContentPolicy(),
secMan);
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
return rv = NS_ERROR_CONTENT_BLOCKED;
}
return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
}
}
// If this script loader is being used to make a new worker then we need
// to do a same-origin check. Otherwise we need to clear the load with the
// security manager.
if (aIsWorkerScript) {
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
// We pass true as the 3rd argument to checkMayLoad here.
// This allows workers in sandboxed documents to load data URLs
// (and other URLs that inherit their principal from their
// creator.)
rv = principal->CheckMayLoad(uri, false, true);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
else {
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
// Get Content Security Policy from parent document to pass into channel.
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = principal->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannelPolicy> channelPolicy;
if (csp) {
channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = channelPolicy->SetContentSecurityPolicy(csp);
NS_ENSURE_SUCCESS(rv, rv);
rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
NS_ENSURE_SUCCESS(rv, rv);
}
uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
flags, channelPolicy);
NS_ENSURE_SUCCESS(rv, rv);
channel.forget(aChannel);
return rv;
}
nsresult
ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
nsIURI* aBaseURI,
nsIDocument* aParentDoc,
const nsString& aScriptURL,
const nsAString& aScriptURL,
nsIChannel** aChannel)
{
AssertIsOnMainThread();
@ -836,7 +822,7 @@ ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
nsresult
ChannelFromScriptURLWorkerThread(JSContext* aCx,
WorkerPrivate* aParent,
const nsString& aScriptURL,
const nsAString& aScriptURL,
nsIChannel** aChannel)
{
aParent->AssertIsOnWorkerThread();
@ -859,7 +845,7 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
return getter->GetResult();
}
void ReportLoadError(JSContext* aCx, const nsString& aURL,
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
nsresult aLoadResult, bool aIsMainThread)
{
NS_LossyConvertUTF16toASCII url(aURL);

View File

@ -22,16 +22,16 @@ nsresult
ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
nsIURI* aBaseURI,
nsIDocument* aParentDoc,
const nsString& aScriptURL,
const nsAString& aScriptURL,
nsIChannel** aChannel);
nsresult
ChannelFromScriptURLWorkerThread(JSContext* aCx,
WorkerPrivate* aParent,
const nsString& aScriptURL,
const nsAString& aScriptURL,
nsIChannel** aChannel);
void ReportLoadError(JSContext* aCx, const nsString& aURL,
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
nsresult aLoadResult, bool aIsMainThread);
bool LoadWorkerScript(JSContext* aCx);

View File

@ -0,0 +1,235 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/Preferences.h"
#include "mozilla/dom/SharedWorkerBinding.h"
#include "nsContentUtils.h"
#include "nsDOMEvent.h"
#include "nsEventDispatcher.h"
#include "nsIClassInfoImpl.h"
#include "MessagePort.h"
#include "RuntimeService.h"
#include "Worker.h"
#include "WorkerPrivate.h"
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
USING_WORKERS_NAMESPACE
namespace {
const char kSharedWorkersEnabledPref[] = "dom.workers.sharedWorkers.enabled";
} // anonymous namespace
SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
WorkerPrivate* aWorkerPrivate)
: nsDOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate),
mSuspended(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
mSerial = aWorkerPrivate->NextMessagePortSerial();
mMessagePort = new MessagePort(aWindow, this, mSerial);
}
SharedWorker::~SharedWorker()
{
AssertIsOnMainThread();
MOZ_ASSERT(!mWorkerPrivate);
}
//static
bool
SharedWorker::PrefEnabled()
{
AssertIsOnMainThread();
return mozilla::Preferences::GetBool(kSharedWorkersEnabledPref, false);
}
// static
already_AddRefed<SharedWorker>
SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
const nsAString& aScriptURL,
const mozilla::dom::Optional<nsAString>& aName,
ErrorResult& aRv)
{
AssertIsOnMainThread();
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(window);
RuntimeService* rts = RuntimeService::GetOrCreateService();
if (!rts) {
aRv = NS_ERROR_NOT_AVAILABLE;
return nullptr;
}
nsString name;
if (aName.WasPassed()) {
name = aName.Value();
}
nsRefPtr<SharedWorker> sharedWorker;
nsresult rv = rts->CreateSharedWorker(aCx, window, aScriptURL, name,
getter_AddRefs(sharedWorker));
if (NS_FAILED(rv)) {
aRv = rv;
return nullptr;
}
return sharedWorker.forget();
}
already_AddRefed<MessagePort>
SharedWorker::Port()
{
AssertIsOnMainThread();
nsRefPtr<MessagePort> messagePort = mMessagePort;
return messagePort.forget();
}
void
SharedWorker::Suspend()
{
AssertIsOnMainThread();
MOZ_ASSERT(!IsSuspended());
mSuspended = true;
}
void
SharedWorker::Resume()
{
AssertIsOnMainThread();
MOZ_ASSERT(IsSuspended());
mSuspended = false;
if (!mSuspendedEvents.IsEmpty()) {
nsTArray<nsCOMPtr<nsIDOMEvent>> events;
mSuspendedEvents.SwapElements(events);
for (uint32_t index = 0; index < events.Length(); index++) {
nsCOMPtr<nsIDOMEvent>& event = events[index];
MOZ_ASSERT(event);
nsCOMPtr<nsIDOMEventTarget> 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(IsSuspended());
mSuspendedEvents.AppendElement(aEvent);
}
void
SharedWorker::Close()
{
AssertIsOnMainThread();
if (mMessagePort) {
mMessagePort->Close();
}
if (mWorkerPrivate) {
AutoSafeJSContext cx;
NoteDeadWorker(cx);
}
}
void
SharedWorker::PostMessage(JSContext* aCx, JS::HandleValue aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnMainThread();
MOZ_ASSERT(mWorkerPrivate);
MOZ_ASSERT(mMessagePort);
mWorkerPrivate->PostMessageToMessagePort(aCx, mMessagePort->Serial(),
aMessage, aTransferable, aRv);
}
void
SharedWorker::NoteDeadWorker(JSContext* aCx)
{
AssertIsOnMainThread();
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->UnregisterSharedWorker(aCx, this);
mWorkerPrivate = nullptr;
}
NS_IMPL_ADDREF_INHERITED(SharedWorker, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SharedWorker, nsDOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SharedWorker)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
nsDOMEventTargetHelper)
tmp->Close();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedEvents)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
JSObject*
SharedWorker::WrapObject(JSContext* aCx, JS::HandleObject aScope)
{
AssertIsOnMainThread();
return SharedWorkerBinding::Wrap(aCx, aScope, this);
}
nsresult
SharedWorker::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
AssertIsOnMainThread();
nsIDOMEvent*& event = aVisitor.mDOMEvent;
if (IsSuspended() && event) {
QueueEvent(event);
aVisitor.mCanHandle = false;
aVisitor.mParentTarget = nullptr;
return NS_OK;
}
return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
}

104
dom/workers/SharedWorker.h Normal file
View File

@ -0,0 +1,104 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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_sharedworker_h__
#define mozilla_dom_workers_sharedworker_h__
#include "Workers.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsDOMEventTargetHelper.h"
class nsIDOMEvent;
class nsPIDOMWindow;
BEGIN_WORKERS_NAMESPACE
class MessagePort;
class RuntimeService;
class WorkerPrivate;
class SharedWorker MOZ_FINAL : public nsDOMEventTargetHelper
{
friend class MessagePort;
friend class RuntimeService;
typedef mozilla::ErrorResult ErrorResult;
typedef mozilla::dom::GlobalObject GlobalObject;
WorkerPrivate* mWorkerPrivate;
nsRefPtr<MessagePort> mMessagePort;
nsTArray<nsCOMPtr<nsIDOMEvent>> mSuspendedEvents;
uint64_t mSerial;
bool mSuspended;
public:
static bool
PrefEnabled();
static already_AddRefed<SharedWorker>
Constructor(const GlobalObject& aGlobal, JSContext* aCx,
const nsAString& aScriptURL, const Optional<nsAString>& aName,
ErrorResult& aRv);
already_AddRefed<MessagePort>
Port();
uint64_t
Serial() const
{
return mSerial;
}
bool
IsSuspended() const
{
return mSuspended;
}
void
Suspend();
void
Resume();
void
QueueEvent(nsIDOMEvent* aEvent);
void
Close();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SharedWorker, nsDOMEventTargetHelper)
IMPL_EVENT_HANDLER(error)
virtual JSObject*
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
virtual nsresult
PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
private:
// This class can only be created from the RuntimeService.
SharedWorker(nsPIDOMWindow* aWindow, WorkerPrivate* aWorkerPrivate);
// This class is reference-counted and will be destroyed from Release().
~SharedWorker();
// Only called by MessagePort.
void
PostMessage(JSContext* aCx, JS::HandleValue aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
// Only called by RuntimeService.
void
NoteDeadWorker(JSContext* aCx);
};
END_WORKERS_NAMESPACE
#endif // mozilla_dom_workers_sharedworker_h__

View File

@ -8,6 +8,7 @@
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/EventHandlerBinding.h"
#include "nsJSUtils.h"
#include "jsapi.h"
#include "EventTarget.h"
@ -91,69 +92,43 @@ public:
static WorkerPrivate*
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
static JSObject*
Create(JSContext* aCx, WorkerPrivate* aParentObj, const nsAString& aScriptURL,
bool aIsChromeWorker, bool aIsSharedWorker,
const nsAString& aSharedWorkerName);
protected:
static bool
ConstructInternal(JSContext* aCx, unsigned aArgc, jsval* aVp,
bool aIsChromeWorker, const JSClass* aClass)
ConstructInternal(JSContext* aCx, JS::CallArgs aArgs, bool aIsChromeWorker)
{
if (!aArgc) {
if (!aArgs.length()) {
JS_ReportError(aCx, "Constructor requires at least one argument!");
return false;
}
JS::Rooted<JSString*> scriptURL(aCx, JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]));
if (!scriptURL) {
nsDependentJSString scriptURL;
if (!scriptURL.init(aCx, aArgs[0])) {
return false;
}
jsval priv = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
CONSTRUCTOR_SLOT_PARENT);
JS::Rooted<JS::Value> priv(aCx,
js::GetFunctionNativeReserved(&aArgs.callee(), CONSTRUCTOR_SLOT_PARENT));
RuntimeService* runtimeService;
WorkerPrivate* parent;
if (JSVAL_IS_VOID(priv)) {
runtimeService = RuntimeService::GetOrCreateService();
if (!runtimeService) {
JS_ReportError(aCx, "Failed to create runtime service!");
return false;
}
if (priv.isUndefined()) {
parent = NULL;
}
else {
runtimeService = RuntimeService::GetService();
parent = static_cast<WorkerPrivate*>(JSVAL_TO_PRIVATE(priv));
} else {
parent = static_cast<WorkerPrivate*>(priv.get().toPrivate());
parent->AssertIsOnWorkerThread();
}
JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, aClass, nullptr, nullptr));
JS::Rooted<JSObject*> obj(aCx,
Create(aCx, parent, scriptURL, aIsChromeWorker, false, EmptyString()));
if (!obj) {
return false;
}
// Ensure that the DOM_OBJECT_SLOT always has a PrivateValue set, as this
// will be accessed in the Trace() method if WorkerPrivate::Create()
// triggers a GC.
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
nsRefPtr<WorkerPrivate> worker =
WorkerPrivate::Create(aCx, obj, parent, scriptURL, aIsChromeWorker);
if (!worker) {
return false;
}
// Worker now owned by the JS object.
NS_ADDREF(worker.get());
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(worker));
if (!runtimeService->RegisterWorker(aCx, worker)) {
return false;
}
// Worker now also owned by its thread.
NS_ADDREF(worker.get());
JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
aArgs.rval().setObject(*obj);
return true;
}
@ -281,9 +256,10 @@ private:
}
static bool
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
return ConstructInternal(aCx, aArgc, aVp, false, Class());
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return ConstructInternal(aCx, args, false);
}
static void
@ -489,9 +465,10 @@ private:
}
static bool
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
return ConstructInternal(aCx, aArgc, aVp, true, Class());
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return ConstructInternal(aCx, args, true);
}
static void
@ -574,6 +551,63 @@ Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
return NULL;
}
JSObject*
Worker::Create(JSContext* aCx, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
bool aIsSharedWorker, const nsAString& aSharedWorkerName)
{
MOZ_ASSERT_IF(aIsSharedWorker, !aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(!aIsSharedWorker, aSharedWorkerName.IsEmpty());
RuntimeService* runtimeService;
if (aParent) {
runtimeService = RuntimeService::GetService();
NS_ASSERTION(runtimeService, "Null runtime service!");
}
else {
runtimeService = RuntimeService::GetOrCreateService();
if (!runtimeService) {
JS_ReportError(aCx, "Failed to create runtime service!");
return nullptr;
}
}
const JSClass* classPtr = aIsChromeWorker ? ChromeWorker::Class() : Class();
JS::Rooted<JSObject*> obj(aCx,
JS_NewObject(aCx, const_cast<JSClass*>(classPtr), nullptr, nullptr));
if (!obj) {
return nullptr;
}
// Ensure that the DOM_OBJECT_SLOT always has a PrivateValue set, as this will
// be accessed in the Trace() method if WorkerPrivate::Create() triggers a GC.
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
nsRefPtr<WorkerPrivate> worker =
WorkerPrivate::Create(aCx, obj, aParent, aScriptURL, aIsChromeWorker,
aIsSharedWorker, aSharedWorkerName);
if (!worker) {
// It'd be better if we could avoid allocating the JSObject until after we
// make sure we have a WorkerPrivate, but failing that we should at least
// make sure that the DOM_OBJECT_SLOT always has a PrivateValue.
return nullptr;
}
// Worker now owned by the JS object.
NS_ADDREF(worker.get());
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(worker));
if (!runtimeService->RegisterWorker(aCx, worker)) {
return nullptr;
}
// Worker now also owned by its thread.
NS_ADDREF(worker.get());
return obj;
}
} // anonymous namespace
BEGIN_WORKERS_NAMESPACE

View File

@ -8,6 +8,7 @@
#include "Workers.h"
BEGIN_WORKERS_NAMESPACE
namespace worker {

View File

@ -0,0 +1,225 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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 "WorkerMessagePort.h"
#include "DOMBindingInlines.h"
#include "Events.h"
#include "WorkerPrivate.h"
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
USING_WORKERS_NAMESPACE
using namespace mozilla::dom::workers::events;
namespace {
bool
DispatchMessageEvent(JSContext* aCx, JS::HandleObject aMessagePort,
JSAutoStructuredCloneBuffer& aBuffer,
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
{
MOZ_ASSERT(aMessagePort);
JSAutoStructuredCloneBuffer buffer;
aBuffer.swap(buffer);
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
aClonedObjects.SwapElements(clonedObjects);
JS::Rooted<JSObject*> event(aCx,
CreateMessageEvent(aCx, buffer, clonedObjects, false));
if (!event) {
return false;
}
bool dummy;
return DispatchEventToTarget(aCx, aMessagePort, event, &dummy);
}
class QueuedMessageEventRunnable : public WorkerRunnable
{
JSAutoStructuredCloneBuffer mBuffer;
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
nsRefPtr<WorkerMessagePort> mMessagePort;
JSObject* mMessagePortObject;
public:
QueuedMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
JSAutoStructuredCloneBuffer& aBuffer,
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
: WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
RunWhenClearing),
mMessagePortObject(nullptr)
{
aWorkerPrivate->AssertIsOnWorkerThread();
mBuffer.swap(aBuffer);
mClonedObjects.SwapElements(aClonedObjects);
}
bool
Hold(JSContext* aCx, WorkerMessagePort* aMessagePort)
{
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aMessagePort);
MOZ_ASSERT(aMessagePort->GetJSObject());
MOZ_ASSERT(!mMessagePortObject);
if (!JS_AddNamedObjectRoot(aCx, &mMessagePortObject,
"WorkerMessagePort::MessageEventRunnable::"
"mMessagePortObject")) {
return false;
}
mMessagePortObject = aMessagePort->GetJSObject();
mMessagePort = aMessagePort;
return true;
}
bool
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->AssertIsOnWorkerThread();
return true;
}
void
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aDispatchResult)
{
aWorkerPrivate->AssertIsOnWorkerThread();
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
JS::Rooted<JSObject*> messagePortObject(aCx, mMessagePortObject);
mMessagePortObject = nullptr;
JS_RemoveObjectRoot(aCx, &mMessagePortObject);
nsRefPtr<WorkerMessagePort> messagePort;
mMessagePort.swap(messagePort);
return DispatchMessageEvent(aCx, messagePortObject, mBuffer,
mClonedObjects);
}
};
} // anonymous namespace
void
WorkerMessagePort::_trace(JSTracer* aTrc)
{
EventTarget::_trace(aTrc);
}
void
WorkerMessagePort::_finalize(JSFreeOp* aFop)
{
EventTarget::_finalize(aFop);
}
void
WorkerMessagePort::PostMessage(
JSContext* /* aCx */, JS::HandleValue aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv)
{
if (mClosed) {
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
return;
}
JSContext* cx = GetJSContext();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
MOZ_ASSERT(workerPrivate);
workerPrivate->PostMessageToParentMessagePort(cx, Serial(), aMessage,
aTransferable, aRv);
}
void
WorkerMessagePort::Start()
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(GetJSContext());
MOZ_ASSERT(workerPrivate);
if (mClosed) {
NS_WARNING("Called start() after calling close()!");
return;
}
if (mStarted) {
return;
}
mStarted = true;
if (!mQueuedMessages.IsEmpty()) {
for (uint32_t index = 0; index < mQueuedMessages.Length(); index++) {
MessageInfo& info = mQueuedMessages[index];
nsRefPtr<QueuedMessageEventRunnable> runnable =
new QueuedMessageEventRunnable(workerPrivate, info.mBuffer,
info.mClonedObjects);
JSContext* cx = GetJSContext();
if (!runnable->Hold(cx, this) ||
!runnable->Dispatch(cx)) {
NS_WARNING("Failed to dispatch queued event!");
break;
}
}
mQueuedMessages.Clear();
}
}
bool
WorkerMessagePort::MaybeDispatchEvent(
JSContext* aCx,
JSAutoStructuredCloneBuffer& aBuffer,
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
{
if (mClosed) {
NS_WARNING("Not going to ever run this event!");
aBuffer.clear();
aClonedObjects.Clear();
return true;
}
if (!mStarted) {
// Queue the message for later.
MessageInfo* info = mQueuedMessages.AppendElement();
info->mBuffer.swap(aBuffer);
info->mClonedObjects.SwapElements(aClonedObjects);
return true;
}
// Go ahead and dispatch the event.
JS::Rooted<JSObject*> target(aCx, GetJSObject());
return DispatchMessageEvent(aCx, target, aBuffer, aClonedObjects);
}
void
WorkerMessagePort::CloseInternal()
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(GetJSContext());
MOZ_ASSERT(workerPrivate);
MOZ_ASSERT(!IsClosed());
MOZ_ASSERT_IF(mStarted, mQueuedMessages.IsEmpty());
mClosed = true;
workerPrivate->DisconnectMessagePort(Serial());
if (!mStarted) {
mQueuedMessages.Clear();
}
}

View File

@ -0,0 +1,103 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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_workermessageport_h__
#define mozilla_dom_workers_workermessageport_h__
#include "js/StructuredClone.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/workers/bindings/EventTarget.h"
BEGIN_WORKERS_NAMESPACE
class WorkerPrivate;
class WorkerMessagePort : public EventTarget
{
friend class WorkerPrivate;
typedef mozilla::ErrorResult ErrorResult;
struct MessageInfo
{
JSAutoStructuredCloneBuffer mBuffer;
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
};
nsTArray<MessageInfo> mQueuedMessages;
uint64_t mSerial;
bool mStarted;
bool mClosed;
public:
virtual void
_trace(JSTracer* aTrc) MOZ_OVERRIDE;
virtual void
_finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
void
PostMessage(JSContext* aCx, JS::HandleValue aMessage,
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
void
Start();
void
Close()
{
if (!IsClosed()) {
CloseInternal();
}
}
already_AddRefed<EventHandlerNonNull>
GetOnmessage(ErrorResult& aRv)
{
return GetEventListener(NS_LITERAL_STRING("message"), aRv);
}
void
SetOnmessage(EventHandlerNonNull* aListener, ErrorResult& aRv)
{
SetEventListener(NS_LITERAL_STRING("message"), aListener, aRv);
if (!aRv.Failed()) {
Start();
}
}
uint64_t
Serial() const
{
return mSerial;
}
bool
MaybeDispatchEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aBuffer,
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
bool
IsClosed() const
{
return mClosed;
}
private:
// Only created by WorkerPrivate.
WorkerMessagePort(JSContext* aCx, uint64_t aSerial)
: EventTarget(aCx), mSerial(aSerial), mStarted(false), mClosed(false)
{ }
virtual ~WorkerMessagePort()
{ }
void
CloseInternal();
};
END_WORKERS_NAMESPACE
#endif // mozilla_dom_workers_workermessageport_h__

File diff suppressed because it is too large Load Diff

View File

@ -19,9 +19,11 @@
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsEventQueue.h"
#include "nsHashKeys.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsTPriorityQueue.h"
@ -33,6 +35,7 @@
class JSAutoStructuredCloneBuffer;
class nsIChannel;
class nsIContentSecurityPolicy;
class nsIDocument;
class nsIPrincipal;
class nsIScriptContext;
@ -46,6 +49,9 @@ class RuntimeStats;
BEGIN_WORKERS_NAMESPACE
class MessagePort;
class SharedWorker;
class WorkerMessagePort;
class WorkerPrivate;
class WorkerRunnable : public nsIRunnable
@ -246,7 +252,50 @@ public:
nsCString mHash;
};
struct LoadInfo
{
// All of these should be released in ForgetMainThreadObjects.
nsCOMPtr<nsIURI> mBaseURI;
nsCOMPtr<nsIURI> mResolvedScriptURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
nsCOMPtr<nsIChannel> mChannel;
nsCString mDomain;
bool mEvalAllowed;
bool mReportCSPViolations;
bool mXHRParamsAllowed;
bool mPrincipalIsSystem;
LoadInfo()
: mEvalAllowed(false), mReportCSPViolations(false),
mXHRParamsAllowed(false), mPrincipalIsSystem(false)
{ }
void
StealFrom(LoadInfo& aOther)
{
mBaseURI = aOther.mBaseURI.forget();
mResolvedScriptURI = aOther.mResolvedScriptURI.forget();
mPrincipal = aOther.mPrincipal.forget();
mScriptContext = aOther.mScriptContext.forget();
mWindow = aOther.mWindow.forget();
mCSP = aOther.mCSP.forget();
mChannel = aOther.mChannel.forget();
mDomain = aOther.mDomain;
mEvalAllowed = aOther.mEvalAllowed;
mReportCSPViolations = aOther.mReportCSPViolations;
mXHRParamsAllowed = aOther.mXHRParamsAllowed;
mPrincipalIsSystem = aOther.mPrincipalIsSystem;
}
};
protected:
typedef mozilla::ErrorResult ErrorResult;
SharedMutex mMutex;
mozilla::CondVar mCondVar;
mozilla::CondVar mMemoryReportCondVar;
@ -254,19 +303,10 @@ protected:
private:
JSObject* mJSObject;
WorkerPrivate* mParent;
JSContext* mParentJSContext;
nsString mScriptURL;
nsCString mDomain;
nsString mSharedWorkerName;
LocationInfo mLocationInfo;
// Main-thread things.
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMPtr<nsIURI> mBaseURI;
nsCOMPtr<nsIURI> mScriptURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
LoadInfo mLoadInfo;
// Only used for top level workers.
nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
@ -277,28 +317,24 @@ private:
// Protected by mMutex.
JSSettings mJSSettings;
// Only touched on the parent thread (currently this is always the main
// thread as SharedWorkers are always top-level).
nsDataHashtable<nsUint64HashKey, SharedWorker*> mSharedWorkers;
uint64_t mBusyCount;
uint64_t mMessagePortSerial;
Status mParentStatus;
bool mJSObjectRooted;
bool mParentSuspended;
bool mIsChromeWorker;
bool mPrincipalIsSystem;
bool mMainThreadObjectsForgotten;
bool mEvalAllowed;
bool mReportCSPViolations;
bool mIsSharedWorker;
protected:
WorkerPrivateParent(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
JSContext* aParentJSContext, const nsAString& aScriptURL,
bool aIsChromeWorker, const nsACString& aDomain,
nsCOMPtr<nsPIDOMWindow>& aWindow,
nsCOMPtr<nsIScriptContext>& aScriptContext,
nsCOMPtr<nsIURI>& aBaseURI,
nsCOMPtr<nsIPrincipal>& aPrincipal,
nsCOMPtr<nsIChannel>& aChannel,
nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
bool aEvalAllowed,
bool aReportCSPViolations);
WorkerPrivateParent(JSContext* aCx, JS::HandleObject aObject,
WorkerPrivate* aParent, const nsAString& aScriptURL,
bool aIsChromeWorker, bool aIsSharedWorker,
const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
~WorkerPrivateParent();
@ -320,6 +356,11 @@ private:
return NotifyPrivate(aCx, Terminating);
}
bool
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable,
bool aToMessagePort, uint64_t aMessagePortSerial);
public:
// May be called on any thread...
bool
@ -345,13 +386,14 @@ public:
}
bool
Suspend(JSContext* aCx);
void
Resume(JSContext* aCx);
Suspend(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
SynchronizeAndResume(nsIScriptContext* aCx);
Resume(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow,
nsIScriptContext* aScriptContext);
virtual void
_trace(JSTracer* aTrc) MOZ_OVERRIDE;
@ -387,7 +429,24 @@ public:
bool
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable);
JS::Handle<JS::Value> aTransferable)
{
return PostMessageInternal(aCx, aMessage, aTransferable, false, 0);
}
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,
JSAutoStructuredCloneBuffer& aBuffer,
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects);
uint64_t
GetInnerWindowId();
@ -411,6 +470,24 @@ public:
void
GarbageCollect(JSContext* aCx, bool aShrinking);
bool
RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
void
UnregisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
void
BroadcastErrorToSharedWorkers(JSContext* aCx,
const nsAString& aMessage,
const nsAString& aFilename,
const nsAString& aLine,
uint32_t aLineNumber,
uint32_t aColumnNumber,
uint32_t aFlags);
void
WorkerScriptLoaded();
void
QueueRunnable(WorkerRunnable* aRunnable)
{
@ -457,7 +534,7 @@ public:
GetScriptContext() const
{
AssertIsOnMainThread();
return mScriptContext;
return mLoadInfo.mScriptContext;
}
JSObject*
@ -475,38 +552,31 @@ public:
const nsCString&
Domain() const
{
return mDomain;
return mLoadInfo.mDomain;
}
nsIURI*
GetBaseURI() const
{
AssertIsOnMainThread();
return mBaseURI;
return mLoadInfo.mBaseURI;
}
void
SetBaseURI(nsIURI* aBaseURI);
nsIURI*
GetScriptURI() const
GetResolvedScriptURI() const
{
AssertIsOnMainThread();
return mScriptURI;
}
void
SetScriptURI(nsIURI* aScriptURI)
{
AssertIsOnMainThread();
mScriptURI = aScriptURI;
return mLoadInfo.mResolvedScriptURI;
}
nsIPrincipal*
GetPrincipal() const
{
AssertIsOnMainThread();
return mPrincipal;
return mLoadInfo.mPrincipal;
}
void
@ -515,60 +585,72 @@ public:
bool
UsesSystemPrincipal() const
{
return mPrincipalIsSystem;
return mLoadInfo.mPrincipalIsSystem;
}
nsIChannel*
GetChannel() const
already_AddRefed<nsIChannel>
ForgetWorkerChannel()
{
AssertIsOnMainThread();
return mChannel;
return mLoadInfo.mChannel.forget();
}
nsIDocument*
GetDocument() const
{
AssertIsOnMainThread();
return mWindow ? mWindow->GetExtantDoc() : nullptr;
return mLoadInfo.mWindow ? mLoadInfo.mWindow->GetExtantDoc() : nullptr;
}
nsPIDOMWindow*
GetWindow()
{
AssertIsOnMainThread();
return mWindow;
return mLoadInfo.mWindow;
}
nsIContentSecurityPolicy*
GetCSP() const
{
AssertIsOnMainThread();
return mCSP;
return mLoadInfo.mCSP;
}
void
SetCSP(nsIContentSecurityPolicy* aCSP)
{
AssertIsOnMainThread();
mCSP = aCSP;
mLoadInfo.mCSP = aCSP;
}
bool
IsEvalAllowed() const
{
return mEvalAllowed;
return mLoadInfo.mEvalAllowed;
}
void
SetEvalAllowed(bool aEvalAllowed)
{
mEvalAllowed = aEvalAllowed;
mLoadInfo.mEvalAllowed = aEvalAllowed;
}
bool
GetReportCSPViolations() const
{
return mReportCSPViolations;
return mLoadInfo.mReportCSPViolations;
}
bool
XHRParamsAllowed() const
{
return mLoadInfo.mXHRParamsAllowed;
}
void
SetXHRParamsAllowed(bool aAllowed)
{
mLoadInfo.mXHRParamsAllowed = aAllowed;
}
LocationInfo&
@ -590,6 +672,43 @@ public:
return mIsChromeWorker;
}
bool
IsSharedWorker() const
{
return mIsSharedWorker;
}
const nsString&
SharedWorkerName() const
{
return mSharedWorkerName;
}
uint64_t
NextMessagePortSerial()
{
AssertIsOnMainThread();
return mMessagePortSerial++;
}
void
GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker> >& aSharedWorkers);
void
CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow);
void
RegisterHostObjectURI(const nsACString& aURI);
void
UnregisterHostObjectURI(const nsACString& aURI);
void
StealHostObjectURIs(nsTArray<nsCString>& aArray);
virtual JSObject*
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
#ifdef DEBUG
void
AssertIsOnParentThread() const;
@ -605,10 +724,6 @@ public:
AssertInnerWindowIsCorrect() const
{ }
#endif
void RegisterHostObjectURI(const nsACString& aURI);
void UnregisterHostObjectURI(const nsACString& aURI);
void StealHostObjectURIs(nsTArray<nsCString>& aArray);
};
class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
@ -658,6 +773,8 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
nsCOMPtr<nsITimer> mTimer;
nsRefPtr<MemoryReporter> mMemoryReporter;
nsDataHashtable<nsUint64HashKey, WorkerMessagePort*> mWorkerPorts;
mozilla::TimeStamp mKillTime;
uint32_t mErrorHandlerRecursionCount;
uint32_t mNextTimeoutId;
@ -669,7 +786,6 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
bool mCloseHandlerFinished;
bool mMemoryReporterRunning;
bool mBlockedForMemoryReporter;
bool mXHRParamsAllowed;
#ifdef DEBUG
nsCOMPtr<nsIThread> mThread;
@ -679,8 +795,15 @@ public:
~WorkerPrivate();
static already_AddRefed<WorkerPrivate>
Create(JSContext* aCx, JS::Handle<JSObject*> aObj, WorkerPrivate* aParent,
JS::Handle<JSString*> aScriptURL, bool aIsChromeWorker);
Create(JSContext* aCx, JS::HandleObject aObject, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
bool aIsSharedWorker, const nsAString& aSharedWorkerName,
LoadInfo* aLoadInfo = nullptr);
static nsresult
GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
LoadInfo* aLoadInfo);
void
DoRunLoop(JSContext* aCx);
@ -764,8 +887,20 @@ public:
DestroySyncLoop(uint32_t aSyncLoopKey);
bool
PostMessageToParent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> transferable);
PostMessageToParent(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable)
{
return PostMessageToParentInternal(aCx, aMessage, aTransferable, false, 0);
}
void
PostMessageToParentMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value > >& aTransferable,
ErrorResult& aRv);
bool
NotifyInternal(JSContext* aCx, Status aStatus);
@ -812,18 +947,6 @@ public:
bool
BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
bool
XHRParamsAllowed() const
{
return mXHRParamsAllowed;
}
void
SetXHRParamsAllowed(bool aAllowed)
{
mXHRParamsAllowed = aAllowed;
}
#ifdef JS_GC_ZEAL
void
UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency);
@ -885,16 +1008,20 @@ public:
BeginCTypesCall();
}
bool
ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial);
void
DisconnectMessagePort(uint64_t aMessagePortSerial);
WorkerMessagePort*
GetMessagePort(uint64_t aMessagePortSerial);
private:
WorkerPrivate(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
JSContext* aParentJSContext, const nsAString& aScriptURL,
bool aIsChromeWorker, const nsACString& aDomain,
nsCOMPtr<nsPIDOMWindow>& aWindow,
nsCOMPtr<nsIScriptContext>& aScriptContext,
nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
nsCOMPtr<nsIChannel>& aChannel,
nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
bool aReportCSPViolations, bool aXHRParamsAllowed);
WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
WorkerPrivate* aParent, const nsAString& aScriptURL,
bool aIsChromeWorker, bool aIsSharedWorker,
const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
bool
Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
@ -960,8 +1087,17 @@ private:
void
WaitForWorkerEvents(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT);
static bool
CheckXHRParamsAllowed(nsPIDOMWindow* aWindow);
static PLDHashOperator
TraceMessagePorts(const uint64_t& aKey,
WorkerMessagePort* aData,
void* aUserArg);
bool
PostMessageToParentInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable,
bool aToMessagePort,
uint64_t aMessagePortSerial);
};
WorkerPrivate*

View File

@ -141,7 +141,8 @@ protected:
_trace(JSTracer* aTrc) MOZ_OVERRIDE
{
for (int32_t i = 0; i < SLOT_COUNT; i++) {
JS_CallHeapValueTracer(aTrc, &mSlots[i], "WorkerGlobalScope instance slot");
JS_CallHeapValueTracer(aTrc, &mSlots[i],
"WorkerGlobalScope instance slot");
}
mWorker->TraceInternal(aTrc);
EventTarget::_trace(aTrc);
@ -296,8 +297,10 @@ private:
JSObject* wrapper = &JS_CALLEE(aCx, aVp).toObject();
JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
JS::Rooted<JS::Value> scope(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
JS::Rooted<JS::Value> listener(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
JS::Rooted<JS::Value> scope(aCx,
js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
JS::Rooted<JS::Value> listener(aCx,
js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
JS_ASSERT(scope.isObject());
@ -319,7 +322,8 @@ private:
}
if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval) &&
!JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, rval.address())) {
!JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL,
rval.address())) {
return false;
}
@ -334,9 +338,9 @@ private:
MOZ_ASSERT(scope);
ErrorResult rv;
nsRefPtr<EventHandlerNonNull> adaptor =
scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to get event listener!");
return false;
@ -445,7 +449,7 @@ private:
static bool
Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -466,7 +470,7 @@ private:
static bool
ImportScripts(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -487,7 +491,7 @@ private:
static bool
SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -498,7 +502,8 @@ private:
}
JS::Rooted<JS::Value> dummy(aCx);
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", dummy.address())) {
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
dummy.address())) {
return false;
}
@ -508,7 +513,7 @@ private:
static bool
ClearTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -534,7 +539,7 @@ private:
static bool
SetInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -545,7 +550,8 @@ private:
}
JS::Rooted<JS::Value> dummy(aCx);
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", dummy.address())) {
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
dummy.address())) {
return false;
}
@ -555,7 +561,7 @@ private:
static bool
ClearInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -581,7 +587,7 @@ private:
static bool
Dump(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -615,7 +621,7 @@ private:
static bool
AtoB(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -625,7 +631,8 @@ private:
}
JS::Rooted<JS::Value> string(aCx);
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", string.address())) {
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
string.address())) {
return false;
}
@ -641,7 +648,7 @@ private:
static bool
BtoA(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -651,7 +658,8 @@ private:
}
JS::Rooted<JS::Value> binary(aCx);
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", binary.address())) {
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
binary.address())) {
return false;
}
@ -676,8 +684,8 @@ NS_INTERFACE_MAP_END
const JSClass WorkerGlobalScope::sClass = {
"WorkerGlobalScope",
0,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
};
const JSPropertySpec WorkerGlobalScope::sProperties[] = {
@ -747,12 +755,13 @@ public:
static JSObject*
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
{
JSObject* proto =
JS::Rooted<JSObject*> proto(aCx,
JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
sProperties, sFunctions, NULL, NULL);
sProperties, sFunctions, NULL, NULL));
if (proto) {
void* domClass = const_cast<DOMClass *>(DOMClassStruct());
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
JS::PrivateValue(domClass));
}
return proto;
}
@ -807,9 +816,9 @@ private:
MOZ_ASSERT(scope);
ErrorResult rv;
nsRefPtr<EventHandlerNonNull> handler =
scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to get event listener!");
return false;
@ -852,8 +861,7 @@ private:
} else {
handler = nullptr;
}
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
handler, rv);
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), handler, rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to set event listener!");
@ -894,8 +902,8 @@ private:
}
static bool
Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId, unsigned aFlags,
JS::MutableHandle<JSObject*> aObjp)
Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
{
bool resolved;
if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
@ -933,7 +941,7 @@ private:
static bool
PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
if (!obj) {
return false;
}
@ -967,9 +975,10 @@ const DOMJSClass DedicatedWorkerGlobalScope::sClass = {
"DedicatedWorkerGlobalScope",
JSCLASS_DOM_GLOBAL | JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_NEW_RESOLVE,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub,
Finalize, NULL, NULL, NULL, NULL, Trace
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
JS_StrictPropertyStub, JS_EnumerateStub,
reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, Finalize, nullptr,
nullptr, nullptr, nullptr, Trace
},
{
INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
@ -1023,29 +1032,354 @@ const char* const DedicatedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
"onmessage",
};
class SharedWorkerGlobalScope : public WorkerGlobalScope
{
static DOMJSClass sClass;
static DOMIfaceAndProtoJSClass sProtoClass;
static const JSPropertySpec sProperties[];
enum
{
STRING_onconnect = 0,
STRING_COUNT
};
static const char* const sEventStrings[STRING_COUNT];
public:
static const JSClass*
Class()
{
return sClass.ToJSClass();
}
static const JSClass*
ProtoClass()
{
return sProtoClass.ToJSClass();
}
static const DOMClass*
DOMClassStruct()
{
return &sClass.mClass;
}
static JSObject*
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
{
JS::Rooted<JSObject*> proto(aCx,
JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
sProperties, nullptr, nullptr, nullptr));
if (proto) {
void* domClass = const_cast<DOMClass *>(DOMClassStruct());
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
JS::PrivateValue(domClass));
}
return proto;
}
static bool
InitPrivate(JSContext* aCx, JSObject* aObj, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(JS_GetClass(aObj) == Class());
dom::AllocateProtoAndIfaceCache(aObj);
nsRefPtr<SharedWorkerGlobalScope> scope =
new SharedWorkerGlobalScope(aCx, aWorkerPrivate);
js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(scope));
scope->SetIsDOMBinding();
scope->SetWrapper(aObj);
scope.forget();
return true;
}
protected:
SharedWorkerGlobalScope(JSContext* aCx, WorkerPrivate* aWorker)
: WorkerGlobalScope(aCx, aWorker)
{
MOZ_COUNT_CTOR(mozilla::dom::workers::SharedWorkerGlobalScope);
}
~SharedWorkerGlobalScope()
{
MOZ_COUNT_DTOR(mozilla::dom::workers::SharedWorkerGlobalScope);
}
private:
using EventTarget::GetEventListener;
using EventTarget::SetEventListener;
static bool
IsSharedWorkerGlobalScope(JS::Handle<JS::Value> aVal)
{
return aVal.isObject() && JS_GetClass(&aVal.toObject()) == Class();
}
static bool
GetOnconnectImpl(JSContext* aCx, JS::CallArgs aArgs)
{
auto name = sEventStrings[STRING_onconnect];
auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
MOZ_ASSERT(scope);
ErrorResult rv;
nsRefPtr<EventHandlerNonNull> handler =
scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to get event listener!");
return false;
}
if (!handler) {
aArgs.rval().setNull();
} else {
aArgs.rval().setObject(*handler->Callable());
}
return true;
}
static bool
GetOnconnect(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
auto args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
GetOnconnectImpl>(aCx, args);
}
static bool
SetOnconnectImpl(JSContext* aCx, JS::CallArgs aArgs)
{
auto name = sEventStrings[STRING_onconnect];
auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
MOZ_ASSERT(scope);
if (aArgs.length() == 0 || !aArgs[0].isObject()) {
JS_ReportError(aCx, "Not an event listener!");
return false;
}
ErrorResult rv;
JS::Rooted<JSObject*> listenerObj(aCx, aArgs[0].toObjectOrNull());
nsRefPtr<EventHandlerNonNull> handler;
if (listenerObj && JS_ObjectIsCallable(aCx, listenerObj)) {
handler = new EventHandlerNonNull(listenerObj);
} else {
handler = nullptr;
}
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), handler, rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to set event listener!");
return false;
}
aArgs.rval().setUndefined();
return true;
}
static bool
SetOnconnect(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
auto args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
SetOnconnectImpl>(aCx, args);
}
static bool
GetNameImpl(JSContext* aCx, JS::CallArgs aArgs)
{
auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), "name");
MOZ_ASSERT(scope);
auto name = scope->mWorker->SharedWorkerName();
MOZ_ASSERT(!name.IsVoid());
JS::Rooted<JSString*> nameStr(aCx,
JS_InternUCStringN(aCx, name.get(), name.Length()));
if (!nameStr) {
return false;
}
aArgs.rval().setString(nameStr);
return true;
}
static bool
GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
auto args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
GetNameImpl>(aCx, args);
}
static SharedWorkerGlobalScope*
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
{
const JSClass* classPtr = JS_GetClass(aObj);
if (classPtr == Class()) {
return UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
}
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
JSMSG_INCOMPATIBLE_PROTO, Class()->name, aFunctionName,
classPtr->name);
return nullptr;
}
static bool
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
JSMSG_WRONG_CONSTRUCTOR, Class()->name);
return false;
}
static bool
Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
{
bool resolved;
if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
return false;
}
aObjp.set(resolved ? aObj.get() : nullptr);
return true;
}
static void
Finalize(JSFreeOp* aFop, JSObject* aObj)
{
MOZ_ASSERT(JS_GetClass(aObj) == Class());
SharedWorkerGlobalScope* scope =
UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
if (scope) {
DestroyProtoAndIfaceCache(aObj);
scope->_finalize(aFop);
}
}
static void
Trace(JSTracer* aTrc, JSObject* aObj)
{
MOZ_ASSERT(JS_GetClass(aObj) == Class());
SharedWorkerGlobalScope* scope =
UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
if (scope) {
TraceProtoAndIfaceCache(aTrc, aObj);
scope->_trace(aTrc);
}
}
};
DOMJSClass SharedWorkerGlobalScope::sClass = {
{
// We don't have to worry about Xray expando slots here because we'll never
// have an Xray wrapper to a worker global scope.
"SharedWorkerGlobalScope",
JSCLASS_DOM_GLOBAL | JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_NEW_RESOLVE,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
JS_StrictPropertyStub, JS_EnumerateStub,
reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, Finalize, nullptr,
nullptr, nullptr, nullptr, Trace
},
{
INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
false,
&sWorkerNativePropertyHooks
}
};
DOMIfaceAndProtoJSClass SharedWorkerGlobalScope::sProtoClass = {
{
// XXXbz we use "SharedWorkerGlobalScope" here to match sClass
// so that we can JS_InitClass this JSClass and then
// call JS_NewObject with our sClass and have it find the right
// prototype.
"SharedWorkerGlobalScope",
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
nullptr, /* finalize */
nullptr, /* checkAccess */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
JSCLASS_NO_INTERNAL_MEMBERS
},
eInterfacePrototype,
&sWorkerNativePropertyHooks,
"[object SharedWorkerGlobalScope]",
prototypes::id::_ID_Count,
0
};
const JSPropertySpec SharedWorkerGlobalScope::sProperties[] = {
JS_PSGS(sEventStrings[STRING_onconnect], GetOnconnect, SetOnconnect,
JSPROP_ENUMERATE),
JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE),
JS_PS_END
};
const char* const SharedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
"onconnect",
};
WorkerGlobalScope*
WorkerGlobalScope::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
const char* aFunctionName)
{
const JSClass* classPtr = JS_GetClass(aObj);
// We can only make DedicatedWorkerGlobalScope, not WorkerGlobalScope, so this
// should never happen.
JS_ASSERT(classPtr != Class());
// We can only make [Dedicated|Shared]WorkerGlobalScope, not
// WorkerGlobalScope, so this should never happen.
MOZ_ASSERT(classPtr != Class());
if (classPtr == DedicatedWorkerGlobalScope::Class()) {
return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj);
}
JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
sClass.name, aFunctionName, classPtr->name);
return NULL;
if (classPtr == SharedWorkerGlobalScope::Class()) {
return UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
}
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
classPtr->name);
return nullptr;
}
bool
WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> aVal)
{
return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
if (!aVal.isObject()) {
return false;
}
auto classPtr = JS_GetClass(&aVal.toObject());
return classPtr == DedicatedWorkerGlobalScope::Class() ||
classPtr == SharedWorkerGlobalScope::Class();
}
} /* anonymous namespace */
@ -1053,32 +1387,42 @@ WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
BEGIN_WORKERS_NAMESPACE
JSObject*
CreateDedicatedWorkerGlobalScope(JSContext* aCx)
CreateGlobalScope(JSContext* aCx)
{
using namespace mozilla::dom;
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
JS_ASSERT(worker);
MOZ_ASSERT(worker);
const JSClass* classPtr = worker->IsSharedWorker() ?
SharedWorkerGlobalScope::Class() :
DedicatedWorkerGlobalScope::Class();
JS::CompartmentOptions options;
if (worker->IsChromeWorker())
if (worker->IsChromeWorker()) {
options.setVersion(JSVERSION_LATEST);
}
JS::Rooted<JSObject*> global(aCx,
JS_NewGlobalObject(aCx, DedicatedWorkerGlobalScope::Class(),
GetWorkerPrincipal(), JS::DontFireOnNewGlobalHook, options));
JS_NewGlobalObject(aCx, classPtr, GetWorkerPrincipal(),
JS::DontFireOnNewGlobalHook, options));
if (!global) {
return NULL;
return nullptr;
}
JSAutoCompartment ac(aCx, global);
// Make the private slots now so that all our instance checks succeed.
if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
return NULL;
if (worker->IsSharedWorker()) {
if (!SharedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
return nullptr;
}
} else if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
return nullptr;
}
// Proto chain should be:
// global -> DedicatedWorkerGlobalScope
// global -> [Dedicated|Shared]WorkerGlobalScope
// -> WorkerGlobalScope
// -> EventTarget
// -> Object
@ -1086,43 +1430,45 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
JS::Rooted<JSObject*> eventTargetProto(aCx,
EventTargetBinding_workers::GetProtoObject(aCx, global));
if (!eventTargetProto) {
return NULL;
return nullptr;
}
JS::Rooted<JSObject*> scopeProto(aCx,
WorkerGlobalScope::InitClass(aCx, global, eventTargetProto));
if (!scopeProto) {
return NULL;
return nullptr;
}
JS::Rooted<JSObject*> dedicatedScopeProto(aCx,
JS::Rooted<JSObject*> finalScopeProto(aCx,
worker->IsSharedWorker() ?
SharedWorkerGlobalScope::InitClass(aCx, global, scopeProto) :
DedicatedWorkerGlobalScope::InitClass(aCx, global, scopeProto));
if (!dedicatedScopeProto) {
return NULL;
if (!finalScopeProto) {
return nullptr;
}
if (!JS_SetPrototype(aCx, global, dedicatedScopeProto)) {
return NULL;
if (!JS_SetPrototype(aCx, global, finalScopeProto)) {
return nullptr;
}
JSObject* workerProto = worker::InitClass(aCx, global, eventTargetProto,
false);
JS::Rooted<JSObject*> workerProto(aCx,
worker::InitClass(aCx, global, eventTargetProto, false));
if (!workerProto) {
return NULL;
return nullptr;
}
if (worker->IsChromeWorker()) {
if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
!DefineChromeWorkerFunctions(aCx, global) ||
!DefineOSFileConstants(aCx, global)) {
return NULL;
return nullptr;
}
}
// Init other classes we care about.
if (!events::InitClasses(aCx, global, false) ||
!file::InitClasses(aCx, global)) {
return NULL;
return nullptr;
}
// Init other paris-bindings.
@ -1137,11 +1483,11 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
!URLBinding_workers::GetConstructorObject(aCx, global) ||
!WorkerLocationBinding_workers::GetConstructorObject(aCx, global) ||
!WorkerNavigatorBinding_workers::GetConstructorObject(aCx, global)) {
return NULL;
return nullptr;
}
if (!JS_DefineProfilingFunctions(aCx, global)) {
return NULL;
return nullptr;
}
JS_FireOnNewGlobalObject(aCx, global);
@ -1149,11 +1495,4 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
return global;
}
bool
ClassIsWorkerGlobalScope(const JSClass* aClass)
{
return WorkerGlobalScope::Class() == aClass ||
DedicatedWorkerGlobalScope::Class() == aClass;
}
END_WORKERS_NAMESPACE

View File

@ -11,10 +11,7 @@
BEGIN_WORKERS_NAMESPACE
JSObject*
CreateDedicatedWorkerGlobalScope(JSContext* aCx);
bool
ClassIsWorkerGlobalScope(const JSClass* aClass);
CreateGlobalScope(JSContext* aCx);
END_WORKERS_NAMESPACE

View File

@ -168,13 +168,13 @@ ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp);
void
CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
void
SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
void
ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow);
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
class WorkerTask {
public:

View File

@ -20,9 +20,12 @@ EXPORTS.mozilla.dom.workers.bindings += [
'EventTarget.h',
'FileReaderSync.h',
'Location.h',
'MessagePort.h',
'Navigator.h',
'SharedWorker.h',
'URL.h',
'WorkerFeature.h',
'WorkerMessagePort.h',
'XMLHttpRequest.h',
'XMLHttpRequestEventTarget.h',
'XMLHttpRequestUpload.h',
@ -37,12 +40,15 @@ CPP_SOURCES += [
'File.cpp',
'FileReaderSync.cpp',
'Location.cpp',
'MessagePort.cpp',
'Navigator.cpp',
'Principal.cpp',
'RuntimeService.cpp',
'ScriptLoader.cpp',
'SharedWorker.cpp',
'URL.cpp',
'Worker.cpp',
'WorkerMessagePort.cpp',
'WorkerPrivate.cpp',
'WorkerScope.cpp',
'XMLHttpRequest.cpp',

View File

@ -8,4 +8,3 @@ MOCHITEST_FILES += \
test_xhr_timeout.html \
$(NULL)
endif

View File

@ -22,6 +22,8 @@ support-files =
json_worker.js
location_worker.js
longThread_worker.js
multi_sharedWorker_frame.html
multi_sharedWorker_sharedWorker.js
navigator_worker.js
newError_worker.js
recursion_worker.js
@ -30,6 +32,7 @@ support-files =
relativeLoad_worker.js
relativeLoad_worker2.js
rvals_worker.js
sharedWorker_sharedWorker.js
simpleThread_worker.js
suspend_iframe.html
suspend_worker.js
@ -73,6 +76,8 @@ support-files =
[test_loadError.html]
[test_location.html]
[test_longThread.html]
[test_multi_sharedWorker.html]
[test_multi_sharedWorker_lifetimes.html]
[test_navigator.html]
[test_newError.html]
[test_recursion.html]
@ -81,6 +86,7 @@ support-files =
[test_resolveWorker-assignment.html]
[test_resolveWorker.html]
[test_rvals.html]
[test_sharedWorker.html]
[test_simpleThread.html]
[test_suspend.html]
[test_terminate.html]

View File

@ -0,0 +1,52 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for SharedWorker</title>
</head>
<body>
<script type="text/javascript;version=1.7">
"use strict";
function debug(message) {
if (typeof(message) != "string") {
throw new Error("debug() only accepts strings!");
}
parent.postMessage(message, "*");
}
let worker;
window.addEventListener("message", function(event) {
if (!worker) {
worker = new SharedWorker("multi_sharedWorker_sharedWorker.js",
"FrameWorker");
worker.onerror = function(event) {
debug("Worker error: " + event.message);
event.preventDefault();
let data = {
type: "error",
message: event.message,
filename: event.filename,
lineno: event.lineno,
isErrorEvent: event instanceof ErrorEvent
};
parent.postMessage(data, "*");
};
worker.port.onmessage = function(event) {
debug("Worker message: " + JSON.stringify(event.data));
parent.postMessage(event.data, "*");
};
}
debug("Posting message: " + JSON.stringify(event.data));
worker.port.postMessage(event.data);
});
</script>
</body>
</html>

View File

@ -0,0 +1,72 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
if (self.name != "FrameWorker") {
throw new Error("Bad worker name: " + self.name);
}
var registeredPorts = [];
var errorCount = 0;
var storedData;
self.onconnect = function(event) {
var port = event.ports[0];
if (registeredPorts.length) {
var data = {
type: "connect"
};
registeredPorts.forEach(function(registeredPort) {
registeredPort.postMessage(data);
});
}
port.onmessage = function(event) {
switch (event.data.command) {
case "start":
break;
case "error":
throw new Error("Expected");
case "store":
storedData = event.data.data;
break;
case "retrieve":
var data = {
type: "result",
data: storedData
};
port.postMessage(data);
break;
default:
throw new Error("Unknown command '" + error.data.command + "'");
}
};
registeredPorts.push(port);
};
self.onerror = function(message, filename, lineno) {
if (!errorCount++) {
var data = {
type: "worker-error",
message: message,
filename: filename,
lineno: lineno
};
registeredPorts.forEach(function (registeredPort) {
registeredPort.postMessage(data);
});
// Prevent the error from propagating the first time only.
return true;
}
};

View File

@ -0,0 +1,82 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
if (!("self" in this)) {
throw new Error("No 'self' exists on SharedWorkerGlobalScope!");
}
if (this !== self) {
throw new Error("'self' not equal to global object!");
}
if (!(self instanceof SharedWorkerGlobalScope)) {
throw new Error("self not a SharedWorkerGlobalScope instance!");
}
var propsToCheck = [
"location",
"navigator",
"close",
"importScripts",
"setTimeout",
"clearTimeout",
"setInterval",
"clearInterval",
"dump",
"atob",
"btoa"
];
for (var index = 0; index < propsToCheck.length; index++) {
var prop = propsToCheck[index];
if (!(prop in self)) {
throw new Error("SharedWorkerGlobalScope has no '" + prop + "' property!");
}
}
onconnect = function(event) {
if (!(event instanceof WorkerMessageEvent)) {
throw new Error("'connect' event is not a WorkerMessageEvent!");
}
if (!("ports" in event)) {
throw new Error("'connect' event doesn't have a 'ports' property!");
}
if (!Array.isArray(event.ports)) {
throw new Error("'connect' event has 'ports' property that isn't an " +
"Array!");
}
if (event.ports.length != 1) {
throw new Error("'connect' event has a 'ports' property with length '" +
event.ports.length + "'!");
}
if (!event.ports[0]) {
throw new Error("'connect' event has a null 'ports[0]' property!");
}
if (!(event.ports[0] instanceof WorkerMessagePort)) {
throw new Error("'connect' event has a 'ports[0]' property that isn't a " +
"MessagePort!");
}
if (event.data) {
throw new Error("'connect' event has data: " + event.data);
}
event.ports[0].onmessage = function(event) {
if (!(event instanceof WorkerMessageEvent)) {
throw new Error("'message' event is not a WorkerMessageEvent!");
}
if (!("ports" in event)) {
throw new Error("'message' event doesn't have a 'ports' property!");
}
if (!Array.isArray(event.ports)) {
throw new Error("'message' event has 'ports' property that isn't an " +
"Array!");
}
if (event.ports.length) {
throw new Error("'message' event has a 'ports' property with length '" +
event.ports.length + "'!");
}
event.target.postMessage(event.data);
throw new Error(event.data);
};
};

View File

@ -0,0 +1,251 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for SharedWorker</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
<script class="testbody" type="text/javascript;version=1.7">
"use strict";
const swPref = "dom.workers.sharedWorkers.enabled";
const basePath =
location.pathname.substring(0,
location.pathname.lastIndexOf("/") + 1);
const baseURL = location.origin + basePath;
const frameRelativeURL = "multi_sharedWorker_frame.html";
const frameAbsoluteURL = baseURL + frameRelativeURL;
const workerAbsoluteURL =
baseURL + "multi_sharedWorker_sharedWorker.js";
const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
const errorMessage = "Error: Expected";
const errorLineno = 34;
let testGenerator = (function() {
SimpleTest.waitForExplicitFinish();
ok(!("SharedWorker" in window), "No SharedWorker without pref set");
ok(!("WorkerMessagePort" in window),
"No WorkerMessagePort without pref set");
SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, sendToGenerator);
yield undefined;
window.addEventListener("message", function(event) {
if (typeof(event.data) == "string") {
info(event.data);
} else {
sendToGenerator(event);
}
});
let frame1 = document.getElementById("frame1");
frame1.src = frameRelativeURL;
frame1.onload = sendToGenerator;
yield undefined;
frame1 = frame1.contentWindow;
let frame2 = document.getElementById("frame2");
frame2.src = frameAbsoluteURL;
frame2.onload = sendToGenerator;
yield undefined;
frame2 = frame2.contentWindow;
let data = {
command: "start"
};
frame1.postMessage(data, "*");
frame2.postMessage(data, "*");
let event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame1, "First window got the event");
is(event.data.type, "connect", "Got a connect message");
data = {
command: "retrieve"
};
frame1.postMessage(data, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame1, "First window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, undefined, "No data stored yet");
frame2.postMessage(data, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame2, "Second window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, undefined, "No data stored yet");
data = {
command: "store",
data: storedData
};
frame2.postMessage(data, "*");
data = {
command: "retrieve"
};
frame1.postMessage(data, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame1, "First window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, storedData, "Got stored data");
// This will generate two MessageEvents, one for each window.
let sawFrame1Error = false;
let sawFrame2Error = false;
data = {
command: "error"
};
frame1.postMessage(data, "*");
// First event.
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.data.type, "worker-error", "Got an error message");
is(event.data.message, errorMessage, "Got correct error message");
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
is(event.data.lineno, errorLineno, "Got correct lineno");
if (event.source == frame1) {
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
sawFrame1Error = true;
} else if (event.source == frame2) {
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
sawFrame2Error = true;
} else {
ok(false, "Saw error from unknown window");
}
// Second event
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.data.type, "worker-error", "Got an error message");
is(event.data.message, errorMessage, "Got correct error message");
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
is(event.data.lineno, errorLineno, "Got correct lineno");
if (event.source == frame1) {
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
sawFrame1Error = true;
} else if (event.source == frame2) {
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
sawFrame2Error = true;
} else {
ok(false, "Saw error from unknown window");
}
is(sawFrame1Error, true, "Saw error for frame1");
is(sawFrame2Error, true, "Saw error for frame2");
// This will generate two MessageEvents, one for each window.
sawFrame1Error = false;
sawFrame2Error = false;
data = {
command: "error"
};
frame1.postMessage(data, "*");
// First event.
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.data.type, "error", "Got an error message");
is(event.data.message, errorMessage, "Got correct error message");
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
is(event.data.lineno, errorLineno, "Got correct lineno");
is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
if (event.source == frame1) {
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
sawFrame1Error = true;
} else if (event.source == frame2) {
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
sawFrame2Error = true;
} else {
ok(false, "Saw error from unknown window");
}
// Second event
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.data.type, "error", "Got an error message");
is(event.data.message, errorMessage, "Got correct error message");
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
is(event.data.lineno, errorLineno, "Got correct lineno");
is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
if (event.source == frame1) {
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
sawFrame1Error = true;
} else if (event.source == frame2) {
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
sawFrame2Error = true;
} else {
ok(false, "Saw error from unknown window");
}
is(sawFrame1Error, true, "Saw error for frame1");
is(sawFrame2Error, true, "Saw error for frame2");
// Try a shared worker in a different origin.
frame1 = document.getElementById("frame1");
frame1.src = "http://example.org" + basePath + frameRelativeURL;
frame1.onload = sendToGenerator;
yield undefined;
frame1 = frame1.contentWindow;
data = {
command: "retrieve"
};
frame1.postMessage(data, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame1, "First window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, undefined, "No data stored yet");
frame2.postMessage(data, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame2, "First window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, storedData, "Got stored data");
window.removeEventListener("message", sendToGenerator);
SimpleTest.finish();
yield undefined;
})();
let sendToGenerator = testGenerator.send.bind(testGenerator);
</script>
</head>
<body onload="testGenerator.next();">
<iframe id="frame1"></iframe>
<iframe id="frame2"></iframe>
</body>
</html>

View File

@ -0,0 +1,151 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for SharedWorker</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
<script class="testbody" type="text/javascript;version=1.7">
"use strict";
const swPref = "dom.workers.sharedWorkers.enabled";
const bfCachePref = "browser.sessionhistory.cache_subframes";
const frameRelativeURL = "multi_sharedWorker_frame.html";
const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
let testGenerator = (function() {
SimpleTest.waitForExplicitFinish();
ok(!("SharedWorker" in window), "No SharedWorker without pref set");
ok(!("WorkerMessagePort" in window),
"No WorkerMessagePort without pref set");
SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, sendToGenerator);
yield undefined;
window.addEventListener("message", function(event) {
if (typeof(event.data) == "string") {
info(event.data);
} else {
sendToGenerator(event);
}
});
let frame = document.getElementById("frame");
frame.src = frameRelativeURL;
frame.onload = sendToGenerator;
yield undefined;
frame = frame.contentWindow;
frame.postMessage({ command: "retrieve" }, "*");
let event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame, "Correct window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, undefined, "No data stored yet");
frame.postMessage({ command: "store", data: storedData }, "*");
frame.postMessage({ command: "retrieve" }, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame, "Correct window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, storedData, "Got stored data");
// Navigate when the bfcache is disabled.
info("Navigating to about:blank");
let frame = document.getElementById("frame");
frame.onload = sendToGenerator;
frame.src = "about:blank";
frame.contentWindow.document.body.offsetTop;
yield undefined;
info("Navigating to " + frameRelativeURL);
frame.src = frameRelativeURL;
frame.contentWindow.document.body.offsetTop;
yield undefined;
frame = frame.contentWindow;
frame.postMessage({ command: "retrieve" }, "*");
let event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame, "Correct window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, undefined, "No data stored");
frame.postMessage({ command: "store", data: storedData }, "*");
frame.postMessage({ command: "retrieve" }, "*");
event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame, "Correct window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, storedData, "Got stored data");
info("Enabling '" + bfCachePref + "' pref");
SpecialPowers.pushPrefEnv({ set: [[bfCachePref, true]] },
sendToGenerator);
yield undefined;
// Navigate when the bfcache is enabled.
let frame = document.getElementById("frame");
frame.onload = sendToGenerator;
info("Navigating to about:blank");
frame.src = "about:blank";
frame.contentWindow.document.body.offsetTop;
yield undefined;
for (let i = 0; i < 3; i++) {
info("Running GC");
SpecialPowers.gc();
info("Waiting the event queue to clear");
SpecialPowers.executeSoon(sendToGenerator);
yield undefined;
}
info("Navigating to " + frameRelativeURL);
frame.src = frameRelativeURL;
frame.contentWindow.document.body.offsetTop;
yield undefined;
frame = frame.contentWindow;
frame.postMessage({ command: "retrieve" }, "*");
let event = yield undefined;
ok(event instanceof MessageEvent, "Got a MessageEvent");
is(event.source, frame, "Correct window got the event");
is(event.data.type, "result", "Got a result message");
is(event.data.data, storedData, "Still have data stored");
info("Resetting '" + bfCachePref + "' pref");
SpecialPowers.popPrefEnv(sendToGenerator);
yield undefined;
window.removeEventListener("message", sendToGenerator);
SimpleTest.finish();
yield undefined;
})();
let sendToGenerator = testGenerator.send.bind(testGenerator);
</script>
</head>
<body onload="testGenerator.next();">
<iframe id="frame"></iframe>
</body>
</html>

View File

@ -0,0 +1,76 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for SharedWorker</title>
<script src="/tests/SimpleTest/SimpleTest.js">
</script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script class="testbody">
"use strict";
const swPref = "dom.workers.sharedWorkers.enabled";
const href = window.location.href;
const filename = "sharedWorker_sharedWorker.js";
const sentMessage = "ping";
const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) +
filename;
const errorLine = 80;
const errorColumn = 0;
ok(!("SharedWorker" in window), "No SharedWorker without pref set");
ok(!("WorkerMessagePort" in window),
"No WorkerMessagePort without pref set");
SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, function() {
var worker = new SharedWorker(filename);
ok(worker instanceof SharedWorker, "Got SharedWorker instance");
ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'");
ok(worker.port instanceof WorkerMessagePort,
"Shared worker has MessagePort");
var receivedMessage;
var receivedError;
worker.port.onmessage = function(event) {
ok(event instanceof MessageEvent, "Got a MessageEvent");
ok(event.target === worker.port,
"MessageEvent has correct 'target' property");
is(event.data, sentMessage, "Got correct message");
ok(receivedMessage === undefined, "Haven't gotten message yet");
ok(receivedError === undefined, "Haven't gotten error yet");
receivedMessage = event.data;
};
worker.onerror = function(event) {
ok(event instanceof ErrorEvent, "Got an ErrorEvent");
is(event.message, "Error: " + sentMessage, "Got correct error");
is(event.filename, errorFilename, "Got correct filename");
is(event.lineno, errorLine, "Got correct lineno");
is(event.column, errorColumn, "Got correct column");
ok(receivedMessage !== undefined, "Got message already");
ok(receivedError === undefined, "Haven't gotten error yet");
receivedError = event.message;
event.preventDefault();
SimpleTest.finish();
};
worker.port.postMessage(sentMessage);
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -103,6 +103,9 @@ pref("dom.workers.enabled", true);
// The number of workers per domain allowed to run concurrently.
pref("dom.workers.maxPerDomain", 20);
// Whether or not Shared Web Workers are enabled.
pref("dom.workers.sharedWorkers.enabled", false);
// Whether nonzero values can be returned from performance.timing.*
pref("dom.enable_performance", true);