Bug 919885: Move Worker to WebIDL and thread-agnostic event dispatch. r=bent,bz

This commit is contained in:
Kyle Huey 2013-11-05 22:16:24 +08:00
parent 3e9b1fbaae
commit e034802efa
19 changed files with 545 additions and 1029 deletions

View File

@ -141,9 +141,6 @@
// Drag and drop
#include "nsIDOMDataTransfer.h"
// Workers
#include "mozilla/dom/workers/Workers.h"
#include "nsIDOMFile.h"
#include "nsDOMBlobBuilder.h" // nsDOMMultipartFile
@ -196,7 +193,6 @@
using namespace mozilla;
using namespace mozilla::dom;
using mozilla::dom::workers::ResolveWorkerClasses;
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@ -3585,16 +3581,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
return NS_OK;
}
// Handle resolving if id refers to a name resolved by DOM worker code.
JS::Rooted<JSObject*> tmp(cx, nullptr);
if (!ResolveWorkerClasses(cx, obj, id, flags, &tmp)) {
return NS_ERROR_FAILURE;
}
if (tmp) {
*objp = tmp;
return NS_OK;
}
bool did_resolve = false;
nsresult rv = GlobalResolve(win, cx, obj, id, &did_resolve);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -29,6 +29,9 @@
# will not be made available on the main thread.
# * customFinalize - The native class will use a custom finalize hook
# (defaults to true for workers, false otherwise).
# * customWrapperManagement - The native class will be responsible for
# preserving its own wrapper (no AddProperty
# hook will be generated, defaults to false).
# * notflattened - The native type does not have nsIClassInfo, so when
# wrapping it the right IID needs to be passed in.
# * register - True if this binding should be registered. Defaults to true.
@ -201,6 +204,13 @@ DOMInterfaces = {
'concrete': False
},
'ChromeWorker': {
'headerFile': 'mozilla/dom/WorkerPrivate.h',
'nativeType': 'mozilla::dom::workers::ChromeWorkerPrivate',
'customFinalize': True,
'customWrapperManagement': True,
},
'DOMRectList': {
'headerFile': 'mozilla/dom/DOMRect.h',
'resultNotAddRefed': [ 'item' ]
@ -1443,6 +1453,16 @@ DOMInterfaces = {
'workers': True
}],
'Worker': {
'headerFile': 'mozilla/dom/WorkerPrivate.h',
'nativeType': 'mozilla::dom::workers::WorkerPrivate',
'implicitJSContext': [
'terminate',
],
'customFinalize': True,
'customWrapperManagement': True,
},
'WorkerLocation': {
'headerFile': 'mozilla/dom/workers/bindings/Location.h',
'workers': True,

View File

@ -61,6 +61,10 @@ def isTypeCopyConstructible(type):
(type.isDictionary() and
CGDictionary.isDictionaryCopyConstructible(type.inner)))
def wantsAddProperty(desc):
return desc.concrete and not desc.nativeOwnership == 'worker' and \
desc.wrapperCache and not desc.customWrapperManagement
class CGThing():
"""
Abstract base class for things that spit out code.
@ -215,7 +219,7 @@ static const DOMJSClass Class = {
};
""" % (self.descriptor.interface.identifier.name,
classFlags,
ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.nativeOwnership == 'worker' and self.descriptor.wrapperCache else 'JS_PropertyStub',
ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
enumerateHook, newResolveHook, FINALIZE_HOOK_NAME, callHook, traceHook,
CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
@ -707,9 +711,16 @@ class CGHeaders(CGWrapper):
if desc.interface.isExternal():
continue
def addHeaderForFunc(func):
if func is None:
return
# Include the right class header, which we can only do
# if this is a class member function.
if func is not None and "::" in func:
if not desc.headerIsDefault:
# An explicit header file was provided, assume that we know
# what we're doing.
return
if "::" in func:
# Strip out the function name and convert "::" to "/"
bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
for m in desc.interface.members:
@ -8144,7 +8155,7 @@ class CGDescriptor(CGThing):
cgThings.append(CGConstructNavigatorObject(descriptor))
if descriptor.concrete and not descriptor.proxy:
if not descriptor.nativeOwnership == 'worker' and descriptor.wrapperCache:
if wantsAddProperty(descriptor):
cgThings.append(CGAddPropertyHook(descriptor))
# Always have a finalize hook, regardless of whether the class

View File

@ -238,6 +238,7 @@ class Descriptor(DescriptorProvider):
headerDefault = self.nativeType
headerDefault = headerDefault.replace("::", "/") + ".h"
self.headerFile = desc.get('headerFile', headerDefault)
self.headerIsDefault = self.headerFile == headerDefault
if self.jsImplParent == self.nativeType:
self.jsImplParentHeader = self.headerFile
else:
@ -356,12 +357,17 @@ class Descriptor(DescriptorProvider):
(self.interface.identifier.name, self.nativeOwnership))
self.customTrace = (self.nativeOwnership == 'worker')
self.customFinalize = desc.get('customFinalize', self.nativeOwnership == 'worker')
self.customWrapperManagement = desc.get('customWrapperManagement', False)
if desc.get('wantsQI', None) != None:
self._wantsQI = desc.get('wantsQI', None)
self.wrapperCache = (not self.interface.isCallback() and
(self.nativeOwnership == 'worker' or
(self.nativeOwnership != 'owned' and
desc.get('wrapperCache', True))))
if self.customWrapperManagement and not self.wrapperCache:
raise TypeError("Descriptor for %s has customWrapperManagement "
"but is not wrapperCached." %
(self.interface.identifier.name))
def make_name(name):
return name + "_workers" if self.workers else name

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/ErrorResult.h"
#include "nsCOMPtr.h"
#include "nsGenericHTMLElement.h"
#include "nsWrapperCache.h"
// Forward declare this before we include TestCodeGenBinding.h, because that header relies on including

View File

@ -593,6 +593,7 @@ var interfaceNamesInGlobalScope =
"WheelEvent",
"Window",
"WindowUtils",
"Worker",
"XMLDocument",
"XMLHttpRequest",
"XMLHttpRequestUpload",

31
dom/webidl/Worker.webidl Normal file
View File

@ -0,0 +1,31 @@
/* -*- 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/.
*
* The origin of this IDL file is
* http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and Opera
* Software ASA.
* You are granted a license to use, reproduce and create derivative works of
* this document.
*/
[Constructor(DOMString scriptURL),
Func="mozilla::dom::workers::WorkerPrivate::WorkerAvailable"]
interface Worker : EventTarget {
void terminate();
[Throws]
void postMessage(any message, optional sequence<any> transfer);
attribute EventHandler onmessage;
};
Worker implements AbstractWorker;
[Constructor(DOMString scriptURL),
Func="mozilla::dom::workers::ChromeWorkerPrivate::WorkerAvailable"]
interface ChromeWorker : Worker {
};

View File

@ -415,6 +415,8 @@ WEBIDL_FILES = [
'WebSocket.webidl',
'WheelEvent.webidl',
'WifiOptions.webidl',
'Window.webidl',
'Worker.webidl',
'WorkerLocation.webidl',
'WorkerMessagePort.webidl',
'WorkerNavigator.webidl',

View File

@ -28,7 +28,10 @@
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/dom/AtomList.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/EventTargetBinding.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Preferences.h"
#include "mozilla/Util.h"
@ -48,7 +51,6 @@
#include "Events.h"
#include "SharedWorker.h"
#include "Worker.h"
#include "WorkerPrivate.h"
#ifdef MOZ_NUWA_PROCESS
@ -94,7 +96,6 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
#define MAX_IDLE_THREADS 20
#define PREF_WORKERS_PREFIX "dom.workers."
#define PREF_WORKERS_ENABLED PREF_WORKERS_PREFIX "enabled"
#define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
#define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
@ -162,9 +163,9 @@ jsid gStringIDs[ID_COUNT] = { JSID_VOID };
const char* gStringChars[] = {
"Worker",
"ChromeWorker",
"WorkerEvent",
"WorkerMessageEvent",
"WorkerErrorEvent"
"Event",
"MessageEvent",
"ErrorEvent"
// XXX Don't care about ProgressEvent since it should never leak to the main
// thread.
@ -982,12 +983,13 @@ public:
BEGIN_WORKERS_NAMESPACE
// Entry point for the DOM.
// Entry point for main thread non-window globals.
bool
ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
{
AssertIsOnMainThread();
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
// Make sure our strings are interned.
if (JSID_IS_VOID(gStringIDs[0])) {
@ -1003,51 +1005,28 @@ ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid
}
}
bool isChrome = false;
bool shouldResolve = false;
for (uint32_t i = 0; i < ID_COUNT; i++) {
if (gStringIDs[i] == aId) {
isChrome = nsContentUtils::IsCallerChrome();
// Don't resolve if this is ChromeWorker and we're not chrome. Otherwise
// always resolve.
shouldResolve = gStringIDs[ID_ChromeWorker] == aId ? isChrome : true;
shouldResolve = true;
break;
}
}
if (shouldResolve) {
// Don't do anything if workers are disabled.
if (!isChrome && !Preferences::GetBool(PREF_WORKERS_ENABLED)) {
aObjp.set(nullptr);
return true;
}
JSObject* eventTarget = EventTargetBinding_workers::GetProtoObject(aCx, aObj);
if (!eventTarget) {
return false;
}
JSObject* worker = worker::InitClass(aCx, aObj, eventTarget, true);
if (!worker) {
return false;
}
if (isChrome && !chromeworker::InitClass(aCx, aObj, worker, true)) {
return false;
}
if (!events::InitClasses(aCx, aObj, true)) {
return false;
}
aObjp.set(aObj);
if (!shouldResolve) {
aObjp.set(nullptr);
return true;
}
// Not resolved.
aObjp.set(nullptr);
if (!WorkerBinding::GetConstructorObject(aCx, aObj) ||
!ChromeWorkerBinding::GetConstructorObject(aCx, aObj) ||
!ErrorEventBinding::GetConstructorObject(aCx, aObj) ||
!MessageEventBinding::GetConstructorObject(aCx, aObj)) {
return false;
}
aObjp.set(aObj);
return true;
}
@ -2021,17 +2000,20 @@ RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
}
nsresult
RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal,
const nsAString& aScriptURL,
const nsAString& aName,
SharedWorker** aSharedWorker)
{
AssertIsOnMainThread();
MOZ_ASSERT(aCx);
MOZ_ASSERT(aWindow);
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(window);
JSContext* cx = aGlobal.GetContext();
WorkerPrivate::LoadInfo loadInfo;
nsresult rv = WorkerPrivate::GetLoadInfo(aCx, aWindow, nullptr, aScriptURL,
nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
false, &loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
@ -2041,7 +2023,7 @@ RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec);
NS_ENSURE_SUCCESS(rv, rv);
WorkerPrivate* workerPrivate = nullptr;
nsRefPtr<WorkerPrivate> workerPrivate;
{
MutexAutoLock lock(mMutex);
@ -2058,26 +2040,22 @@ RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
bool created = false;
if (!workerPrivate) {
nsRefPtr<WorkerPrivate> newWorkerPrivate =
WorkerPrivate::Create(aCx, JS::NullPtr(), nullptr, aScriptURL, false,
WorkerPrivate::WorkerTypeShared, aName, &loadInfo);
NS_ENSURE_TRUE(newWorkerPrivate, NS_ERROR_FAILURE);
if (!RegisterWorker(aCx, newWorkerPrivate)) {
NS_WARNING("Failed to register worker!");
return NS_ERROR_FAILURE;
}
ErrorResult rv;
workerPrivate =
WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
WorkerPrivate::WorkerTypeShared, aName,
&loadInfo, rv);
NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode());
created = true;
newWorkerPrivate.forget(&workerPrivate);
}
MOZ_ASSERT(workerPrivate->IsSharedWorker());
nsRefPtr<SharedWorker> sharedWorker =
new SharedWorker(aWindow, workerPrivate);
new SharedWorker(window, workerPrivate);
if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
if (!workerPrivate->RegisterSharedWorker(cx, sharedWorker)) {
NS_WARNING("Worker is unreachable, this shouldn't happen!");
sharedWorker->Close();
return NS_ERROR_FAILURE;
@ -2087,9 +2065,9 @@ RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
// worker already existed.
if (!created) {
nsTArray<WorkerPrivate*>* windowArray;
if (!mWindowMap.Get(aWindow, &windowArray)) {
if (!mWindowMap.Get(window, &windowArray)) {
windowArray = new nsTArray<WorkerPrivate*>(1);
mWindowMap.Put(aWindow, windowArray);
mWindowMap.Put(window, windowArray);
}
if (!windowArray->Contains(workerPrivate)) {

View File

@ -145,8 +145,9 @@ public:
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
nsresult
CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
const nsAString& aScriptURL, const nsAString& aName,
CreateSharedWorker(const GlobalObject& aGlobal,
const nsAString& aScriptURL,
const nsAString& aName,
SharedWorker** aSharedWorker);
void

View File

@ -16,7 +16,6 @@
#include "MessagePort.h"
#include "RuntimeService.h"
#include "Worker.h"
#include "WorkerPrivate.h"
using mozilla::dom::Optional;
@ -67,9 +66,6 @@ SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
{
AssertIsOnMainThread();
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(window);
RuntimeService* rts = RuntimeService::GetOrCreateService();
if (!rts) {
aRv = NS_ERROR_NOT_AVAILABLE;
@ -82,7 +78,7 @@ SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
}
nsRefPtr<SharedWorker> sharedWorker;
nsresult rv = rts->CreateSharedWorker(aCx, window, aScriptURL, name,
nsresult rv = rts->CreateSharedWorker(aGlobal, aScriptURL, name,
getter_AddRefs(sharedWorker));
if (NS_FAILED(rv)) {
aRv = rv;

View File

@ -28,7 +28,7 @@ class SharedWorker MOZ_FINAL : public nsDOMEventTargetHelper
typedef mozilla::ErrorResult ErrorResult;
typedef mozilla::dom::GlobalObject GlobalObject;
WorkerPrivate* mWorkerPrivate;
nsRefPtr<WorkerPrivate> mWorkerPrivate;
nsRefPtr<MessagePort> mMessagePort;
nsTArray<nsCOMPtr<nsIDOMEvent>> mSuspendedEvents;
uint64_t mSerial;
@ -83,7 +83,8 @@ public:
private:
// This class can only be created from the RuntimeService.
SharedWorker(nsPIDOMWindow* aWindow, WorkerPrivate* aWorkerPrivate);
SharedWorker(nsPIDOMWindow* aWindow,
WorkerPrivate* aWorkerPrivate);
// This class is reference-counted and will be destroyed from Release().
~SharedWorker();

View File

@ -1,678 +0,0 @@
/* -*- 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 "Worker.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/EventHandlerBinding.h"
#include "nsJSUtils.h"
#include "jsapi.h"
#include "EventTarget.h"
#include "RuntimeService.h"
#include "WorkerPrivate.h"
#include "WorkerInlines.h"
#define FUNCTION_FLAGS \
JSPROP_ENUMERATE
USING_WORKERS_NAMESPACE
using namespace mozilla::dom;
using mozilla::ErrorResult;
namespace {
class Worker
{
static const DOMJSClass sClass;
static const DOMIfaceAndProtoJSClass sProtoClass;
static const JSPropertySpec sProperties[];
static const JSFunctionSpec sFunctions[];
protected:
enum {
// The constructor function holds a WorkerPrivate* in its first reserved
// slot.
CONSTRUCTOR_SLOT_PARENT = 0
};
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,
bool aMainRuntime)
{
JS::Rooted<JSObject*> proto(aCx,
js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
Construct, 0, sProperties, sFunctions,
nullptr, nullptr));
if (!proto) {
return nullptr;
}
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
if (!aMainRuntime) {
WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
parent->AssertIsOnWorkerThread();
JSObject* constructor = JS_GetConstructor(aCx, proto);
if (!constructor)
return nullptr;
js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
PRIVATE_TO_JSVAL(parent));
}
return proto;
}
static WorkerPrivate*
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
static JSObject*
Create(JSContext* aCx, WorkerPrivate* aParentObj, const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerPrivate::WorkerType aWorkerType,
const nsAString& aSharedWorkerName);
protected:
static bool
ConstructInternal(JSContext* aCx, JS::CallArgs aArgs, bool aIsChromeWorker)
{
if (!aArgs.length()) {
JS_ReportError(aCx, "Constructor requires at least one argument!");
return false;
}
JS::RootedString scriptURLStr(aCx, JS_ValueToString(aCx, aArgs[0]));
if (!scriptURLStr) {
return false;
}
nsDependentJSString scriptURL;
if (!scriptURL.init(aCx, scriptURLStr)) {
return false;
}
JS::Rooted<JS::Value> priv(aCx,
js::GetFunctionNativeReserved(&aArgs.callee(), CONSTRUCTOR_SLOT_PARENT));
WorkerPrivate* parent;
if (priv.isUndefined()) {
parent = nullptr;
} else {
parent = static_cast<WorkerPrivate*>(priv.get().toPrivate());
parent->AssertIsOnWorkerThread();
}
JS::Rooted<JSObject*> obj(aCx,
Create(aCx, parent, scriptURL, aIsChromeWorker,
WorkerPrivate::WorkerTypeDedicated, EmptyString()));
if (!obj) {
return false;
}
aArgs.rval().setObject(*obj);
return true;
}
private:
// No instance of this class should ever be created so these are explicitly
// left without an implementation to prevent linking in case someone tries to
// make one.
Worker();
~Worker();
static bool
IsWorker(JS::Handle<JS::Value> v)
{
return v.isObject() && ClassIsWorker(JS_GetClass(&v.toObject()));
}
static bool
GetEventListener(JSContext* aCx, const JS::CallArgs aArgs,
const nsAString &aNameStr)
{
WorkerPrivate* worker =
GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
NS_ConvertUTF16toUTF8(aNameStr).get());
MOZ_ASSERT(worker);
ErrorResult rv;
nsRefPtr<EventHandlerNonNull> handler =
worker->GetEventListener(Substring(aNameStr, 2), rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to get listener!");
return false;
}
if (!handler) {
aArgs.rval().setNull();
} else {
aArgs.rval().setObject(*handler->Callable());
}
return true;
}
static bool
GetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
{
return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
}
static bool
GetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsWorker, GetOnerrorImpl>(aCx, args);
}
static bool
GetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
{
return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
}
static bool
GetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsWorker, GetOnmessageImpl>(aCx, args);
}
static bool
SetEventListener(JSContext* aCx, JS::CallArgs aArgs,
const nsAString& aNameStr)
{
WorkerPrivate* worker =
GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
NS_ConvertUTF16toUTF8(aNameStr).get());
MOZ_ASSERT(worker);
JS::Rooted<JSObject*> listener(aCx);
if (!JS_ValueToObject(aCx, aArgs.get(0), &listener)) {
return false;
}
nsRefPtr<EventHandlerNonNull> handler;
if (listener && JS_ObjectIsCallable(aCx, listener)) {
handler = new EventHandlerNonNull(listener);
} else {
handler = nullptr;
}
ErrorResult rv;
worker->SetEventListener(Substring(aNameStr, 2), handler, rv);
if (rv.Failed()) {
JS_ReportError(aCx, "Failed to set listener!");
return false;
}
aArgs.rval().setUndefined();
return true;
}
static bool
SetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
{
return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
}
static bool
SetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsWorker, SetOnerrorImpl>(aCx, args);
}
static bool
SetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
{
return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
}
static bool
SetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return JS::CallNonGenericMethod<IsWorker, SetOnmessageImpl>(aCx, args);
}
static bool
Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return ConstructInternal(aCx, args, false);
}
static void
Finalize(JSFreeOp* aFop, JSObject* aObj)
{
JS_ASSERT(JS_GetClass(aObj) == Class());
WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
if (worker) {
worker->_finalize(aFop);
}
}
static void
Trace(JSTracer* aTrc, JSObject* aObj)
{
JS_ASSERT(JS_GetClass(aObj) == Class());
WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
if (worker) {
worker->_trace(aTrc);
}
}
static bool
Terminate(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
if (!obj) {
return false;
}
const char* name = sFunctions[0].name;
WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
if (!worker) {
return !JS_IsExceptionPending(aCx);
}
if (!worker->Terminate(aCx)) {
return false;
}
JS_RVAL(aCx, aVp).setUndefined();
return true;
}
static bool
PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
{
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
if (!obj) {
return false;
}
const char* name = sFunctions[1].name;
WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
if (!worker) {
return !JS_IsExceptionPending(aCx);
}
JS::Rooted<JS::Value> message(aCx);
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/v",
message.address(), transferable.address())) {
return false;
}
if (!worker->PostMessage(aCx, message, transferable)) {
return false;
}
JS_RVAL(aCx, aVp).setUndefined();
return true;
}
};
const DOMJSClass Worker::sClass = {
{
"Worker",
JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3) |
JSCLASS_IMPLEMENTS_BARRIERS,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
nullptr, nullptr, nullptr, nullptr, Trace
},
{
INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
false,
&sWorkerNativePropertyHooks
}
};
const DOMIfaceAndProtoJSClass Worker::sProtoClass = {
{
// XXXbz we use "Worker" here to match sClass so that we can
// js::InitClassWithReserved this JSClass and then call
// JS_NewObject with our sClass and have it find the right
// prototype.
"Worker",
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 Worker]",
prototypes::id::_ID_Count,
0
};
const JSPropertySpec Worker::sProperties[] = {
JS_PSGS("onerror", GetOnerror, SetOnerror, JSPROP_ENUMERATE),
JS_PSGS("onmessage", GetOnmessage, SetOnmessage, JSPROP_ENUMERATE),
JS_PS_END
};
const JSFunctionSpec Worker::sFunctions[] = {
JS_FN("terminate", Terminate, 0, FUNCTION_FLAGS),
JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
JS_FS_END
};
class ChromeWorker : public Worker
{
static const DOMJSClass sClass;
static const DOMIfaceAndProtoJSClass sProtoClass;
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,
bool aMainRuntime)
{
JS::Rooted<JSObject*> proto(aCx,
js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
Construct, 0, nullptr, nullptr, nullptr,
nullptr));
if (!proto) {
return nullptr;
}
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
if (!aMainRuntime) {
WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
parent->AssertIsOnWorkerThread();
JSObject* constructor = JS_GetConstructor(aCx, proto);
if (!constructor)
return nullptr;
js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
PRIVATE_TO_JSVAL(parent));
}
return proto;
}
private:
// No instance of this class should ever be created so these are explicitly
// left without an implementation to prevent linking in case someone tries to
// make one.
ChromeWorker();
~ChromeWorker();
static WorkerPrivate*
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
{
if (aObj) {
const JSClass* classPtr = JS_GetClass(aObj);
if (classPtr == Class()) {
return UnwrapDOMObject<WorkerPrivate>(aObj);
}
}
return Worker::GetInstancePrivate(aCx, aObj, aFunctionName);
}
static bool
Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
return ConstructInternal(aCx, args, true);
}
static void
Finalize(JSFreeOp* aFop, JSObject* aObj)
{
JS_ASSERT(JS_GetClass(aObj) == Class());
WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
if (worker) {
worker->_finalize(aFop);
}
}
static void
Trace(JSTracer* aTrc, JSObject* aObj)
{
JS_ASSERT(JS_GetClass(aObj) == Class());
WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
if (worker) {
worker->_trace(aTrc);
}
}
};
const DOMJSClass ChromeWorker::sClass = {
{ "ChromeWorker",
JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3) |
JSCLASS_IMPLEMENTS_BARRIERS,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
nullptr, nullptr, nullptr, nullptr, Trace,
},
{
INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
false,
&sWorkerNativePropertyHooks
}
};
const DOMIfaceAndProtoJSClass ChromeWorker::sProtoClass = {
{
// XXXbz we use "ChromeWorker" here to match sClass so that we can
// js::InitClassWithReserved this JSClass and then call
// JS_NewObject with our sClass and have it find the right
// prototype.
"ChromeWorker",
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 ChromeWorker]",
prototypes::id::_ID_Count,
0
};
WorkerPrivate*
Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
const char* aFunctionName)
{
const JSClass* classPtr = JS_GetClass(aObj);
if (ClassIsWorker(classPtr)) {
return UnwrapDOMObject<WorkerPrivate>(aObj);
}
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
JSMSG_INCOMPATIBLE_PROTO, Class()->name,
aFunctionName, classPtr->name);
return nullptr;
}
JSObject*
Worker::Create(JSContext* aCx, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
WorkerPrivate::WorkerType aWorkerType,
const nsAString& aSharedWorkerName)
{
MOZ_ASSERT_IF(aWorkerType == WorkerPrivate::WorkerTypeShared,
!aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(aWorkerType != WorkerPrivate::WorkerTypeShared,
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,
aWorkerType, 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
namespace worker {
JSObject*
InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
bool aMainRuntime)
{
return Worker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
}
} // namespace worker
WorkerCrossThreadDispatcher*
GetWorkerCrossThreadDispatcher(JSContext* aCx, jsval aWorker)
{
if (JSVAL_IS_PRIMITIVE(aWorker)) {
return nullptr;
}
WorkerPrivate* w =
Worker::GetInstancePrivate(aCx, JSVAL_TO_OBJECT(aWorker),
"GetWorkerCrossThreadDispatcher");
if (!w) {
return nullptr;
}
return w->GetCrossThreadDispatcher();
}
namespace chromeworker {
bool
InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
bool aMainRuntime)
{
return !!ChromeWorker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
}
} // namespace chromeworker
bool
ClassIsWorker(const JSClass* aClass)
{
return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
}
bool
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
return false;
}
END_WORKERS_NAMESPACE

View File

@ -1,35 +0,0 @@
/* -*- 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_worker_h__
#define mozilla_dom_workers_worker_h__
#include "Workers.h"
BEGIN_WORKERS_NAMESPACE
namespace worker {
JSObject*
InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
bool aMainRuntime);
} // namespace worker
namespace chromeworker {
bool
InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
bool aMainRuntime);
} // namespace chromeworker
bool
ClassIsWorker(const JSClass* aClass);
END_WORKERS_NAMESPACE
#endif /* mozilla_dom_workers_worker_h__ */

View File

@ -13,6 +13,7 @@
#include "nsIDOMDOMException.h"
#include "nsIDOMEvent.h"
#include "nsIDOMFile.h"
#include "nsIDOMMessageEvent.h"
#include "nsIDocument.h"
#include "nsIDocShell.h"
#include "nsIMemoryReporter.h"
@ -35,13 +36,17 @@
#include "mozilla/Likely.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/ImageDataBinding.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/Preferences.h"
#include "mozilla/Util.h"
#include "nsAlgorithm.h"
#include "nsContentUtils.h"
#include "nsCxPusher.h"
#include "nsError.h"
#include "nsEventDispatcher.h"
#include "nsDOMMessageEvent.h"
#include "nsDOMJSUtils.h"
#include "nsHostObjectProtocolHandler.h"
@ -66,7 +71,6 @@
#include "RuntimeService.h"
#include "ScriptLoader.h"
#include "SharedWorker.h"
#include "Worker.h"
#include "WorkerFeature.h"
#include "WorkerMessagePort.h"
#include "WorkerScope.h"
@ -77,6 +81,8 @@
// GC will run five seconds after the last event is processed.
#define IDLE_GC_TIMER_DELAY_MS 5000
#define PREF_WORKERS_ENABLED "dom.workers.enabled"
using mozilla::InternalScriptErrorEvent;
using mozilla::MutexAutoLock;
using mozilla::TimeDuration;
@ -888,8 +894,6 @@ public:
{
MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker());
bool mainRuntime;
JS::Rooted<JSObject*> target(aCx);
if (mTarget == ParentThread) {
// Don't fire this event if the JS object has been disconnected from the
// private object.
@ -905,39 +909,65 @@ public:
mClonedObjects);
}
mainRuntime = !aWorkerPrivate->GetParent();
target = aWorkerPrivate->GetJSObject();
NS_ASSERTION(target, "Must have a target!");
if (aWorkerPrivate->IsSuspended()) {
aWorkerPrivate->QueueRunnable(this);
return true;
}
aWorkerPrivate->AssertInnerWindowIsCorrect();
}
else {
NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
"Badness!");
if (mToMessagePort) {
WorkerMessagePort* port =
aWorkerPrivate->GetMessagePort(mMessagePortSerial);
if (!port) {
// Must have been closed already.
return true;
}
return port->MaybeDispatchEvent(aCx, mBuffer, mClonedObjects);
// Release reference to objects that were AddRef'd for
// cloning into worker when array goes out of scope.
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
clonedObjects.SwapElements(mClonedObjects);
JS::Rooted<JS::Value> messageData(aCx);
if (!mBuffer.read(aCx, messageData.address(),
workers::WorkerStructuredCloneCallbacks(!aWorkerPrivate->GetParent()))) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
mainRuntime = false;
target = JS::CurrentGlobalOrNull(aCx);
nsRefPtr<nsDOMMessageEvent> event =
new nsDOMMessageEvent(aWorkerPrivate, nullptr, nullptr);
nsresult rv =
event->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
true /* cancelable */,
messageData,
EmptyString(),
EmptyString(),
nullptr);
if (NS_FAILED(rv)) {
xpc::Throw(aCx, rv);
return false;
}
event->SetTrusted(true);
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
nsEventStatus dummy = nsEventStatus_eIgnore;
aWorkerPrivate->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
return true;
}
MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
if (mToMessagePort) {
nsRefPtr<WorkerMessagePort> port =
aWorkerPrivate->GetMessagePort(mMessagePortSerial);
if (!port) {
// Must have been closed already.
return true;
}
return port->MaybeDispatchEvent(aCx, mBuffer, mClonedObjects);
}
JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
NS_ASSERTION(target, "This should never be null!");
JS::Rooted<JSObject*> event(aCx,
CreateMessageEvent(aCx, mBuffer, mClonedObjects, mainRuntime));
CreateMessageEvent(aCx, mBuffer, mClonedObjects, false));
if (!event) {
return false;
}
@ -978,6 +1008,17 @@ public:
{
return aWorkerPrivate->NotifyInternal(aCx, mStatus);
}
void
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aDispatchResult)
{
if (!aDispatchResult) {
// We couldn't dispatch to the worker, which means it's already dead.
// Undo the busy count modification.
aWorkerPrivate->ModifyBusyCount(aCx, false);
}
}
};
class CloseRunnable MOZ_FINAL : public WorkerControlRunnable
@ -1065,7 +1106,7 @@ public:
return true;
}
JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetJSObject());
JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
uint64_t innerWindowId;
bool fireAtScope = true;
@ -1094,9 +1135,10 @@ public:
innerWindowId = aWorkerPrivate->GetInnerWindowId();
}
return ReportErrorRunnable::ReportError(aCx, parent, fireAtScope, target,
mMessage, mFilename, mLine,
mLineNumber, mColumnNumber, mFlags,
return ReportErrorRunnable::ReportError(aCx, parent, fireAtScope,
aWorkerPrivate, mMessage,
mFilename, mLine, mLineNumber,
mColumnNumber, mFlags,
mErrorNumber, innerWindowId);
}
@ -1105,14 +1147,17 @@ public:
WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
}
// aWorkerPrivate is the worker thread we're on (or the main thread, if null)
// aTarget is the worker object that we are going to fire an error at
// (if any).
static bool
ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aFireAtScope, JSObject* aTarget, const nsString& aMessage,
const nsString& aFilename, const nsString& aLine,
uint32_t aLineNumber, uint32_t aColumnNumber, uint32_t aFlags,
bool aFireAtScope, WorkerPrivate* aTarget,
const nsString& aMessage, const nsString& aFilename,
const nsString& aLine, uint32_t aLineNumber,
uint32_t aColumnNumber, uint32_t aFlags,
uint32_t aErrorNumber, uint64_t aInnerWindowId)
{
JS::Rooted<JSObject*> target(aCx, aTarget);
if (aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
}
@ -1136,27 +1181,28 @@ public:
// they show up in the error console.
if (!JSREPORT_IS_WARNING(aFlags)) {
// First fire an ErrorEvent at the worker.
if (target) {
JS::Rooted<JSObject*> event(aCx,
CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate));
if (!event) {
return false;
}
if (aTarget) {
ErrorEventInit init;
init.mMessage = aMessage;
init.mFilename = aFilename;
init.mLineno = aLineNumber;
init.mCancelable = true;
bool preventDefaultCalled;
if (!DispatchEventToTarget(aCx, target, event, &preventDefaultCalled)) {
return false;
}
nsRefPtr<ErrorEvent> event =
ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
if (preventDefaultCalled) {
nsEventStatus status = nsEventStatus_eIgnore;
aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
if (status == nsEventStatus_eConsumeNoDefault) {
return true;
}
}
// Now fire an event at the global object, but don't do that if the error
// code is too much recursion and this is the same script threw the error.
if (aFireAtScope && (target || aErrorNumber != JSMSG_OVER_RECURSED)) {
target = JS::CurrentGlobalOrNull(aCx);
if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
NS_ASSERTION(target, "This should never be null!");
bool preventDefaultCalled;
@ -1754,7 +1800,7 @@ WorkerRunnable::Run()
if (mTarget == WorkerThread) {
targetCompartmentObject = JS::CurrentGlobalOrNull(cx);
} else {
targetCompartmentObject = mWorkerPrivate->GetJSObject();
targetCompartmentObject = mWorkerPrivate->GetWrapper();
}
NS_ASSERTION(cx, "Must have a context!");
@ -2017,30 +2063,33 @@ NS_IMPL_ISUPPORTS1(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
template <class Derived>
WorkerPrivateParent<Derived>::WorkerPrivateParent(
JSContext* aCx,
JS::HandleObject aObject,
WorkerPrivate* aParent,
const nsAString& aScriptURL,
bool aIsChromeWorker,
WorkerType aWorkerType,
JSContext* aCx,
WorkerPrivate* aParent,
const nsAString& aScriptURL,
bool aIsChromeWorker,
WorkerType aWorkerType,
const nsAString& aSharedWorkerName,
LoadInfo& aLoadInfo)
: EventTarget(aParent ? aCx : nullptr), mMutex("WorkerPrivateParent Mutex"),
: mMutex("WorkerPrivateParent Mutex"),
mCondVar(mMutex, "WorkerPrivateParent CondVar"),
mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
mJSObject(aObject), mParent(aParent), mScriptURL(aScriptURL),
mParent(aParent), mScriptURL(aScriptURL),
mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
mParentStatus(Pending), mJSObjectRooted(false), mParentSuspended(false),
mParentStatus(Pending), mRooted(false), mParentSuspended(false),
mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
mWorkerType(aWorkerType)
{
MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
MOZ_ASSERT_IF(IsSharedWorker(), !aObject && !aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(!IsSharedWorker(), aObject && aSharedWorkerName.IsEmpty());
SetIsDOMBinding();
MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid() &&
NS_IsMainThread());
MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
if (aLoadInfo.mWindow) {
NS_ASSERTION(aLoadInfo.mWindow->IsInnerWindow(),
"Should have inner window here!");
AssertIsOnMainThread();
MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
"Should have inner window here!");
BindToOwner(aLoadInfo.mWindow);
}
mLoadInfo.StealFrom(aLoadInfo);
@ -2055,17 +2104,65 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
RuntimeService::GetDefaultJSSettings(mJSSettings);
}
if (IsDedicatedWorker()) {
SetIsDOMBinding();
SetWrapper(aObject);
}
}
template <class Derived>
WorkerPrivateParent<Derived>::~WorkerPrivateParent()
{
MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivateParent);
MOZ_ASSERT(!mRooted);
DropJSObjects(this);
}
template <class Derived>
NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, nsDOMEventTargetHelper)
template <class Derived>
NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, nsDOMEventTargetHelper)
template <class Derived>
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
// No new interfaces, just cycle collection.
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
template <class Derived>
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
nsDOMEventTargetHelper)
// Nothing else to traverse
// The various strong references in LoadInfo are managed manually and cannot
// be cycle collected.
tmp->AssertIsOnParentThread();
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
template <class Derived>
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
nsDOMEventTargetHelper)
tmp->AssertIsOnParentThread();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
template <class Derived>
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
nsDOMEventTargetHelper)
tmp->AssertIsOnParentThread();
NS_IMPL_CYCLE_COLLECTION_TRACE_END
template <class Derived>
JSObject*
WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope)
{
MOZ_ASSERT(!mIsSharedWorker,
"We should never wrap a WorkerPrivate for a SharedWorker");
AssertIsOnParentThread();
JSObject* obj = WorkerBinding::Wrap(aCx, aScope, ParentAsWorkerPrivate());
if (mRooted) {
PreserveWrapper(this);
}
return obj;
}
template <class Derived>
@ -2361,54 +2458,23 @@ WorkerPrivateParent<Derived>::SynchronizeAndResume(
return true;
}
template <class Derived>
void
WorkerPrivateParent<Derived>::_trace(JSTracer* aTrc)
{
// This should only happen on the parent thread but we can't assert that
// because it can also happen on the cycle collector thread when this is a
// top-level worker.
EventTarget::_trace(aTrc);
}
template <class Derived>
void
WorkerPrivateParent<Derived>::_finalize(JSFreeOp* aFop)
{
AssertIsOnParentThread();
MOZ_ASSERT(mJSObject);
MOZ_ASSERT(!mJSObjectRooted);
MOZ_ASSERT(!mRooted);
// Clear the JS object.
mJSObject = nullptr;
ClearWrapper();
// Ensure that we're held alive across the TerminatePrivate call, and then
// release the reference our wrapper held to us.
nsRefPtr<WorkerPrivateParent<Derived> > kungFuDeathGrip = dont_AddRef(this);
if (!TerminatePrivate(nullptr)) {
NS_WARNING("Failed to terminate!");
}
// Before calling through to the base class we need to grab another reference
// if we're on the main thread. Otherwise the base class' _Finalize method
// will call Release, and some of our members cannot be released during
// finalization. Of course, if those members are already gone then we can skip
// this mess...
WorkerPrivateParent<Derived>* extraSelfRef = nullptr;
if (!mParent && !mMainThreadObjectsForgotten) {
AssertIsOnMainThread();
NS_ADDREF(extraSelfRef = this);
}
EventTarget::_finalize(aFop);
if (extraSelfRef) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(extraSelfRef,
&WorkerPrivateParent<Derived>::Release);
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
NS_WARNING("Failed to proxy release, this will leak!");
}
}
}
template <class Derived>
@ -2437,18 +2503,14 @@ WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
if (aIncrease) {
if (mBusyCount++ == 0 && mJSObject) {
if (!RootJSObject(aCx, true)) {
return false;
}
if (mBusyCount++ == 0) {
Root(true);
}
return true;
}
if (--mBusyCount == 0 && mJSObject) {
if (!RootJSObject(aCx, false)) {
return false;
}
if (--mBusyCount == 0) {
Root(false);
bool shouldCancel;
{
@ -2465,27 +2527,29 @@ WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
}
template <class Derived>
bool
WorkerPrivateParent<Derived>::RootJSObject(JSContext* aCx, bool aRoot)
void
WorkerPrivateParent<Derived>::Root(bool aRoot)
{
AssertIsOnParentThread();
if (aRoot != mJSObjectRooted) {
if (aRoot) {
NS_ASSERTION(mJSObject, "Nothing to root?");
if (!JS_AddNamedObjectRoot(aCx, &mJSObject, "Worker root")) {
NS_WARNING("JS_AddNamedObjectRoot failed!");
return false;
}
}
else {
JS_RemoveObjectRoot(aCx, &mJSObject);
}
mJSObjectRooted = aRoot;
if (aRoot == mRooted) {
return;
}
return true;
if (aRoot) {
NS_ADDREF_THIS();
if (GetWrapperPreserveColor()) {
PreserveWrapper(this);
}
}
else {
if (GetWrapperPreserveColor()) {
ReleaseWrapper(this);
}
NS_RELEASE_THIS();
}
mRooted = aRoot;
}
template <class Derived>
@ -2515,20 +2579,21 @@ WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
}
template <class Derived>
bool
void
WorkerPrivateParent<Derived>::PostMessageInternal(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value> >& aTransferable,
bool aToMessagePort,
uint64_t aMessagePortSerial)
uint64_t aMessagePortSerial,
ErrorResult& aRv)
{
AssertIsOnParentThread();
{
MutexAutoLock lock(mMutex);
if (mParentStatus > Running) {
return true;
return;
}
}
@ -2552,18 +2617,34 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
}
}
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
JSObject* array =
JS_NewArrayObject(aCx, realTransferable.Length(),
const_cast<JS::Value*>(realTransferable.Elements()));
if (!array) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
transferable.setObject(*array);
}
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
JSAutoStructuredCloneBuffer buffer;
if (!buffer.write(aCx, aMessage, aTransferable, callbacks, &clonedObjects)) {
return false;
if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
nsRefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(ParentAsWorkerPrivate(),
WorkerRunnable::WorkerThread, buffer,
clonedObjects, aToMessagePort, aMessagePortSerial);
return runnable->Dispatch(aCx);
if (!runnable->Dispatch(aCx)) {
aRv.Throw(NS_ERROR_FAILURE);
}
}
template <class Derived>
@ -2577,23 +2658,8 @@ WorkerPrivateParent<Derived>::PostMessageToMessagePort(
{
AssertIsOnMainThread();
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
JSObject* array =
JS_NewArrayObject(aCx, realTransferable.Length(),
const_cast<jsval*>(realTransferable.Elements()));
if (!array) {
aRv = NS_ERROR_OUT_OF_MEMORY;
return;
}
transferable.setObject(*array);
}
if (!PostMessageInternal(aCx, aMessage, transferable, true,
aMessagePortSerial)) {
aRv = NS_ERROR_FAILURE;
}
PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
aRv);
}
template <class Derived>
@ -3191,13 +3257,13 @@ WorkerPrivateParent<Derived>::ParentJSContext() const
nsContentUtils::GetSafeJSContext();
}
WorkerPrivate::WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
WorkerPrivate::WorkerPrivate(JSContext* aCx,
WorkerPrivate* aParent,
const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
const nsAString& aSharedWorkerName,
LoadInfo& aLoadInfo)
: WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aScriptURL,
: WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
aIsChromeWorker, aWorkerType,
aSharedWorkerName, aLoadInfo),
mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
@ -3206,57 +3272,139 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
mCloseHandlerFinished(false), mMemoryReporterRunning(false),
mBlockedForMemoryReporter(false)
{
MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
MOZ_ASSERT_IF(IsSharedWorker(), !aObject && !aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(!IsSharedWorker(), aObject && aSharedWorkerName.IsEmpty());
MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
}
WorkerPrivate::~WorkerPrivate()
{
MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate);
}
// static
already_AddRefed<WorkerPrivate>
WorkerPrivate::Create(JSContext* aCx, JS::HandleObject aObject,
WorkerPrivate* aParent, const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
const nsAString& aSharedWorkerName, LoadInfo* aLoadInfo)
WorkerPrivate::Constructor(const GlobalObject& aGlobal,
const nsAString& aScriptURL,
ErrorResult& aRv)
{
if (aParent) {
aParent->AssertIsOnWorkerThread();
return WorkerPrivate::Constructor(aGlobal, aScriptURL, false, WorkerTypeDedicated,
EmptyString(), nullptr, aRv);
}
// static
bool
WorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
{
// If we're already on a worker workers are clearly enabled.
if (!NS_IsMainThread()) {
return true;
}
// If our caller is chrome, workers are always available.
if (nsContentUtils::IsCallerChrome()) {
return true;
}
// Else check the pref.
return Preferences::GetBool(PREF_WORKERS_ENABLED);
}
// static
already_AddRefed<ChromeWorkerPrivate>
ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
const nsAString& aScriptURL,
ErrorResult& aRv)
{
return WorkerPrivate::Constructor(aGlobal, aScriptURL, true, WorkerTypeDedicated,
EmptyString(), nullptr, aRv).downcast<ChromeWorkerPrivate>();
}
// static
bool
ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
{
// Chrome is always allowed to use workers, and content is never allowed to
// use ChromeWorker, so all we have to check is the caller.
return nsContentUtils::ThreadsafeIsCallerChrome();
}
// static
already_AddRefed<WorkerPrivate>
WorkerPrivate::Constructor(const GlobalObject& aGlobal,
const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
const nsAString& aSharedWorkerName,
LoadInfo* aLoadInfo, ErrorResult& aRv)
{
WorkerPrivate* parent = NS_IsMainThread() ?
nullptr :
GetCurrentThreadWorkerPrivate();
if (parent) {
parent->AssertIsOnWorkerThread();
} else {
AssertIsOnMainThread();
}
JSContext* cx = aGlobal.GetContext();
MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared,
!aObject && !aSharedWorkerName.IsVoid());
!aSharedWorkerName.IsVoid());
MOZ_ASSERT_IF(aWorkerType != WorkerTypeShared,
aObject && aSharedWorkerName.IsEmpty());
aSharedWorkerName.IsEmpty());
mozilla::Maybe<LoadInfo> stackLoadInfo;
if (!aLoadInfo) {
stackLoadInfo.construct();
nsresult rv = GetLoadInfo(aCx, nullptr, aParent, aScriptURL,
nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL,
aIsChromeWorker, stackLoadInfo.addr());
if (NS_FAILED(rv)) {
scriptloader::ReportLoadError(aCx, aScriptURL, rv, !aParent);
scriptloader::ReportLoadError(cx, aScriptURL, rv, !parent);
aRv.Throw(rv);
return nullptr;
}
aLoadInfo = stackLoadInfo.addr();
}
// NB: This has to be done before creating the WorkerPrivate, because it will
// attempt to use static variables that are initialized in the RuntimeService
// constructor.
RuntimeService* runtimeService;
if (!parent) {
runtimeService = RuntimeService::GetOrCreateService();
if (!runtimeService) {
JS_ReportError(cx, "Failed to create runtime service!");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
}
else {
runtimeService = RuntimeService::GetService();
}
MOZ_ASSERT(runtimeService);
nsRefPtr<WorkerPrivate> worker =
new WorkerPrivate(aCx, aObject, aParent, aScriptURL, aIsChromeWorker,
new WorkerPrivate(cx, parent, aScriptURL, aIsChromeWorker,
aWorkerType, aSharedWorkerName, *aLoadInfo);
nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
if (!compiler->Dispatch(aCx)) {
if (!compiler->Dispatch(cx)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!runtimeService->RegisterWorker(cx, worker)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
// The worker will be owned by its JSObject (via the reference we return from
// this function), but it also needs to be owned by its thread, so AddRef it
// again.
NS_ADDREF(worker.get());
return worker.forget();
}
@ -5012,15 +5160,6 @@ WorkerPrivateParent<Derived>::StealHostObjectURIs(nsTArray<nsCString>& aArray)
aArray.SwapElements(mHostObjectURIs);
}
template <class Derived>
JSObject*
WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx,
JS::HandleObject aScope)
{
MOZ_CRASH("This should never be called!");
return nullptr;
}
WorkerCrossThreadDispatcher*
WorkerPrivate::GetCrossThreadDispatcher()
{
@ -5176,6 +5315,24 @@ WorkerPrivate::AssertIsOnWorkerThread() const
BEGIN_WORKERS_NAMESPACE
WorkerCrossThreadDispatcher*
GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker)
{
if (!aWorker.isObject()) {
return nullptr;
}
WorkerPrivate* w = nullptr;
UNWRAP_OBJECT(Worker, aCx, &aWorker.toObject(), w);
MOZ_ASSERT(w);
return w->GetCrossThreadDispatcher();
}
// Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
// templates.
template <>
WorkerPrivateParent<WorkerPrivate>::cycleCollection WorkerPrivateParent<WorkerPrivate>::_cycleCollectorGlobal = WorkerPrivateParent<WorkerPrivate>::cycleCollection();
// Force instantiation.
template class WorkerPrivateParent<WorkerPrivate>;

View File

@ -20,6 +20,7 @@
#include "mozilla/dom/BindingDeclarations.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsDOMEventTargetHelper.h"
#include "nsEventQueue.h"
#include "nsHashKeys.h"
#include "nsString.h"
@ -28,7 +29,6 @@
#include "nsTPriorityQueue.h"
#include "StructuredCloneTags.h"
#include "EventTarget.h"
#include "Queue.h"
#include "WorkerFeature.h"
@ -234,7 +234,7 @@ public:
};
template <class Derived>
class WorkerPrivateParent : public EventTarget
class WorkerPrivateParent : public nsDOMEventTargetHelper
{
class SynchronizeAndResumeRunnable;
@ -306,11 +306,12 @@ protected:
mozilla::CondVar mMemoryReportCondVar;
private:
JSObject* mJSObject;
WorkerPrivate* mParent;
nsString mScriptURL;
nsString mSharedWorkerName;
LocationInfo mLocationInfo;
// The lifetime of these objects within LoadInfo is managed explicitly;
// they do not need to be cycle collected.
LoadInfo mLoadInfo;
// Only used for top level workers.
@ -330,16 +331,16 @@ private:
uint64_t mBusyCount;
uint64_t mMessagePortSerial;
Status mParentStatus;
bool mJSObjectRooted;
bool mRooted;
bool mParentSuspended;
bool mIsChromeWorker;
bool mMainThreadObjectsForgotten;
WorkerType mWorkerType;
protected:
WorkerPrivateParent(JSContext* aCx, JS::HandleObject aObject,
WorkerPrivate* aParent, const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
WorkerPrivateParent(JSContext* aCx, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
WorkerType aWorkerType,
const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
~WorkerPrivateParent();
@ -362,12 +363,21 @@ private:
return NotifyPrivate(aCx, Terminating);
}
bool
void
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable,
bool aToMessagePort, uint64_t aMessagePortSerial);
const Optional<Sequence<JS::Value> >& aTransferable,
bool aToMessagePort, uint64_t aMessagePortSerial,
ErrorResult& aRv);
public:
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent,
nsDOMEventTargetHelper)
// May be called on any thread...
bool
Start();
@ -401,23 +411,20 @@ public:
SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow,
nsIScriptContext* aScriptContext);
virtual void
_trace(JSTracer* aTrc) MOZ_OVERRIDE;
virtual void
_finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
void
_finalize(JSFreeOp* aFop);
void
Finish(JSContext* aCx)
{
RootJSObject(aCx, false);
Root(false);
}
bool
Terminate(JSContext* aCx)
{
AssertIsOnParentThread();
RootJSObject(aCx, false);
Root(false);
return TerminatePrivate(aCx);
}
@ -427,24 +434,25 @@ public:
bool
ModifyBusyCount(JSContext* aCx, bool aIncrease);
bool
RootJSObject(JSContext* aCx, bool aRoot);
void
Root(bool aRoot);
void
ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
bool
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransferable)
const Optional<Sequence<JS::Value> >& aTransferable,
ErrorResult& aRv)
{
return PostMessageInternal(aCx, aMessage, aTransferable, false, 0);
PostMessageInternal(aCx, aMessage, aTransferable, false, 0, aRv);
}
void
PostMessageToMessagePort(JSContext* aCx,
uint64_t aMessagePortSerial,
JS::Handle<JS::Value> aMessage,
const Optional<Sequence<JS::Value > >& aTransferable,
const Optional<Sequence<JS::Value> >& aTransferable,
ErrorResult& aRv);
bool
@ -543,12 +551,6 @@ public:
return mLoadInfo.mScriptContext;
}
JSObject*
GetJSObject() const
{
return mJSObject;
}
const nsString&
ScriptURL() const
{
@ -720,8 +722,8 @@ public:
void
StealHostObjectURIs(nsTArray<nsCString>& aArray);
virtual JSObject*
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
IMPL_EVENT_HANDLER(message)
IMPL_EVENT_HANDLER(error)
#ifdef DEBUG
void
@ -805,14 +807,23 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
nsCOMPtr<nsIThread> mThread;
#endif
public:
protected:
~WorkerPrivate();
public:
static already_AddRefed<WorkerPrivate>
Create(JSContext* aCx, JS::HandleObject aObject, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
WorkerType aWorkerType, const nsAString& aSharedWorkerName,
LoadInfo* aLoadInfo = nullptr);
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
ErrorResult& aRv);
static already_AddRefed<WorkerPrivate>
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
const nsAString& aSharedWorkerName,
LoadInfo* aLoadInfo, ErrorResult& aRv);
static bool
WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
static nsresult
GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
@ -1032,10 +1043,10 @@ public:
GetMessagePort(uint64_t aMessagePortSerial);
private:
WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
WorkerPrivate* aParent, const nsAString& aScriptURL,
bool aIsChromeWorker, WorkerType aWorkerType,
const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
WorkerType aWorkerType, const nsAString& aSharedWorkerName,
LoadInfo& aLoadInfo);
bool
Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
@ -1114,9 +1125,31 @@ private:
uint64_t aMessagePortSerial);
};
// This class is only used to trick the DOM bindings. We never create
// instances of it, and static_casting to it is fine since it doesn't add
// anything to WorkerPrivate.
class ChromeWorkerPrivate : public WorkerPrivate
{
public:
static already_AddRefed<ChromeWorkerPrivate>
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
ErrorResult& rv);
static bool
WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
private:
ChromeWorkerPrivate() MOZ_DELETE;
ChromeWorkerPrivate(const ChromeWorkerPrivate& aRHS) MOZ_DELETE;
ChromeWorkerPrivate& operator =(const ChromeWorkerPrivate& aRHS) MOZ_DELETE;
};
WorkerPrivate*
GetWorkerPrivateFromContext(JSContext* aCx);
WorkerPrivate*
GetCurrentThreadWorkerPrivate();
bool
IsCurrentThreadRunningChromeWorker();

View File

@ -23,6 +23,7 @@
#include "mozilla/dom/XMLHttpRequestBinding.h"
#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
#include "mozilla/dom/URLBinding.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerLocationBinding.h"
#include "mozilla/dom/WorkerNavigatorBinding.h"
#include "mozilla/OSFileConstants.h"
@ -45,7 +46,6 @@
#include "Principal.h"
#include "RuntimeService.h" // For WorkersDumpEnabled().
#include "ScriptLoader.h"
#include "Worker.h"
#include "WorkerPrivate.h"
#include "XMLHttpRequest.h"
@ -1473,16 +1473,10 @@ CreateGlobalScope(JSContext* aCx)
return nullptr;
}
JS::Rooted<JSObject*> workerProto(aCx,
worker::InitClass(aCx, global, eventTargetProto, false));
if (!workerProto) {
return nullptr;
}
if (worker->IsChromeWorker()) {
if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
!DefineChromeWorkerFunctions(aCx, global) ||
!DefineOSFileConstants(aCx, global)) {
if (!DefineChromeWorkerFunctions(aCx, global) ||
!DefineOSFileConstants(aCx, global) ||
!ChromeWorkerBinding::GetConstructorObject(aCx, global)) {
return nullptr;
}
}
@ -1503,6 +1497,7 @@ CreateGlobalScope(JSContext* aCx)
!XMLHttpRequestBinding_workers::GetConstructorObject(aCx, global) ||
!XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, global) ||
!URLBinding_workers::GetConstructorObject(aCx, global) ||
!WorkerBinding::GetConstructorObject(aCx, global) ||
!WorkerLocationBinding_workers::GetConstructorObject(aCx, global) ||
!WorkerNavigatorBinding_workers::GetConstructorObject(aCx, global)) {
return nullptr;
@ -1517,4 +1512,11 @@ CreateGlobalScope(JSContext* aCx)
return global;
}
bool
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
return false;
}
END_WORKERS_NAMESPACE

View File

@ -9,6 +9,10 @@ TEST_DIRS += ['test']
MODULE = 'dom'
# Public stuff.
EXPORTS.mozilla.dom += [
'WorkerPrivate.h',
]
EXPORTS.mozilla.dom.workers += [
'Workers.h',
]
@ -47,7 +51,6 @@ SOURCES += [
'ScriptLoader.cpp',
'SharedWorker.cpp',
'URL.cpp',
'Worker.cpp',
'WorkerMessagePort.cpp',
'WorkerPrivate.cpp',
'WorkerScope.cpp',

View File

@ -171,8 +171,8 @@ let Scheduler = {
if (error instanceof PromiseWorker.WorkerError) {
throw OS.File.Error.fromMsg(error.data);
}
// Extract something meaningful from WorkerErrorEvent
if (typeof error == "object" && error && error.constructor.name == "WorkerErrorEvent") {
// Extract something meaningful from ErrorEvent
if (error instanceof ErrorEvent) {
let message = error.message;
if (message == "uncaught exception: [object StopIteration]") {
throw StopIteration;