mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
Bug 643325 - Implement SharedWorker. r=khuey.
--HG-- extra : transplant_source : %B6%DD%5B%B6%D1%2C%9D%A9%FCK%60%E8%8F%0C-xz%91%5E%9E
This commit is contained in:
parent
c802aa00c3
commit
80ebdbabd2
@ -135,6 +135,10 @@ const kEventConstructors = {
|
||||
return e;
|
||||
},
|
||||
},
|
||||
ErrorEvent: { create: function (aName, aProps) {
|
||||
return new ErrorEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
ElementReplaceEvent: { create: function (aName, aProps) {
|
||||
return new ElementReplaceEvent(aName, aProps);
|
||||
},
|
||||
|
@ -1433,12 +1433,7 @@ nsGlobalWindow::FreeInnerObjects()
|
||||
NotifyDOMWindowDestroyed(this);
|
||||
|
||||
// Kill all of the workers for this window.
|
||||
// We push a cx so that exceptions get reported in the right DOM Window.
|
||||
{
|
||||
nsIScriptContext *scx = GetContextInternal();
|
||||
AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
|
||||
mozilla::dom::workers::CancelWorkersForWindow(cx, this);
|
||||
}
|
||||
mozilla::dom::workers::CancelWorkersForWindow(this);
|
||||
|
||||
// Close all offline storages for this window.
|
||||
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
|
||||
@ -11240,12 +11235,7 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
|
||||
DisableGamepadUpdates();
|
||||
|
||||
// Suspend all of the workers for this window.
|
||||
// We push a cx so that exceptions get reported in the right DOM Window.
|
||||
{
|
||||
nsIScriptContext *scx = GetContextInternal();
|
||||
AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
|
||||
mozilla::dom::workers::SuspendWorkersForWindow(cx, this);
|
||||
}
|
||||
mozilla::dom::workers::SuspendWorkersForWindow(this);
|
||||
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
|
||||
@ -11334,10 +11324,7 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
|
||||
}
|
||||
|
||||
// Resume all of the workers for this window.
|
||||
// We push a cx so that exceptions get reported in the right DOM Window.
|
||||
nsIScriptContext *scx = GetContextInternal();
|
||||
AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
|
||||
mozilla::dom::workers::ResumeWorkersForWindow(scx, this);
|
||||
mozilla::dom::workers::ResumeWorkersForWindow(this);
|
||||
|
||||
// Restore all of the timeouts, using the stored time remaining
|
||||
// (stored in timeout->mTimeRemaining).
|
||||
@ -12129,5 +12116,4 @@ nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
|
||||
#undef WINDOW_ONLY_EVENT
|
||||
#undef BEFOREUNLOAD_EVENT
|
||||
#undef ERROR_EVENT
|
||||
#undef EVENT
|
||||
|
||||
#undef EVENT
|
@ -621,4 +621,4 @@ struct ParentObject {
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_BindingDeclarations_h__
|
||||
#endif // mozilla_dom_BindingDeclarations_h__
|
@ -83,6 +83,10 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::dom::Activity',
|
||||
},
|
||||
|
||||
'AbstractWorker': {
|
||||
'concrete': False
|
||||
},
|
||||
|
||||
'AnimationEvent': {
|
||||
'nativeType': 'nsDOMAnimationEvent',
|
||||
},
|
||||
@ -943,6 +947,12 @@ DOMInterfaces = {
|
||||
'nativeType': 'nsDOMScrollAreaEvent',
|
||||
},
|
||||
|
||||
'SharedWorker': {
|
||||
'nativeType': 'mozilla::dom::workers::SharedWorker',
|
||||
'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
|
||||
'implicitJSContext': [ 'constructor' ],
|
||||
},
|
||||
|
||||
'SimpleGestureEvent': {
|
||||
'nativeType': 'nsDOMSimpleGestureEvent',
|
||||
},
|
||||
@ -1438,6 +1448,18 @@ DOMInterfaces = {
|
||||
'workers': True,
|
||||
},
|
||||
|
||||
'WorkerMessagePort': [{
|
||||
'nativeType': 'mozilla::dom::workers::MessagePort',
|
||||
'headerFile': 'mozilla/dom/workers/bindings/MessagePort.h',
|
||||
'implicitJSContext': [ 'postMessage' ],
|
||||
},
|
||||
{
|
||||
'nativeType': 'mozilla::dom::workers::WorkerMessagePort',
|
||||
'headerFile': 'mozilla/dom/workers/bindings/WorkerMessagePort.h',
|
||||
'workers': True,
|
||||
'nativeOwnership': 'worker',
|
||||
}],
|
||||
|
||||
'WorkerNavigator': {
|
||||
'headerFile': 'mozilla/dom/workers/bindings/Navigator.h',
|
||||
'workers': True,
|
||||
@ -1863,4 +1885,4 @@ addExternalIface('CameraReleaseCallback', nativeType='nsICameraReleaseCallback',
|
||||
addExternalIface('CameraStartRecordingCallback', nativeType='nsICameraStartRecordingCallback', headerFile='nsIDOMCameraManager.h')
|
||||
addExternalIface('CameraPreviewStateChange', nativeType='nsICameraPreviewStateChange', headerFile='nsIDOMCameraManager.h')
|
||||
addExternalIface('CameraPreviewStreamCallback', nativeType='nsICameraPreviewStreamCallback', headerFile='nsIDOMCameraManager.h')
|
||||
addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')
|
||||
addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')
|
@ -184,6 +184,7 @@ var interfaceNamesInGlobalScope =
|
||||
"DynamicsCompressorNode",
|
||||
"Element",
|
||||
"ElementReplaceEvent",
|
||||
"ErrorEvent",
|
||||
"Event",
|
||||
"EventListenerInfo",
|
||||
"EventSource",
|
||||
|
10
dom/webidl/AbstractWorker.webidl
Normal file
10
dom/webidl/AbstractWorker.webidl
Normal file
@ -0,0 +1,10 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
[NoInterfaceObject]
|
||||
interface AbstractWorker {
|
||||
attribute EventHandler onerror;
|
||||
};
|
21
dom/webidl/ErrorEvent.webidl
Normal file
21
dom/webidl/ErrorEvent.webidl
Normal file
@ -0,0 +1,21 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
[Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
|
||||
interface ErrorEvent : Event
|
||||
{
|
||||
readonly attribute DOMString message;
|
||||
readonly attribute DOMString filename;
|
||||
readonly attribute unsigned long lineno;
|
||||
readonly attribute unsigned long column;
|
||||
};
|
||||
|
||||
dictionary ErrorEventInit : EventInit
|
||||
{
|
||||
DOMString message = "";
|
||||
DOMString filename = "";
|
||||
unsigned long lineno = 0;
|
||||
unsigned long column = 0;
|
||||
};
|
13
dom/webidl/SharedWorker.webidl
Normal file
13
dom/webidl/SharedWorker.webidl
Normal file
@ -0,0 +1,13 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
[PrefControlled,
|
||||
Constructor(DOMString scriptURL, optional DOMString name)]
|
||||
interface SharedWorker : EventTarget {
|
||||
readonly attribute WorkerMessagePort port;
|
||||
};
|
||||
|
||||
SharedWorker implements AbstractWorker;
|
18
dom/webidl/WorkerMessagePort.webidl
Normal file
18
dom/webidl/WorkerMessagePort.webidl
Normal file
@ -0,0 +1,18 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
// XXX Remove me soon!
|
||||
[PrefControlled]
|
||||
interface WorkerMessagePort : EventTarget {
|
||||
[Throws]
|
||||
void postMessage(any message, optional sequence<any> transferable);
|
||||
|
||||
void start();
|
||||
|
||||
void close();
|
||||
|
||||
[SetterThrows=Workers, GetterThrows=Workers]
|
||||
attribute EventHandler onmessage;
|
||||
};
|
@ -16,6 +16,7 @@ PREPROCESSED_WEBIDL_FILES = [
|
||||
]
|
||||
|
||||
WEBIDL_FILES = [
|
||||
'AbstractWorker.webidl',
|
||||
'AnalyserNode.webidl',
|
||||
'AnimationEvent.webidl',
|
||||
'ArchiveReader.webidl',
|
||||
@ -366,6 +367,7 @@ WEBIDL_FILES = [
|
||||
'ScriptProcessorNode.webidl',
|
||||
'ScrollAreaEvent.webidl',
|
||||
'SettingsManager.webidl',
|
||||
'SharedWorker.webidl',
|
||||
'SimpleGestureEvent.webidl',
|
||||
'SourceBuffer.webidl',
|
||||
'SourceBufferList.webidl',
|
||||
@ -406,6 +408,7 @@ WEBIDL_FILES = [
|
||||
'WifiOptions.webidl',
|
||||
'Window.webidl',
|
||||
'WorkerLocation.webidl',
|
||||
'WorkerMessagePort.webidl',
|
||||
'WorkerNavigator.webidl',
|
||||
'XMLDocument.webidl',
|
||||
'XMLHttpRequest.webidl',
|
||||
@ -537,6 +540,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
||||
'BlobEvent.webidl',
|
||||
'DeviceLightEvent.webidl',
|
||||
'DeviceProximityEvent.webidl',
|
||||
'ErrorEvent.webidl',
|
||||
'MediaStreamEvent.webidl',
|
||||
'MozInterAppMessageEvent.webidl',
|
||||
'RTCDataChannelEvent.webidl',
|
||||
@ -551,4 +555,3 @@ if CONFIG['MOZ_GAMEPAD']:
|
||||
'GamepadEvent.webidl',
|
||||
]
|
||||
|
||||
|
||||
|
@ -6,17 +6,19 @@
|
||||
#ifndef mozilla_dom_workers_dombindinginlines_h__
|
||||
#define mozilla_dom_workers_dombindinginlines_h__
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/dom/JSSlots.h"
|
||||
#include "mozilla/dom/URLBinding.h"
|
||||
#include "mozilla/dom/WorkerMessagePortBinding.h"
|
||||
#include "mozilla/dom/XMLHttpRequestBinding.h"
|
||||
#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
|
||||
#include "mozilla/dom/URLBinding.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class URL;
|
||||
class WorkerMessagePort;
|
||||
class XMLHttpRequest;
|
||||
class XMLHttpRequestUpload;
|
||||
class URL;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -45,9 +47,10 @@ struct WrapPrototypeTraits
|
||||
} \
|
||||
};
|
||||
|
||||
SPECIALIZE_PROTO_TRAITS(URL)
|
||||
SPECIALIZE_PROTO_TRAITS(WorkerMessagePort)
|
||||
SPECIALIZE_PROTO_TRAITS(XMLHttpRequest)
|
||||
SPECIALIZE_PROTO_TRAITS(XMLHttpRequestUpload)
|
||||
SPECIALIZE_PROTO_TRAITS(URL)
|
||||
|
||||
#undef SPECIALIZE_PROTO_TRAITS
|
||||
|
||||
|
@ -414,8 +414,10 @@ public:
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JSAutoStructuredCloneBuffer& aData,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime)
|
||||
Create(JSContext* aCx, JS::Handle<JSObject*> aParent,
|
||||
JSAutoStructuredCloneBuffer& aData,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
|
||||
bool aMainRuntime)
|
||||
{
|
||||
JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "message"));
|
||||
if (!type) {
|
||||
@ -429,16 +431,57 @@ public:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> ports(aCx, JS_NewArrayObject(aCx, 0, nullptr));
|
||||
if (!ports) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MessageEvent* priv = new MessageEvent(aMainRuntime);
|
||||
SetJSPrivateSafeish(obj, priv);
|
||||
|
||||
InitMessageEventCommon(aCx, obj, priv, type, false, false, NULL, NULL, NULL,
|
||||
true);
|
||||
ports, true);
|
||||
|
||||
priv->mBuffer.swap(aData);
|
||||
priv->mClonedObjects.SwapElements(aClonedObjects);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
Create(JSContext* aCx, JS::Handle<JSObject*> aParent,
|
||||
JS::Handle<JSString*> aType, bool aBubbles, bool aCancelable,
|
||||
JS::Handle<JSString*> aData, JS::Handle<JSString*> aOrigin,
|
||||
JS::Handle<JSObject*> aSource, JS::Handle<JSObject*> aMessagePort,
|
||||
bool aIsTrusted)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx,
|
||||
JS_NewObject(aCx, &sClass, nullptr, aParent));
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> ports(aCx);
|
||||
if (aMessagePort) {
|
||||
JS::Value port = OBJECT_TO_JSVAL(aMessagePort);
|
||||
ports = JS_NewArrayObject(aCx, 1, &port);
|
||||
} else {
|
||||
ports = JS_NewArrayObject(aCx, 0, nullptr);
|
||||
}
|
||||
|
||||
if (!ports) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MessageEvent* priv = new MessageEvent(false);
|
||||
SetJSPrivateSafeish(obj, priv);
|
||||
|
||||
InitMessageEventCommon(aCx, obj, priv, aType, aBubbles, aCancelable, aData,
|
||||
aOrigin, aSource, ports, aIsTrusted);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected:
|
||||
MessageEvent(bool aMainRuntime)
|
||||
: mMainRuntime(aMainRuntime)
|
||||
@ -455,6 +498,7 @@ protected:
|
||||
SLOT_data = Event::SLOT_COUNT,
|
||||
SLOT_origin,
|
||||
SLOT_source,
|
||||
SLOT_ports,
|
||||
|
||||
SLOT_COUNT,
|
||||
SLOT_FIRST = SLOT_data
|
||||
@ -479,7 +523,7 @@ private:
|
||||
InitMessageEventCommon(JSContext* aCx, JSObject* aObj, Event* aEvent,
|
||||
JSString* aType, bool aBubbles, bool aCancelable,
|
||||
JSString* aData, JSString* aOrigin, JSObject* aSource,
|
||||
bool aIsTrusted)
|
||||
JS::Handle<JSObject*> aMessagePorts, bool aIsTrusted)
|
||||
{
|
||||
jsval emptyString = JS_GetEmptyStringValue(aCx);
|
||||
|
||||
@ -490,6 +534,7 @@ private:
|
||||
JS_SetReservedSlot(aObj, SLOT_origin,
|
||||
aOrigin ? STRING_TO_JSVAL(aOrigin) : emptyString);
|
||||
JS_SetReservedSlot(aObj, SLOT_source, OBJECT_TO_JSVAL(aSource));
|
||||
JS_SetReservedSlot(aObj, SLOT_ports, OBJECT_TO_JSVAL(aMessagePorts));
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -591,7 +636,7 @@ private:
|
||||
}
|
||||
|
||||
InitMessageEventCommon(aCx, obj, event, type, bubbles, cancelable,
|
||||
data, origin, source, false);
|
||||
data, origin, source, JS::NullPtr(), false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -616,6 +661,8 @@ const JSPropertySpec MessageEvent::sProperties[] = {
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("source", Property<SLOT_source>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("ports", Property<SLOT_ports>::Get, GetterOnlyJSNative,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
@ -1049,6 +1096,25 @@ CreateProgressEvent(JSContext* aCx, JS::Handle<JSString*> aType, bool aLengthCom
|
||||
aTotal);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
CreateConnectEvent(JSContext* aCx, JS::Handle<JSObject*> aMessagePort)
|
||||
{
|
||||
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
|
||||
JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "connect"));
|
||||
if (!type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> emptyStr(aCx, JS_GetEmptyString(JS_GetRuntime(aCx)));
|
||||
if (!emptyStr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MessageEvent::Create(aCx, global, type, false, false, emptyStr,
|
||||
emptyStr, JS::NullPtr(), aMessagePort, true);
|
||||
}
|
||||
|
||||
bool
|
||||
IsSupportedEventClass(JSObject* aEvent)
|
||||
{
|
||||
@ -1100,4 +1166,4 @@ DispatchEventToTarget(JSContext* aCx, JS::Handle<JSObject*> aTarget,
|
||||
|
||||
} // namespace events
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
END_WORKERS_NAMESPACE
|
@ -35,6 +35,9 @@ JSObject*
|
||||
CreateProgressEvent(JSContext* aCx, JS::Handle<JSString*> aType,
|
||||
bool aLengthComputable, double aLoaded, double aTotal);
|
||||
|
||||
JSObject*
|
||||
CreateConnectEvent(JSContext* aCx, JS::Handle<JSObject*> aMessagePort);
|
||||
|
||||
bool
|
||||
IsSupportedEventClass(JSObject* aEvent);
|
||||
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include "FileReaderSync.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/FileReaderSyncBinding.h"
|
||||
#include "nsCExternalHandlerService.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -21,12 +25,9 @@
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "File.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "DOMBindingInlines.h"
|
||||
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
using namespace mozilla;
|
||||
@ -35,6 +36,7 @@ using mozilla::dom::GlobalObject;
|
||||
|
||||
NS_IMPL_ADDREF(FileReaderSync)
|
||||
NS_IMPL_RELEASE(FileReaderSync)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(FileReaderSync)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
@ -48,6 +50,12 @@ FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
return frs.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
FileReaderSync::WrapObject(JSContext* aCx, JS::HandleObject aScope)
|
||||
{
|
||||
return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScopeObj,
|
||||
|
@ -10,9 +10,6 @@
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsICharsetDetectionObserver.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/FileReaderSyncBinding.h"
|
||||
|
||||
class nsIInputStream;
|
||||
class nsIDOMBlob;
|
||||
@ -39,10 +36,7 @@ public:
|
||||
static already_AddRefed<FileReaderSync>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
|
||||
}
|
||||
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aScope);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
|
@ -5,9 +5,6 @@
|
||||
|
||||
#include "Location.h"
|
||||
|
||||
#include "DOMBindingInlines.h"
|
||||
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "mozilla/dom/WorkerLocationBinding.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
209
dom/workers/MessagePort.cpp
Normal file
209
dom/workers/MessagePort.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePort.h"
|
||||
|
||||
#include "mozilla/dom/WorkerMessagePortBinding.h"
|
||||
#include "nsDOMEvent.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
|
||||
#include "SharedWorker.h"
|
||||
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::Sequence;
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
class DelayedEventRunnable MOZ_FINAL : public nsIRunnable
|
||||
{
|
||||
nsRefPtr<MessagePort> mMessagePort;
|
||||
nsCOMPtr<nsIDOMEvent> mEvent;
|
||||
|
||||
public:
|
||||
DelayedEventRunnable(MessagePort* aMessagePort,
|
||||
already_AddRefed<nsIDOMEvent> aEvent)
|
||||
: mMessagePort(aMessagePort), mEvent(aEvent)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
MOZ_ASSERT(aEvent.get());
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
|
||||
uint64_t aSerial)
|
||||
: nsDOMEventTargetHelper(aWindow), mSharedWorker(aSharedWorker),
|
||||
mSerial(aSerial), mStarted(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aSharedWorker);
|
||||
}
|
||||
|
||||
MessagePort::~MessagePort()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
MessagePort::PrefEnabled()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// Currently tied to the SharedWorker preference.
|
||||
return SharedWorker::PrefEnabled();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::PostMessage(JSContext* aCx, JS::HandleValue aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (IsClosed()) {
|
||||
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Start()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (IsClosed()) {
|
||||
NS_WARNING("Called start() after calling close()!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStarted = true;
|
||||
|
||||
if (!mQueuedEvents.IsEmpty()) {
|
||||
for (uint32_t index = 0; index < mQueuedEvents.Length(); index++) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new DelayedEventRunnable(this, mQueuedEvents[index].forget());
|
||||
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
||||
NS_WARNING("Failed to dispatch queued event!");
|
||||
}
|
||||
}
|
||||
mQueuedEvents.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::QueueEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aEvent);
|
||||
MOZ_ASSERT(!IsClosed());
|
||||
MOZ_ASSERT(!mStarted);
|
||||
|
||||
mQueuedEvents.AppendElement(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::CloseInternal()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!IsClosed());
|
||||
MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
|
||||
|
||||
NS_WARN_IF_FALSE(mStarted, "Called close() before start()!");
|
||||
|
||||
if (!mStarted) {
|
||||
mQueuedEvents.Clear();
|
||||
}
|
||||
|
||||
mSharedWorker = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(MessagePort, nsDOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(MessagePort, nsDOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
|
||||
nsDOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
|
||||
nsDOMEventTargetHelper)
|
||||
tmp->Close();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
JSObject*
|
||||
MessagePort::WrapObject(JSContext* aCx, JS::HandleObject aScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return WorkerMessagePortBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MessagePort::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIDOMEvent*& event = aVisitor.mDOMEvent;
|
||||
|
||||
if (event) {
|
||||
bool preventDispatch = false;
|
||||
|
||||
if (IsClosed()) {
|
||||
preventDispatch = true;
|
||||
} else if (mSharedWorker->IsSuspended()) {
|
||||
mSharedWorker->QueueEvent(event);
|
||||
preventDispatch = true;
|
||||
} else if (!mStarted) {
|
||||
QueueEvent(event);
|
||||
preventDispatch = true;
|
||||
}
|
||||
|
||||
if (preventDispatch) {
|
||||
aVisitor.mCanHandle = false;
|
||||
aVisitor.mParentTarget = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DelayedEventRunnable, nsIRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DelayedEventRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mMessagePort);
|
||||
MOZ_ASSERT(mEvent);
|
||||
|
||||
bool ignored;
|
||||
nsresult rv = mMessagePort->DispatchEvent(mEvent, &ignored);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
112
dom/workers/MessagePort.h
Normal file
112
dom/workers/MessagePort.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_messageport_h_
|
||||
#define mozilla_dom_workers_messageport_h_
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
|
||||
class nsIDOMEvent;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class SharedWorker;
|
||||
|
||||
class MessagePort MOZ_FINAL : public nsDOMEventTargetHelper
|
||||
{
|
||||
friend class SharedWorker;
|
||||
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
|
||||
nsRefPtr<SharedWorker> mSharedWorker;
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>> mQueuedEvents;
|
||||
uint64_t mSerial;
|
||||
bool mStarted;
|
||||
|
||||
public:
|
||||
static bool
|
||||
PrefEnabled();
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::HandleValue aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Start();
|
||||
|
||||
void
|
||||
Close()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!IsClosed()) {
|
||||
CloseInternal();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Serial() const
|
||||
{
|
||||
return mSerial;
|
||||
}
|
||||
|
||||
void
|
||||
QueueEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, nsDOMEventTargetHelper)
|
||||
|
||||
EventHandlerNonNull*
|
||||
GetOnmessage()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
|
||||
}
|
||||
|
||||
void
|
||||
SetOnmessage(EventHandlerNonNull* aCallback)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
bool
|
||||
IsClosed() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return !mSharedWorker;
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// This class can only be created by SharedWorker.
|
||||
MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
|
||||
uint64_t aSerial);
|
||||
|
||||
// This class is reference-counted and will be destroyed from Release().
|
||||
~MessagePort();
|
||||
|
||||
void
|
||||
CloseInternal();
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_messageport_h_
|
@ -5,10 +5,10 @@
|
||||
|
||||
#include "Navigator.h"
|
||||
|
||||
#include "DOMBindingInlines.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "mozilla/dom/WorkerNavigatorBinding.h"
|
||||
|
||||
#include "RuntimeService.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator)
|
||||
|
@ -6,15 +6,19 @@
|
||||
|
||||
#include "RuntimeService.h"
|
||||
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPlatformCharset.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include <algorithm>
|
||||
@ -36,12 +40,14 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
#include "OSFileConstants.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include "Events.h"
|
||||
#include "SharedWorker.h"
|
||||
#include "Worker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
@ -1030,32 +1036,32 @@ ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid
|
||||
}
|
||||
|
||||
void
|
||||
CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow)
|
||||
CancelWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RuntimeService* runtime = RuntimeService::GetService();
|
||||
if (runtime) {
|
||||
runtime->CancelWorkersForWindow(aCx, aWindow);
|
||||
runtime->CancelWorkersForWindow(aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow)
|
||||
SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RuntimeService* runtime = RuntimeService::GetService();
|
||||
if (runtime) {
|
||||
runtime->SuspendWorkersForWindow(aCx, aWindow);
|
||||
runtime->SuspendWorkersForWindow(aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow)
|
||||
ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RuntimeService* runtime = RuntimeService::GetService();
|
||||
if (runtime) {
|
||||
runtime->ResumeWorkersForWindow(aCx, aWindow);
|
||||
runtime->ResumeWorkersForWindow(aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1205,11 +1211,32 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
|
||||
|
||||
const nsString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
|
||||
nsCString sharedWorkerScriptSpec;
|
||||
|
||||
if (isSharedWorker) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
|
||||
NS_ASSERTION(scriptURI, "Null script URI!");
|
||||
|
||||
nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("GetSpec failed?!");
|
||||
xpc::Throw(aCx, rv);
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
|
||||
}
|
||||
|
||||
const nsCString& domain = aWorkerPrivate->Domain();
|
||||
|
||||
WorkerDomainInfo* domainInfo;
|
||||
bool queued = false;
|
||||
{
|
||||
const nsCString& domain = aWorkerPrivate->Domain();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!mDomainMap.Get(domain, &domainInfo)) {
|
||||
@ -1220,26 +1247,29 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
mDomainMap.Put(domain, domainInfo);
|
||||
}
|
||||
|
||||
if (domainInfo) {
|
||||
queued = gMaxWorkersPerDomain &&
|
||||
domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
|
||||
!domain.IsEmpty();
|
||||
queued = gMaxWorkersPerDomain &&
|
||||
domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
|
||||
!domain.IsEmpty();
|
||||
|
||||
if (queued) {
|
||||
domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
|
||||
}
|
||||
else if (parent) {
|
||||
domainInfo->mChildWorkerCount++;
|
||||
}
|
||||
else {
|
||||
domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
|
||||
}
|
||||
if (queued) {
|
||||
domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
|
||||
}
|
||||
else if (parent) {
|
||||
domainInfo->mChildWorkerCount++;
|
||||
}
|
||||
else {
|
||||
domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
if (!domainInfo) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return false;
|
||||
if (isSharedWorker) {
|
||||
MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(sharedWorkerScriptSpec));
|
||||
|
||||
SharedWorkerInfo* sharedWorkerInfo =
|
||||
new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
|
||||
sharedWorkerName);
|
||||
domainInfo->mSharedWorkerInfos.Put(sharedWorkerScriptSpec,
|
||||
sharedWorkerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// From here on out we must call UnregisterWorker if something fails!
|
||||
@ -1267,15 +1297,15 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
|
||||
nsTArray<WorkerPrivate*>* windowArray;
|
||||
if (!mWindowMap.Get(window, &windowArray)) {
|
||||
NS_ASSERTION(!parent, "Shouldn't have a parent here!");
|
||||
|
||||
windowArray = new nsTArray<WorkerPrivate*>(1);
|
||||
mWindowMap.Put(window, windowArray);
|
||||
}
|
||||
|
||||
NS_ASSERTION(!windowArray->Contains(aWorkerPrivate),
|
||||
"Already know about this worker!");
|
||||
windowArray->AppendElement(aWorkerPrivate);
|
||||
if (!windowArray->Contains(aWorkerPrivate)) {
|
||||
windowArray->AppendElement(aWorkerPrivate);
|
||||
} else {
|
||||
MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
|
||||
}
|
||||
}
|
||||
|
||||
if (!queued && !ScheduleWorker(aCx, aWorkerPrivate)) {
|
||||
@ -1295,10 +1325,10 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
const nsCString& domain = aWorkerPrivate->Domain();
|
||||
|
||||
WorkerPrivate* queuedWorker = nullptr;
|
||||
{
|
||||
const nsCString& domain = aWorkerPrivate->Domain();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
WorkerDomainInfo* domainInfo;
|
||||
@ -1322,6 +1352,17 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
|
||||
}
|
||||
|
||||
if (aWorkerPrivate->IsSharedWorker()) {
|
||||
MatchSharedWorkerInfo match(aWorkerPrivate);
|
||||
domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
|
||||
&match);
|
||||
|
||||
if (match.mSharedWorkerInfo) {
|
||||
domainInfo->mSharedWorkerInfos.Remove(
|
||||
match.mSharedWorkerInfo->mScriptSpec);
|
||||
}
|
||||
}
|
||||
|
||||
// See if there's a queued worker we can schedule.
|
||||
if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
|
||||
!domainInfo->mQueuedWorkers.IsEmpty()) {
|
||||
@ -1342,23 +1383,39 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
}
|
||||
}
|
||||
|
||||
if (aWorkerPrivate->IsSharedWorker()) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsAutoTArray<nsRefPtr<SharedWorker>, 5> sharedWorkersToNotify;
|
||||
aWorkerPrivate->GetAllSharedWorkers(sharedWorkersToNotify);
|
||||
|
||||
for (uint32_t index = 0; index < sharedWorkersToNotify.Length(); index++) {
|
||||
MOZ_ASSERT(sharedWorkersToNotify[index]);
|
||||
sharedWorkersToNotify[index]->NoteDeadWorker(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
parent->RemoveChildWorker(aCx, aWorkerPrivate);
|
||||
}
|
||||
else if (aWorkerPrivate->IsSharedWorker()) {
|
||||
mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
|
||||
}
|
||||
else {
|
||||
// May be null.
|
||||
nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
|
||||
|
||||
nsTArray<WorkerPrivate*>* windowArray;
|
||||
if (!mWindowMap.Get(window, &windowArray)) {
|
||||
NS_ERROR("Don't have an entry for this window!");
|
||||
MOZ_ASSERT(false, "Don't have an entry for this window!");
|
||||
}
|
||||
|
||||
NS_ASSERTION(windowArray->Contains(aWorkerPrivate),
|
||||
"Don't know about this worker!");
|
||||
windowArray->RemoveElement(aWorkerPrivate);
|
||||
if (!windowArray->RemoveElement(aWorkerPrivate)) {
|
||||
MOZ_ASSERT(false, "Worker wasn't in the correct window array!");
|
||||
}
|
||||
|
||||
if (windowArray->IsEmpty()) {
|
||||
NS_ASSERTION(!queuedWorker, "How can this be?!");
|
||||
MOZ_ASSERT(!queuedWorker, "queuedWorker should be in this array!");
|
||||
mWindowMap.Remove(window);
|
||||
}
|
||||
}
|
||||
@ -1794,6 +1851,48 @@ RuntimeService::AddAllTopLevelWorkersToArray(const nsACString& aKey,
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
PLDHashOperator
|
||||
RuntimeService::RemoveSharedWorkerFromWindowMap(
|
||||
nsPIDOMWindow* aKey,
|
||||
nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
|
||||
void* aUserArg)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aData.get());
|
||||
MOZ_ASSERT(aUserArg);
|
||||
|
||||
auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
|
||||
|
||||
MOZ_ASSERT(workerPrivate->IsSharedWorker());
|
||||
|
||||
if (aData->RemoveElement(workerPrivate)) {
|
||||
MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
|
||||
|
||||
if (aData->IsEmpty()) {
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
PLDHashOperator
|
||||
RuntimeService::FindSharedWorkerInfo(const nsACString& aKey,
|
||||
SharedWorkerInfo* aData,
|
||||
void* aUserArg)
|
||||
{
|
||||
auto match = static_cast<MatchSharedWorkerInfo*>(aUserArg);
|
||||
|
||||
if (aData->mWorkerPrivate == match->mWorkerPrivate) {
|
||||
match->mSharedWorkerInfo = aData;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow,
|
||||
nsTArray<WorkerPrivate*>& aWorkers)
|
||||
@ -1811,59 +1910,169 @@ RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::CancelWorkersForWindow(JSContext* aCx,
|
||||
nsPIDOMWindow* aWindow)
|
||||
RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsAutoTArray<WorkerPrivate*, 100> workers;
|
||||
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
|
||||
GetWorkersForWindow(aWindow, workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
|
||||
MOZ_ASSERT(sgo);
|
||||
|
||||
nsIScriptContext* scx = sgo->GetContext();
|
||||
|
||||
AutoPushJSContext cx(scx ?
|
||||
scx->GetNativeContext() :
|
||||
nsContentUtils::GetSafeJSContext());
|
||||
|
||||
for (uint32_t index = 0; index < workers.Length(); index++) {
|
||||
if (!workers[index]->Cancel(aCx)) {
|
||||
NS_WARNING("Failed to cancel worker!");
|
||||
WorkerPrivate*& worker = workers[index];
|
||||
|
||||
if (worker->IsSharedWorker()) {
|
||||
worker->CloseSharedWorkersForWindow(aWindow);
|
||||
} else if (!worker->Cancel(cx)) {
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::SuspendWorkersForWindow(JSContext* aCx,
|
||||
nsPIDOMWindow* aWindow)
|
||||
RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsAutoTArray<WorkerPrivate*, 100> workers;
|
||||
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
|
||||
GetWorkersForWindow(aWindow, workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
|
||||
MOZ_ASSERT(sgo);
|
||||
|
||||
nsIScriptContext* scx = sgo->GetContext();
|
||||
|
||||
AutoPushJSContext cx(scx ?
|
||||
scx->GetNativeContext() :
|
||||
nsContentUtils::GetSafeJSContext());
|
||||
|
||||
for (uint32_t index = 0; index < workers.Length(); index++) {
|
||||
if (!workers[index]->Suspend(aCx)) {
|
||||
NS_WARNING("Failed to cancel worker!");
|
||||
if (!workers[index]->Suspend(cx, aWindow)) {
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::ResumeWorkersForWindow(nsIScriptContext* aCx,
|
||||
nsPIDOMWindow* aWindow)
|
||||
RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsAutoTArray<WorkerPrivate*, 100> workers;
|
||||
nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
|
||||
GetWorkersForWindow(aWindow, workers);
|
||||
|
||||
if (!workers.IsEmpty()) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
|
||||
MOZ_ASSERT(sgo);
|
||||
|
||||
nsIScriptContext* scx = sgo->GetContext();
|
||||
|
||||
AutoPushJSContext cx(scx ?
|
||||
scx->GetNativeContext() :
|
||||
nsContentUtils::GetSafeJSContext());
|
||||
|
||||
for (uint32_t index = 0; index < workers.Length(); index++) {
|
||||
if (!workers[index]->SynchronizeAndResume(aCx)) {
|
||||
NS_WARNING("Failed to cancel worker!");
|
||||
if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) {
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
|
||||
const nsAString& aScriptURL,
|
||||
const nsAString& aName,
|
||||
SharedWorker** aSharedWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
WorkerPrivate::LoadInfo loadInfo;
|
||||
nsresult rv = WorkerPrivate::GetLoadInfo(aCx, aWindow, nullptr, aScriptURL,
|
||||
false, &loadInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
MOZ_ASSERT(loadInfo.mResolvedScriptURI);
|
||||
|
||||
nsCString scriptSpec;
|
||||
rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
WorkerPrivate* workerPrivate = nullptr;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
WorkerDomainInfo* domainInfo;
|
||||
SharedWorkerInfo* sharedWorkerInfo;
|
||||
|
||||
if (mDomainMap.Get(loadInfo.mDomain, &domainInfo) &&
|
||||
domainInfo->mSharedWorkerInfos.Get(scriptSpec, &sharedWorkerInfo) &&
|
||||
sharedWorkerInfo->mName == aName) {
|
||||
workerPrivate = sharedWorkerInfo->mWorkerPrivate;
|
||||
}
|
||||
}
|
||||
|
||||
bool created = false;
|
||||
|
||||
if (!workerPrivate) {
|
||||
nsRefPtr<WorkerPrivate> newWorkerPrivate =
|
||||
WorkerPrivate::Create(aCx, JS::NullPtr(), nullptr, aScriptURL, false,
|
||||
true, aName, &loadInfo);
|
||||
NS_ENSURE_TRUE(newWorkerPrivate, NS_ERROR_FAILURE);
|
||||
|
||||
if (!RegisterWorker(aCx, newWorkerPrivate)) {
|
||||
NS_WARNING("Failed to register worker!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
created = true;
|
||||
newWorkerPrivate.forget(&workerPrivate);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(workerPrivate->IsSharedWorker());
|
||||
|
||||
nsRefPtr<SharedWorker> sharedWorker =
|
||||
new SharedWorker(aWindow, workerPrivate);
|
||||
|
||||
if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
|
||||
NS_WARNING("Failed to dispatch to worker!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// This is normally handled in RegisterWorker, but that wasn't called if the
|
||||
// worker already existed.
|
||||
if (!created) {
|
||||
nsTArray<WorkerPrivate*>* windowArray;
|
||||
if (!mWindowMap.Get(aWindow, &windowArray)) {
|
||||
windowArray = new nsTArray<WorkerPrivate*>(1);
|
||||
mWindowMap.Put(aWindow, windowArray);
|
||||
}
|
||||
|
||||
if (!windowArray->Contains(workerPrivate)) {
|
||||
windowArray->AppendElement(workerPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
sharedWorker.forget(aSharedWorker);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
RuntimeService::NoteIdleThread(nsIThread* aThread)
|
||||
{
|
||||
|
@ -14,9 +14,11 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
@ -27,18 +29,35 @@ class nsPIDOMWindow;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class SharedWorker;
|
||||
class WorkerPrivate;
|
||||
|
||||
class RuntimeService MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
struct SharedWorkerInfo
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCString mScriptSpec;
|
||||
nsString mName;
|
||||
|
||||
SharedWorkerInfo(WorkerPrivate* aWorkerPrivate,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aName)
|
||||
: mWorkerPrivate(aWorkerPrivate), mScriptSpec(aScriptSpec), mName(aName)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct WorkerDomainInfo
|
||||
{
|
||||
nsCString mDomain;
|
||||
nsTArray<WorkerPrivate*> mActiveWorkers;
|
||||
nsTArray<WorkerPrivate*> mQueuedWorkers;
|
||||
nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos;
|
||||
uint32_t mChildWorkerCount;
|
||||
|
||||
WorkerDomainInfo() : mActiveWorkers(1), mChildWorkerCount(0) { }
|
||||
WorkerDomainInfo()
|
||||
: mActiveWorkers(1), mChildWorkerCount(0)
|
||||
{ }
|
||||
|
||||
uint32_t
|
||||
ActiveWorkerCount() const
|
||||
@ -53,6 +72,16 @@ class RuntimeService MOZ_FINAL : public nsIObserver
|
||||
mozilla::TimeStamp mExpirationTime;
|
||||
};
|
||||
|
||||
struct MatchSharedWorkerInfo
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
SharedWorkerInfo* mSharedWorkerInfo;
|
||||
|
||||
MatchSharedWorkerInfo(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate), mSharedWorkerInfo(nullptr)
|
||||
{ }
|
||||
};
|
||||
|
||||
mozilla::Mutex mMutex;
|
||||
|
||||
// Protected by mMutex.
|
||||
@ -62,7 +91,8 @@ class RuntimeService MOZ_FINAL : public nsIObserver
|
||||
nsTArray<IdleThreadInfo> mIdleThreadArray;
|
||||
|
||||
// *Not* protected by mMutex.
|
||||
nsClassHashtable<nsPtrHashKey<nsPIDOMWindow>, nsTArray<WorkerPrivate*> > mWindowMap;
|
||||
nsClassHashtable<nsPtrHashKey<nsPIDOMWindow>,
|
||||
nsTArray<WorkerPrivate*> > mWindowMap;
|
||||
|
||||
// Only used on the main thread.
|
||||
nsCOMPtr<nsITimer> mIdleThreadTimer;
|
||||
@ -106,13 +136,18 @@ public:
|
||||
UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
|
||||
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
|
||||
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow);
|
||||
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
nsresult
|
||||
CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
|
||||
const nsAString& aScriptURL, const nsAString& aName,
|
||||
SharedWorker** aSharedWorker);
|
||||
|
||||
const nsACString&
|
||||
GetDetectorName() const
|
||||
@ -219,6 +254,16 @@ private:
|
||||
WorkerDomainInfo* aData,
|
||||
void* aUserArg);
|
||||
|
||||
static PLDHashOperator
|
||||
RemoveSharedWorkerFromWindowMap(nsPIDOMWindow* aKey,
|
||||
nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
|
||||
void* aUserArg);
|
||||
|
||||
static PLDHashOperator
|
||||
FindSharedWorkerInfo(const nsACString& aKey,
|
||||
SharedWorkerInfo* aData,
|
||||
void* aUserArg);
|
||||
|
||||
void
|
||||
GetWorkersForWindow(nsPIDOMWindow* aWindow,
|
||||
nsTArray<WorkerPrivate*>& aWorkers);
|
||||
|
@ -38,10 +38,12 @@
|
||||
|
||||
#define MAX_CONCURRENT_SCRIPTS 1000
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
|
||||
|
||||
namespace {
|
||||
|
||||
namespace scriptloader {
|
||||
static
|
||||
nsresult
|
||||
ChannelFromScriptURL(nsIPrincipal* principal,
|
||||
nsIURI* baseURI,
|
||||
@ -49,19 +51,85 @@ ChannelFromScriptURL(nsIPrincipal* principal,
|
||||
nsILoadGroup* loadGroup,
|
||||
nsIIOService* ios,
|
||||
nsIScriptSecurityManager* secMan,
|
||||
const nsString& aScriptURL,
|
||||
const nsAString& aScriptURL,
|
||||
bool aIsWorkerScript,
|
||||
nsIChannel** aChannel);
|
||||
nsIChannel** aChannel)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
} // namespace scriptloader
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
|
||||
aScriptURL, parentDoc,
|
||||
baseURI);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
// If we're part of a document then check the content load policy.
|
||||
if (parentDoc) {
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
|
||||
principal, parentDoc,
|
||||
NS_LITERAL_CSTRING("text/javascript"),
|
||||
nullptr, &shouldLoad,
|
||||
nsContentUtils::GetContentPolicy(),
|
||||
secMan);
|
||||
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
|
||||
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
|
||||
return rv = NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
|
||||
}
|
||||
}
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
// If this script loader is being used to make a new worker then we need
|
||||
// to do a same-origin check. Otherwise we need to clear the load with the
|
||||
// security manager.
|
||||
if (aIsWorkerScript) {
|
||||
nsCString scheme;
|
||||
rv = uri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
using mozilla::dom::Throw;
|
||||
// We pass true as the 3rd argument to checkMayLoad here.
|
||||
// This allows workers in sandboxed documents to load data URLs
|
||||
// (and other URLs that inherit their principal from their
|
||||
// creator.)
|
||||
rv = principal->CheckMayLoad(uri, false, true);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
else {
|
||||
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Get Content Security Policy from parent document to pass into channel.
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->GetCsp(getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIChannelPolicy> channelPolicy;
|
||||
if (csp) {
|
||||
channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channelPolicy->SetContentSecurityPolicy(csp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
|
||||
flags, channelPolicy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
channel.forget(aChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
class ScriptLoaderRunnable;
|
||||
|
||||
@ -275,7 +343,7 @@ public:
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
if (mIsWorkerScript) {
|
||||
// May be null.
|
||||
channel = mWorkerPrivate->GetChannel();
|
||||
channel = mWorkerPrivate->ForgetWorkerChannel();
|
||||
}
|
||||
|
||||
// All of these can potentially be null, but that should be ok. We'll either
|
||||
@ -295,9 +363,8 @@ public:
|
||||
nsresult& rv = loadInfo.mLoadResult;
|
||||
|
||||
if (!channel) {
|
||||
rv = scriptloader::ChannelFromScriptURL(principal, baseURI, parentDoc,
|
||||
loadGroup, ios, secMan,
|
||||
loadInfo.mURL, mIsWorkerScript,
|
||||
rv = ChannelFromScriptURL(principal, baseURI, parentDoc, loadGroup, ios,
|
||||
secMan, loadInfo.mURL, mIsWorkerScript,
|
||||
getter_AddRefs(channel));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
@ -481,6 +548,12 @@ public:
|
||||
void
|
||||
ExecuteFinishedScripts()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mIsWorkerScript) {
|
||||
mWorkerPrivate->WorkerScriptLoaded();
|
||||
}
|
||||
|
||||
uint32_t firstIndex = UINT32_MAX;
|
||||
uint32_t lastIndex = UINT32_MAX;
|
||||
|
||||
@ -544,14 +617,14 @@ class ChannelGetterRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mParentWorker;
|
||||
uint32_t mSyncQueueKey;
|
||||
const nsString& mScriptURL;
|
||||
const nsAString& mScriptURL;
|
||||
nsIChannel** mChannel;
|
||||
nsresult mResult;
|
||||
|
||||
public:
|
||||
ChannelGetterRunnable(WorkerPrivate* aParentWorker,
|
||||
uint32_t aSyncQueueKey,
|
||||
const nsString& aScriptURL,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
: mParentWorker(aParentWorker), mSyncQueueKey(aSyncQueueKey),
|
||||
mScriptURL(aScriptURL), mChannel(aChannel), mResult(NS_ERROR_FAILURE)
|
||||
@ -723,98 +796,11 @@ BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
namespace scriptloader {
|
||||
|
||||
// static
|
||||
nsresult
|
||||
ChannelFromScriptURL(nsIPrincipal* principal,
|
||||
nsIURI* baseURI,
|
||||
nsIDocument* parentDoc,
|
||||
nsILoadGroup* loadGroup,
|
||||
nsIIOService* ios,
|
||||
nsIScriptSecurityManager* secMan,
|
||||
const nsString& aScriptURL,
|
||||
bool aIsWorkerScript,
|
||||
nsIChannel** aChannel)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
|
||||
aScriptURL, parentDoc,
|
||||
baseURI);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
// If we're part of a document then check the content load policy.
|
||||
if (parentDoc) {
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
|
||||
principal, parentDoc,
|
||||
NS_LITERAL_CSTRING("text/javascript"),
|
||||
nullptr, &shouldLoad,
|
||||
nsContentUtils::GetContentPolicy(),
|
||||
secMan);
|
||||
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
|
||||
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
|
||||
return rv = NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
|
||||
}
|
||||
}
|
||||
|
||||
// If this script loader is being used to make a new worker then we need
|
||||
// to do a same-origin check. Otherwise we need to clear the load with the
|
||||
// security manager.
|
||||
if (aIsWorkerScript) {
|
||||
nsCString scheme;
|
||||
rv = uri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We pass true as the 3rd argument to checkMayLoad here.
|
||||
// This allows workers in sandboxed documents to load data URLs
|
||||
// (and other URLs that inherit their principal from their
|
||||
// creator.)
|
||||
rv = principal->CheckMayLoad(uri, false, true);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
else {
|
||||
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
|
||||
// Get Content Security Policy from parent document to pass into channel.
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->GetCsp(getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIChannelPolicy> channelPolicy;
|
||||
if (csp) {
|
||||
channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channelPolicy->SetContentSecurityPolicy(csp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
|
||||
flags, channelPolicy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
channel.forget(aChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
|
||||
nsIURI* aBaseURI,
|
||||
nsIDocument* aParentDoc,
|
||||
const nsString& aScriptURL,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
@ -836,7 +822,7 @@ ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
|
||||
nsresult
|
||||
ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
WorkerPrivate* aParent,
|
||||
const nsString& aScriptURL,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel)
|
||||
{
|
||||
aParent->AssertIsOnWorkerThread();
|
||||
@ -859,7 +845,7 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
return getter->GetResult();
|
||||
}
|
||||
|
||||
void ReportLoadError(JSContext* aCx, const nsString& aURL,
|
||||
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
|
||||
nsresult aLoadResult, bool aIsMainThread)
|
||||
{
|
||||
NS_LossyConvertUTF16toASCII url(aURL);
|
||||
|
@ -22,16 +22,16 @@ nsresult
|
||||
ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
|
||||
nsIURI* aBaseURI,
|
||||
nsIDocument* aParentDoc,
|
||||
const nsString& aScriptURL,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel);
|
||||
|
||||
nsresult
|
||||
ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
WorkerPrivate* aParent,
|
||||
const nsString& aScriptURL,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel);
|
||||
|
||||
void ReportLoadError(JSContext* aCx, const nsString& aURL,
|
||||
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
|
||||
nsresult aLoadResult, bool aIsMainThread);
|
||||
|
||||
bool LoadWorkerScript(JSContext* aCx);
|
||||
|
235
dom/workers/SharedWorker.cpp
Normal file
235
dom/workers/SharedWorker.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SharedWorker.h"
|
||||
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/SharedWorkerBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMEvent.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
|
||||
#include "MessagePort.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "Worker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::Sequence;
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
const char kSharedWorkersEnabledPref[] = "dom.workers.sharedWorkers.enabled";
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: nsDOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate),
|
||||
mSuspended(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
mSerial = aWorkerPrivate->NextMessagePortSerial();
|
||||
|
||||
mMessagePort = new MessagePort(aWindow, this, mSerial);
|
||||
}
|
||||
|
||||
SharedWorker::~SharedWorker()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mWorkerPrivate);
|
||||
}
|
||||
|
||||
//static
|
||||
bool
|
||||
SharedWorker::PrefEnabled()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return mozilla::Preferences::GetBool(kSharedWorkersEnabledPref, false);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<SharedWorker>
|
||||
SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
const nsAString& aScriptURL,
|
||||
const mozilla::dom::Optional<nsAString>& aName,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
MOZ_ASSERT(window);
|
||||
|
||||
RuntimeService* rts = RuntimeService::GetOrCreateService();
|
||||
if (!rts) {
|
||||
aRv = NS_ERROR_NOT_AVAILABLE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString name;
|
||||
if (aName.WasPassed()) {
|
||||
name = aName.Value();
|
||||
}
|
||||
|
||||
nsRefPtr<SharedWorker> sharedWorker;
|
||||
nsresult rv = rts->CreateSharedWorker(aCx, window, aScriptURL, name,
|
||||
getter_AddRefs(sharedWorker));
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv = rv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sharedWorker.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MessagePort>
|
||||
SharedWorker::Port()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsRefPtr<MessagePort> messagePort = mMessagePort;
|
||||
return messagePort.forget();
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::Suspend()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!IsSuspended());
|
||||
|
||||
mSuspended = true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::Resume()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(IsSuspended());
|
||||
|
||||
mSuspended = false;
|
||||
|
||||
if (!mSuspendedEvents.IsEmpty()) {
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>> events;
|
||||
mSuspendedEvents.SwapElements(events);
|
||||
|
||||
for (uint32_t index = 0; index < events.Length(); index++) {
|
||||
nsCOMPtr<nsIDOMEvent>& event = events[index];
|
||||
MOZ_ASSERT(event);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
if (NS_SUCCEEDED(event->GetTarget(getter_AddRefs(target)))) {
|
||||
bool ignored;
|
||||
if (NS_FAILED(target->DispatchEvent(event, &ignored))) {
|
||||
NS_WARNING("Failed to dispatch event!");
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Failed to get target!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::QueueEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aEvent);
|
||||
MOZ_ASSERT(IsSuspended());
|
||||
|
||||
mSuspendedEvents.AppendElement(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::Close()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mMessagePort) {
|
||||
mMessagePort->Close();
|
||||
}
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
AutoSafeJSContext cx;
|
||||
NoteDeadWorker(cx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::PostMessage(JSContext* aCx, JS::HandleValue aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
MOZ_ASSERT(mMessagePort);
|
||||
|
||||
mWorkerPrivate->PostMessageToMessagePort(aCx, mMessagePort->Serial(),
|
||||
aMessage, aTransferable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::NoteDeadWorker(JSContext* aCx)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
|
||||
mWorkerPrivate->UnregisterSharedWorker(aCx, this);
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(SharedWorker, nsDOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(SharedWorker, nsDOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SharedWorker)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
|
||||
nsDOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
|
||||
nsDOMEventTargetHelper)
|
||||
tmp->Close();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
JSObject*
|
||||
SharedWorker::WrapObject(JSContext* aCx, JS::HandleObject aScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return SharedWorkerBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SharedWorker::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIDOMEvent*& event = aVisitor.mDOMEvent;
|
||||
|
||||
if (IsSuspended() && event) {
|
||||
QueueEvent(event);
|
||||
|
||||
aVisitor.mCanHandle = false;
|
||||
aVisitor.mParentTarget = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
|
||||
}
|
104
dom/workers/SharedWorker.h
Normal file
104
dom/workers/SharedWorker.h
Normal file
@ -0,0 +1,104 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_sharedworker_h__
|
||||
#define mozilla_dom_workers_sharedworker_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
|
||||
class nsIDOMEvent;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class MessagePort;
|
||||
class RuntimeService;
|
||||
class WorkerPrivate;
|
||||
|
||||
class SharedWorker MOZ_FINAL : public nsDOMEventTargetHelper
|
||||
{
|
||||
friend class MessagePort;
|
||||
friend class RuntimeService;
|
||||
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
typedef mozilla::dom::GlobalObject GlobalObject;
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<MessagePort> mMessagePort;
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>> mSuspendedEvents;
|
||||
uint64_t mSerial;
|
||||
bool mSuspended;
|
||||
|
||||
public:
|
||||
static bool
|
||||
PrefEnabled();
|
||||
|
||||
static already_AddRefed<SharedWorker>
|
||||
Constructor(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
const nsAString& aScriptURL, const Optional<nsAString>& aName,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<MessagePort>
|
||||
Port();
|
||||
|
||||
uint64_t
|
||||
Serial() const
|
||||
{
|
||||
return mSerial;
|
||||
}
|
||||
|
||||
bool
|
||||
IsSuspended() const
|
||||
{
|
||||
return mSuspended;
|
||||
}
|
||||
|
||||
void
|
||||
Suspend();
|
||||
|
||||
void
|
||||
Resume();
|
||||
|
||||
void
|
||||
QueueEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
void
|
||||
Close();
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SharedWorker, nsDOMEventTargetHelper)
|
||||
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// This class can only be created from the RuntimeService.
|
||||
SharedWorker(nsPIDOMWindow* aWindow, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// This class is reference-counted and will be destroyed from Release().
|
||||
~SharedWorker();
|
||||
|
||||
// Only called by MessagePort.
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::HandleValue aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Only called by RuntimeService.
|
||||
void
|
||||
NoteDeadWorker(JSContext* aCx);
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_sharedworker_h__
|
@ -8,6 +8,7 @@
|
||||
#include "mozilla/dom/DOMJSClass.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/EventHandlerBinding.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "EventTarget.h"
|
||||
@ -91,69 +92,43 @@ public:
|
||||
static WorkerPrivate*
|
||||
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
|
||||
|
||||
static JSObject*
|
||||
Create(JSContext* aCx, WorkerPrivate* aParentObj, const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, bool aIsSharedWorker,
|
||||
const nsAString& aSharedWorkerName);
|
||||
|
||||
protected:
|
||||
static bool
|
||||
ConstructInternal(JSContext* aCx, unsigned aArgc, jsval* aVp,
|
||||
bool aIsChromeWorker, const JSClass* aClass)
|
||||
ConstructInternal(JSContext* aCx, JS::CallArgs aArgs, bool aIsChromeWorker)
|
||||
{
|
||||
if (!aArgc) {
|
||||
if (!aArgs.length()) {
|
||||
JS_ReportError(aCx, "Constructor requires at least one argument!");
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> scriptURL(aCx, JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]));
|
||||
if (!scriptURL) {
|
||||
nsDependentJSString scriptURL;
|
||||
if (!scriptURL.init(aCx, aArgs[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jsval priv = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
|
||||
CONSTRUCTOR_SLOT_PARENT);
|
||||
JS::Rooted<JS::Value> priv(aCx,
|
||||
js::GetFunctionNativeReserved(&aArgs.callee(), CONSTRUCTOR_SLOT_PARENT));
|
||||
|
||||
RuntimeService* runtimeService;
|
||||
WorkerPrivate* parent;
|
||||
|
||||
if (JSVAL_IS_VOID(priv)) {
|
||||
runtimeService = RuntimeService::GetOrCreateService();
|
||||
if (!runtimeService) {
|
||||
JS_ReportError(aCx, "Failed to create runtime service!");
|
||||
return false;
|
||||
}
|
||||
if (priv.isUndefined()) {
|
||||
parent = NULL;
|
||||
}
|
||||
else {
|
||||
runtimeService = RuntimeService::GetService();
|
||||
parent = static_cast<WorkerPrivate*>(JSVAL_TO_PRIVATE(priv));
|
||||
} else {
|
||||
parent = static_cast<WorkerPrivate*>(priv.get().toPrivate());
|
||||
parent->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, aClass, nullptr, nullptr));
|
||||
JS::Rooted<JSObject*> obj(aCx,
|
||||
Create(aCx, parent, scriptURL, aIsChromeWorker, false, EmptyString()));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that the DOM_OBJECT_SLOT always has a PrivateValue set, as this
|
||||
// will be accessed in the Trace() method if WorkerPrivate::Create()
|
||||
// triggers a GC.
|
||||
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
|
||||
|
||||
nsRefPtr<WorkerPrivate> worker =
|
||||
WorkerPrivate::Create(aCx, obj, parent, scriptURL, aIsChromeWorker);
|
||||
if (!worker) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Worker now owned by the JS object.
|
||||
NS_ADDREF(worker.get());
|
||||
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(worker));
|
||||
|
||||
if (!runtimeService->RegisterWorker(aCx, worker)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Worker now also owned by its thread.
|
||||
NS_ADDREF(worker.get());
|
||||
|
||||
JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
|
||||
aArgs.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -281,9 +256,10 @@ private:
|
||||
}
|
||||
|
||||
static bool
|
||||
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
return ConstructInternal(aCx, aArgc, aVp, false, Class());
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return ConstructInternal(aCx, args, false);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -489,9 +465,10 @@ private:
|
||||
}
|
||||
|
||||
static bool
|
||||
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
return ConstructInternal(aCx, aArgc, aVp, true, Class());
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return ConstructInternal(aCx, args, true);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -574,6 +551,63 @@ Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Worker::Create(JSContext* aCx, WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
bool aIsSharedWorker, const nsAString& aSharedWorkerName)
|
||||
{
|
||||
MOZ_ASSERT_IF(aIsSharedWorker, !aSharedWorkerName.IsVoid());
|
||||
MOZ_ASSERT_IF(!aIsSharedWorker, aSharedWorkerName.IsEmpty());
|
||||
|
||||
RuntimeService* runtimeService;
|
||||
if (aParent) {
|
||||
runtimeService = RuntimeService::GetService();
|
||||
NS_ASSERTION(runtimeService, "Null runtime service!");
|
||||
}
|
||||
else {
|
||||
runtimeService = RuntimeService::GetOrCreateService();
|
||||
if (!runtimeService) {
|
||||
JS_ReportError(aCx, "Failed to create runtime service!");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const JSClass* classPtr = aIsChromeWorker ? ChromeWorker::Class() : Class();
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx,
|
||||
JS_NewObject(aCx, const_cast<JSClass*>(classPtr), nullptr, nullptr));
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure that the DOM_OBJECT_SLOT always has a PrivateValue set, as this will
|
||||
// be accessed in the Trace() method if WorkerPrivate::Create() triggers a GC.
|
||||
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
|
||||
|
||||
nsRefPtr<WorkerPrivate> worker =
|
||||
WorkerPrivate::Create(aCx, obj, aParent, aScriptURL, aIsChromeWorker,
|
||||
aIsSharedWorker, aSharedWorkerName);
|
||||
if (!worker) {
|
||||
// It'd be better if we could avoid allocating the JSObject until after we
|
||||
// make sure we have a WorkerPrivate, but failing that we should at least
|
||||
// make sure that the DOM_OBJECT_SLOT always has a PrivateValue.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Worker now owned by the JS object.
|
||||
NS_ADDREF(worker.get());
|
||||
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(worker));
|
||||
|
||||
if (!runtimeService->RegisterWorker(aCx, worker)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Worker now also owned by its thread.
|
||||
NS_ADDREF(worker.get());
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
namespace worker {
|
||||
@ -31,4 +32,4 @@ ClassIsWorker(const JSClass* aClass);
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_worker_h__ */
|
||||
#endif /* mozilla_dom_workers_worker_h__ */
|
225
dom/workers/WorkerMessagePort.cpp
Normal file
225
dom/workers/WorkerMessagePort.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WorkerMessagePort.h"
|
||||
|
||||
#include "DOMBindingInlines.h"
|
||||
#include "Events.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::Sequence;
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
using namespace mozilla::dom::workers::events;
|
||||
|
||||
namespace {
|
||||
|
||||
bool
|
||||
DispatchMessageEvent(JSContext* aCx, JS::HandleObject aMessagePort,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
{
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
aBuffer.swap(buffer);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
aClonedObjects.SwapElements(clonedObjects);
|
||||
|
||||
JS::Rooted<JSObject*> event(aCx,
|
||||
CreateMessageEvent(aCx, buffer, clonedObjects, false));
|
||||
if (!event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dummy;
|
||||
return DispatchEventToTarget(aCx, aMessagePort, event, &dummy);
|
||||
}
|
||||
|
||||
|
||||
class QueuedMessageEventRunnable : public WorkerRunnable
|
||||
{
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
|
||||
nsRefPtr<WorkerMessagePort> mMessagePort;
|
||||
JSObject* mMessagePortObject;
|
||||
|
||||
public:
|
||||
QueuedMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
|
||||
RunWhenClearing),
|
||||
mMessagePortObject(nullptr)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mBuffer.swap(aBuffer);
|
||||
mClonedObjects.SwapElements(aClonedObjects);
|
||||
}
|
||||
|
||||
bool
|
||||
Hold(JSContext* aCx, WorkerMessagePort* aMessagePort)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
MOZ_ASSERT(aMessagePort->GetJSObject());
|
||||
MOZ_ASSERT(!mMessagePortObject);
|
||||
|
||||
if (!JS_AddNamedObjectRoot(aCx, &mMessagePortObject,
|
||||
"WorkerMessagePort::MessageEventRunnable::"
|
||||
"mMessagePortObject")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mMessagePortObject = aMessagePort->GetJSObject();
|
||||
mMessagePort = aMessagePort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
JS::Rooted<JSObject*> messagePortObject(aCx, mMessagePortObject);
|
||||
mMessagePortObject = nullptr;
|
||||
|
||||
JS_RemoveObjectRoot(aCx, &mMessagePortObject);
|
||||
|
||||
nsRefPtr<WorkerMessagePort> messagePort;
|
||||
mMessagePort.swap(messagePort);
|
||||
|
||||
return DispatchMessageEvent(aCx, messagePortObject, mBuffer,
|
||||
mClonedObjects);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
WorkerMessagePort::_trace(JSTracer* aTrc)
|
||||
{
|
||||
EventTarget::_trace(aTrc);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerMessagePort::_finalize(JSFreeOp* aFop)
|
||||
{
|
||||
EventTarget::_finalize(aFop);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerMessagePort::PostMessage(
|
||||
JSContext* /* aCx */, JS::HandleValue aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mClosed) {
|
||||
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = GetJSContext();
|
||||
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
workerPrivate->PostMessageToParentMessagePort(cx, Serial(), aMessage,
|
||||
aTransferable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerMessagePort::Start()
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(GetJSContext());
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
if (mClosed) {
|
||||
NS_WARNING("Called start() after calling close()!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStarted = true;
|
||||
|
||||
if (!mQueuedMessages.IsEmpty()) {
|
||||
for (uint32_t index = 0; index < mQueuedMessages.Length(); index++) {
|
||||
MessageInfo& info = mQueuedMessages[index];
|
||||
|
||||
nsRefPtr<QueuedMessageEventRunnable> runnable =
|
||||
new QueuedMessageEventRunnable(workerPrivate, info.mBuffer,
|
||||
info.mClonedObjects);
|
||||
|
||||
JSContext* cx = GetJSContext();
|
||||
|
||||
if (!runnable->Hold(cx, this) ||
|
||||
!runnable->Dispatch(cx)) {
|
||||
NS_WARNING("Failed to dispatch queued event!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
mQueuedMessages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerMessagePort::MaybeDispatchEvent(
|
||||
JSContext* aCx,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
{
|
||||
if (mClosed) {
|
||||
NS_WARNING("Not going to ever run this event!");
|
||||
aBuffer.clear();
|
||||
aClonedObjects.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mStarted) {
|
||||
// Queue the message for later.
|
||||
MessageInfo* info = mQueuedMessages.AppendElement();
|
||||
info->mBuffer.swap(aBuffer);
|
||||
info->mClonedObjects.SwapElements(aClonedObjects);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Go ahead and dispatch the event.
|
||||
JS::Rooted<JSObject*> target(aCx, GetJSObject());
|
||||
return DispatchMessageEvent(aCx, target, aBuffer, aClonedObjects);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerMessagePort::CloseInternal()
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(GetJSContext());
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
MOZ_ASSERT(!IsClosed());
|
||||
MOZ_ASSERT_IF(mStarted, mQueuedMessages.IsEmpty());
|
||||
|
||||
mClosed = true;
|
||||
|
||||
workerPrivate->DisconnectMessagePort(Serial());
|
||||
|
||||
if (!mStarted) {
|
||||
mQueuedMessages.Clear();
|
||||
}
|
||||
}
|
103
dom/workers/WorkerMessagePort.h
Normal file
103
dom/workers/WorkerMessagePort.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_workermessageport_h__
|
||||
#define mozilla_dom_workers_workermessageport_h__
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/workers/bindings/EventTarget.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerPrivate;
|
||||
|
||||
class WorkerMessagePort : public EventTarget
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
|
||||
struct MessageInfo
|
||||
{
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
|
||||
};
|
||||
|
||||
nsTArray<MessageInfo> mQueuedMessages;
|
||||
uint64_t mSerial;
|
||||
bool mStarted;
|
||||
bool mClosed;
|
||||
|
||||
public:
|
||||
virtual void
|
||||
_trace(JSTracer* aTrc) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
_finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::HandleValue aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Start();
|
||||
|
||||
void
|
||||
Close()
|
||||
{
|
||||
if (!IsClosed()) {
|
||||
CloseInternal();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<EventHandlerNonNull>
|
||||
GetOnmessage(ErrorResult& aRv)
|
||||
{
|
||||
return GetEventListener(NS_LITERAL_STRING("message"), aRv);
|
||||
}
|
||||
|
||||
void
|
||||
SetOnmessage(EventHandlerNonNull* aListener, ErrorResult& aRv)
|
||||
{
|
||||
SetEventListener(NS_LITERAL_STRING("message"), aListener, aRv);
|
||||
if (!aRv.Failed()) {
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Serial() const
|
||||
{
|
||||
return mSerial;
|
||||
}
|
||||
|
||||
bool
|
||||
MaybeDispatchEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
|
||||
|
||||
bool
|
||||
IsClosed() const
|
||||
{
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
private:
|
||||
// Only created by WorkerPrivate.
|
||||
WorkerMessagePort(JSContext* aCx, uint64_t aSerial)
|
||||
: EventTarget(aCx), mSerial(aSerial), mStarted(false), mClosed(false)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerMessagePort()
|
||||
{ }
|
||||
|
||||
void
|
||||
CloseInternal();
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workermessageport_h__
|
File diff suppressed because it is too large
Load Diff
@ -19,9 +19,11 @@
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsEventQueue.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsTPriorityQueue.h"
|
||||
@ -33,6 +35,7 @@
|
||||
|
||||
class JSAutoStructuredCloneBuffer;
|
||||
class nsIChannel;
|
||||
class nsIContentSecurityPolicy;
|
||||
class nsIDocument;
|
||||
class nsIPrincipal;
|
||||
class nsIScriptContext;
|
||||
@ -46,6 +49,9 @@ class RuntimeStats;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class MessagePort;
|
||||
class SharedWorker;
|
||||
class WorkerMessagePort;
|
||||
class WorkerPrivate;
|
||||
|
||||
class WorkerRunnable : public nsIRunnable
|
||||
@ -246,7 +252,50 @@ public:
|
||||
nsCString mHash;
|
||||
};
|
||||
|
||||
struct LoadInfo
|
||||
{
|
||||
// All of these should be released in ForgetMainThreadObjects.
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
nsCOMPtr<nsIURI> mResolvedScriptURI;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
|
||||
nsCString mDomain;
|
||||
|
||||
bool mEvalAllowed;
|
||||
bool mReportCSPViolations;
|
||||
bool mXHRParamsAllowed;
|
||||
bool mPrincipalIsSystem;
|
||||
|
||||
LoadInfo()
|
||||
: mEvalAllowed(false), mReportCSPViolations(false),
|
||||
mXHRParamsAllowed(false), mPrincipalIsSystem(false)
|
||||
{ }
|
||||
|
||||
void
|
||||
StealFrom(LoadInfo& aOther)
|
||||
{
|
||||
mBaseURI = aOther.mBaseURI.forget();
|
||||
mResolvedScriptURI = aOther.mResolvedScriptURI.forget();
|
||||
mPrincipal = aOther.mPrincipal.forget();
|
||||
mScriptContext = aOther.mScriptContext.forget();
|
||||
mWindow = aOther.mWindow.forget();
|
||||
mCSP = aOther.mCSP.forget();
|
||||
mChannel = aOther.mChannel.forget();
|
||||
mDomain = aOther.mDomain;
|
||||
mEvalAllowed = aOther.mEvalAllowed;
|
||||
mReportCSPViolations = aOther.mReportCSPViolations;
|
||||
mXHRParamsAllowed = aOther.mXHRParamsAllowed;
|
||||
mPrincipalIsSystem = aOther.mPrincipalIsSystem;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
|
||||
SharedMutex mMutex;
|
||||
mozilla::CondVar mCondVar;
|
||||
mozilla::CondVar mMemoryReportCondVar;
|
||||
@ -254,19 +303,10 @@ protected:
|
||||
private:
|
||||
JSObject* mJSObject;
|
||||
WorkerPrivate* mParent;
|
||||
JSContext* mParentJSContext;
|
||||
nsString mScriptURL;
|
||||
nsCString mDomain;
|
||||
nsString mSharedWorkerName;
|
||||
LocationInfo mLocationInfo;
|
||||
|
||||
// Main-thread things.
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
nsCOMPtr<nsIURI> mScriptURI;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
LoadInfo mLoadInfo;
|
||||
|
||||
// Only used for top level workers.
|
||||
nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
|
||||
@ -277,28 +317,24 @@ private:
|
||||
// Protected by mMutex.
|
||||
JSSettings mJSSettings;
|
||||
|
||||
// Only touched on the parent thread (currently this is always the main
|
||||
// thread as SharedWorkers are always top-level).
|
||||
nsDataHashtable<nsUint64HashKey, SharedWorker*> mSharedWorkers;
|
||||
|
||||
uint64_t mBusyCount;
|
||||
uint64_t mMessagePortSerial;
|
||||
Status mParentStatus;
|
||||
bool mJSObjectRooted;
|
||||
bool mParentSuspended;
|
||||
bool mIsChromeWorker;
|
||||
bool mPrincipalIsSystem;
|
||||
bool mMainThreadObjectsForgotten;
|
||||
bool mEvalAllowed;
|
||||
bool mReportCSPViolations;
|
||||
bool mIsSharedWorker;
|
||||
|
||||
protected:
|
||||
WorkerPrivateParent(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
|
||||
JSContext* aParentJSContext, const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, const nsACString& aDomain,
|
||||
nsCOMPtr<nsPIDOMWindow>& aWindow,
|
||||
nsCOMPtr<nsIScriptContext>& aScriptContext,
|
||||
nsCOMPtr<nsIURI>& aBaseURI,
|
||||
nsCOMPtr<nsIPrincipal>& aPrincipal,
|
||||
nsCOMPtr<nsIChannel>& aChannel,
|
||||
nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
|
||||
bool aEvalAllowed,
|
||||
bool aReportCSPViolations);
|
||||
WorkerPrivateParent(JSContext* aCx, JS::HandleObject aObject,
|
||||
WorkerPrivate* aParent, const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, bool aIsSharedWorker,
|
||||
const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
|
||||
|
||||
~WorkerPrivateParent();
|
||||
|
||||
@ -320,6 +356,11 @@ private:
|
||||
return NotifyPrivate(aCx, Terminating);
|
||||
}
|
||||
|
||||
bool
|
||||
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransferable,
|
||||
bool aToMessagePort, uint64_t aMessagePortSerial);
|
||||
|
||||
public:
|
||||
// May be called on any thread...
|
||||
bool
|
||||
@ -345,13 +386,14 @@ public:
|
||||
}
|
||||
|
||||
bool
|
||||
Suspend(JSContext* aCx);
|
||||
|
||||
void
|
||||
Resume(JSContext* aCx);
|
||||
Suspend(JSContext* aCx, nsPIDOMWindow* aWindow);
|
||||
|
||||
bool
|
||||
SynchronizeAndResume(nsIScriptContext* aCx);
|
||||
Resume(JSContext* aCx, nsPIDOMWindow* aWindow);
|
||||
|
||||
bool
|
||||
SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow,
|
||||
nsIScriptContext* aScriptContext);
|
||||
|
||||
virtual void
|
||||
_trace(JSTracer* aTrc) MOZ_OVERRIDE;
|
||||
@ -387,7 +429,24 @@ public:
|
||||
|
||||
bool
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransferable);
|
||||
JS::Handle<JS::Value> aTransferable)
|
||||
{
|
||||
return PostMessageInternal(aCx, aMessage, aTransferable, false, 0);
|
||||
}
|
||||
|
||||
void
|
||||
PostMessageToMessagePort(JSContext* aCx,
|
||||
uint64_t aMessagePortSerial,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value > >& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
DispatchMessageEventToMessagePort(
|
||||
JSContext* aCx,
|
||||
uint64_t aMessagePortSerial,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects);
|
||||
|
||||
uint64_t
|
||||
GetInnerWindowId();
|
||||
@ -411,6 +470,24 @@ public:
|
||||
void
|
||||
GarbageCollect(JSContext* aCx, bool aShrinking);
|
||||
|
||||
bool
|
||||
RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
|
||||
|
||||
void
|
||||
UnregisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
|
||||
|
||||
void
|
||||
BroadcastErrorToSharedWorkers(JSContext* aCx,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aFilename,
|
||||
const nsAString& aLine,
|
||||
uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber,
|
||||
uint32_t aFlags);
|
||||
|
||||
void
|
||||
WorkerScriptLoaded();
|
||||
|
||||
void
|
||||
QueueRunnable(WorkerRunnable* aRunnable)
|
||||
{
|
||||
@ -457,7 +534,7 @@ public:
|
||||
GetScriptContext() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mScriptContext;
|
||||
return mLoadInfo.mScriptContext;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -475,38 +552,31 @@ public:
|
||||
const nsCString&
|
||||
Domain() const
|
||||
{
|
||||
return mDomain;
|
||||
return mLoadInfo.mDomain;
|
||||
}
|
||||
|
||||
nsIURI*
|
||||
GetBaseURI() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mBaseURI;
|
||||
return mLoadInfo.mBaseURI;
|
||||
}
|
||||
|
||||
void
|
||||
SetBaseURI(nsIURI* aBaseURI);
|
||||
|
||||
nsIURI*
|
||||
GetScriptURI() const
|
||||
GetResolvedScriptURI() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mScriptURI;
|
||||
}
|
||||
|
||||
void
|
||||
SetScriptURI(nsIURI* aScriptURI)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mScriptURI = aScriptURI;
|
||||
return mLoadInfo.mResolvedScriptURI;
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
GetPrincipal() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mPrincipal;
|
||||
return mLoadInfo.mPrincipal;
|
||||
}
|
||||
|
||||
void
|
||||
@ -515,60 +585,72 @@ public:
|
||||
bool
|
||||
UsesSystemPrincipal() const
|
||||
{
|
||||
return mPrincipalIsSystem;
|
||||
return mLoadInfo.mPrincipalIsSystem;
|
||||
}
|
||||
|
||||
nsIChannel*
|
||||
GetChannel() const
|
||||
already_AddRefed<nsIChannel>
|
||||
ForgetWorkerChannel()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mChannel;
|
||||
return mLoadInfo.mChannel.forget();
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
GetDocument() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mWindow ? mWindow->GetExtantDoc() : nullptr;
|
||||
return mLoadInfo.mWindow ? mLoadInfo.mWindow->GetExtantDoc() : nullptr;
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
GetWindow()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mWindow;
|
||||
return mLoadInfo.mWindow;
|
||||
}
|
||||
|
||||
nsIContentSecurityPolicy*
|
||||
GetCSP() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mCSP;
|
||||
return mLoadInfo.mCSP;
|
||||
}
|
||||
|
||||
void
|
||||
SetCSP(nsIContentSecurityPolicy* aCSP)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mCSP = aCSP;
|
||||
mLoadInfo.mCSP = aCSP;
|
||||
}
|
||||
|
||||
bool
|
||||
IsEvalAllowed() const
|
||||
{
|
||||
return mEvalAllowed;
|
||||
return mLoadInfo.mEvalAllowed;
|
||||
}
|
||||
|
||||
void
|
||||
SetEvalAllowed(bool aEvalAllowed)
|
||||
{
|
||||
mEvalAllowed = aEvalAllowed;
|
||||
mLoadInfo.mEvalAllowed = aEvalAllowed;
|
||||
}
|
||||
|
||||
bool
|
||||
GetReportCSPViolations() const
|
||||
{
|
||||
return mReportCSPViolations;
|
||||
return mLoadInfo.mReportCSPViolations;
|
||||
}
|
||||
|
||||
bool
|
||||
XHRParamsAllowed() const
|
||||
{
|
||||
return mLoadInfo.mXHRParamsAllowed;
|
||||
}
|
||||
|
||||
void
|
||||
SetXHRParamsAllowed(bool aAllowed)
|
||||
{
|
||||
mLoadInfo.mXHRParamsAllowed = aAllowed;
|
||||
}
|
||||
|
||||
LocationInfo&
|
||||
@ -590,6 +672,43 @@ public:
|
||||
return mIsChromeWorker;
|
||||
}
|
||||
|
||||
bool
|
||||
IsSharedWorker() const
|
||||
{
|
||||
return mIsSharedWorker;
|
||||
}
|
||||
|
||||
const nsString&
|
||||
SharedWorkerName() const
|
||||
{
|
||||
return mSharedWorkerName;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
NextMessagePortSerial()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mMessagePortSerial++;
|
||||
}
|
||||
|
||||
void
|
||||
GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker> >& aSharedWorkers);
|
||||
|
||||
void
|
||||
CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
RegisterHostObjectURI(const nsACString& aURI);
|
||||
|
||||
void
|
||||
UnregisterHostObjectURI(const nsACString& aURI);
|
||||
|
||||
void
|
||||
StealHostObjectURIs(nsTArray<nsCString>& aArray);
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
AssertIsOnParentThread() const;
|
||||
@ -605,10 +724,6 @@ public:
|
||||
AssertInnerWindowIsCorrect() const
|
||||
{ }
|
||||
#endif
|
||||
|
||||
void RegisterHostObjectURI(const nsACString& aURI);
|
||||
void UnregisterHostObjectURI(const nsACString& aURI);
|
||||
void StealHostObjectURIs(nsTArray<nsCString>& aArray);
|
||||
};
|
||||
|
||||
class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
||||
@ -658,6 +773,8 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsRefPtr<MemoryReporter> mMemoryReporter;
|
||||
|
||||
nsDataHashtable<nsUint64HashKey, WorkerMessagePort*> mWorkerPorts;
|
||||
|
||||
mozilla::TimeStamp mKillTime;
|
||||
uint32_t mErrorHandlerRecursionCount;
|
||||
uint32_t mNextTimeoutId;
|
||||
@ -669,7 +786,6 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
||||
bool mCloseHandlerFinished;
|
||||
bool mMemoryReporterRunning;
|
||||
bool mBlockedForMemoryReporter;
|
||||
bool mXHRParamsAllowed;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
@ -679,8 +795,15 @@ public:
|
||||
~WorkerPrivate();
|
||||
|
||||
static already_AddRefed<WorkerPrivate>
|
||||
Create(JSContext* aCx, JS::Handle<JSObject*> aObj, WorkerPrivate* aParent,
|
||||
JS::Handle<JSString*> aScriptURL, bool aIsChromeWorker);
|
||||
Create(JSContext* aCx, JS::HandleObject aObject, WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
bool aIsSharedWorker, const nsAString& aSharedWorkerName,
|
||||
LoadInfo* aLoadInfo = nullptr);
|
||||
|
||||
static nsresult
|
||||
GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
LoadInfo* aLoadInfo);
|
||||
|
||||
void
|
||||
DoRunLoop(JSContext* aCx);
|
||||
@ -764,8 +887,20 @@ public:
|
||||
DestroySyncLoop(uint32_t aSyncLoopKey);
|
||||
|
||||
bool
|
||||
PostMessageToParent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> transferable);
|
||||
PostMessageToParent(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransferable)
|
||||
{
|
||||
return PostMessageToParentInternal(aCx, aMessage, aTransferable, false, 0);
|
||||
}
|
||||
|
||||
void
|
||||
PostMessageToParentMessagePort(
|
||||
JSContext* aCx,
|
||||
uint64_t aMessagePortSerial,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value > >& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
NotifyInternal(JSContext* aCx, Status aStatus);
|
||||
@ -812,18 +947,6 @@ public:
|
||||
bool
|
||||
BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
|
||||
|
||||
bool
|
||||
XHRParamsAllowed() const
|
||||
{
|
||||
return mXHRParamsAllowed;
|
||||
}
|
||||
|
||||
void
|
||||
SetXHRParamsAllowed(bool aAllowed)
|
||||
{
|
||||
mXHRParamsAllowed = aAllowed;
|
||||
}
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
void
|
||||
UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency);
|
||||
@ -885,16 +1008,20 @@ public:
|
||||
BeginCTypesCall();
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial);
|
||||
|
||||
void
|
||||
DisconnectMessagePort(uint64_t aMessagePortSerial);
|
||||
|
||||
WorkerMessagePort*
|
||||
GetMessagePort(uint64_t aMessagePortSerial);
|
||||
|
||||
private:
|
||||
WorkerPrivate(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
|
||||
JSContext* aParentJSContext, const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, const nsACString& aDomain,
|
||||
nsCOMPtr<nsPIDOMWindow>& aWindow,
|
||||
nsCOMPtr<nsIScriptContext>& aScriptContext,
|
||||
nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
|
||||
nsCOMPtr<nsIChannel>& aChannel,
|
||||
nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
|
||||
bool aReportCSPViolations, bool aXHRParamsAllowed);
|
||||
WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
|
||||
WorkerPrivate* aParent, const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, bool aIsSharedWorker,
|
||||
const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
|
||||
|
||||
bool
|
||||
Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
|
||||
@ -960,8 +1087,17 @@ private:
|
||||
void
|
||||
WaitForWorkerEvents(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT);
|
||||
|
||||
static bool
|
||||
CheckXHRParamsAllowed(nsPIDOMWindow* aWindow);
|
||||
static PLDHashOperator
|
||||
TraceMessagePorts(const uint64_t& aKey,
|
||||
WorkerMessagePort* aData,
|
||||
void* aUserArg);
|
||||
|
||||
bool
|
||||
PostMessageToParentInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransferable,
|
||||
bool aToMessagePort,
|
||||
uint64_t aMessagePortSerial);
|
||||
};
|
||||
|
||||
WorkerPrivate*
|
||||
|
@ -141,7 +141,8 @@ protected:
|
||||
_trace(JSTracer* aTrc) MOZ_OVERRIDE
|
||||
{
|
||||
for (int32_t i = 0; i < SLOT_COUNT; i++) {
|
||||
JS_CallHeapValueTracer(aTrc, &mSlots[i], "WorkerGlobalScope instance slot");
|
||||
JS_CallHeapValueTracer(aTrc, &mSlots[i],
|
||||
"WorkerGlobalScope instance slot");
|
||||
}
|
||||
mWorker->TraceInternal(aTrc);
|
||||
EventTarget::_trace(aTrc);
|
||||
@ -296,8 +297,10 @@ private:
|
||||
JSObject* wrapper = &JS_CALLEE(aCx, aVp).toObject();
|
||||
JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
|
||||
|
||||
JS::Rooted<JS::Value> scope(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
|
||||
JS::Rooted<JS::Value> listener(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
|
||||
JS::Rooted<JS::Value> scope(aCx,
|
||||
js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
|
||||
JS::Rooted<JS::Value> listener(aCx,
|
||||
js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
|
||||
|
||||
JS_ASSERT(scope.isObject());
|
||||
|
||||
@ -319,7 +322,8 @@ private:
|
||||
}
|
||||
|
||||
if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval) &&
|
||||
!JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, rval.address())) {
|
||||
!JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL,
|
||||
rval.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -334,9 +338,9 @@ private:
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
nsRefPtr<EventHandlerNonNull> adaptor =
|
||||
scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
|
||||
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to get event listener!");
|
||||
return false;
|
||||
@ -445,7 +449,7 @@ private:
|
||||
static bool
|
||||
Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -466,7 +470,7 @@ private:
|
||||
static bool
|
||||
ImportScripts(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -487,7 +491,7 @@ private:
|
||||
static bool
|
||||
SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -498,7 +502,8 @@ private:
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> dummy(aCx);
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", dummy.address())) {
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
|
||||
dummy.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -508,7 +513,7 @@ private:
|
||||
static bool
|
||||
ClearTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -534,7 +539,7 @@ private:
|
||||
static bool
|
||||
SetInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -545,7 +550,8 @@ private:
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> dummy(aCx);
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", dummy.address())) {
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
|
||||
dummy.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -555,7 +561,7 @@ private:
|
||||
static bool
|
||||
ClearInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -581,7 +587,7 @@ private:
|
||||
static bool
|
||||
Dump(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -615,7 +621,7 @@ private:
|
||||
static bool
|
||||
AtoB(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -625,7 +631,8 @@ private:
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> string(aCx);
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", string.address())) {
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
|
||||
string.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -641,7 +648,7 @@ private:
|
||||
static bool
|
||||
BtoA(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -651,7 +658,8 @@ private:
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> binary(aCx);
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", binary.address())) {
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
|
||||
binary.address())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -676,8 +684,8 @@ NS_INTERFACE_MAP_END
|
||||
const JSClass WorkerGlobalScope::sClass = {
|
||||
"WorkerGlobalScope",
|
||||
0,
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
|
||||
JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
|
||||
};
|
||||
|
||||
const JSPropertySpec WorkerGlobalScope::sProperties[] = {
|
||||
@ -747,12 +755,13 @@ public:
|
||||
static JSObject*
|
||||
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
|
||||
{
|
||||
JSObject* proto =
|
||||
JS::Rooted<JSObject*> proto(aCx,
|
||||
JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
|
||||
sProperties, sFunctions, NULL, NULL);
|
||||
sProperties, sFunctions, NULL, NULL));
|
||||
if (proto) {
|
||||
void* domClass = const_cast<DOMClass *>(DOMClassStruct());
|
||||
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
|
||||
JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
|
||||
JS::PrivateValue(domClass));
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
@ -807,9 +816,9 @@ private:
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
nsRefPtr<EventHandlerNonNull> handler =
|
||||
scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
|
||||
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to get event listener!");
|
||||
return false;
|
||||
@ -852,8 +861,7 @@ private:
|
||||
} else {
|
||||
handler = nullptr;
|
||||
}
|
||||
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
|
||||
handler, rv);
|
||||
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), handler, rv);
|
||||
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to set event listener!");
|
||||
@ -894,8 +902,8 @@ private:
|
||||
}
|
||||
|
||||
static bool
|
||||
Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId, unsigned aFlags,
|
||||
JS::MutableHandle<JSObject*> aObjp)
|
||||
Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
|
||||
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
|
||||
{
|
||||
bool resolved;
|
||||
if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
|
||||
@ -933,7 +941,7 @@ private:
|
||||
static bool
|
||||
PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
@ -967,9 +975,10 @@ const DOMJSClass DedicatedWorkerGlobalScope::sClass = {
|
||||
"DedicatedWorkerGlobalScope",
|
||||
JSCLASS_DOM_GLOBAL | JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_NEW_RESOLVE,
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub,
|
||||
Finalize, NULL, NULL, NULL, NULL, Trace
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
|
||||
JS_StrictPropertyStub, JS_EnumerateStub,
|
||||
reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, Finalize, nullptr,
|
||||
nullptr, nullptr, nullptr, Trace
|
||||
},
|
||||
{
|
||||
INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
|
||||
@ -1023,29 +1032,354 @@ const char* const DedicatedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
|
||||
"onmessage",
|
||||
};
|
||||
|
||||
class SharedWorkerGlobalScope : public WorkerGlobalScope
|
||||
{
|
||||
static DOMJSClass sClass;
|
||||
static DOMIfaceAndProtoJSClass sProtoClass;
|
||||
static const JSPropertySpec sProperties[];
|
||||
|
||||
enum
|
||||
{
|
||||
STRING_onconnect = 0,
|
||||
|
||||
STRING_COUNT
|
||||
};
|
||||
|
||||
static const char* const sEventStrings[STRING_COUNT];
|
||||
|
||||
public:
|
||||
static const JSClass*
|
||||
Class()
|
||||
{
|
||||
return sClass.ToJSClass();
|
||||
}
|
||||
|
||||
static const JSClass*
|
||||
ProtoClass()
|
||||
{
|
||||
return sProtoClass.ToJSClass();
|
||||
}
|
||||
|
||||
static const DOMClass*
|
||||
DOMClassStruct()
|
||||
{
|
||||
return &sClass.mClass;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
|
||||
{
|
||||
JS::Rooted<JSObject*> proto(aCx,
|
||||
JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
|
||||
sProperties, nullptr, nullptr, nullptr));
|
||||
if (proto) {
|
||||
void* domClass = const_cast<DOMClass *>(DOMClassStruct());
|
||||
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
|
||||
JS::PrivateValue(domClass));
|
||||
}
|
||||
return proto;
|
||||
}
|
||||
|
||||
static bool
|
||||
InitPrivate(JSContext* aCx, JSObject* aObj, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(JS_GetClass(aObj) == Class());
|
||||
|
||||
dom::AllocateProtoAndIfaceCache(aObj);
|
||||
|
||||
nsRefPtr<SharedWorkerGlobalScope> scope =
|
||||
new SharedWorkerGlobalScope(aCx, aWorkerPrivate);
|
||||
|
||||
js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(scope));
|
||||
|
||||
scope->SetIsDOMBinding();
|
||||
scope->SetWrapper(aObj);
|
||||
|
||||
scope.forget();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
SharedWorkerGlobalScope(JSContext* aCx, WorkerPrivate* aWorker)
|
||||
: WorkerGlobalScope(aCx, aWorker)
|
||||
{
|
||||
MOZ_COUNT_CTOR(mozilla::dom::workers::SharedWorkerGlobalScope);
|
||||
}
|
||||
|
||||
~SharedWorkerGlobalScope()
|
||||
{
|
||||
MOZ_COUNT_DTOR(mozilla::dom::workers::SharedWorkerGlobalScope);
|
||||
}
|
||||
|
||||
private:
|
||||
using EventTarget::GetEventListener;
|
||||
using EventTarget::SetEventListener;
|
||||
|
||||
static bool
|
||||
IsSharedWorkerGlobalScope(JS::Handle<JS::Value> aVal)
|
||||
{
|
||||
return aVal.isObject() && JS_GetClass(&aVal.toObject()) == Class();
|
||||
}
|
||||
|
||||
static bool
|
||||
GetOnconnectImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
auto name = sEventStrings[STRING_onconnect];
|
||||
|
||||
auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
nsRefPtr<EventHandlerNonNull> handler =
|
||||
scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to get event listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
aArgs.rval().setNull();
|
||||
} else {
|
||||
aArgs.rval().setObject(*handler->Callable());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetOnconnect(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
auto args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
|
||||
GetOnconnectImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnconnectImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
auto name = sEventStrings[STRING_onconnect];
|
||||
|
||||
auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (aArgs.length() == 0 || !aArgs[0].isObject()) {
|
||||
JS_ReportError(aCx, "Not an event listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ErrorResult rv;
|
||||
|
||||
JS::Rooted<JSObject*> listenerObj(aCx, aArgs[0].toObjectOrNull());
|
||||
nsRefPtr<EventHandlerNonNull> handler;
|
||||
if (listenerObj && JS_ObjectIsCallable(aCx, listenerObj)) {
|
||||
handler = new EventHandlerNonNull(listenerObj);
|
||||
} else {
|
||||
handler = nullptr;
|
||||
}
|
||||
scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), handler, rv);
|
||||
|
||||
if (rv.Failed()) {
|
||||
JS_ReportError(aCx, "Failed to set event listener!");
|
||||
return false;
|
||||
}
|
||||
|
||||
aArgs.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetOnconnect(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
auto args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
|
||||
SetOnconnectImpl>(aCx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
GetNameImpl(JSContext* aCx, JS::CallArgs aArgs)
|
||||
{
|
||||
auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), "name");
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
auto name = scope->mWorker->SharedWorkerName();
|
||||
MOZ_ASSERT(!name.IsVoid());
|
||||
|
||||
JS::Rooted<JSString*> nameStr(aCx,
|
||||
JS_InternUCStringN(aCx, name.get(), name.Length()));
|
||||
if (!nameStr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aArgs.rval().setString(nameStr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
auto args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
|
||||
GetNameImpl>(aCx, args);
|
||||
}
|
||||
|
||||
|
||||
static SharedWorkerGlobalScope*
|
||||
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
|
||||
{
|
||||
const JSClass* classPtr = JS_GetClass(aObj);
|
||||
if (classPtr == Class()) {
|
||||
return UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
|
||||
}
|
||||
|
||||
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_INCOMPATIBLE_PROTO, Class()->name, aFunctionName,
|
||||
classPtr->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
|
||||
{
|
||||
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_WRONG_CONSTRUCTOR, Class()->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
|
||||
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
|
||||
{
|
||||
bool resolved;
|
||||
if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aObjp.set(resolved ? aObj.get() : nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
Finalize(JSFreeOp* aFop, JSObject* aObj)
|
||||
{
|
||||
MOZ_ASSERT(JS_GetClass(aObj) == Class());
|
||||
SharedWorkerGlobalScope* scope =
|
||||
UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
|
||||
if (scope) {
|
||||
DestroyProtoAndIfaceCache(aObj);
|
||||
scope->_finalize(aFop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Trace(JSTracer* aTrc, JSObject* aObj)
|
||||
{
|
||||
MOZ_ASSERT(JS_GetClass(aObj) == Class());
|
||||
SharedWorkerGlobalScope* scope =
|
||||
UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
|
||||
if (scope) {
|
||||
TraceProtoAndIfaceCache(aTrc, aObj);
|
||||
scope->_trace(aTrc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DOMJSClass SharedWorkerGlobalScope::sClass = {
|
||||
{
|
||||
// We don't have to worry about Xray expando slots here because we'll never
|
||||
// have an Xray wrapper to a worker global scope.
|
||||
"SharedWorkerGlobalScope",
|
||||
JSCLASS_DOM_GLOBAL | JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_NEW_RESOLVE,
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
|
||||
JS_StrictPropertyStub, JS_EnumerateStub,
|
||||
reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, Finalize, nullptr,
|
||||
nullptr, nullptr, nullptr, Trace
|
||||
},
|
||||
{
|
||||
INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
|
||||
false,
|
||||
&sWorkerNativePropertyHooks
|
||||
}
|
||||
};
|
||||
|
||||
DOMIfaceAndProtoJSClass SharedWorkerGlobalScope::sProtoClass = {
|
||||
{
|
||||
// XXXbz we use "SharedWorkerGlobalScope" here to match sClass
|
||||
// so that we can JS_InitClass this JSClass and then
|
||||
// call JS_NewObject with our sClass and have it find the right
|
||||
// prototype.
|
||||
"SharedWorkerGlobalScope",
|
||||
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
|
||||
JS_PropertyStub, /* addProperty */
|
||||
JS_DeletePropertyStub, /* delProperty */
|
||||
JS_PropertyStub, /* getProperty */
|
||||
JS_StrictPropertyStub, /* setProperty */
|
||||
JS_EnumerateStub,
|
||||
JS_ResolveStub,
|
||||
JS_ConvertStub,
|
||||
nullptr, /* finalize */
|
||||
nullptr, /* checkAccess */
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
nullptr, /* trace */
|
||||
JSCLASS_NO_INTERNAL_MEMBERS
|
||||
},
|
||||
eInterfacePrototype,
|
||||
&sWorkerNativePropertyHooks,
|
||||
"[object SharedWorkerGlobalScope]",
|
||||
prototypes::id::_ID_Count,
|
||||
0
|
||||
};
|
||||
|
||||
const JSPropertySpec SharedWorkerGlobalScope::sProperties[] = {
|
||||
JS_PSGS(sEventStrings[STRING_onconnect], GetOnconnect, SetOnconnect,
|
||||
JSPROP_ENUMERATE),
|
||||
JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const char* const SharedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
|
||||
"onconnect",
|
||||
};
|
||||
|
||||
WorkerGlobalScope*
|
||||
WorkerGlobalScope::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
|
||||
const char* aFunctionName)
|
||||
{
|
||||
const JSClass* classPtr = JS_GetClass(aObj);
|
||||
|
||||
// We can only make DedicatedWorkerGlobalScope, not WorkerGlobalScope, so this
|
||||
// should never happen.
|
||||
JS_ASSERT(classPtr != Class());
|
||||
// We can only make [Dedicated|Shared]WorkerGlobalScope, not
|
||||
// WorkerGlobalScope, so this should never happen.
|
||||
MOZ_ASSERT(classPtr != Class());
|
||||
|
||||
if (classPtr == DedicatedWorkerGlobalScope::Class()) {
|
||||
return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj);
|
||||
}
|
||||
|
||||
JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
|
||||
sClass.name, aFunctionName, classPtr->name);
|
||||
return NULL;
|
||||
if (classPtr == SharedWorkerGlobalScope::Class()) {
|
||||
return UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
|
||||
}
|
||||
|
||||
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
|
||||
classPtr->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
|
||||
WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> aVal)
|
||||
{
|
||||
return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
|
||||
if (!aVal.isObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto classPtr = JS_GetClass(&aVal.toObject());
|
||||
|
||||
return classPtr == DedicatedWorkerGlobalScope::Class() ||
|
||||
classPtr == SharedWorkerGlobalScope::Class();
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
@ -1053,32 +1387,42 @@ WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
JSObject*
|
||||
CreateDedicatedWorkerGlobalScope(JSContext* aCx)
|
||||
CreateGlobalScope(JSContext* aCx)
|
||||
{
|
||||
using namespace mozilla::dom;
|
||||
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
JS_ASSERT(worker);
|
||||
MOZ_ASSERT(worker);
|
||||
|
||||
const JSClass* classPtr = worker->IsSharedWorker() ?
|
||||
SharedWorkerGlobalScope::Class() :
|
||||
DedicatedWorkerGlobalScope::Class();
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
if (worker->IsChromeWorker())
|
||||
if (worker->IsChromeWorker()) {
|
||||
options.setVersion(JSVERSION_LATEST);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx,
|
||||
JS_NewGlobalObject(aCx, DedicatedWorkerGlobalScope::Class(),
|
||||
GetWorkerPrincipal(), JS::DontFireOnNewGlobalHook, options));
|
||||
JS_NewGlobalObject(aCx, classPtr, GetWorkerPrincipal(),
|
||||
JS::DontFireOnNewGlobalHook, options));
|
||||
if (!global) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSAutoCompartment ac(aCx, global);
|
||||
|
||||
// Make the private slots now so that all our instance checks succeed.
|
||||
if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
|
||||
return NULL;
|
||||
if (worker->IsSharedWorker()) {
|
||||
if (!SharedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Proto chain should be:
|
||||
// global -> DedicatedWorkerGlobalScope
|
||||
// global -> [Dedicated|Shared]WorkerGlobalScope
|
||||
// -> WorkerGlobalScope
|
||||
// -> EventTarget
|
||||
// -> Object
|
||||
@ -1086,43 +1430,45 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
|
||||
JS::Rooted<JSObject*> eventTargetProto(aCx,
|
||||
EventTargetBinding_workers::GetProtoObject(aCx, global));
|
||||
if (!eventTargetProto) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> scopeProto(aCx,
|
||||
WorkerGlobalScope::InitClass(aCx, global, eventTargetProto));
|
||||
if (!scopeProto) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> dedicatedScopeProto(aCx,
|
||||
JS::Rooted<JSObject*> finalScopeProto(aCx,
|
||||
worker->IsSharedWorker() ?
|
||||
SharedWorkerGlobalScope::InitClass(aCx, global, scopeProto) :
|
||||
DedicatedWorkerGlobalScope::InitClass(aCx, global, scopeProto));
|
||||
if (!dedicatedScopeProto) {
|
||||
return NULL;
|
||||
if (!finalScopeProto) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JS_SetPrototype(aCx, global, dedicatedScopeProto)) {
|
||||
return NULL;
|
||||
if (!JS_SetPrototype(aCx, global, finalScopeProto)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject* workerProto = worker::InitClass(aCx, global, eventTargetProto,
|
||||
false);
|
||||
JS::Rooted<JSObject*> workerProto(aCx,
|
||||
worker::InitClass(aCx, global, eventTargetProto, false));
|
||||
if (!workerProto) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (worker->IsChromeWorker()) {
|
||||
if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
|
||||
!DefineChromeWorkerFunctions(aCx, global) ||
|
||||
!DefineOSFileConstants(aCx, global)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Init other classes we care about.
|
||||
if (!events::InitClasses(aCx, global, false) ||
|
||||
!file::InitClasses(aCx, global)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Init other paris-bindings.
|
||||
@ -1137,11 +1483,11 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
|
||||
!URLBinding_workers::GetConstructorObject(aCx, global) ||
|
||||
!WorkerLocationBinding_workers::GetConstructorObject(aCx, global) ||
|
||||
!WorkerNavigatorBinding_workers::GetConstructorObject(aCx, global)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JS_DefineProfilingFunctions(aCx, global)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS_FireOnNewGlobalObject(aCx, global);
|
||||
@ -1149,11 +1495,4 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
|
||||
return global;
|
||||
}
|
||||
|
||||
bool
|
||||
ClassIsWorkerGlobalScope(const JSClass* aClass)
|
||||
{
|
||||
return WorkerGlobalScope::Class() == aClass ||
|
||||
DedicatedWorkerGlobalScope::Class() == aClass;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
@ -11,11 +11,8 @@
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
JSObject*
|
||||
CreateDedicatedWorkerGlobalScope(JSContext* aCx);
|
||||
|
||||
bool
|
||||
ClassIsWorkerGlobalScope(const JSClass* aClass);
|
||||
CreateGlobalScope(JSContext* aCx);
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_workerscope_h__ */
|
||||
#endif /* mozilla_dom_workers_workerscope_h__ */
|
@ -168,13 +168,13 @@ ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid
|
||||
unsigned aFlags, JS::MutableHandle<JSObject*> aObjp);
|
||||
|
||||
void
|
||||
CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
|
||||
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
|
||||
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow);
|
||||
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
|
||||
|
||||
class WorkerTask {
|
||||
public:
|
||||
|
@ -20,9 +20,12 @@ EXPORTS.mozilla.dom.workers.bindings += [
|
||||
'EventTarget.h',
|
||||
'FileReaderSync.h',
|
||||
'Location.h',
|
||||
'MessagePort.h',
|
||||
'Navigator.h',
|
||||
'SharedWorker.h',
|
||||
'URL.h',
|
||||
'WorkerFeature.h',
|
||||
'WorkerMessagePort.h',
|
||||
'XMLHttpRequest.h',
|
||||
'XMLHttpRequestEventTarget.h',
|
||||
'XMLHttpRequestUpload.h',
|
||||
@ -37,12 +40,15 @@ CPP_SOURCES += [
|
||||
'File.cpp',
|
||||
'FileReaderSync.cpp',
|
||||
'Location.cpp',
|
||||
'MessagePort.cpp',
|
||||
'Navigator.cpp',
|
||||
'Principal.cpp',
|
||||
'RuntimeService.cpp',
|
||||
'ScriptLoader.cpp',
|
||||
'SharedWorker.cpp',
|
||||
'URL.cpp',
|
||||
'Worker.cpp',
|
||||
'WorkerMessagePort.cpp',
|
||||
'WorkerPrivate.cpp',
|
||||
'WorkerScope.cpp',
|
||||
'XMLHttpRequest.cpp',
|
||||
@ -64,4 +70,4 @@ LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/xpcom/build',
|
||||
]
|
||||
]
|
@ -8,4 +8,3 @@ MOCHITEST_FILES += \
|
||||
test_xhr_timeout.html \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
@ -22,6 +22,8 @@ support-files =
|
||||
json_worker.js
|
||||
location_worker.js
|
||||
longThread_worker.js
|
||||
multi_sharedWorker_frame.html
|
||||
multi_sharedWorker_sharedWorker.js
|
||||
navigator_worker.js
|
||||
newError_worker.js
|
||||
recursion_worker.js
|
||||
@ -30,6 +32,7 @@ support-files =
|
||||
relativeLoad_worker.js
|
||||
relativeLoad_worker2.js
|
||||
rvals_worker.js
|
||||
sharedWorker_sharedWorker.js
|
||||
simpleThread_worker.js
|
||||
suspend_iframe.html
|
||||
suspend_worker.js
|
||||
@ -73,6 +76,8 @@ support-files =
|
||||
[test_loadError.html]
|
||||
[test_location.html]
|
||||
[test_longThread.html]
|
||||
[test_multi_sharedWorker.html]
|
||||
[test_multi_sharedWorker_lifetimes.html]
|
||||
[test_navigator.html]
|
||||
[test_newError.html]
|
||||
[test_recursion.html]
|
||||
@ -81,6 +86,7 @@ support-files =
|
||||
[test_resolveWorker-assignment.html]
|
||||
[test_resolveWorker.html]
|
||||
[test_rvals.html]
|
||||
[test_sharedWorker.html]
|
||||
[test_simpleThread.html]
|
||||
[test_suspend.html]
|
||||
[test_terminate.html]
|
||||
|
52
dom/workers/test/multi_sharedWorker_frame.html
Normal file
52
dom/workers/test/multi_sharedWorker_frame.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for SharedWorker</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
function debug(message) {
|
||||
if (typeof(message) != "string") {
|
||||
throw new Error("debug() only accepts strings!");
|
||||
}
|
||||
parent.postMessage(message, "*");
|
||||
}
|
||||
|
||||
let worker;
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
if (!worker) {
|
||||
worker = new SharedWorker("multi_sharedWorker_sharedWorker.js",
|
||||
"FrameWorker");
|
||||
worker.onerror = function(event) {
|
||||
debug("Worker error: " + event.message);
|
||||
event.preventDefault();
|
||||
|
||||
let data = {
|
||||
type: "error",
|
||||
message: event.message,
|
||||
filename: event.filename,
|
||||
lineno: event.lineno,
|
||||
isErrorEvent: event instanceof ErrorEvent
|
||||
};
|
||||
parent.postMessage(data, "*");
|
||||
};
|
||||
|
||||
worker.port.onmessage = function(event) {
|
||||
debug("Worker message: " + JSON.stringify(event.data));
|
||||
parent.postMessage(event.data, "*");
|
||||
};
|
||||
}
|
||||
|
||||
debug("Posting message: " + JSON.stringify(event.data));
|
||||
worker.port.postMessage(event.data);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
72
dom/workers/test/multi_sharedWorker_sharedWorker.js
Normal file
72
dom/workers/test/multi_sharedWorker_sharedWorker.js
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
if (self.name != "FrameWorker") {
|
||||
throw new Error("Bad worker name: " + self.name);
|
||||
}
|
||||
|
||||
var registeredPorts = [];
|
||||
var errorCount = 0;
|
||||
var storedData;
|
||||
|
||||
self.onconnect = function(event) {
|
||||
var port = event.ports[0];
|
||||
|
||||
if (registeredPorts.length) {
|
||||
var data = {
|
||||
type: "connect"
|
||||
};
|
||||
|
||||
registeredPorts.forEach(function(registeredPort) {
|
||||
registeredPort.postMessage(data);
|
||||
});
|
||||
}
|
||||
|
||||
port.onmessage = function(event) {
|
||||
switch (event.data.command) {
|
||||
case "start":
|
||||
break;
|
||||
|
||||
case "error":
|
||||
throw new Error("Expected");
|
||||
|
||||
case "store":
|
||||
storedData = event.data.data;
|
||||
break;
|
||||
|
||||
case "retrieve":
|
||||
var data = {
|
||||
type: "result",
|
||||
data: storedData
|
||||
};
|
||||
port.postMessage(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Unknown command '" + error.data.command + "'");
|
||||
}
|
||||
};
|
||||
|
||||
registeredPorts.push(port);
|
||||
};
|
||||
|
||||
self.onerror = function(message, filename, lineno) {
|
||||
if (!errorCount++) {
|
||||
var data = {
|
||||
type: "worker-error",
|
||||
message: message,
|
||||
filename: filename,
|
||||
lineno: lineno
|
||||
};
|
||||
|
||||
registeredPorts.forEach(function (registeredPort) {
|
||||
registeredPort.postMessage(data);
|
||||
});
|
||||
|
||||
// Prevent the error from propagating the first time only.
|
||||
return true;
|
||||
}
|
||||
};
|
82
dom/workers/test/sharedWorker_sharedWorker.js
Normal file
82
dom/workers/test/sharedWorker_sharedWorker.js
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
if (!("self" in this)) {
|
||||
throw new Error("No 'self' exists on SharedWorkerGlobalScope!");
|
||||
}
|
||||
if (this !== self) {
|
||||
throw new Error("'self' not equal to global object!");
|
||||
}
|
||||
if (!(self instanceof SharedWorkerGlobalScope)) {
|
||||
throw new Error("self not a SharedWorkerGlobalScope instance!");
|
||||
}
|
||||
|
||||
var propsToCheck = [
|
||||
"location",
|
||||
"navigator",
|
||||
"close",
|
||||
"importScripts",
|
||||
"setTimeout",
|
||||
"clearTimeout",
|
||||
"setInterval",
|
||||
"clearInterval",
|
||||
"dump",
|
||||
"atob",
|
||||
"btoa"
|
||||
];
|
||||
|
||||
for (var index = 0; index < propsToCheck.length; index++) {
|
||||
var prop = propsToCheck[index];
|
||||
if (!(prop in self)) {
|
||||
throw new Error("SharedWorkerGlobalScope has no '" + prop + "' property!");
|
||||
}
|
||||
}
|
||||
|
||||
onconnect = function(event) {
|
||||
if (!(event instanceof WorkerMessageEvent)) {
|
||||
throw new Error("'connect' event is not a WorkerMessageEvent!");
|
||||
}
|
||||
if (!("ports" in event)) {
|
||||
throw new Error("'connect' event doesn't have a 'ports' property!");
|
||||
}
|
||||
if (!Array.isArray(event.ports)) {
|
||||
throw new Error("'connect' event has 'ports' property that isn't an " +
|
||||
"Array!");
|
||||
}
|
||||
if (event.ports.length != 1) {
|
||||
throw new Error("'connect' event has a 'ports' property with length '" +
|
||||
event.ports.length + "'!");
|
||||
}
|
||||
if (!event.ports[0]) {
|
||||
throw new Error("'connect' event has a null 'ports[0]' property!");
|
||||
}
|
||||
if (!(event.ports[0] instanceof WorkerMessagePort)) {
|
||||
throw new Error("'connect' event has a 'ports[0]' property that isn't a " +
|
||||
"MessagePort!");
|
||||
}
|
||||
if (event.data) {
|
||||
throw new Error("'connect' event has data: " + event.data);
|
||||
}
|
||||
|
||||
event.ports[0].onmessage = function(event) {
|
||||
if (!(event instanceof WorkerMessageEvent)) {
|
||||
throw new Error("'message' event is not a WorkerMessageEvent!");
|
||||
}
|
||||
if (!("ports" in event)) {
|
||||
throw new Error("'message' event doesn't have a 'ports' property!");
|
||||
}
|
||||
if (!Array.isArray(event.ports)) {
|
||||
throw new Error("'message' event has 'ports' property that isn't an " +
|
||||
"Array!");
|
||||
}
|
||||
if (event.ports.length) {
|
||||
throw new Error("'message' event has a 'ports' property with length '" +
|
||||
event.ports.length + "'!");
|
||||
}
|
||||
event.target.postMessage(event.data);
|
||||
throw new Error(event.data);
|
||||
};
|
||||
};
|
251
dom/workers/test/test_multi_sharedWorker.html
Normal file
251
dom/workers/test/test_multi_sharedWorker.html
Normal file
@ -0,0 +1,251 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for SharedWorker</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
const swPref = "dom.workers.sharedWorkers.enabled";
|
||||
|
||||
const basePath =
|
||||
location.pathname.substring(0,
|
||||
location.pathname.lastIndexOf("/") + 1);
|
||||
const baseURL = location.origin + basePath;
|
||||
|
||||
const frameRelativeURL = "multi_sharedWorker_frame.html";
|
||||
const frameAbsoluteURL = baseURL + frameRelativeURL;
|
||||
const workerAbsoluteURL =
|
||||
baseURL + "multi_sharedWorker_sharedWorker.js";
|
||||
|
||||
const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
const errorMessage = "Error: Expected";
|
||||
const errorLineno = 34;
|
||||
|
||||
let testGenerator = (function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
ok(!("SharedWorker" in window), "No SharedWorker without pref set");
|
||||
ok(!("WorkerMessagePort" in window),
|
||||
"No WorkerMessagePort without pref set");
|
||||
|
||||
SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, sendToGenerator);
|
||||
yield undefined;
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
if (typeof(event.data) == "string") {
|
||||
info(event.data);
|
||||
} else {
|
||||
sendToGenerator(event);
|
||||
}
|
||||
});
|
||||
|
||||
let frame1 = document.getElementById("frame1");
|
||||
frame1.src = frameRelativeURL;
|
||||
frame1.onload = sendToGenerator;
|
||||
|
||||
yield undefined;
|
||||
|
||||
frame1 = frame1.contentWindow;
|
||||
|
||||
let frame2 = document.getElementById("frame2");
|
||||
frame2.src = frameAbsoluteURL;
|
||||
frame2.onload = sendToGenerator;
|
||||
|
||||
yield undefined;
|
||||
|
||||
frame2 = frame2.contentWindow;
|
||||
|
||||
let data = {
|
||||
command: "start"
|
||||
};
|
||||
|
||||
frame1.postMessage(data, "*");
|
||||
frame2.postMessage(data, "*");
|
||||
|
||||
let event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame1, "First window got the event");
|
||||
is(event.data.type, "connect", "Got a connect message");
|
||||
|
||||
data = {
|
||||
command: "retrieve"
|
||||
};
|
||||
frame1.postMessage(data, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame1, "First window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, undefined, "No data stored yet");
|
||||
|
||||
frame2.postMessage(data, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame2, "Second window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, undefined, "No data stored yet");
|
||||
|
||||
data = {
|
||||
command: "store",
|
||||
data: storedData
|
||||
};
|
||||
frame2.postMessage(data, "*");
|
||||
|
||||
data = {
|
||||
command: "retrieve"
|
||||
};
|
||||
frame1.postMessage(data, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame1, "First window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, storedData, "Got stored data");
|
||||
|
||||
// This will generate two MessageEvents, one for each window.
|
||||
let sawFrame1Error = false;
|
||||
let sawFrame2Error = false;
|
||||
|
||||
data = {
|
||||
command: "error"
|
||||
};
|
||||
frame1.postMessage(data, "*");
|
||||
|
||||
// First event.
|
||||
event = yield undefined;
|
||||
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.data.type, "worker-error", "Got an error message");
|
||||
is(event.data.message, errorMessage, "Got correct error message");
|
||||
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
|
||||
is(event.data.lineno, errorLineno, "Got correct lineno");
|
||||
if (event.source == frame1) {
|
||||
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame1Error = true;
|
||||
} else if (event.source == frame2) {
|
||||
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame2Error = true;
|
||||
} else {
|
||||
ok(false, "Saw error from unknown window");
|
||||
}
|
||||
|
||||
// Second event
|
||||
event = yield undefined;
|
||||
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.data.type, "worker-error", "Got an error message");
|
||||
is(event.data.message, errorMessage, "Got correct error message");
|
||||
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
|
||||
is(event.data.lineno, errorLineno, "Got correct lineno");
|
||||
if (event.source == frame1) {
|
||||
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame1Error = true;
|
||||
} else if (event.source == frame2) {
|
||||
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame2Error = true;
|
||||
} else {
|
||||
ok(false, "Saw error from unknown window");
|
||||
}
|
||||
|
||||
is(sawFrame1Error, true, "Saw error for frame1");
|
||||
is(sawFrame2Error, true, "Saw error for frame2");
|
||||
|
||||
// This will generate two MessageEvents, one for each window.
|
||||
sawFrame1Error = false;
|
||||
sawFrame2Error = false;
|
||||
|
||||
data = {
|
||||
command: "error"
|
||||
};
|
||||
frame1.postMessage(data, "*");
|
||||
|
||||
// First event.
|
||||
event = yield undefined;
|
||||
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.data.type, "error", "Got an error message");
|
||||
is(event.data.message, errorMessage, "Got correct error message");
|
||||
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
|
||||
is(event.data.lineno, errorLineno, "Got correct lineno");
|
||||
is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
|
||||
if (event.source == frame1) {
|
||||
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame1Error = true;
|
||||
} else if (event.source == frame2) {
|
||||
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame2Error = true;
|
||||
} else {
|
||||
ok(false, "Saw error from unknown window");
|
||||
}
|
||||
|
||||
// Second event
|
||||
event = yield undefined;
|
||||
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.data.type, "error", "Got an error message");
|
||||
is(event.data.message, errorMessage, "Got correct error message");
|
||||
is(event.data.filename, workerAbsoluteURL, "Got correct filename");
|
||||
is(event.data.lineno, errorLineno, "Got correct lineno");
|
||||
is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
|
||||
if (event.source == frame1) {
|
||||
is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame1Error = true;
|
||||
} else if (event.source == frame2) {
|
||||
is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
|
||||
sawFrame2Error = true;
|
||||
} else {
|
||||
ok(false, "Saw error from unknown window");
|
||||
}
|
||||
|
||||
is(sawFrame1Error, true, "Saw error for frame1");
|
||||
is(sawFrame2Error, true, "Saw error for frame2");
|
||||
|
||||
// Try a shared worker in a different origin.
|
||||
frame1 = document.getElementById("frame1");
|
||||
frame1.src = "http://example.org" + basePath + frameRelativeURL;
|
||||
frame1.onload = sendToGenerator;
|
||||
yield undefined;
|
||||
|
||||
frame1 = frame1.contentWindow;
|
||||
|
||||
data = {
|
||||
command: "retrieve"
|
||||
};
|
||||
frame1.postMessage(data, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame1, "First window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, undefined, "No data stored yet");
|
||||
|
||||
frame2.postMessage(data, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame2, "First window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, storedData, "Got stored data");
|
||||
|
||||
window.removeEventListener("message", sendToGenerator);
|
||||
|
||||
SimpleTest.finish();
|
||||
yield undefined;
|
||||
})();
|
||||
|
||||
let sendToGenerator = testGenerator.send.bind(testGenerator);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="testGenerator.next();">
|
||||
<iframe id="frame1"></iframe>
|
||||
<iframe id="frame2"></iframe>
|
||||
</body>
|
||||
</html>
|
151
dom/workers/test/test_multi_sharedWorker_lifetimes.html
Normal file
151
dom/workers/test/test_multi_sharedWorker_lifetimes.html
Normal file
@ -0,0 +1,151 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for SharedWorker</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
const swPref = "dom.workers.sharedWorkers.enabled";
|
||||
const bfCachePref = "browser.sessionhistory.cache_subframes";
|
||||
|
||||
const frameRelativeURL = "multi_sharedWorker_frame.html";
|
||||
const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
let testGenerator = (function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
ok(!("SharedWorker" in window), "No SharedWorker without pref set");
|
||||
ok(!("WorkerMessagePort" in window),
|
||||
"No WorkerMessagePort without pref set");
|
||||
|
||||
SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, sendToGenerator);
|
||||
yield undefined;
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
if (typeof(event.data) == "string") {
|
||||
info(event.data);
|
||||
} else {
|
||||
sendToGenerator(event);
|
||||
}
|
||||
});
|
||||
|
||||
let frame = document.getElementById("frame");
|
||||
frame.src = frameRelativeURL;
|
||||
frame.onload = sendToGenerator;
|
||||
|
||||
yield undefined;
|
||||
|
||||
frame = frame.contentWindow;
|
||||
frame.postMessage({ command: "retrieve" }, "*");
|
||||
|
||||
let event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame, "Correct window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, undefined, "No data stored yet");
|
||||
|
||||
frame.postMessage({ command: "store", data: storedData }, "*");
|
||||
frame.postMessage({ command: "retrieve" }, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame, "Correct window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, storedData, "Got stored data");
|
||||
|
||||
// Navigate when the bfcache is disabled.
|
||||
info("Navigating to about:blank");
|
||||
let frame = document.getElementById("frame");
|
||||
frame.onload = sendToGenerator;
|
||||
frame.src = "about:blank";
|
||||
frame.contentWindow.document.body.offsetTop;
|
||||
|
||||
yield undefined;
|
||||
|
||||
info("Navigating to " + frameRelativeURL);
|
||||
frame.src = frameRelativeURL;
|
||||
frame.contentWindow.document.body.offsetTop;
|
||||
|
||||
yield undefined;
|
||||
|
||||
frame = frame.contentWindow;
|
||||
frame.postMessage({ command: "retrieve" }, "*");
|
||||
|
||||
let event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame, "Correct window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, undefined, "No data stored");
|
||||
|
||||
frame.postMessage({ command: "store", data: storedData }, "*");
|
||||
frame.postMessage({ command: "retrieve" }, "*");
|
||||
|
||||
event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame, "Correct window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, storedData, "Got stored data");
|
||||
|
||||
info("Enabling '" + bfCachePref + "' pref");
|
||||
SpecialPowers.pushPrefEnv({ set: [[bfCachePref, true]] },
|
||||
sendToGenerator);
|
||||
yield undefined;
|
||||
|
||||
// Navigate when the bfcache is enabled.
|
||||
let frame = document.getElementById("frame");
|
||||
frame.onload = sendToGenerator;
|
||||
|
||||
info("Navigating to about:blank");
|
||||
frame.src = "about:blank";
|
||||
frame.contentWindow.document.body.offsetTop;
|
||||
|
||||
yield undefined;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
info("Running GC");
|
||||
SpecialPowers.gc();
|
||||
|
||||
info("Waiting the event queue to clear");
|
||||
SpecialPowers.executeSoon(sendToGenerator);
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
info("Navigating to " + frameRelativeURL);
|
||||
frame.src = frameRelativeURL;
|
||||
frame.contentWindow.document.body.offsetTop;
|
||||
|
||||
yield undefined;
|
||||
|
||||
frame = frame.contentWindow;
|
||||
frame.postMessage({ command: "retrieve" }, "*");
|
||||
|
||||
let event = yield undefined;
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
is(event.source, frame, "Correct window got the event");
|
||||
is(event.data.type, "result", "Got a result message");
|
||||
is(event.data.data, storedData, "Still have data stored");
|
||||
|
||||
info("Resetting '" + bfCachePref + "' pref");
|
||||
SpecialPowers.popPrefEnv(sendToGenerator);
|
||||
yield undefined;
|
||||
|
||||
window.removeEventListener("message", sendToGenerator);
|
||||
|
||||
SimpleTest.finish();
|
||||
yield undefined;
|
||||
})();
|
||||
|
||||
let sendToGenerator = testGenerator.send.bind(testGenerator);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="testGenerator.next();">
|
||||
<iframe id="frame"></iframe>
|
||||
</body>
|
||||
</html>
|
76
dom/workers/test/test_sharedWorker.html
Normal file
76
dom/workers/test/test_sharedWorker.html
Normal file
@ -0,0 +1,76 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for SharedWorker</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js">
|
||||
</script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script class="testbody">
|
||||
"use strict";
|
||||
|
||||
const swPref = "dom.workers.sharedWorkers.enabled";
|
||||
|
||||
const href = window.location.href;
|
||||
const filename = "sharedWorker_sharedWorker.js";
|
||||
const sentMessage = "ping";
|
||||
const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) +
|
||||
filename;
|
||||
const errorLine = 80;
|
||||
const errorColumn = 0;
|
||||
|
||||
ok(!("SharedWorker" in window), "No SharedWorker without pref set");
|
||||
ok(!("WorkerMessagePort" in window),
|
||||
"No WorkerMessagePort without pref set");
|
||||
|
||||
SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, function() {
|
||||
var worker = new SharedWorker(filename);
|
||||
|
||||
ok(worker instanceof SharedWorker, "Got SharedWorker instance");
|
||||
ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'");
|
||||
ok(worker.port instanceof WorkerMessagePort,
|
||||
"Shared worker has MessagePort");
|
||||
|
||||
var receivedMessage;
|
||||
var receivedError;
|
||||
|
||||
worker.port.onmessage = function(event) {
|
||||
ok(event instanceof MessageEvent, "Got a MessageEvent");
|
||||
ok(event.target === worker.port,
|
||||
"MessageEvent has correct 'target' property");
|
||||
is(event.data, sentMessage, "Got correct message");
|
||||
ok(receivedMessage === undefined, "Haven't gotten message yet");
|
||||
ok(receivedError === undefined, "Haven't gotten error yet");
|
||||
receivedMessage = event.data;
|
||||
};
|
||||
|
||||
worker.onerror = function(event) {
|
||||
ok(event instanceof ErrorEvent, "Got an ErrorEvent");
|
||||
is(event.message, "Error: " + sentMessage, "Got correct error");
|
||||
is(event.filename, errorFilename, "Got correct filename");
|
||||
is(event.lineno, errorLine, "Got correct lineno");
|
||||
is(event.column, errorColumn, "Got correct column");
|
||||
ok(receivedMessage !== undefined, "Got message already");
|
||||
ok(receivedError === undefined, "Haven't gotten error yet");
|
||||
receivedError = event.message;
|
||||
event.preventDefault();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
worker.port.postMessage(sentMessage);
|
||||
});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -103,6 +103,9 @@ pref("dom.workers.enabled", true);
|
||||
// The number of workers per domain allowed to run concurrently.
|
||||
pref("dom.workers.maxPerDomain", 20);
|
||||
|
||||
// Whether or not Shared Web Workers are enabled.
|
||||
pref("dom.workers.sharedWorkers.enabled", false);
|
||||
|
||||
// Whether nonzero values can be returned from performance.timing.*
|
||||
pref("dom.enable_performance", true);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user