mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 919885: Move Worker to WebIDL and thread-agnostic event dispatch. r=bent,bz
This commit is contained in:
parent
3e9b1fbaae
commit
e034802efa
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -593,6 +593,7 @@ var interfaceNamesInGlobalScope =
|
||||
"WheelEvent",
|
||||
"Window",
|
||||
"WindowUtils",
|
||||
"Worker",
|
||||
"XMLDocument",
|
||||
"XMLHttpRequest",
|
||||
"XMLHttpRequestUpload",
|
||||
|
31
dom/webidl/Worker.webidl
Normal file
31
dom/webidl/Worker.webidl
Normal 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 {
|
||||
};
|
@ -415,6 +415,8 @@ WEBIDL_FILES = [
|
||||
'WebSocket.webidl',
|
||||
'WheelEvent.webidl',
|
||||
'WifiOptions.webidl',
|
||||
'Window.webidl',
|
||||
'Worker.webidl',
|
||||
'WorkerLocation.webidl',
|
||||
'WorkerMessagePort.webidl',
|
||||
'WorkerNavigator.webidl',
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
@ -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__ */
|
@ -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>;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user