Skip to content
Browse files

Backout changeset fdddabd345b9 and changeset 3857a4309fc3 (bug 649537…

…) because of three mochitest-3 intermittent failures which seem to be caused by this on Windows debug builds
  • Loading branch information...
1 parent 0a63ad4 commit 75e3f2b0f2f7d379356f412512c4cb78df90c540 @ehsan ehsan committed Jul 18, 2011
Showing with 13,373 additions and 5,248 deletions.
  1. +1 −1 dom/Makefile.in
  2. +24 −38 dom/base/nsDOMClassInfo.cpp
  3. +3 −0 dom/base/nsDOMClassInfoClasses.h
  4. +22 −19 dom/base/nsGlobalWindow.cpp
  5. +17 −17 dom/base/nsJSEnvironment.cpp
  6. +0 −10 dom/base/nsJSEnvironment.h
  7. +1 −1 dom/dom-config.mk
  8. +15 −18 dom/{src/foo → interfaces/threads}/Makefile.in
  9. +156 −0 dom/interfaces/threads/nsIDOMWorkers.idl
  10. +1 −1 dom/src/Makefile.in
  11. +15 −22 dom/{workers → src/threads}/Makefile.in
  12. +1,608 −0 dom/src/threads/nsDOMThreadService.cpp
  13. +212 −0 dom/src/threads/nsDOMThreadService.h
  14. +2,752 −0 dom/src/threads/nsDOMWorker.cpp
  15. +471 −0 dom/src/threads/nsDOMWorker.h
  16. +635 −0 dom/src/threads/nsDOMWorkerEvents.cpp
  17. +340 −0 dom/src/threads/nsDOMWorkerEvents.h
  18. +223 −0 dom/src/threads/nsDOMWorkerLocation.cpp
  19. +35 −15 dom/{workers/ChromeWorkerScope.h → src/threads/nsDOMWorkerLocation.h}
  20. +135 −0 dom/src/threads/nsDOMWorkerMacros.h
  21. +440 −0 dom/src/threads/nsDOMWorkerMessageHandler.cpp
  22. +175 −0 dom/src/threads/nsDOMWorkerMessageHandler.h
  23. +108 −0 dom/src/threads/nsDOMWorkerNavigator.cpp
  24. +19 −19 dom/{workers/WorkerInlines.h → src/threads/nsDOMWorkerNavigator.h}
  25. +238 −0 dom/src/threads/nsDOMWorkerPool.cpp
  26. +118 −0 dom/src/threads/nsDOMWorkerPool.h
  27. +881 −0 dom/src/threads/nsDOMWorkerScriptLoader.cpp
  28. +225 −0 dom/src/threads/nsDOMWorkerScriptLoader.h
  29. +148 −0 dom/src/threads/nsDOMWorkerSecurityManager.cpp
  30. +20 −30 dom/{workers/XMLHttpRequest.h → src/threads/nsDOMWorkerSecurityManager.h}
  31. +485 −0 dom/src/threads/nsDOMWorkerTimeout.cpp
  32. +190 −0 dom/src/threads/nsDOMWorkerTimeout.h
  33. +910 −0 dom/src/threads/nsDOMWorkerXHR.cpp
  34. +172 −0 dom/src/threads/nsDOMWorkerXHR.h
  35. +182 −0 dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h
  36. +1,187 −0 dom/src/threads/nsDOMWorkerXHRProxy.cpp
  37. +198 −0 dom/src/threads/nsDOMWorkerXHRProxy.h
  38. +13 −12 dom/{workers → src/threads}/test/Makefile.in
  39. +23 −40 dom/{workers/Principal.cpp → src/threads/test/WorkerTest.jsm}
  40. +7 −20 dom/{workers/Principal.h → src/threads/test/WorkerTest_badworker.js}
  41. +76 −0 dom/src/threads/test/WorkerTest_subworker.js
  42. +11 −20 dom/{workers/WorkerScope.h → src/threads/test/WorkerTest_worker.js}
  43. +4 −8 dom/{workers → src/threads}/test/atob_worker.js
  44. +7 −27 dom/{workers/Navigator.h → src/threads/test/chromeWorker_subworker.js}
  45. +30 −24 dom/{workers/Location.h → src/threads/test/chromeWorker_worker.js}
  46. +0 −4 dom/{workers → src/threads}/test/closeOnGC_server.sjs
  47. +0 −4 dom/{workers → src/threads}/test/closeOnGC_worker.js
  48. +5 −0 dom/src/threads/test/close_worker.js
  49. +26 −0 dom/src/threads/test/errorPropagation_worker1.js
  50. +3 −0 dom/src/threads/test/errorPropagation_worker2.js
  51. +28 −0 dom/src/threads/test/fibonacci_worker.js
  52. +8 −0 dom/src/threads/test/functionHandlers_worker.js
  53. +0 −4 dom/{workers → src/threads}/test/importScripts_worker.js
  54. +0 −4 dom/{workers → src/threads}/test/importScripts_worker_imported1.js
  55. +0 −4 dom/{workers → src/threads}/test/importScripts_worker_imported2.js
  56. +2 −0 dom/src/threads/test/importScripts_worker_imported3.js
  57. +2 −0 dom/src/threads/test/importScripts_worker_imported4.js
  58. +4 −17 dom/{workers → src/threads}/test/json_worker.js
  59. +4 −0 dom/src/threads/test/location_worker.js
  60. +0 −4 dom/{workers → src/threads}/test/longThread_worker.js
  61. +0 −4 dom/{workers → src/threads}/test/navigator_worker.js
  62. +1 −0 dom/src/threads/test/newError_worker.js
  63. +34 −0 dom/src/threads/test/recursion_worker.js
  64. +26 −0 dom/src/threads/test/regExpStatics_worker.js
  65. +1 −0 dom/src/threads/test/relativeLoad_import.js
  66. +1 −0 dom/src/threads/test/relativeLoad_sub_import.js
  67. +0 −4 dom/{workers → src/threads}/test/relativeLoad_sub_worker.js
  68. +5 −0 dom/src/threads/test/relativeLoad_sub_worker2.js
  69. +1 −5 dom/{workers → src/threads}/test/relativeLoad_worker.js
  70. +5 −0 dom/src/threads/test/relativeLoad_worker2.js
  71. +26 −0 dom/src/threads/test/scopeOnerror_worker.js
  72. +2 −18 dom/{workers → src/threads}/test/simpleThread_worker.js
  73. +3 −9 dom/{workers → src/threads}/test/suspend_iframe.html
  74. +5 −0 dom/src/threads/test/suspend_worker.js
  75. +0 −4 dom/{workers → src/threads}/test/terminate_worker.js
  76. 0 dom/{workers → src/threads}/test/testXHR.txt
  77. +0 −4 dom/{workers → src/threads}/test/test_404.html
  78. +0 −4 dom/{workers → src/threads}/test/test_atob.html
  79. +10 −4 dom/{workers → src/threads}/test/test_chromeWorker.html
  80. +97 −0 dom/src/threads/test/test_chromeWorker.xul
  81. +88 −0 dom/src/threads/test/test_chromeWorkerComponent.xul
  82. +90 −0 dom/src/threads/test/test_chromeWorkerJSM.xul
  83. +0 −4 dom/{workers → src/threads}/test/test_close.html
  84. +0 −4 dom/{workers → src/threads}/test/test_closeOnGC.html
  85. +107 −0 dom/src/threads/test/test_errorPropagation.html
  86. +2 −12 dom/{workers → src/threads}/test/test_fibonacci.html
  87. +14 −5 dom/src/{foo/test_foo.html → threads/test/test_functionHandlers.html}
  88. +0 −4 dom/{workers → src/threads}/test/test_importScripts.html
  89. +0 −4 dom/{workers → src/threads}/test/test_json.html
  90. +2 −10 dom/{workers → src/threads}/test/test_location.html
  91. +0 −4 dom/{workers → src/threads}/test/test_longThread.html
  92. +1 −5 dom/{workers → src/threads}/test/test_navigator.html
  93. +0 −5 dom/{workers → src/threads}/test/test_newError.html
  94. +4 −27 dom/{workers → src/threads}/test/test_recursion.html
  95. +47 −0 dom/src/threads/test/test_regExpStatics.html
  96. +1 −5 dom/{workers → src/threads}/test/test_relativeLoad.html
  97. +44 −0 dom/src/threads/test/test_scopeOnerror.html
  98. +1 −7 dom/{workers → src/threads}/test/test_simpleThread.html
  99. +2 −6 dom/{workers → src/threads}/test/test_suspend.html
  100. +0 −4 dom/{workers → src/threads}/test/test_terminate.html
  101. +0 −6 dom/{workers → src/threads}/test/test_threadErrors.html
  102. +0 −4 dom/{workers → src/threads}/test/test_threadTimeouts.html
  103. +0 −5 dom/{workers → src/threads}/test/test_throwingOnerror.html
  104. +0 −4 dom/{workers → src/threads}/test/test_xhr.html
  105. +5 −5 dom/{workers → src/threads}/test/test_xhrAbort.html
  106. +45 −0 dom/src/threads/test/test_xpcom.html
  107. +4 −0 dom/src/threads/test/threadErrors_worker1.js
  108. +4 −0 dom/src/threads/test/threadErrors_worker2.js
  109. +5 −0 dom/src/threads/test/threadErrors_worker3.js
  110. +4 −0 dom/src/threads/test/threadErrors_worker4.js
  111. +0 −4 dom/{workers → src/threads}/test/threadTimeouts_worker.js
  112. +0 −4 dom/{workers → src/threads}/test/throwingOnerror_worker.js
  113. +52 −0 dom/src/threads/test/xhrAbort_worker.js
  114. +1 −8 dom/{workers → src/threads}/test/xhr_worker.js
  115. +18 −0 dom/src/threads/test/xpcom_worker.js
  116. +0 −102 dom/workers/ChromeWorkerScope.cpp
  117. +0 −248 dom/workers/EventTarget.cpp
  118. +0 −109 dom/workers/EventTarget.h
  119. +0 −1,124 dom/workers/Events.cpp
  120. +0 −88 dom/workers/Events.h
  121. +0 −303 dom/workers/Exceptions.cpp
  122. +0 −86 dom/workers/Exceptions.h
  123. +0 −458 dom/workers/ListenerManager.cpp
  124. +0 −157 dom/workers/ListenerManager.h
  125. +0 −241 dom/workers/Location.cpp
  126. +0 −217 dom/workers/Navigator.cpp
  127. +0 −235 dom/workers/Queue.h
  128. +0 −1,071 dom/workers/RuntimeService.cpp
  129. +0 −224 dom/workers/RuntimeService.h
Sorry, we could not display the entire diff because it was too big.
View
2 dom/Makefile.in
@@ -62,6 +62,7 @@ DIRS = \
interfaces/json \
interfaces/offline \
interfaces/geolocation \
+ interfaces/threads \
interfaces/notification \
interfaces/svg \
$(NULL)
@@ -80,7 +81,6 @@ DIRS += \
indexedDB \
system \
ipc \
- workers \
$(NULL)
ifdef ENABLE_TESTS
View
62 dom/base/nsDOMClassInfo.cpp
@@ -460,7 +460,7 @@
#include "nsIDOMGeoPositionError.h"
// Workers
-#include "mozilla/dom/workers/Workers.h"
+#include "nsDOMWorker.h"
#include "nsDOMFile.h"
#include "nsDOMFileReader.h"
@@ -599,6 +599,9 @@ DOMCI_DATA(ContentFrameMessageManager, void)
DOMCI_DATA(DOMPrototype, void)
DOMCI_DATA(DOMConstructor, void)
+DOMCI_DATA(Worker, void)
+DOMCI_DATA(ChromeWorker, void)
+
#define NS_DEFINE_CLASSINFO_DATA_WITH_NAME(_class, _name, _helper, \
_flags) \
{ #_name, \
@@ -1428,6 +1431,11 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(MathMLElement, Element, nsElementSH,
ELEMENT_SCRIPTABLE_FLAGS)
+ NS_DEFINE_CLASSINFO_DATA(Worker, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
+ NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeWorker, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
NS_DEFINE_CLASSINFO_DATA(WebGLRenderingContext, nsWebGLViewportHandlerSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(WebGLBuffer, nsDOMGenericSH,
@@ -1564,6 +1572,8 @@ struct nsConstructorFuncMapData
static const nsConstructorFuncMapData kConstructorFuncMap[] =
{
+ NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Worker, nsDOMWorker::NewWorker)
+ NS_DEFINE_CONSTRUCTOR_FUNC_DATA(ChromeWorker, nsDOMWorker::NewChromeWorker)
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(File, nsDOMFileFile::NewFile)
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozBlobBuilder, NS_NewBlobBuilder)
};
@@ -4134,6 +4144,18 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeSelector)
DOM_CLASSINFO_MAP_END
+ DOM_CLASSINFO_MAP_BEGIN(Worker, nsIWorker)
+ DOM_CLASSINFO_MAP_ENTRY(nsIWorker)
+ DOM_CLASSINFO_MAP_ENTRY(nsIAbstractWorker)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+ DOM_CLASSINFO_MAP_END
+
+ DOM_CLASSINFO_MAP_BEGIN(ChromeWorker, nsIWorker)
+ DOM_CLASSINFO_MAP_ENTRY(nsIWorker)
+ DOM_CLASSINFO_MAP_ENTRY(nsIAbstractWorker)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+ DOM_CLASSINFO_MAP_END
+
DOM_CLASSINFO_MAP_BEGIN(WebGLRenderingContext, nsIDOMWebGLRenderingContext)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMWebGLRenderingContext)
DOM_CLASSINFO_MAP_END
@@ -6044,30 +6066,8 @@ nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
const nsGlobalNameStruct *name_struct;
rv = GetNameStruct(NS_ConvertASCIItoUTF16(dom_class->name), &name_struct);
- if (NS_FAILED(rv)) {
- return rv;
- }
-
if (!name_struct) {
- // This isn't a normal DOM object, see if this constructor lives on its
- // prototype chain.
- jsval val;
- if (!JS_GetProperty(cx, obj, "prototype", &val)) {
- return NS_ERROR_UNEXPECTED;
- }
-
- JS_ASSERT(!JSVAL_IS_PRIMITIVE(val));
- JSObject *dot_prototype = JSVAL_TO_OBJECT(val);
-
- JSObject *proto = JS_GetPrototype(cx, dom_obj);
- for ( ; proto; proto = JS_GetPrototype(cx, proto)) {
- if (proto == dot_prototype) {
- *bp = PR_TRUE;
- break;
- }
- }
-
- return NS_OK;
+ return rv;
}
if (name_struct->mType != nsGlobalNameStruct::eTypeClassConstructor &&
@@ -6702,10 +6702,6 @@ ContentWindowGetter(JSContext *cx, uintN argc, jsval *vp)
return ::JS_GetProperty(cx, obj, "content", vp);
}
-static JSNewResolveOp sOtherResolveFuncs[] = {
- mozilla::dom::workers::ResolveWorkerClasses
-};
-
NS_IMETHODIMP
nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, PRUint32 flags,
@@ -6932,16 +6928,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
if (!(flags & JSRESOLVE_ASSIGNING)) {
JSAutoRequest ar(cx);
- // Resolve special classes.
- for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sOtherResolveFuncs); i++) {
- if (!sOtherResolveFuncs[i](cx, obj, id, flags, objp)) {
- return NS_ERROR_FAILURE;
- }
- if (*objp) {
- return NS_OK;
- }
- }
-
// Call GlobalResolve() after we call FindChildWithName() so
// that named child frames will override external properties
// which have been registered with the script namespace manager.
View
3 dom/base/nsDOMClassInfoClasses.h
@@ -462,6 +462,9 @@ DOMCI_CLASS(MozTouchEvent)
DOMCI_CLASS(MathMLElement)
+DOMCI_CLASS(Worker)
+DOMCI_CLASS(ChromeWorker)
+
// WebGL
DOMCI_CLASS(WebGLRenderingContext)
DOMCI_CLASS(WebGLBuffer)
View
41 dom/base/nsGlobalWindow.cpp
@@ -91,9 +91,9 @@
#include "nsLayoutStatics.h"
#include "nsCycleCollector.h"
#include "nsCCUncollectableMarker.h"
+#include "nsDOMThreadService.h"
#include "nsAutoJSValHolder.h"
#include "nsDOMMediaQueryList.h"
-#include "mozilla/dom/workers/Workers.h"
// Interfaces Needed
#include "nsIFrame.h"
@@ -1244,11 +1244,18 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope)
NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
// Kill all of the workers for this window.
- nsIScriptContext *scx = GetContextInternal();
- JSContext *cx = scx ?
- static_cast<JSContext*>(scx->GetNativeContext()) :
- nsnull;
- mozilla::dom::workers::CancelWorkersForWindow(cx, this);
+ nsDOMThreadService* dts = nsDOMThreadService::get();
+ if (dts) {
+ nsIScriptContext *scx = GetContextInternal();
+
+ JSContext *cx = scx ? (JSContext *)scx->GetNativeContext() : nsnull;
+
+ // Have to suspend this request here because CancelWorkersForGlobal will
+ // lock until the worker has died and that could cause a deadlock.
+ JSAutoSuspendRequest asr(cx);
+
+ dts->CancelWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
+ }
// Close all IndexedDB databases for this window.
indexedDB::IndexedDatabaseManager* idbManager =
@@ -9921,13 +9928,11 @@ nsGlobalWindow::SuspendTimeouts(PRUint32 aIncrease,
if (!suspended) {
DisableDeviceMotionUpdates();
- // Suspend all of the workers for this window.
- nsIScriptContext *scx = GetContextInternal();
- JSContext *cx = scx ?
- static_cast<JSContext*>(scx->GetNativeContext()) :
- nsnull;
- mozilla::dom::workers::SuspendWorkersForWindow(cx, this);
-
+ nsDOMThreadService* dts = nsDOMThreadService::get();
+ if (dts) {
+ dts->SuspendWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
+ }
+
TimeStamp now = TimeStamp::Now();
for (nsTimeout *t = FirstTimeout(); IsTimeout(t); t = t->Next()) {
// Set mTimeRemaining to be the time remaining for this timer.
@@ -9999,12 +10004,10 @@ nsGlobalWindow::ResumeTimeouts(PRBool aThawChildren)
if (shouldResume) {
EnableDeviceMotionUpdates();
- // Resume all of the workers for this window.
- nsIScriptContext *scx = GetContextInternal();
- JSContext *cx = scx ?
- static_cast<JSContext*>(scx->GetNativeContext()) :
- nsnull;
- mozilla::dom::workers::ResumeWorkersForWindow(cx, this);
+ nsDOMThreadService* dts = nsDOMThreadService::get();
+ if (dts) {
+ dts->ResumeWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
+ }
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mTimeRemaining).
View
34 dom/base/nsJSEnvironment.cpp
@@ -3690,32 +3690,32 @@ ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
return jsPrincipals;
}
-JSObject*
-NS_DOMReadStructuredClone(JSContext* cx,
- JSStructuredCloneReader* reader,
- uint32 tag,
- uint32 data,
- void* closure)
+static JSObject*
+DOMReadStructuredClone(JSContext* cx,
+ JSStructuredCloneReader* reader,
+ uint32 tag,
+ uint32 data,
+ void* closure)
{
// We don't currently support any extensions to structured cloning.
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return nsnull;
}
-JSBool
-NS_DOMWriteStructuredClone(JSContext* cx,
- JSStructuredCloneWriter* writer,
- JSObject* obj,
- void *closure)
+static JSBool
+DOMWriteStructuredClone(JSContext* cx,
+ JSStructuredCloneWriter* writer,
+ JSObject* obj,
+ void *closure)
{
// We don't currently support any extensions to structured cloning.
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return JS_FALSE;
}
-void
-NS_DOMStructuredCloneError(JSContext* cx,
- uint32 errorid)
+static void
+DOMStructuredCloneError(JSContext* cx,
+ uint32 errorid)
{
// We don't currently support any extensions to structured cloning.
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
@@ -3759,9 +3759,9 @@ nsJSRuntime::Init()
// Set up the structured clone callbacks.
static JSStructuredCloneCallbacks cloneCallbacks = {
- NS_DOMReadStructuredClone,
- NS_DOMWriteStructuredClone,
- NS_DOMStructuredCloneError
+ DOMReadStructuredClone,
+ DOMWriteStructuredClone,
+ DOMStructuredCloneError
};
JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
View
10 dom/base/nsJSEnvironment.h
@@ -363,14 +363,4 @@ nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime);
/* prototypes */
void NS_ScriptErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
-JSObject* NS_DOMReadStructuredClone(JSContext* cx,
- JSStructuredCloneReader* reader, uint32 tag,
- uint32 data, void* closure);
-
-JSBool NS_DOMWriteStructuredClone(JSContext* cx,
- JSStructuredCloneWriter* writer,
- JSObject* obj, void *closure);
-
-void NS_DOMStructuredCloneError(JSContext* cx, uint32 errorid);
-
#endif /* nsJSEnvironment_h___ */
View
2 dom/dom-config.mk
@@ -7,7 +7,7 @@ DOM_SRCDIRS = \
dom/src/offline \
dom/src/geolocation \
dom/src/notification \
- dom/workers \
+ dom/src/threads \
content/xbl/src \
content/xul/document/src \
content/events/src \
View
33 dom/src/foo/Makefile.in → dom/interfaces/threads/Makefile.in
@@ -1,3 +1,4 @@
+#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
@@ -11,20 +12,19 @@
# for the specific language governing rights and limitations under the
# License.
#
-# The Original Code is worker threads.
+# The Original Code is mozilla.org code.
#
-# The Initial Developer of the Original Code is
-# Mozilla Corporation
-# Portions created by the Initial Developer are Copyright (C) 2008
+# The Initial Developer of the Original Code is Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
# Ben Turner <bent.mozilla@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
@@ -36,20 +36,17 @@
#
# ***** END LICENSE BLOCK *****
-DEPTH = ../../..
-topsrcdir = @top_srcdir@
-srcdir = @srcdir@
-VPATH = @srcdir@
-
-relativesrcdir = dom/src/foo
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
+MODULE = dom
+XPIDL_MODULE = dom_threads
+GRE_MODULE = 1
-_TEST_FILES = \
- test_foo.html \
- $(NULL)
+XPIDLSRCS = nsIDOMWorkers.idl
-libs:: $(_TEST_FILES)
- $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+include $(topsrcdir)/config/rules.mk
View
156 dom/interfaces/threads/nsIDOMWorkers.idl
@@ -0,0 +1,156 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Ben Turner <bent.mozilla@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * From http://www.whatwg.org/specs/web-workers/current-work
+ */
+
+#include "nsIDOMEvent.idl"
+#include "nsIDOMEventTarget.idl"
+
+interface nsIDOMEventListener;
+
+[scriptable, uuid(ab3725b8-3fca-40cc-a42c-92fb154ef01d)]
+interface nsIWorkerMessagePort : nsISupports
+{
+ void postMessage(/* in JSObject aMessage */);
+};
+
+[scriptable, uuid(508f2d49-e9a0-4fe8-bd33-321820173b4a)]
+interface nsIWorkerMessageEvent : nsIDOMEvent
+{
+ readonly attribute DOMString data;
+ readonly attribute DOMString origin;
+
+ readonly attribute nsISupports source;
+
+ void initMessageEvent(in DOMString aTypeArg,
+ in boolean aCanBubbleArg,
+ in boolean aCancelableArg,
+ in DOMString aDataArg,
+ in DOMString aOriginArg,
+ in nsISupports aSourceArg);
+};
+
+[scriptable, uuid(73d82c1d-05de-49c9-a23b-7121ff09a67a)]
+interface nsIWorkerErrorEvent : nsIDOMEvent
+{
+ readonly attribute DOMString message;
+ readonly attribute DOMString filename;
+
+ readonly attribute unsigned long lineno;
+
+ void initErrorEvent(in DOMString aTypeArg,
+ in boolean aCanBubbleArg,
+ in boolean aCancelableArg,
+ in DOMString aMessageArg,
+ in DOMString aFilenameArg,
+ in unsigned long aLinenoArg);
+};
+
+[scriptable, uuid(17a005c3-4f2f-4bb6-b169-c181fa6873de)]
+interface nsIWorkerLocation : nsISupports
+{
+ readonly attribute AUTF8String href;
+ readonly attribute AUTF8String protocol;
+ readonly attribute AUTF8String host;
+ readonly attribute AUTF8String hostname;
+ readonly attribute AUTF8String port;
+ readonly attribute AUTF8String pathname;
+ readonly attribute AUTF8String search;
+ readonly attribute AUTF8String hash;
+
+ AUTF8String toString();
+};
+
+[scriptable, uuid(74fb665a-e477-4ce2-b3c6-c58b1b28b6c3)]
+interface nsIWorkerNavigator : nsISupports
+{
+ readonly attribute DOMString appName;
+ readonly attribute DOMString appVersion;
+ readonly attribute DOMString platform;
+ readonly attribute DOMString userAgent;
+};
+
+[scriptable, uuid(c111e7d3-8044-4458-aa7b-637696ffb841)]
+interface nsIWorkerGlobalScope : nsISupports
+{
+ readonly attribute nsIWorkerGlobalScope self;
+ readonly attribute nsIWorkerNavigator navigator;
+ readonly attribute nsIWorkerLocation location;
+
+ attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, uuid(5c55ea4b-e4ac-4ceb-bfeb-46bd5e521b8a)]
+interface nsIWorkerScope : nsIWorkerGlobalScope
+{
+ void postMessage(/* in JSObject aMessage */);
+
+ void close();
+
+ attribute nsIDOMEventListener onmessage;
+ attribute nsIDOMEventListener onclose;
+};
+
+[scriptable, builtinclass, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)]
+interface nsIAbstractWorker : nsIDOMEventTarget
+{
+ attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, builtinclass, uuid(daf945c3-8d29-4724-8939-dd383f7d27a7)]
+interface nsIWorker : nsIAbstractWorker
+{
+ void postMessage(/* in JSObject aMessage */);
+
+ attribute nsIDOMEventListener onmessage;
+
+ void terminate();
+};
+
+[scriptable, uuid(cfc4bb32-ca83-4d58-9b6f-66f8054a333a)]
+interface nsIWorkerFactory : nsISupports
+{
+ nsIWorker newChromeWorker(/* in DOMString aScriptURL */);
+};
+
+%{ C++
+#define NS_WORKERFACTORY_CONTRACTID \
+"@mozilla.org/threads/workerfactory;1"
+%}
View
2 dom/src/Makefile.in
@@ -42,6 +42,6 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
-DIRS = jsurl events storage offline json geolocation notification foo
+DIRS = jsurl events storage offline json geolocation threads notification
include $(topsrcdir)/config/rules.mk
View
37 dom/workers/Makefile.in → dom/src/threads/Makefile.in
@@ -36,46 +36,39 @@
#
# ***** END LICENSE BLOCK *****
-DEPTH = ../..
+DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
-LIBRARY_NAME = domworkers_s
+LIBRARY_NAME = domthreads_s
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
-EXPORTS_NAMESPACES = mozilla/dom/workers
-
-EXPORTS_mozilla/dom/workers = Workers.h
-
CPPSRCS = \
- ChromeWorkerScope.cpp \
- Events.cpp \
- EventTarget.cpp \
- Exceptions.cpp \
- ListenerManager.cpp \
- Location.cpp \
- Navigator.cpp \
- Principal.cpp \
- RuntimeService.cpp \
- ScriptLoader.cpp \
- Worker.cpp \
- WorkerPrivate.cpp \
- WorkerScope.cpp \
- XMLHttpRequest.cpp \
- XMLHttpRequestPrivate.cpp \
+ nsDOMThreadService.cpp \
+ nsDOMWorker.cpp \
+ nsDOMWorkerEvents.cpp \
+ nsDOMWorkerLocation.cpp \
+ nsDOMWorkerMessageHandler.cpp \
+ nsDOMWorkerNavigator.cpp \
+ nsDOMWorkerPool.cpp \
+ nsDOMWorkerScriptLoader.cpp \
+ nsDOMWorkerSecurityManager.cpp \
+ nsDOMWorkerTimeout.cpp \
+ nsDOMWorkerXHR.cpp \
+ nsDOMWorkerXHRProxy.cpp \
$(NULL)
LOCAL_INCLUDES = \
-I$(topsrcdir)/content/base/src \
-I$(topsrcdir)/content/events/src \
-I$(topsrcdir)/dom/base \
+ -I$(topsrcdir)/dom/src/json \
-I$(topsrcdir)/js/src/xpconnect/src \
- -I$(topsrcdir)/xpcom/build \
$(NULL)
ifdef ENABLE_TESTS
View
1,608 dom/src/threads/nsDOMThreadService.cpp
@@ -0,0 +1,1608 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ * Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jscntxt.h"
+
+#include "nsDOMThreadService.h"
+
+// Interfaces
+#include "nsIComponentManager.h"
+#include "nsIConsoleService.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIEventTarget.h"
+#include "nsIJSContextStack.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIObserverService.h"
+#include "nsIScriptError.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPriority.h"
+#include "nsIThreadPool.h"
+#include "nsIXPConnect.h"
+#include "nsPIDOMWindow.h"
+
+// Other includes
+#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
+#include "nsDeque.h"
+#include "nsGlobalWindow.h"
+#include "nsIClassInfoImpl.h"
+#include "nsStringBuffer.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsXPCOMCIDInternal.h"
+#include "pratom.h"
+#include "prthread.h"
+#include "mozilla/Preferences.h"
+
+// DOMWorker includes
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerMacros.h"
+#include "nsDOMWorkerMessageHandler.h"
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerSecurityManager.h"
+#include "nsDOMWorkerTimeout.h"
+
+using namespace mozilla;
+
+#ifdef PR_LOGGING
+PRLogModuleInfo *gDOMThreadsLog = nsnull;
+#endif
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+// The maximum number of threads in the internal thread pool
+#define THREADPOOL_MAX_THREADS 3
+
+PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= 1);
+
+// The maximum number of idle threads in the internal thread pool
+#define THREADPOOL_IDLE_THREADS 3
+
+PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= THREADPOOL_IDLE_THREADS);
+
+// As we suspend threads for various reasons (navigating away from the page,
+// loading scripts, etc.) we open another slot in the thread pool for another
+// worker to use. We can't do this forever so we set an absolute cap on the
+// number of threads we'll allow to prevent DOS attacks.
+#define THREADPOOL_THREAD_CAP 20
+
+PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >= THREADPOOL_MAX_THREADS);
+
+// A "bad" value for the NSPR TLS functions.
+#define BAD_TLS_INDEX (PRUintn)-1
+
+// Easy access for static functions. No reference here.
+static nsDOMThreadService* gDOMThreadService = nsnull;
+
+// These pointers actually carry references and must be released.
+static nsIObserverService* gObserverService = nsnull;
+static nsIJSRuntimeService* gJSRuntimeService = nsnull;
+static nsIThreadJSContextStack* gThreadJSContextStack = nsnull;
+static nsIXPCSecurityManager* gWorkerSecurityManager = nsnull;
+
+PRUintn gJSContextIndex = BAD_TLS_INDEX;
+
+static const char* sPrefsToWatch[] = {
+ "dom.max_script_run_time"
+};
+
+// The length of time the close handler is allowed to run in milliseconds.
+static PRUint32 gWorkerCloseHandlerTimeoutMS = 10000;
+
+/**
+ * Simple class to automatically destroy a JSContext to make error handling
+ * easier.
+ */
+class JSAutoContextDestroyer
+{
+public:
+ JSAutoContextDestroyer(JSContext* aCx)
+ : mCx(aCx) { }
+
+ ~JSAutoContextDestroyer() {
+ if (mCx) {
+ nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
+ }
+ }
+
+ operator JSContext*() {
+ return mCx;
+ }
+
+ JSContext* forget() {
+ JSContext* cx = mCx;
+ mCx = nsnull;
+ return cx;
+ }
+
+private:
+ JSContext* mCx;
+};
+
+class nsDestroyJSContextRunnable : public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsDestroyJSContextRunnable(JSContext* aCx)
+ : mCx(aCx)
+ {
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aCx, "Null pointer!");
+ NS_ASSERTION(!JS_GetGlobalObject(aCx), "Should not have a global!");
+
+ // We're removing this context from this thread. Let the JS engine know.
+ JS_ClearContextThread(aCx);
+ }
+
+ NS_IMETHOD Run()
+ {
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ // We're about to use this context on this thread. Let the JS engine know.
+ if (!!JS_SetContextThread(mCx)) {
+ NS_WARNING("JS_SetContextThread failed!");
+ }
+
+ if (nsContentUtils::XPConnect()) {
+ nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
+ }
+ else {
+ NS_WARNING("Failed to release JSContext!");
+ }
+
+ return NS_OK;
+ }
+
+private:
+ JSContext* mCx;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDestroyJSContextRunnable, nsIRunnable)
+
+/**
+ * This class is used as to post an error to the worker's outer handler.
+ */
+class nsReportErrorRunnable : public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsReportErrorRunnable(nsDOMWorker* aWorker,
+ nsIScriptError* aScriptError)
+ : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()),
+ mScriptError(aScriptError) {
+ NS_ASSERTION(aScriptError, "Null pointer!");
+ }
+
+ NS_IMETHOD Run() {
+ if (mWorker->IsCanceled()) {
+ return NS_OK;
+ }
+
+#ifdef DEBUG
+ {
+ nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+ if (NS_IsMainThread()) {
+ NS_ASSERTION(!parent, "Shouldn't have a parent on the main thread!");
+ }
+ else {
+ NS_ASSERTION(parent, "Should have a parent!");
+
+ JSContext* cx = nsDOMThreadService::get()->GetCurrentContext();
+ NS_ASSERTION(cx, "No context!");
+
+ nsDOMWorker* currentWorker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+ NS_ASSERTION(currentWorker == parent, "Wrong worker!");
+ }
+ }
+#endif
+
+ NS_NAMED_LITERAL_STRING(errorStr, "error");
+
+ nsresult rv;
+
+ if (mWorker->HasListeners(errorStr)) {
+ // Construct the error event.
+ nsString message;
+ rv = mScriptError->GetErrorMessage(message);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString filename;
+ rv = mScriptError->GetSourceName(filename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 lineno;
+ rv = mScriptError->GetLineNumber(&lineno);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
+ NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+ rv = event->InitErrorEvent(errorStr, PR_FALSE, PR_TRUE, message,
+ filename, lineno);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ event->SetTarget(static_cast<nsDOMWorkerMessageHandler*>(mWorker));
+
+ PRBool stopPropagation = PR_FALSE;
+ rv = mWorker->DispatchEvent(static_cast<nsDOMWorkerEvent*>(event),
+ &stopPropagation);
+ if (NS_SUCCEEDED(rv) && stopPropagation) {
+ return NS_OK;
+ }
+ }
+
+ nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+ if (!parent) {
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (consoleService) {
+ rv = consoleService->LogMessage(mScriptError);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+ }
+
+ nsRefPtr<nsReportErrorRunnable> runnable =
+ new nsReportErrorRunnable(parent, mScriptError);
+ if (runnable) {
+ nsRefPtr<nsDOMWorker> grandparent = parent->GetParent();
+ rv = grandparent ?
+ nsDOMThreadService::get()->Dispatch(grandparent, runnable) :
+ NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+ }
+
+private:
+ nsRefPtr<nsDOMWorker> mWorker;
+ nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+ nsCOMPtr<nsIScriptError> mScriptError;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
+
+/**
+ * Used to post an expired timeout to the correct worker.
+ */
+class nsDOMWorkerTimeoutRunnable : public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
+ : mTimeout(aTimeout) { }
+
+ NS_IMETHOD Run() {
+ return mTimeout->Run();
+ }
+protected:
+ nsRefPtr<nsDOMWorkerTimeout> mTimeout;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeoutRunnable, nsIRunnable)
+
+class nsDOMWorkerKillRunnable : public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsDOMWorkerKillRunnable(nsDOMWorker* aWorker)
+ : mWorker(aWorker) { }
+
+ NS_IMETHOD Run() {
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ mWorker->Kill();
+ return NS_OK;
+ }
+
+private:
+ nsRefPtr<nsDOMWorker> mWorker;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerKillRunnable, nsIRunnable)
+
+/**
+ * This class exists to solve a particular problem: Calling Dispatch on a
+ * thread pool will always create a new thread to service the runnable as long
+ * as the thread limit has not been reached. Since our DOM workers can only be
+ * accessed by one thread at a time we could end up spawning a new thread that
+ * does nothing but wait initially. There is no way to control this behavior
+ * currently so we cheat by using a runnable that emulates a thread. The
+ * nsDOMThreadService's monitor protects the queue of events.
+ */
+class nsDOMWorkerRunnable : public nsIRunnable
+{
+ friend class nsDOMThreadService;
+
+public:
+ NS_DECL_ISUPPORTS
+
+ nsDOMWorkerRunnable(nsDOMWorker* aWorker)
+ : mWorker(aWorker), mCloseTimeoutInterval(0), mKillWorkerWhenDone(PR_FALSE) {
+ }
+
+ virtual ~nsDOMWorkerRunnable() {
+ ClearQueue();
+ }
+
+ void PutRunnable(nsIRunnable* aRunnable,
+ PRIntervalTime aTimeoutInterval,
+ PRBool aClearQueue) {
+ NS_ASSERTION(aRunnable, "Null pointer!");
+
+ gDOMThreadService->mReentrantMonitor.AssertCurrentThreadIn();
+
+ if (NS_LIKELY(!aTimeoutInterval)) {
+ NS_ADDREF(aRunnable);
+ mRunnables.Push(aRunnable);
+ }
+ else {
+ NS_ASSERTION(!mCloseRunnable, "More than one close runnable?!");
+ if (aClearQueue) {
+ ClearQueue();
+ }
+ mCloseRunnable = aRunnable;
+ mCloseTimeoutInterval = aTimeoutInterval;
+ mKillWorkerWhenDone = PR_TRUE;
+ }
+ }
+
+ void SetCloseRunnableTimeout(PRIntervalTime aTimeoutInterval) {
+ NS_ASSERTION(aTimeoutInterval, "No timeout specified!");
+ NS_ASSERTION(aTimeoutInterval!= PR_INTERVAL_NO_TIMEOUT, "Bad timeout!");
+
+ // No need to enter the monitor because we should already be in it.
+
+ NS_ASSERTION(mWorker->GetExpirationTime() == PR_INTERVAL_NO_TIMEOUT,
+ "Asked to set timeout on a runnable with no close handler!");
+
+ // This may actually overflow but we don't care - the worst that could
+ // happen is that the close handler could run for a slightly different
+ // amount of time and the spec leaves the time up to us anyway.
+ mWorker->SetExpirationTime(PR_IntervalNow() + aTimeoutInterval);
+ }
+
+ NS_IMETHOD Run() {
+ NS_ASSERTION(!NS_IsMainThread(),
+ "This should *never* run on the main thread!");
+
+ // This must have been set up by the thread service
+ NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+ // Make sure we have a JSContext to run everything on.
+ JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+ if (!cx) {
+ NS_ERROR("nsDOMThreadService didn't give us a context! Are we out of memory?");
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION(!JS_GetGlobalObject(cx), "Shouldn't have a global!");
+
+ if (mWorker->IsPrivileged()) {
+ JS_SetVersion(cx, JSVERSION_LATEST);
+ }
+ else {
+ JS_SetVersion(cx, JSVERSION_DEFAULT);
+ }
+
+ JS_SetContextPrivate(cx, mWorker);
+
+ // Go ahead and trigger the operation callback for this context before we
+ // try to run any JS. That way we'll be sure to cancel or suspend as soon as
+ // possible if the compilation takes too long.
+ JS_TriggerOperationCallback(cx);
+
+ PRBool killWorkerWhenDone;
+ {
+ nsLazyAutoRequest ar;
+ JSAutoEnterCompartment ac;
+
+ // Tell the worker which context it will be using
+ if (mWorker->SetGlobalForContext(cx, &ar, &ac)) {
+ NS_ASSERTION(ar.entered(), "SetGlobalForContext must enter request on success");
+ NS_ASSERTION(ac.entered(), "SetGlobalForContext must enter compartment on success");
+
+ RunQueue(cx, &killWorkerWhenDone);
+
+ // Remove the global object from the context so that it might be garbage
+ // collected.
+ JS_SetGlobalObject(cx, NULL);
+ JS_SetContextPrivate(cx, NULL);
+ }
+ else {
+ NS_ASSERTION(!ar.entered(), "SetGlobalForContext must not enter request on failure");
+ NS_ASSERTION(!ac.entered(), "SetGlobalForContext must not enter compartment on failure");
+
+ {
+ // Code in XPConnect assumes that the context's global object won't be
+ // replaced outside of a request.
+ JSAutoRequest ar2(cx);
+
+ // This is usually due to a parse error in the worker script...
+ JS_SetGlobalObject(cx, NULL);
+ JS_SetContextPrivate(cx, NULL);
+ }
+
+ ReentrantMonitorAutoEnter mon(gDOMThreadService->mReentrantMonitor);
+ killWorkerWhenDone = mKillWorkerWhenDone;
+ gDOMThreadService->WorkerComplete(this);
+ mon.NotifyAll();
+ }
+ }
+
+ if (killWorkerWhenDone) {
+ nsCOMPtr<nsIRunnable> runnable = new nsDOMWorkerKillRunnable(mWorker);
+ NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+ }
+
+protected:
+ void ClearQueue() {
+ nsCOMPtr<nsIRunnable> runnable;
+ while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
+ // Loop until all the runnables are dead.
+ }
+ }
+
+ void RunQueue(JSContext* aCx, PRBool* aCloseRunnableSet) {
+ while (1) {
+ nsCOMPtr<nsIRunnable> runnable;
+ {
+ ReentrantMonitorAutoEnter mon(gDOMThreadService->mReentrantMonitor);
+
+ runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront());
+
+ if (!runnable && mCloseRunnable) {
+ PRIntervalTime expirationTime;
+ if (mCloseTimeoutInterval == PR_INTERVAL_NO_TIMEOUT) {
+ expirationTime = mCloseTimeoutInterval;
+ }
+ else {
+ expirationTime = PR_IntervalNow() + mCloseTimeoutInterval;
+ }
+ mWorker->SetExpirationTime(expirationTime);
+
+ runnable.swap(mCloseRunnable);
+ }
+
+ if (!runnable || mWorker->IsCanceled()) {
+#ifdef PR_LOGGING
+ if (mWorker->IsCanceled()) {
+ LOG(("Bailing out of run loop for canceled worker[0x%p]",
+ static_cast<void*>(mWorker.get())));
+ }
+#endif
+ *aCloseRunnableSet = mKillWorkerWhenDone;
+ gDOMThreadService->WorkerComplete(this);
+ mon.NotifyAll();
+ return;
+ }
+ }
+
+ // Clear out any old cruft hanging around in the regexp statics.
+ if (JSObject *global = JS_GetGlobalObject(aCx))
+ JS_ClearRegExpStatics(aCx, global);
+
+ runnable->Run();
+ }
+ NS_NOTREACHED("Shouldn't ever get here!");
+ }
+
+ // Set at construction
+ nsRefPtr<nsDOMWorker> mWorker;
+
+ // Protected by mReentrantMonitor
+ nsDeque mRunnables;
+ nsCOMPtr<nsIRunnable> mCloseRunnable;
+ PRIntervalTime mCloseTimeoutInterval;
+ PRBool mKillWorkerWhenDone;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerRunnable, nsIRunnable)
+
+/*******************************************************************************
+ * JS environment function and callbacks
+ */
+
+JSBool
+DOMWorkerOperationCallback(JSContext* aCx)
+{
+ nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+ NS_ASSERTION(worker, "This must never be null!");
+
+ PRBool canceled = worker->IsCanceled();
+ if (!canceled && worker->IsSuspended()) {
+ JSAutoSuspendRequest suspended(aCx);
+
+ // Since we're going to block this thread we should open up a new thread
+ // in the thread pool for other workers. Must check the return value to
+ // make sure we don't decrement when we failed.
+ PRBool extraThreadAllowed =
+ NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
+
+ // Flush JIT caches now before suspending to avoid holding memory that we
+ // are not going to use.
+ JS_FlushCaches(aCx);
+
+ for (;;) {
+ ReentrantMonitorAutoEnter mon(worker->Pool()->GetReentrantMonitor());
+
+ // There's a small chance that the worker was canceled after our check
+ // above in which case we shouldn't wait here. We're guaranteed not to
+ // race here because the pool reenters its monitor after canceling each
+ // worker in order to notify its condition variable.
+ canceled = worker->IsCanceled();
+ if (!canceled && worker->IsSuspended()) {
+ mon.Wait();
+ }
+ else {
+ break;
+ }
+ }
+
+ if (extraThreadAllowed) {
+ gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
+ }
+ }
+
+ if (canceled) {
+ LOG(("Forcefully killing JS for worker [0x%p]",
+ static_cast<void*>(worker)));
+ // Kill execution of the currently running JS.
+ JS_ClearPendingException(aCx);
+ return JS_FALSE;
+ }
+ return JS_TRUE;
+}
+
+void
+DOMWorkerErrorReporter(JSContext* aCx,
+ const char* aMessage,
+ JSErrorReport* aReport)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
+
+ nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+
+ if (worker->IsCanceled()) {
+ // We don't want to report errors from canceled workers. It's very likely
+ // that we only returned an error in the first place because the worker was
+ // already canceled.
+ return;
+ }
+
+ if (worker->mErrorHandlerRecursionCount == 2) {
+ // We've somehow ended up in a recursive onerror loop. Bail out.
+ return;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIScriptError> scriptError;
+
+ {
+ // CreateInstance will lock, make sure we suspend our request!
+ JSAutoSuspendRequest ar(aCx);
+
+ scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+ }
+
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIScriptError2> scriptError2(do_QueryInterface(scriptError));
+
+ nsAutoString message, filename, line;
+ PRUint32 lineNumber, columnNumber, flags, errorNumber;
+
+ if (aReport) {
+ if (aReport->ucmessage) {
+ message.Assign(reinterpret_cast<const PRUnichar*>(aReport->ucmessage));
+ }
+ filename.AssignWithConversion(aReport->filename);
+ line.Assign(reinterpret_cast<const PRUnichar*>(aReport->uclinebuf));
+ lineNumber = aReport->lineno;
+ columnNumber = aReport->uctokenptr - aReport->uclinebuf;
+ flags = aReport->flags;
+ errorNumber = aReport->errorNumber;
+ }
+ else {
+ lineNumber = columnNumber = errorNumber = 0;
+ flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
+ }
+
+ if (message.IsEmpty()) {
+ message.AssignWithConversion(aMessage);
+ }
+
+ rv = scriptError2->InitWithWindowID(message.get(), filename.get(), line.get(),
+ lineNumber, columnNumber, flags,
+ "DOM Worker javascript",
+ worker->Pool()->WindowID());
+
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ // Don't call the error handler if we're out of stack space.
+ if (errorNumber != JSMSG_OVER_RECURSED) {
+ // Try the onerror handler for the worker's scope.
+ nsRefPtr<nsDOMWorkerScope> scope = worker->GetInnerScope();
+ NS_ASSERTION(scope, "Null scope!");
+
+ PRBool hasListeners = scope->HasListeners(NS_LITERAL_STRING("error"));
+ if (hasListeners) {
+ nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
+ if (event) {
+ rv = event->InitErrorEvent(NS_LITERAL_STRING("error"), PR_FALSE,
+ PR_TRUE, nsDependentString(message),
+ filename, lineNumber);
+ if (NS_SUCCEEDED(rv)) {
+ event->SetTarget(scope);
+
+ NS_ASSERTION(worker->mErrorHandlerRecursionCount >= 0,
+ "Bad recursion count logic!");
+ worker->mErrorHandlerRecursionCount++;
+
+ PRBool preventDefaultCalled = PR_FALSE;
+ scope->DispatchEvent(static_cast<nsDOMWorkerEvent*>(event),
+ &preventDefaultCalled);
+
+ worker->mErrorHandlerRecursionCount--;
+
+ if (preventDefaultCalled) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // Still unhandled, fire at the onerror handler on the worker.
+ nsCOMPtr<nsIRunnable> runnable =
+ new nsReportErrorRunnable(worker, scriptError);
+ NS_ENSURE_TRUE(runnable,);
+
+ nsRefPtr<nsDOMWorker> parent = worker->GetParent();
+
+ // If this worker has a parent then we need to send the message through the
+ // thread service to be run on the parent's thread. Otherwise it is a
+ // top-level worker and we send the message to the main thread.
+ rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable)
+ : NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+}
+
+/*******************************************************************************
+ * nsDOMThreadService
+ */
+
+nsDOMThreadService::nsDOMThreadService()
+: mReentrantMonitor("nsDOMThreadServer.mReentrantMonitor"),
+ mNavigatorStringsLoaded(PR_FALSE)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+#ifdef PR_LOGGING
+ if (!gDOMThreadsLog) {
+ gDOMThreadsLog = PR_NewLogModule("nsDOMThreads");
+ }
+#endif
+ LOG(("Initializing DOM Thread service"));
+}
+
+nsDOMThreadService::~nsDOMThreadService()
+{
+ LOG(("DOM Thread service destroyed"));
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ Cleanup();
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
+ nsIObserver,
+ nsIThreadPoolListener)
+
+nsresult
+nsDOMThreadService::Init()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
+
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obs =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ obs.forget(&gObserverService);
+
+ RegisterPrefCallbacks();
+
+ mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mThreadPool->SetListener(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mThreadPool->SetThreadLimit(THREADPOOL_MAX_THREADS);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mThreadPool->SetIdleThreadLimit(THREADPOOL_IDLE_THREADS);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRBool success = mWorkersInProgress.Init();
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ success = mPools.Init();
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ success = mThreadsafeContractIDs.Init();
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ success = mJSContexts.SetCapacity(THREADPOOL_THREAD_CAP);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ nsCOMPtr<nsIJSRuntimeService>
+ runtimeSvc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
+ NS_ENSURE_TRUE(runtimeSvc, NS_ERROR_FAILURE);
+ runtimeSvc.forget(&gJSRuntimeService);
+
+ nsCOMPtr<nsIThreadJSContextStack>
+ contextStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
+ NS_ENSURE_TRUE(contextStack, NS_ERROR_FAILURE);
+ contextStack.forget(&gThreadJSContextStack);
+
+ nsCOMPtr<nsIXPCSecurityManager> secMan(new nsDOMWorkerSecurityManager());
+ NS_ENSURE_TRUE(secMan, NS_ERROR_OUT_OF_MEMORY);
+ secMan.forget(&gWorkerSecurityManager);
+
+ if (gJSContextIndex == BAD_TLS_INDEX &&
+ PR_NewThreadPrivateIndex(&gJSContextIndex, NULL) != PR_SUCCESS) {
+ NS_ERROR("PR_NewThreadPrivateIndex failed!");
+ gJSContextIndex = BAD_TLS_INDEX;
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+/* static */
+already_AddRefed<nsDOMThreadService>
+nsDOMThreadService::GetOrInitService()
+{
+ if (!gDOMThreadService) {
+ nsRefPtr<nsDOMThreadService> service = new nsDOMThreadService();
+ NS_ENSURE_TRUE(service, nsnull);
+
+ nsresult rv = service->Init();
+ NS_ENSURE_SUCCESS(rv, nsnull);
+
+ service.swap(gDOMThreadService);
+ }
+
+ nsRefPtr<nsDOMThreadService> service(gDOMThreadService);
+ return service.forget();
+}
+
+/* static */
+nsDOMThreadService*
+nsDOMThreadService::get()
+{
+ return gDOMThreadService;
+}
+
+/* static */
+JSContext*
+nsDOMThreadService::GetCurrentContext()
+{
+ JSContext* cx;
+
+ if (NS_IsMainThread()) {
+ nsresult rv = ThreadJSContextStack()->GetSafeJSContext(&cx);
+ NS_ENSURE_SUCCESS(rv, nsnull);
+ }
+ else {
+ NS_ENSURE_TRUE(gJSContextIndex, nsnull);
+ cx = static_cast<JSContext*>(PR_GetThreadPrivate(gJSContextIndex));
+ }
+
+ return cx;
+}
+
+/* static */
+void
+nsDOMThreadService::Shutdown()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_IF_RELEASE(gDOMThreadService);
+}
+
+void
+nsDOMThreadService::Cleanup()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ // This will either be called at 'xpcom-shutdown' or earlier if the call to
+ // Init fails somehow. We can therefore assume that all services will still
+ // be available here.
+
+ // Cancel all workers that weren't tied to a window.
+ CancelWorkersForGlobal(nsnull);
+
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ NS_ASSERTION(!mPools.Count(), "Live workers left!");
+ mPools.Clear();
+
+ NS_ASSERTION(!mSuspendedWorkers.Length(), "Suspended workers left!");
+ mSuspendedWorkers.Clear();
+ }
+
+ if (gObserverService) {
+ gObserverService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ NS_RELEASE(gObserverService);
+
+ UnregisterPrefCallbacks();
+ }
+
+ // The thread pool holds a circular reference to this service through its
+ // listener. We must shut down the thread pool manually to break this cycle.
+ if (mThreadPool) {
+ mThreadPool->Shutdown();
+ mThreadPool = nsnull;
+ }
+
+ // Need to force a GC so that all of our workers get cleaned up.
+ if (gThreadJSContextStack) {
+ JSContext* safeContext;
+ if (NS_SUCCEEDED(gThreadJSContextStack->GetSafeJSContext(&safeContext))) {
+ JS_GC(safeContext);
+ }
+ NS_RELEASE(gThreadJSContextStack);
+ }
+
+ // These must be released after the thread pool is shut down.
+ NS_IF_RELEASE(gJSRuntimeService);
+ NS_IF_RELEASE(gWorkerSecurityManager);
+}
+
+nsresult
+nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
+ nsIRunnable* aRunnable,
+ PRIntervalTime aTimeoutInterval,
+ PRBool aClearQueue)
+{
+ NS_ASSERTION(aWorker, "Null pointer!");
+ NS_ASSERTION(aRunnable, "Null pointer!");
+
+ if (!mThreadPool) {
+ // This can happen from a nsDOMWorker::Finalize call after the thread pool
+ // has been shutdown. It should never be possible off the main thread.
+ NS_ASSERTION(NS_IsMainThread(),
+ "This should be impossible on a non-main thread!");
+ return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+ }
+
+ // Don't accept the runnable if the worker's close handler has been triggered
+ // (unless, of course, this is the close runnable as indicated by the non-0
+ // timeout value).
+ if (aWorker->IsClosing() && !aTimeoutInterval) {
+ LOG(("Will not dispatch runnable [0x%p] for closing worker [0x%p]",
+ static_cast<void*>(aRunnable), static_cast<void*>(aWorker)));
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
+ workerRunnable->PutRunnable(aRunnable, aTimeoutInterval, aClearQueue);
+ return NS_OK;
+ }
+
+ workerRunnable = new nsDOMWorkerRunnable(aWorker);
+ NS_ENSURE_TRUE(workerRunnable, NS_ERROR_OUT_OF_MEMORY);
+
+ workerRunnable->PutRunnable(aRunnable, aTimeoutInterval, PR_FALSE);
+
+ PRBool success = mWorkersInProgress.Put(aWorker, workerRunnable);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ nsresult rv = mThreadPool->Dispatch(workerRunnable, NS_DISPATCH_NORMAL);
+
+ // XXX This is a mess and it could probably be removed once we have an
+ // infallible malloc implementation.
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch runnable to thread pool!");
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // We exited the monitor after inserting the runnable into the table so make
+ // sure we're removing the right one!
+ nsRefPtr<nsDOMWorkerRunnable> tableRunnable;
+ if (mWorkersInProgress.Get(aWorker, getter_AddRefs(tableRunnable)) &&
+ workerRunnable == tableRunnable) {
+ mWorkersInProgress.Remove(aWorker);
+
+ // And don't forget to tell anyone who's waiting.
+ mon.NotifyAll();
+ }
+
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+nsDOMThreadService::SetWorkerTimeout(nsDOMWorker* aWorker,
+ PRIntervalTime aTimeoutInterval)
+{
+ NS_ASSERTION(aWorker, "Null pointer!");
+ NS_ASSERTION(aTimeoutInterval, "No timeout specified!");
+
+ NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
+ if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
+ workerRunnable->SetCloseRunnableTimeout(aTimeoutInterval);
+ }
+}
+
+void
+nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+
+#ifdef DEBUG
+ nsRefPtr<nsDOMWorker>& debugWorker = aRunnable->mWorker;
+
+ nsRefPtr<nsDOMWorkerRunnable> runnable;
+ NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) &&
+ runnable == aRunnable,
+ "Removing a worker that isn't in our hashtable?!");
+#endif
+
+ mWorkersInProgress.Remove(aRunnable->mWorker);
+}
+
+PRBool
+nsDOMThreadService::QueueSuspendedWorker(nsDOMWorkerRunnable* aRunnable)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+ {
+ // Make sure that the runnable is in mWorkersInProgress.
+ nsRefPtr<nsDOMWorkerRunnable> current;
+ mWorkersInProgress.Get(aRunnable->mWorker, getter_AddRefs(current));
+ NS_ASSERTION(current == aRunnable, "Something crazy wrong here!");
+ }
+#endif
+
+ return mSuspendedWorkers.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE;
+}
+
+/* static */
+JSContext*
+nsDOMThreadService::CreateJSContext()
+{
+ JSRuntime* rt;
+ gJSRuntimeService->GetRuntime(&rt);
+ NS_ENSURE_TRUE(rt, nsnull);
+
+ JSAutoContextDestroyer cx(JS_NewContext(rt, 8192));
+ NS_ENSURE_TRUE(cx, nsnull);
+
+ JS_SetErrorReporter(cx, DOMWorkerErrorReporter);
+
+ JS_SetOperationCallback(cx, DOMWorkerOperationCallback);
+
+ static JSSecurityCallbacks securityCallbacks = {
+ nsDOMWorkerSecurityManager::JSCheckAccess,
+ nsDOMWorkerSecurityManager::JSTranscodePrincipals,
+ nsDOMWorkerSecurityManager::JSFindPrincipal
+ };
+ JS_SetContextSecurityCallbacks(cx, &securityCallbacks);
+
+ JS_ClearContextDebugHooks(cx);
+
+ nsresult rv = nsContentUtils::XPConnect()->
+ SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
+ NS_ENSURE_SUCCESS(rv, nsnull);
+
+ JS_SetNativeStackQuota(cx, 256*1024);
+
+ JS_SetOptions(cx,
+ JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_JIT | JSOPTION_PROFILING);
+ JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 1 * 1024 * 1024);
+
+ return cx.forget();
+}
+
+already_AddRefed<nsDOMWorkerPool>
+nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+ PRBool aRemove)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ nsRefPtr<nsDOMWorkerPool> pool;
+ mPools.Get(aGlobalObject, getter_AddRefs(pool));
+
+ if (aRemove) {
+ mPools.Remove(aGlobalObject);
+ }
+
+ return pool.forget();
+}
+
+void
+nsDOMThreadService::TriggerOperationCallbackForPool(nsDOMWorkerPool* aPool)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+
+ // See if we need to trigger the operation callback on any currently running
+ // contexts.
+ PRUint32 contextCount = mJSContexts.Length();
+ for (PRUint32 index = 0; index < contextCount; index++) {
+ JSContext*& cx = mJSContexts[index];
+ nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+ if (worker && worker->Pool() == aPool) {
+ JS_TriggerOperationCallback(cx);
+ }
+ }
+}
+
+void
+nsDOMThreadService::RescheduleSuspendedWorkerForPool(nsDOMWorkerPool* aPool)
+{
+ mReentrantMonitor.AssertCurrentThreadIn();
+
+ PRUint32 count = mSuspendedWorkers.Length();
+ if (!count) {
+ // Nothing to do here.
+ return;
+ }
+
+ nsTArray<nsDOMWorkerRunnable*> others(count);
+
+ for (PRUint32 index = 0; index < count; index++) {
+ nsDOMWorkerRunnable* runnable = mSuspendedWorkers[index];
+
+#ifdef DEBUG
+ {
+ // Make sure that the runnable never left mWorkersInProgress.
+ nsRefPtr<nsDOMWorkerRunnable> current;
+ mWorkersInProgress.Get(runnable->mWorker, getter_AddRefs(current));
+ NS_ASSERTION(current == runnable, "Something crazy wrong here!");
+ }
+#endif
+
+ if (runnable->mWorker->Pool() == aPool) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ mThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "This shouldn't ever fail!");
+ }
+ else {
+ others.AppendElement(runnable);
+ }
+ }
+
+ mSuspendedWorkers.SwapElements(others);
+}
+
+void
+nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+ nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
+ if (pool) {
+ pool->Cancel();
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ TriggerOperationCallbackForPool(pool);
+ RescheduleSuspendedWorkerForPool(pool);
+ }
+}
+
+void
+nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+ NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+ nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+ if (pool) {
+ pool->Suspend();
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ TriggerOperationCallbackForPool(pool);
+ }
+}
+
+void
+nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+ NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+ nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+ if (pool) {
+ pool->Resume();
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ TriggerOperationCallbackForPool(pool);
+ RescheduleSuspendedWorkerForPool(pool);
+ }
+}
+
+void
+nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
+{
+ NS_ASSERTION(aPool, "Null pointer!");
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mPools.Remove(aPool->ScriptGlobalObject());
+}
+
+void
+nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
+{
+ nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
+ new nsDOMWorkerTimeoutRunnable(aTimeout);
+ NS_ENSURE_TRUE(runnable,);
+
+ Dispatch(aTimeout->GetWorker(), runnable);
+}
+
+nsresult
+nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
+{
+ NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ PRUint32 currentThreadCount;
+ nsresult rv = mThreadPool->GetThreadLimit(&currentThreadCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRInt32 newThreadCount = (PRInt32)currentThreadCount + (PRInt32)aDelta;
+ NS_ASSERTION(newThreadCount >= THREADPOOL_MAX_THREADS,
+ "Can't go below initial thread count!");
+
+ if (newThreadCount > THREADPOOL_THREAD_CAP) {
+ NS_WARNING("Thread pool cap reached!");
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = mThreadPool->SetThreadLimit((PRUint32)newThreadCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we're allowing an extra thread then post a dummy event to the thread
+ // pool so that any pending workers can get started. The thread pool doesn't
+ // do this on its own like it probably should...
+ if (aDelta == 1) {
+ nsCOMPtr<nsIRunnable> dummy(new nsRunnable());
+ if (dummy) {
+ rv = mThreadPool->Dispatch(dummy, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsDOMThreadService::NoteThreadsafeContractId(const nsACString& aContractId,
+ PRBool aIsThreadsafe)
+{
+ NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+ {
+ PRBool isThreadsafe;
+ if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
+ NS_ASSERTION(aIsThreadsafe == isThreadsafe, "Inconsistent threadsafety!");
+ }
+ }
+#endif
+
+ if (!mThreadsafeContractIDs.Put(aContractId, aIsThreadsafe)) {
+ NS_WARNING("Out of memory!");
+ }
+}
+
+ThreadsafeStatus
+nsDOMThreadService::GetContractIdThreadsafeStatus(const nsACString& aContractId)
+{
+ NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ PRBool isThreadsafe;
+ if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
+ return isThreadsafe ? Threadsafe : NotThreadsafe;
+ }
+
+ return Unknown;
+}
+
+// static
+nsIJSRuntimeService*
+nsDOMThreadService::JSRuntimeService()
+{
+ return gJSRuntimeService;
+}
+
+// static
+nsIThreadJSContextStack*
+nsDOMThreadService::ThreadJSContextStack()
+{
+ return gThreadJSContextStack;
+}
+
+// static
+nsIXPCSecurityManager*
+nsDOMThreadService::WorkerSecurityManager()
+{
+ return gWorkerSecurityManager;
+}
+
+/**
+ * See nsIEventTarget
+ */
+NS_IMETHODIMP
+nsDOMThreadService::Dispatch(nsIRunnable* aEvent,
+ PRUint32 aFlags)
+{
+ NS_ENSURE_ARG_POINTER(aEvent);
+ NS_ENSURE_FALSE(aFlags & NS_DISPATCH_SYNC, NS_ERROR_NOT_IMPLEMENTED);
+
+ // This should only ever be called by the timer code! We run the event right
+ // now, but all that does is queue the real event for the proper worker.
+ aEvent->Run();
+
+ return NS_OK;
+}
+
+/**
+ * See nsIEventTarget
+ */
+NS_IMETHODIMP
+nsDOMThreadService::IsOnCurrentThread(PRBool* _retval)
+{
+ NS_NOTREACHED("No one should call this!");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/**
+ * See nsIObserver
+ */
+NS_IMETHODIMP
+nsDOMThreadService::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const PRUnichar* aData)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ Cleanup();
+ return NS_OK;
+ }
+
+ NS_NOTREACHED("Unknown observer topic!");
+ return NS_OK;
+}
+
+/**
+ * See nsIThreadPoolListener
+ */
+NS_IMETHODIMP
+nsDOMThreadService::OnThreadCreated()
+{
+ LOG(("Thread created"));
+
+ nsIThread* current = NS_GetCurrentThread();
+
+ // We want our worker threads to always have a lower priority than the main
+ // thread. NSPR docs say that this isn't incredibly reliable across all
+ // platforms but we hope for the best.
+ nsCOMPtr<nsISupportsPriority> priority(do_QueryInterface(current));
+ NS_ENSURE_TRUE(priority, NS_ERROR_FAILURE);
+
+ nsresult rv = priority->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+ // Set the context up for the worker.
+ JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+ if (!cx) {
+ cx = nsDOMThreadService::CreateJSContext();
+ NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+
+ PRStatus status = PR_SetThreadPrivate(gJSContextIndex, cx);
+ if (status != PR_SUCCESS) {
+ NS_WARNING("Failed to set context on thread!");
+ nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
+ return NS_ERROR_FAILURE;
+ }
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+ JSContext** newContext =
+#endif
+ mJSContexts.AppendElement(cx);
+
+ // We ensure the capacity of this array in Init.
+ NS_ASSERTION(newContext, "Should never fail!");
+ }
+
+ // Make sure that XPConnect knows about this context.
+ gThreadJSContextStack->Push(cx);
+ gThreadJSContextStack->SetSafeJSContext(cx);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMThreadService::OnThreadShuttingDown()
+{
+ LOG(("Thread shutting down"));
+
+ NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+ JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+ NS_WARN_IF_FALSE(cx, "Thread died with no context?");
+ if (cx) {
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ mJSContexts.RemoveElement(cx);
+ }
+
+ JSContext* pushedCx;
+ gThreadJSContextStack->Pop(&pushedCx);
+ NS_ASSERTION(pushedCx == cx, "Popped the wrong context!");
+
+ gThreadJSContextStack->SetSafeJSContext(nsnull);
+
+ // The cycle collector may be running on the main thread. If so we cannot
+ // simply destroy this context. Instead we proxy the context destruction to
+ // the main thread. If that fails somehow then we simply leak the context.
+ nsCOMPtr<nsIRunnable> runnable = new nsDestroyJSContextRunnable(cx);
+
+ if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch release runnable!");
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
+ nsIScriptGlobalObject* aGlobalObject)
+{
+ NS_ASSERTION(aWorker, "Null pointer!");
+
+ if (aGlobalObject && NS_IsMainThread()) {
+ nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+ nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
+ domWindow->GetCurrentInnerWindow() :
+ domWindow.get();
+ NS_ENSURE_STATE(innerWindow);
+
+ nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
+ NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
+
+ aGlobalObject = newGlobal;
+ }
+
+ nsRefPtr<nsDOMWorkerPool> pool;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (!mThreadPool) {
+ // Shutting down!
+ return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+ }
+
+ mPools.Get(aGlobalObject, getter_AddRefs(pool));
+ }
+
+ nsresult rv;
+
+ if (!pool) {
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ if (!mNavigatorStringsLoaded) {
+ rv = NS_GetNavigatorAppName(mAppName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetNavigatorAppVersion(mAppVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetNavigatorPlatform(mPlatform);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetNavigatorUserAgent(mUserAgent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mNavigatorStringsLoaded = PR_TRUE;
+ }
+
+ nsCOMPtr<nsIDocument> document;
+ if (aGlobalObject) {
+ nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+ nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
+ NS_ENSURE_STATE(domDocument);
+
+ document = do_QueryInterface(domDocument);
+ NS_ENSURE_STATE(document);
+ }
+
+ pool = new nsDOMWorkerPool(aGlobalObject, document);
+ NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
+
+ rv = pool->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ PRBool success = mPools.Put(aGlobalObject, pool);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ rv = pool->NoteWorker(aWorker);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aWorker->SetPool(pool);
+ return NS_OK;
+}
+
+void
+nsDOMThreadService::GetAppName(nsAString& aAppName)
+{
+ NS_ASSERTION(mNavigatorStringsLoaded,
+ "Shouldn't call this before we have loaded strings!");
+ aAppName.Assign(mAppName);
+}
+
+void
+nsDOMThreadService::GetAppVersion(nsAString& aAppVersion)
+{
+ NS_ASSERTION(mNavigatorStringsLoaded,
+ "Shouldn't call this before we have loaded strings!");
+ aAppVersion.Assign(mAppVersion);
+}
+
+void
+nsDOMThreadService::GetPlatform(nsAString& aPlatform)
+{
+ NS_ASSERTION(mNavigatorStringsLoaded,
+ "Shouldn't call this before we have loaded strings!");
+ aPlatform.Assign(mPlatform);
+}
+
+void
+nsDOMThreadService::GetUserAgent(nsAString& aUserAgent)
+{
+ NS_ASSERTION(mNavigatorStringsLoaded,
+ "Shouldn't call this before we have loaded strings!");
+ aUserAgent.Assign(mUserAgent);
+}
+
+void
+nsDOMThreadService::RegisterPrefCallbacks()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(sPrefsToWatch); index++) {
+ Preferences::RegisterCallback(PrefCallback, sPrefsToWatch[index]);
+ PrefCallback(sPrefsToWatch[index], nsnull);
+ }
+}
+
+void
+nsDOMThreadService::UnregisterPrefCallbacks()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(sPrefsToWatch); index++) {
+ Preferences::UnregisterCallback(PrefCallback, sPrefsToWatch[index]);
+ }
+}
+
+// static
+int
+nsDOMThreadService::PrefCallback(const char* aPrefName,
+ void* aClosure)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ if(!strcmp(aPrefName, "dom.max_script_run_time")) {
+ // We assume atomic 32bit reads/writes. If this assumption doesn't hold on
+ // some wacky platform then the worst that could happen is that the close
+ // handler will run for a slightly different amount of time.
+ PRUint32 timeoutMS =
+ Preferences::GetUint(aPrefName, gWorkerCloseHandlerTimeoutMS);
+
+ // We must have a timeout value, 0 is not ok. If the pref is set to 0 then
+ // fall back to our default.
+ if (timeoutMS) {
+ gWorkerCloseHandlerTimeoutMS = timeoutMS;
+ }
+ }
+ return 0;
+}