--- a/dom/public/idl/base/nsIDOMNavigator.idl
+++ b/dom/public/idl/base/nsIDOMNavigator.idl
@@ -34,17 +34,19 @@
* 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 "domstubs.idl"
-[scriptable, uuid(777bd8a1-38c1-4b12-ba8f-ff6c2eb8c56b)]
+interface nsIDOMWorkerPool;
+
+[scriptable, uuid(c206f746-52e2-47dd-8ccc-ce76ccda6c6d)]
interface nsIDOMNavigator : nsISupports
{
readonly attribute DOMString appCodeName;
readonly attribute DOMString appName;
readonly attribute DOMString appVersion;
readonly attribute DOMString language;
readonly attribute nsIDOMMimeTypeArray mimeTypes;
readonly attribute DOMString platform;
@@ -58,16 +60,18 @@ interface nsIDOMNavigator : nsISupports
readonly attribute DOMString userAgent;
readonly attribute boolean cookieEnabled;
readonly attribute boolean onLine;
readonly attribute DOMString buildID;
boolean javaEnabled();
boolean taintEnabled();
+ nsIDOMWorkerPool newWorkerPool();
+
// XXX This one's tough, would nsISupports preference(in DOMString
// pref /*, ... */); work?
// jsval preference(/* ... */);
};
[scriptable, uuid(4b4f8316-1dd2-11b2-b265-9a857376d159)]
--- a/dom/public/idl/threads/Makefile.in
+++ b/dom/public/idl/threads/Makefile.in
@@ -42,11 +42,11 @@ srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
XPIDL_MODULE = dom_threads
GRE_MODULE = 1
-XPIDLSRCS = nsIDOMWorkers.idl
+XPIDLSRCS = nsIDOMThreads.idl
include $(topsrcdir)/config/rules.mk
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -453,18 +453,16 @@ enum nsDOMClassInfoID {
eDOMClassInfo_NotifyPaintEvent_id,
eDOMClassInfo_SimpleGestureEvent_id,
#ifdef MOZ_MATHML
eDOMClassInfo_MathMLElement_id,
#endif
- eDOMClassInfo_Worker_id,
-
// This one better be the last one in this list
eDOMClassInfoIDCount
};
/**
* nsIClassInfo helper macros
*/
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -452,19 +452,16 @@
#include "nsIDOMLoadStatus.h"
#include "nsIDOMLoadStatusEvent.h"
// Geolocation
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMGeoPosition.h"
#include "nsIDOMGeoPositionError.h"
-// Workers
-#include "nsDOMWorker.h"
-
#include "nsDOMFile.h"
#include "nsIDOMFileException.h"
// Simple gestures include
#include "nsIDOMSimpleGestureEvent.h"
static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID);
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@@ -1297,19 +1294,16 @@ static nsDOMClassInfoData sClassInfoData
NS_DEFINE_CLASSINFO_DATA(SimpleGestureEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#ifdef MOZ_MATHML
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(MathMLElement, Element, nsElementSH,
ELEMENT_SCRIPTABLE_FLAGS)
#endif
-
- NS_DEFINE_CLASSINFO_DATA(Worker, nsDOMGenericSH,
- DOM_DEFAULT_SCRIPTABLE_FLAGS)
};
// Objects that shuld be constructable through |new Name();|
struct nsContractIDMapData
{
PRInt32 mDOMClassInfoID;
const char *mContractID;
};
@@ -1322,30 +1316,16 @@ static const nsContractIDMapData kConstr
NS_DEFINE_CONSTRUCTOR_DATA(DOMParser, NS_DOMPARSER_CONTRACTID)
NS_DEFINE_CONSTRUCTOR_DATA(XMLSerializer, NS_XMLSERIALIZER_CONTRACTID)
NS_DEFINE_CONSTRUCTOR_DATA(XMLHttpRequest, NS_XMLHTTPREQUEST_CONTRACTID)
NS_DEFINE_CONSTRUCTOR_DATA(XPathEvaluator, NS_XPATH_EVALUATOR_CONTRACTID)
NS_DEFINE_CONSTRUCTOR_DATA(XSLTProcessor,
"@mozilla.org/document-transformer;1?type=xslt")
};
-struct nsConstructorFuncMapData
-{
- PRInt32 mDOMClassInfoID;
- nsDOMConstructorFunc mConstructorFunc;
-};
-
-#define NS_DEFINE_CONSTRUCTOR_FUNC_DATA(_class, _func) \
- { eDOMClassInfo_##_class##_id, _func },
-
-static const nsConstructorFuncMapData kConstructorFuncMap[] =
-{
- NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Worker, nsDOMWorker::NewWorker)
-};
-
nsIXPConnect *nsDOMClassInfo::sXPConnect = nsnull;
nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nsnull;
PRBool nsDOMClassInfo::sIsInitialized = PR_FALSE;
PRBool nsDOMClassInfo::sDisableDocumentAllSupport = PR_FALSE;
PRBool nsDOMClassInfo::sDisableGlobalScopePollutionSupport = PR_FALSE;
jsval nsDOMClassInfo::sTop_id = JSVAL_VOID;
@@ -3567,22 +3547,16 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeSelector)
DOM_CLASSINFO_MAP_END
#endif
- 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
-
#ifdef NS_DEBUG
{
PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
if (i != eDOMClassInfoIDCount) {
NS_ERROR("The number of items in sClassInfoData doesn't match the "
"number of nsIDOMClassInfo ID's, this is bad! Fix it!");
@@ -4987,47 +4961,27 @@ FindConstructorContractID(PRInt32 aDOMCl
for (i = 0; i < NS_ARRAY_LENGTH(kConstructorMap); ++i) {
if (kConstructorMap[i].mDOMClassInfoID == aDOMClassInfoID) {
return kConstructorMap[i].mContractID;
}
}
return nsnull;
}
-static nsDOMConstructorFunc
-FindConstructorFunc(PRInt32 aDOMClassInfoID)
-{
- for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kConstructorFuncMap); ++i) {
- if (kConstructorFuncMap[i].mDOMClassInfoID == aDOMClassInfoID) {
- return kConstructorFuncMap[i].mConstructorFunc;
- }
- }
- return nsnull;
-}
-
static nsresult
BaseStubConstructor(nsIWeakReference* aWeakOwner,
const nsGlobalNameStruct *name_struct, JSContext *cx,
JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
nsresult rv;
nsCOMPtr<nsISupports> native;
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
const char *contractid =
FindConstructorContractID(name_struct->mDOMClassInfoID);
- if (contractid) {
- native = do_CreateInstance(contractid, &rv);
- }
- else {
- nsDOMConstructorFunc func =
- FindConstructorFunc(name_struct->mDOMClassInfoID);
- if (func) {
- rv = func(getter_AddRefs(native));
- }
- }
+ native = do_CreateInstance(contractid, &rv);
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
native = do_CreateInstance(name_struct->mCID, &rv);
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
native = do_CreateInstance(name_struct->mAlias->mCID, &rv);
} else {
native = do_CreateInstance(*name_struct->mData->mConstructorCID, &rv);
}
if (NS_FAILED(rv)) {
@@ -5235,18 +5189,17 @@ private:
// Return NS_OK here, aName just isn't a DOM class but nothing failed.
return NS_OK;
}
static PRBool IsConstructable(const nsGlobalNameStruct *aNameStruct)
{
return
(aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
- (FindConstructorContractID(aNameStruct->mDOMClassInfoID) ||
- FindConstructorFunc(aNameStruct->mDOMClassInfoID))) ||
+ FindConstructorContractID(aNameStruct->mDOMClassInfoID)) ||
(aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo &&
aNameStruct->mData->mConstructorCID) ||
aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructor ||
aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias;
}
const PRUnichar* mClassName;
const PRPackedBool mConstructable;
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -56,17 +56,16 @@ class nsIDOMDocument;
class nsIHTMLDocument;
class nsGlobalWindow;
struct nsDOMClassInfoData;
typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc)
(nsDOMClassInfoData* aData);
-typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject);
struct nsDOMClassInfoData
{
const char *mName;
union {
nsDOMClassInfoConstructorFnc mConstructorFptr;
nsDOMClassInfoExternalConstructorFnc mExternalConstructorFptr;
} u;
--- a/dom/src/base/nsDOMScriptObjectFactory.cpp
+++ b/dom/src/base/nsDOMScriptObjectFactory.cpp
@@ -60,17 +60,16 @@
#include "nsGlobalWindow.h"
#include "nsIJSContextStack.h"
#include "nsISupportsPrimitives.h"
#include "nsDOMException.h"
#include "nsCRT.h"
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
#endif
-#include "nsThreadUtils.h"
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
nsIExceptionProvider* gExceptionProvider = nsnull;
nsDOMScriptObjectFactory::nsDOMScriptObjectFactory() :
mLoadedAllLanguages(PR_FALSE)
{
@@ -391,20 +390,16 @@ nsresult NS_GetScriptRuntimeByID(PRUint3
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMExceptionProvider, nsIExceptionProvider)
NS_IMETHODIMP
nsDOMExceptionProvider::GetException(nsresult result,
nsIException *aDefaultException,
nsIException **_retval)
{
- if (!NS_IsMainThread()) {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
-
switch (NS_ERROR_GET_MODULE(result))
{
case NS_ERROR_MODULE_DOM_RANGE:
return NS_NewRangeException(result, aDefaultException, _retval);
#ifdef MOZ_SVG
case NS_ERROR_MODULE_SVG:
return NS_NewSVGException(result, aDefaultException, _retval);
#endif
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -108,16 +108,17 @@
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMMessageEvent.h"
#include "nsIDOMPopupBlockedEvent.h"
#include "nsIDOMPkcs11.h"
#include "nsIDOMOfflineResourceList.h"
#include "nsIDOMGeoGeolocation.h"
+#include "nsIDOMThreads.h"
#include "nsDOMString.h"
#include "nsIEmbeddingSiteWindow2.h"
#include "nsThreadUtils.h"
#include "nsIEventStateManager.h"
#include "nsIHttpProtocolHandler.h"
#include "nsIJSContextStack.h"
#include "nsIJSRuntimeService.h"
#include "nsIMarkupDocumentViewer.h"
@@ -220,20 +221,16 @@ static PRBool gDragService
#ifdef DEBUG
static PRUint32 gSerialCounter = 0;
#endif
#ifdef DEBUG_jst
PRInt32 gTimeoutCnt = 0;
#endif
-#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
-static PRBool gDOMWindowDumpEnabled = PR_FALSE;
-#endif
-
#if defined(DEBUG_bryner) || defined(DEBUG_chb)
#define DEBUG_PAGE_CACHE
#endif
// The shortest interval/timeout we permit
#define DOM_MIN_TIMEOUT_VALUE 10 // 10ms
// The longest interval (as PRIntervalTime) we permit, or that our
@@ -653,31 +650,19 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
Freeze();
mObserver = nsnull;
}
// We could have failed the first time through trying
// to create the entropy collector, so we should
// try to get one until we succeed.
-
- gRefCnt++;
-
-#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
- if (gRefCnt == 0) {
- static const char* prefName = "browser.dom.window.dump.enabled";
- nsContentUtils::AddBoolPrefVarCache(prefName, &gDOMWindowDumpEnabled);
- gDOMWindowDumpEnabled = nsContentUtils::GetBoolPref(prefName);
- }
-#endif
-
- if (!gEntropyCollector) {
+ if (gRefCnt++ == 0 || !gEntropyCollector) {
CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
}
-
#ifdef DEBUG
printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt,
static_cast<void*>(static_cast<nsIScriptGlobalObject*>(this)),
++gSerialCounter, static_cast<void*>(aOuterWindow));
mSerial = gSerialCounter;
#endif
#ifdef PR_LOGGING
@@ -1677,21 +1662,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
nsRefPtr<nsGlobalWindow> newInnerWindow;
nsCOMPtr<nsIDOMChromeWindow> thisChrome =
do_QueryInterface(static_cast<nsIDOMWindow *>(this));
nsCOMPtr<nsIXPConnectJSObjectHolder> navigatorHolder;
PRBool isChrome = PR_FALSE;
- nsCxPusher cxPusher;
- if (!cxPusher.Push(cx)) {
- return NS_ERROR_FAILURE;
- }
-
JSAutoRequest ar(cx);
// Make sure to clear scope on the outer window *before* we
// initialize the new inner window. If we don't, things
// (Object.prototype etc) could leak from the old outer to the new
// inner scope.
NS_STID_FOR_ID(st_id) {
nsIScriptContext *langContext = GetScriptContextInternal(st_id);
@@ -3843,35 +3823,34 @@ nsGlobalWindow::GetFullScreen(PRBool* aF
}
}
// We are the root window, or something went wrong. Return our internal value.
*aFullScreen = mFullScreen;
return NS_OK;
}
-PRBool
-nsGlobalWindow::DOMWindowDumpEnabled()
+NS_IMETHODIMP
+nsGlobalWindow::Dump(const nsAString& aStr)
{
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
- // In optimized builds we check a pref that controls if we should
- // enable output from dump() or not, in debug builds it's always
- // enabled.
- return gDOMWindowDumpEnabled;
-#else
- return PR_TRUE;
+ {
+ // In optimized builds we check a pref that controls if we should
+ // enable output from dump() or not, in debug builds it's always
+ // enabled.
+
+ // if pref doesn't exist, disable dump output.
+ PRBool enable_dump =
+ nsContentUtils::GetBoolPref("browser.dom.window.dump.enabled");
+
+ if (!enable_dump) {
+ return NS_OK;
+ }
+ }
#endif
-}
-
-NS_IMETHODIMP
-nsGlobalWindow::Dump(const nsAString& aStr)
-{
- if (!DOMWindowDumpEnabled()) {
- return NS_OK;
- }
char *cstr = ToNewUTF8String(aStr);
#if defined(XP_MAC) || defined(XP_MACOSX)
// have to convert \r to \n so that printing to the console works
char *c = cstr, *cEnd = cstr + aStr.Length();
while (c < cEnd) {
if (*c == '\r')
@@ -9459,8 +9438,23 @@ NS_IMETHODIMP nsNavigator::GetGeolocatio
if (!mGeolocation) {
nsCOMPtr<nsIDOMWindow> contentDOMWindow(do_GetInterface(mDocShell));
mGeolocation = new nsGeolocation(contentDOMWindow);
}
NS_IF_ADDREF(*_retval = mGeolocation);
return NS_OK;
}
+
+NS_IMETHODIMP
+nsNavigator::NewWorkerPool(nsIDOMWorkerPool** _retval)
+{
+ nsCOMPtr<nsIDOMThreadService> threadService =
+ nsDOMThreadService::GetOrInitService();
+ NS_ENSURE_TRUE(threadService, NS_ERROR_OUT_OF_MEMORY);
+
+ nsCOMPtr<nsIDOMWorkerPool> newPool;
+ nsresult rv = threadService->CreatePool(getter_AddRefs(newPool));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ newPool.forget(_retval);
+ return NS_OK;
+}
--- a/dom/src/base/nsGlobalWindow.h
+++ b/dom/src/base/nsGlobalWindow.h
@@ -429,17 +429,16 @@ public:
virtual NS_HIDDEN_(void*)
GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey);
virtual NS_HIDDEN_(void)
CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
nsScriptObjectHolder& aHandler);
- static PRBool DOMWindowDumpEnabled();
protected:
// Object Management
virtual ~nsGlobalWindow();
void CleanUp();
void ClearControllers();
void FreeInnerObjects(PRBool aClearScope);
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -2368,21 +2368,16 @@ nsJSContext::InitContext(nsIScriptGlobal
::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
if (!aGlobalObject) {
// If we don't get a global object then there's nothing more to do here.
return NS_OK;
}
- nsCxPusher cxPusher;
- if (!cxPusher.Push(mContext)) {
- return NS_ERROR_FAILURE;
- }
-
nsIXPConnect *xpc = nsContentUtils::XPConnect();
JSObject *global = ::JS_GetGlobalObject(mContext);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
// If there's already a global object in mContext we won't tell
// XPConnect to wrap aGlobalObject since it's already wrapped.
--- a/dom/src/threads/Makefile.in
+++ b/dom/src/threads/Makefile.in
@@ -46,47 +46,46 @@ include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = domthreads_s
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
REQUIRES = \
caps \
content \
- docshell \
gfx \
js \
layout \
locale \
necko \
- plugin \
pref \
string \
thebes \
widget \
xpcom \
xpconnect \
$(NULL)
CPPSRCS = \
nsDOMThreadService.cpp \
- nsDOMWorker.cpp \
- nsDOMWorkerEvents.cpp \
- nsDOMWorkerMessageHandler.cpp \
+ nsDOMWorkerBase.cpp \
nsDOMWorkerPool.cpp \
nsDOMWorkerScriptLoader.cpp \
nsDOMWorkerSecurityManager.cpp \
+ nsDOMWorkerThread.cpp \
nsDOMWorkerTimeout.cpp \
nsDOMWorkerXHR.cpp \
nsDOMWorkerXHRProxy.cpp \
$(NULL)
LOCAL_INCLUDES = \
-I$(topsrcdir)/dom/src/base \
-I$(topsrcdir)/content/base/src \
-I$(topsrcdir)/content/events/src \
$(NULL)
ifdef ENABLE_TESTS
DIRS += test
endif
include $(topsrcdir)/config/rules.mk
+
+#CXXFLAGS += $(WARNINGS_AS_ERRORS)
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -36,50 +36,46 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMThreadService.h"
// Interfaces
#include "nsIComponentManager.h"
+#include "nsIConsoleService.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIEventTarget.h"
#include "nsIGenericFactory.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 "nsAutoLock.h"
#include "nsAutoPtr.h"
#include "nsContentUtils.h"
#include "nsDeque.h"
#include "nsIClassInfoImpl.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMCID.h"
#include "nsXPCOMCIDInternal.h"
#include "pratom.h"
#include "prthread.h"
// DOMWorker includes
-#include "nsDOMWorker.h"
-#include "nsDOMWorkerEvents.h"
-#include "nsDOMWorkerMacros.h"
-#include "nsDOMWorkerMessageHandler.h"
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerSecurityManager.h"
#include "nsDOMWorkerTimeout.h"
#ifdef PR_LOGGING
PRLogModuleInfo *gDOMThreadsLog = nsnull;
#endif
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
@@ -104,16 +100,24 @@ PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >
// The number of times our JS operation callback will be called before yielding
// the thread
#define CALLBACK_YIELD_THRESHOLD 100
// A "bad" value for the NSPR TLS functions.
#define BAD_TLS_INDEX (PRUintn)-1
+// Don't know why nsISupports.idl defines this out...
+#define NS_FORWARD_NSISUPPORTS(_to) \
+ NS_IMETHOD QueryInterface(const nsIID& uuid, void** result) { \
+ return _to QueryInterface(uuid, result); \
+ } \
+ NS_IMETHOD_(nsrefcnt) AddRef(void) { return _to AddRef(); } \
+ NS_IMETHOD_(nsrefcnt) Release(void) { return _to Release(); }
+
// 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;
@@ -146,41 +150,113 @@ public:
return cx;
}
private:
JSContext* mCx;
};
/**
- * This class is used as to post an error to the worker's outer handler.
+ * This class is used as to post an error to the main thread. It logs the error
+ * to the console and calls the pool's onError callback.
*/
-class nsReportErrorRunnable : public nsIRunnable
+class nsReportErrorRunnable : public nsRunnable
+{
+public:
+ nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker)
+ : mError(aError), mWorker(aWorker) { }
+
+ NS_IMETHOD Run() {
+ nsresult rv;
+
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ consoleService->LogMessage(mError);
+ }
+
+ if (!mWorker->IsCanceled()) {
+#ifdef PR_LOGGING
+ nsAutoString message;
+ mError->GetErrorMessage(message);
+#endif
+ nsRefPtr<nsDOMWorkerPool> pool = mWorker->Pool();
+
+ LOG(("Posting error '%s' to pool [0x%p]",
+ NS_LossyConvertUTF16toASCII(message).get(),
+ static_cast<void*>(pool.get())));
+
+ pool->HandleError(mError, mWorker);
+ }
+ return NS_OK;
+ }
+
+private:
+ // XXX Maybe this should be an nsIException...
+ nsCOMPtr<nsIScriptError> mError;
+
+ // Have to carry a strong ref since this is used as a parameter to the
+ // onError callback.
+ nsRefPtr<nsDOMWorkerThread> mWorker;
+};
+
+/**
+ * Need this to expose an nsIScriptError to content JS (implement nsIClassInfo
+ * with DOM_OBJECT flag set.
+ */
+class nsDOMWorkerScriptError : public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
+ NS_DECL_NSICLASSINFO
- nsReportErrorRunnable(nsDOMWorker* aWorker, nsIWorkerMessageEvent* aEvent)
- : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mEvent(aEvent) { }
+ nsDOMWorkerScriptError(nsIScriptError* aError)
+ : mScriptError(this, aError) { }
+
+protected:
- NS_IMETHOD Run() {
- if (mWorker->IsCanceled()) {
- return NS_OK;
- }
+ // Lame, nsIScriptError and nsIClassInfo both have 'readonly attribute
+ // unsigned long flags' so we have to use an inner class to do this the
+ // right way...
+ class InnerScriptError : public nsIScriptError
+ {
+ public:
+ NS_FORWARD_NSISUPPORTS(mParent->)
+ NS_FORWARD_NSISCRIPTERROR(mError->)
+ NS_FORWARD_NSICONSOLEMESSAGE(mError->)
- return mWorker->DispatchEvent(mEvent, nsnull);
- }
+ InnerScriptError(nsDOMWorkerScriptError* aParent, nsIScriptError* aError)
+ : mParent(aParent), mError(aError) { }
-private:
- nsRefPtr<nsDOMWorker> mWorker;
- nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
- nsCOMPtr<nsIWorkerMessageEvent> mEvent;
+ protected:
+ nsDOMWorkerScriptError* mParent;
+ nsCOMPtr<nsIScriptError> mError;
+ };
+
+ InnerScriptError mScriptError;
};
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerScriptError)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerScriptError)
+
+// More hoops to jump through for the identical IDL methods
+NS_INTERFACE_MAP_BEGIN(nsDOMWorkerScriptError)
+ if (aIID.Equals(NS_GET_IID(nsIScriptError)) ||
+ aIID.Equals(NS_GET_IID(nsIConsoleMessage))) {
+ foundInterface = static_cast<nsIConsoleMessage*>(&mScriptError);
+ }
+ else
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerScriptError, nsIScriptError,
+ nsIConsoleMessage)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerScriptError)
/**
* Used to post an expired timeout to the correct worker.
*/
class nsDOMWorkerTimeoutRunnable : public nsRunnable
{
public:
nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
@@ -202,64 +278,68 @@ protected:
* currently so we cheat by using a runnable that emulates a thread. The
* nsDOMThreadService's monitor protects the queue of events.
*/
class nsDOMWorkerRunnable : public nsRunnable
{
friend class nsDOMThreadService;
public:
- nsDOMWorkerRunnable(nsDOMWorker* aWorker)
+ nsDOMWorkerRunnable(nsDOMWorkerThread* aWorker)
: mWorker(aWorker) { }
virtual ~nsDOMWorkerRunnable() {
nsCOMPtr<nsIRunnable> runnable;
while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
// Loop until all the runnables are dead.
}
+
+ // Only release mWorker on the main thread!
+ nsDOMWorkerThread* worker = nsnull;
+ mWorker.swap(worker);
+
+ nsISupports* supports = NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, worker);
+ NS_ASSERTION(supports, "This should never be null!");
+
+ nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
+ NS_ProxyRelease(mainThread, supports);
}
void PutRunnable(nsIRunnable* aRunnable) {
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ADDREF(aRunnable);
// No need to enter the monitor because we should already be in it.
mRunnables.Push(aRunnable);
}
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);
NS_ASSERTION(cx, "nsDOMThreadService didn't give us a context!");
- NS_ASSERTION(!JS_GetGlobalObject(cx), "Shouldn't have a global!");
-
JS_SetContextPrivate(cx, mWorker);
// Tell the worker which context it will be using
if (mWorker->SetGlobalForContext(cx)) {
RunQueue();
// Remove the global object from the context so that it might be garbage
// collected.
JS_SetGlobalObject(cx, NULL);
JS_SetContextPrivate(cx, NULL);
}
else {
// This is usually due to a parse error in the worker script...
JS_SetGlobalObject(cx, NULL);
- JS_SetContextPrivate(cx, NULL);
nsAutoMonitor mon(gDOMThreadService->mMonitor);
gDOMThreadService->WorkerComplete(this);
mon.NotifyAll();
}
return NS_OK;
}
@@ -297,30 +377,30 @@ protected:
nsresult rv =
#endif
runnable->Run();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Runnable failed!");
}
}
// Set at construction
- nsRefPtr<nsDOMWorker> mWorker;
+ nsRefPtr<nsDOMWorkerThread> mWorker;
// Protected by mMonitor
nsDeque mRunnables;
};
/*******************************************************************************
* JS environment function and callbacks
*/
JSBool
DOMWorkerOperationCallback(JSContext* aCx)
{
- nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+ nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
// Want a strong ref here to make sure that the monitor we wait on won't go
// away.
nsRefPtr<nsDOMWorkerPool> pool;
PRBool wasSuspended = PR_FALSE;
PRBool extraThreadAllowed = PR_FALSE;
jsrefcount suspendDepth = 0;
@@ -397,17 +477,17 @@ DOMWorkerOperationCallback(JSContext* aC
void
DOMWorkerErrorReporter(JSContext* aCx,
const char* aMessage,
JSErrorReport* aReport)
{
NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
- nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+ nsDOMWorkerThread* worker = (nsDOMWorkerThread*)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;
}
@@ -426,40 +506,31 @@ DOMWorkerErrorReporter(JSContext* aCx,
reinterpret_cast<const PRUnichar*>(aReport->uclinebuf);
PRUint32 column = aReport->uctokenptr - aReport->uclinebuf;
rv = errorObject->Init(message, filename.get(), line, aReport->lineno,
column, aReport->flags, "DOM Worker javascript");
NS_ENSURE_SUCCESS(rv,);
- nsCString finalMessage;
- rv = errorObject->ToString(finalMessage);
- NS_ENSURE_SUCCESS(rv,);
-
- nsRefPtr<nsDOMWorkerMessageEvent> event(new nsDOMWorkerMessageEvent());
- NS_ENSURE_TRUE(event,);
+ nsRefPtr<nsDOMWorkerScriptError> domError =
+ new nsDOMWorkerScriptError(errorObject);
+ NS_ENSURE_TRUE(domError,);
- rv = event->InitMessageEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE,
- NS_ConvertUTF8toUTF16(finalMessage),
- EmptyString(), nsnull);
- NS_ENSURE_SUCCESS(rv,);
+ nsCOMPtr<nsIScriptError> scriptError(do_QueryInterface(domError));
+ NS_ENSURE_TRUE(scriptError,);
- event->SetTarget(worker);
+ nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
+ NS_ENSURE_TRUE(mainThread,);
- nsCOMPtr<nsIRunnable> runnable(new nsReportErrorRunnable(worker, event));
+ nsCOMPtr<nsIRunnable> runnable =
+ new nsReportErrorRunnable(scriptError, worker);
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);
+ rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv,);
}
/*******************************************************************************
* nsDOMThreadService
*/
nsDOMThreadService::nsDOMThreadService()
@@ -482,19 +553,20 @@ nsDOMThreadService::~nsDOMThreadService(
Cleanup();
if (mMonitor) {
nsAutoMonitor::DestroyMonitor(mMonitor);
}
}
-NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget,
nsIObserver,
- nsIThreadPoolListener)
+ nsIThreadPoolListener,
+ nsIDOMThreadService)
nsresult
nsDOMThreadService::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
nsresult rv;
@@ -520,19 +592,16 @@ nsDOMThreadService::Init()
NS_ENSURE_SUCCESS(rv, rv);
mMonitor = nsAutoMonitor::NewMonitor("nsDOMThreadService::mMonitor");
NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
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);
-
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);
@@ -548,59 +617,41 @@ nsDOMThreadService::Init()
gJSContextIndex = BAD_TLS_INDEX;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* static */
-already_AddRefed<nsDOMThreadService>
+already_AddRefed<nsIDOMThreadService>
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);
+ nsCOMPtr<nsIDOMThreadService> 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
@@ -631,25 +682,20 @@ nsDOMThreadService::Cleanup()
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);
-
- nsAutoMonitor mon(mMonitor);
- NS_ASSERTION(!mPools.Count(), "Live workers left!");
-
- mPools.Clear();
}
nsresult
-nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
+nsDOMThreadService::Dispatch(nsDOMWorkerThread* aWorker,
nsIRunnable* aRunnable)
{
NS_ASSERTION(aWorker, "Null pointer!");
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
if (aWorker->IsCanceled()) {
@@ -704,27 +750,40 @@ nsDOMThreadService::Dispatch(nsDOMWorker
void
nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
{
// No need to be in the monitor here because we should already be in it.
#ifdef DEBUG
- nsRefPtr<nsDOMWorker>& debugWorker = aRunnable->mWorker;
+ nsRefPtr<nsDOMWorkerThread>& 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);
}
+void
+nsDOMThreadService::WaitForCanceledWorker(nsDOMWorkerThread* aWorker)
+{
+ NS_ASSERTION(aWorker->IsCanceled(),
+ "Waiting on a worker that isn't canceled!");
+
+ nsAutoMonitor mon(mMonitor);
+
+ while (mWorkersInProgress.Get(aWorker, nsnull)) {
+ mon.Wait();
+ }
+}
+
/* static */
JSContext*
nsDOMThreadService::CreateJSContext()
{
JSRuntime* rt;
gJSRuntimeService->GetRuntime(&rt);
NS_ENSURE_TRUE(rt, nsnull);
@@ -746,74 +805,52 @@ nsDOMThreadService::CreateJSContext()
nsresult rv = nsContentUtils::XPConnect()->
SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
NS_ENSURE_SUCCESS(rv, nsnull);
return cx.forget();
}
-already_AddRefed<nsDOMWorkerPool>
-nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
- PRBool aRemove)
-{
- NS_ASSERTION(aGlobalObject, "Null pointer!");
-
- nsAutoMonitor mon(mMonitor);
-
- nsRefPtr<nsDOMWorkerPool> pool;
- mPools.Get(aGlobalObject, getter_AddRefs(pool));
-
- if (aRemove) {
- mPools.Remove(aGlobalObject);
- }
-
- return pool.forget();
-}
+#define LOOP_OVER_POOLS(_func, _args) \
+ PR_BEGIN_MACRO \
+ PRUint32 poolCount = mPools.Length(); \
+ for (PRUint32 i = 0; i < poolCount; i++) { \
+ mPools[i]-> _func _args ; \
+ } \
+ PR_END_MACRO
void
nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
- NS_ASSERTION(aGlobalObject, "Null pointer!");
-
- nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
- if (pool) {
- pool->Cancel();
- }
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
- NS_ASSERTION(aGlobalObject, "Null pointer!");
-
- nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
- if (pool) {
- pool->Suspend();
- }
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
- NS_ASSERTION(aGlobalObject, "Null pointer!");
-
- nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
- if (pool) {
- pool->Resume();
- }
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject));
}
void
-nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
+nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool)
{
- NS_ASSERTION(aPool, "Null pointer!");
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- nsAutoMonitor mon(mMonitor);
- mPools.Remove(aPool->ScriptGlobalObject());
+ NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!");
+ mPools.RemoveElement(aPool);
}
void
nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
{
nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
new nsDOMWorkerTimeoutRunnable(aTimeout);
NS_ENSURE_TRUE(runnable,);
@@ -821,18 +858,16 @@ nsDOMThreadService::TimeoutReady(nsDOMWo
Dispatch(aTimeout->GetWorker(), runnable);
}
nsresult
nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
{
NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
- nsAutoMonitor mon(mMonitor);
-
PRUint32 currentThreadCount;
nsresult rv = mThreadPool->GetThreadLimit(¤tThreadCount);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 newThreadCount = (PRInt32)currentThreadCount + (PRInt32)aDelta;
NS_ASSERTION(newThreadCount >= THREADPOOL_MAX_THREADS,
"Can't go below initial thread count!");
@@ -872,16 +907,17 @@ 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
*/
@@ -970,74 +1006,36 @@ nsDOMThreadService::OnThreadShuttingDown
gThreadJSContextStack->SetSafeJSContext(nsnull);
nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
}
return NS_OK;
}
-nsresult
-nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
- nsIScriptGlobalObject* aGlobalObject)
+/**
+ * See nsIDOMThreadService
+ */
+NS_IMETHODIMP
+nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
{
- NS_ASSERTION(aWorker, "Null pointer!");
- NS_ASSERTION(aGlobalObject, "Null pointer!");
-
- if (NS_IsMainThread()) {
- nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
- NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- 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;
- {
- nsAutoMonitor mon(mMonitor);
-
- if (!mThreadPool) {
- // Shutting down!
- return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
- }
+ NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
- mPools.Get(aGlobalObject, getter_AddRefs(pool));
- }
-
- nsresult rv;
-
- if (!pool) {
- NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
- nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
- NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
-
- nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
- NS_ENSURE_STATE(domDocument);
+ nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller();
+ NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED);
- nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
- NS_ENSURE_STATE(document);
-
- pool = new nsDOMWorkerPool(aGlobalObject, document);
- NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
+ nsCOMPtr<nsIDocument> callingDocument(do_QueryInterface(domDocument));
+ NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE);
- rv = pool->Init();
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsAutoMonitor mon(mMonitor);
+ nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool(callingDocument));
+ NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
- PRBool success = mPools.Put(aGlobalObject, pool);
- NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
- }
-
- rv = pool->NoteWorker(aWorker);
+ nsresult rv = pool->Init();
NS_ENSURE_SUCCESS(rv, rv);
- aWorker->SetPool(pool);
+ NS_ASSERTION(!mPools.Contains(pool), "Um?!");
+ mPools.AppendElement(pool);
+
+ NS_ADDREF(*_retval = pool);
return NS_OK;
}
--- a/dom/src/threads/nsDOMThreadService.h
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -39,72 +39,68 @@
#ifndef __NSDOMTHREADSERVICE_H__
#define __NSDOMTHREADSERVICE_H__
// Interfaces
#include "nsIEventTarget.h"
#include "nsIObserver.h"
#include "nsIThreadPool.h"
+#include "nsIDOMThreads.h"
// Other includes
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsRefPtrHashtable.h"
#include "nsTPtrArray.h"
#include "prmon.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gDOMThreadsLog;
#endif
-class nsDOMWorker;
class nsDOMWorkerPool;
class nsDOMWorkerRunnable;
+class nsDOMWorkerThread;
class nsDOMWorkerTimeout;
class nsIJSRuntimeService;
class nsIScriptGlobalObject;
class nsIThreadJSContextStack;
class nsIXPConnect;
class nsIXPCSecurityManager;
class nsDOMThreadService : public nsIEventTarget,
public nsIObserver,
- public nsIThreadPoolListener
+ public nsIThreadPoolListener,
+ public nsIDOMThreadService
{
- friend class nsDOMWorker;
friend class nsDOMWorkerPool;
friend class nsDOMWorkerRunnable;
friend class nsDOMWorkerThread;
friend class nsDOMWorkerTimeout;
friend class nsDOMWorkerXHR;
friend class nsDOMWorkerXHRProxy;
friend class nsLayoutStatics;
- friend void DOMWorkerErrorReporter(JSContext* aCx,
- const char* aMessage,
- JSErrorReport* aReport);
-
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEVENTTARGET
NS_DECL_NSIOBSERVER
NS_DECL_NSITHREADPOOLLISTENER
+ NS_DECL_NSIDOMTHREADSERVICE
// Any DOM consumers that need access to this service should use this method.
- static already_AddRefed<nsDOMThreadService> GetOrInitService();
+ static already_AddRefed<nsIDOMThreadService> GetOrInitService();
// Simple getter for this service. This does not create the service if it
// hasn't been created already, and it never AddRef's!
static nsDOMThreadService* get();
- static JSContext* GetCurrentContext();
-
// Easy access to the services we care about.
static nsIJSRuntimeService* JSRuntimeService();
static nsIThreadJSContextStack* ThreadJSContextStack();
static nsIXPCSecurityManager* WorkerSecurityManager();
void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
@@ -115,39 +111,34 @@ private:
nsDOMThreadService();
~nsDOMThreadService();
nsresult Init();
void Cleanup();
static void Shutdown();
- nsresult Dispatch(nsDOMWorker* aWorker,
+ nsresult Dispatch(nsDOMWorkerThread* aWorker,
nsIRunnable* aRunnable);
void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
+ void WaitForCanceledWorker(nsDOMWorkerThread* aWorker);
+
static JSContext* CreateJSContext();
- already_AddRefed<nsDOMWorkerPool>
- GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
- PRBool aRemove);
-
- void NoteEmptyPool(nsDOMWorkerPool* aPool);
+ void NoteDyingPool(nsDOMWorkerPool* aPool);
void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
- nsresult RegisterWorker(nsDOMWorker* aWorker,
- nsIScriptGlobalObject* aGlobalObject);
-
// Our internal thread pool.
nsCOMPtr<nsIThreadPool> mThreadPool;
- // Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
- nsRefPtrHashtable<nsISupportsHashKey, nsDOMWorkerPool> mPools;
+ // Weak references, only ever touched on the main thread!
+ nsTPtrArray<nsDOMWorkerPool> mPools;
// mMonitor protects all access to mWorkersInProgress and
// mCreationsInProgress.
PRMonitor* mMonitor;
// A map from nsDOMWorkerThread to nsDOMWorkerRunnable.
nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerRunnable> mWorkersInProgress;
};
--- a/dom/src/threads/nsDOMWorkerPool.cpp
+++ b/dom/src/threads/nsDOMWorkerPool.cpp
@@ -53,190 +53,283 @@
// Other includes
#include "nsAutoLock.h"
#include "nsContentUtils.h"
#include "nsDOMJSUtils.h"
#include "nsThreadUtils.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
-#include "nsDOMWorker.h"
+#include "nsDOMWorkerThread.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
-nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
- nsIDocument* aDocument)
-: mParentGlobal(aGlobalObject),
- mParentDocument(aDocument),
- mMonitor(nsnull),
- mCanceled(PR_FALSE),
- mSuspended(PR_FALSE)
+#define LOOP_OVER_WORKERS(_func, _args) \
+ PR_BEGIN_MACRO \
+ PRUint32 workerCount = mWorkers.Length(); \
+ for (PRUint32 i = 0; i < workerCount; i++) { \
+ mWorkers[i]-> _func _args ; \
+ } \
+ PR_END_MACRO
+
+nsDOMWorkerPool::nsDOMWorkerPool(nsIDocument* aDocument)
+: mParentGlobal(nsnull),
+ mParentDocument(aDocument)
{
- NS_ASSERTION(aGlobalObject, "Must have a global object!");
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDocument, "Must have a document!");
}
nsDOMWorkerPool::~nsDOMWorkerPool()
{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ LOOP_OVER_WORKERS(Cancel, ());
+
+ nsDOMThreadService::get()->NoteDyingPool(this);
+
if (mMonitor) {
nsAutoMonitor::DestroyMonitor(mMonitor);
}
}
-NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPool)
-NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool)
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool,
+ nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool)
nsresult
nsDOMWorkerPool::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ nsIScriptGlobalObject* globalObject =
+ mParentDocument->GetScriptGlobalObject();
+ NS_ENSURE_STATE(globalObject);
+
+ nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(globalObject));
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+ nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
+ domWindow->GetCurrentInnerWindow() :
+ domWindow.get();
+ NS_ENSURE_STATE(innerWindow);
+
+ nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(innerWindow));
+ NS_ENSURE_TRUE(globalSupports, NS_ERROR_NO_INTERFACE);
+
+ // We don't want a strong ref, this guy owns us.
+ mParentGlobal = globalSupports.get();
+
mMonitor = nsAutoMonitor::NewMonitor("nsDOMWorkerPool::mMonitor");
NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
nsresult
-nsDOMWorkerPool::NoteWorker(nsDOMWorker* aWorker)
+nsDOMWorkerPool::HandleMessage(const nsAString& aMessage,
+ nsDOMWorkerBase* aSource)
{
- NS_ASSERTION(aWorker, "Null pointer!");
-
- PRBool suspendWorker;
-
- {
- nsAutoMonitor mon(mMonitor);
-
- if (mCanceled) {
- return NS_ERROR_ABORT;
- }
-
- nsDOMWorker** newWorker = mWorkers.AppendElement(aWorker);
- NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
-
- suspendWorker = mSuspended;
+ nsCOMPtr<nsIDOMWorkerMessageListener> messageListener =
+ nsDOMWorkerBase::GetMessageListener();
+ if (!messageListener) {
+ LOG(("Message received on a worker with no listener!"));
+ return NS_OK;
}
- if (suspendWorker) {
- aWorker->Suspend();
- }
+ nsCOMPtr<nsISupports> source;
+ aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
+ NS_ASSERTION(source, "Impossible!");
+
+ messageListener->OnMessage(aMessage, source);
+ return NS_OK;
+}
+
+nsresult
+nsDOMWorkerPool::DispatchMessage(nsIRunnable* aRunnable)
+{
+ // Can be called from many different threads!
+
+ nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
+ NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+ nsresult rv = mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
-nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker)
+nsDOMWorkerPool::HandleError(nsIScriptError* aError,
+ nsDOMWorkerThread* aSource)
{
- NS_ASSERTION(aWorker, "Null pointer!");
-
- PRBool removeFromThreadService = PR_FALSE;
-
- {
- nsAutoMonitor mon(mMonitor);
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
- mWorkers.RemoveElement(aWorker);
-
- if (!mCanceled && !mWorkers.Length()) {
- removeFromThreadService = PR_TRUE;
- }
- }
-
- if (removeFromThreadService) {
- nsRefPtr<nsDOMWorkerPool> kungFuDeathGrip(this);
- nsDOMThreadService::get()->NoteEmptyPool(this);
+ if (mErrorListener) {
+ mErrorListener->OnError(aError,
+ NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource));
}
}
void
-nsDOMWorkerPool::GetWorkers(nsTArray<nsDOMWorker*>& aArray)
+nsDOMWorkerPool::NoteDyingWorker(nsDOMWorkerThread* aWorker)
{
- aArray.Clear();
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- nsAutoMonitor mon(mMonitor);
-#ifdef DEBUG
- nsDOMWorker** newWorkers =
-#endif
- aArray.AppendElements(mWorkers);
- NS_WARN_IF_FALSE(newWorkers, "Out of memory!");
+ NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
+ mWorkers.RemoveElement(aWorker);
}
void
-nsDOMWorkerPool::Cancel()
-{
- NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- NS_ASSERTION(!mCanceled, "Canceled more than once!");
-
- {
- nsAutoMonitor mon(mMonitor);
-
- mCanceled = PR_TRUE;
-
- nsAutoTArray<nsDOMWorker*, 10> workers;
- GetWorkers(workers);
-
- PRUint32 count = workers.Length();
- if (count) {
- for (PRUint32 index = 0; index < count; index++) {
- workers[index]->Cancel();
- }
- mon.NotifyAll();
- }
- }
-
- mParentGlobal = nsnull;
- mParentDocument = nsnull;
-}
-
-void
-nsDOMWorkerPool::Suspend()
+nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- nsAutoTArray<nsDOMWorker*, 10> workers;
- {
- nsAutoMonitor mon(mMonitor);
-
- NS_ASSERTION(!mSuspended, "Suspended more than once!");
- mSuspended = PR_TRUE;
+ nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
+ NS_ASSERTION(globalSupports, "Null pointer?!");
- GetWorkers(workers);
- }
-
- PRUint32 count = workers.Length();
- for (PRUint32 index = 0; index < count; index++) {
- workers[index]->Suspend();
+ if (globalSupports == mParentGlobal) {
+ LOOP_OVER_WORKERS(Cancel, ());
+ mWorkers.Clear();
+ if (IsSuspended()) {
+ nsAutoMonitor mon(mMonitor);
+ mon.NotifyAll();
+ }
}
}
void
-nsDOMWorkerPool::Resume()
+nsDOMWorkerPool::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
+ NS_ASSERTION(globalSupports, "Null pointer?!");
+
+ if (globalSupports == mParentGlobal) {
+ LOOP_OVER_WORKERS(Suspend, ());
+ Suspend();
+ }
+}
+
+void
+nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- nsAutoTArray<nsDOMWorker*, 10> workers;
- {
- nsAutoMonitor mon(mMonitor);
-
- NS_ASSERTION(mSuspended, "Not suspended!");
- mSuspended = PR_FALSE;
+ nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
+ NS_ASSERTION(globalSupports, "Null pointer?!");
- GetWorkers(workers);
- }
+ if (globalSupports == mParentGlobal) {
+ LOOP_OVER_WORKERS(Resume, ());
+ Resume();
- PRUint32 count = workers.Length();
- if (count) {
- for (PRUint32 index = 0; index < count; index++) {
- workers[index]->Resume();
- }
nsAutoMonitor mon(mMonitor);
mon.NotifyAll();
}
}
+nsIDocument*
+nsDOMWorkerPool::ParentDocument()
+{
+ NS_ASSERTION(NS_IsMainThread(),
+ "Don't touch the non-threadsafe document off the main thread!");
+ return mParentDocument;
+}
+
nsIScriptContext*
nsDOMWorkerPool::ScriptContext()
{
NS_ASSERTION(NS_IsMainThread(),
"Don't touch the non-threadsafe script context off the main "
"thread!");
- return mParentGlobal->GetContext();
+ return mParentDocument->GetScriptGlobalObject()->GetContext();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
+{
+ nsresult rv = PostMessageInternal(aMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::SetMessageListener(nsIDOMWorkerMessageListener* aListener)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ nsDOMWorkerBase::SetMessageListener(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::GetMessageListener(nsIDOMWorkerMessageListener** aListener)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ nsCOMPtr<nsIDOMWorkerMessageListener> listener = nsDOMWorkerBase::GetMessageListener();
+ listener.forget(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::SetErrorListener(nsIDOMWorkerErrorListener* aListener)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ mErrorListener = aListener;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::GetErrorListener(nsIDOMWorkerErrorListener** aListener)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_IF_ADDREF(*aListener = mErrorListener);
+ return NS_OK;
}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::CreateWorker(const nsAString& aFullScript,
+ nsIDOMWorkerThread** _retval)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ NS_ENSURE_ARG(!aFullScript.IsEmpty());
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsRefPtr<nsDOMWorkerThread> worker =
+ new nsDOMWorkerThread(this, aFullScript, PR_FALSE);
+ NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = worker->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
+ mWorkers.AppendElement(worker);
+
+ NS_ADDREF(*_retval = worker);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerPool::CreateWorkerFromURL(const nsAString& aScriptURL,
+ nsIDOMWorkerThread** _retval)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ NS_ENSURE_ARG(!aScriptURL.IsEmpty());
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsRefPtr<nsDOMWorkerThread> worker =
+ new nsDOMWorkerThread(this, aScriptURL, PR_TRUE);
+ NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = worker->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
+ mWorkers.AppendElement(worker);
+
+ NS_ADDREF(*_retval = worker);
+ return NS_OK;
+}
--- a/dom/src/threads/nsDOMWorkerPool.h
+++ b/dom/src/threads/nsDOMWorkerPool.h
@@ -35,78 +35,99 @@
* 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 ***** */
#ifndef __NSDOMWORKERPOOL_H__
#define __NSDOMWORKERPOOL_H__
+// Bases
+#include "nsDOMWorkerBase.h"
+#include "nsIClassInfo.h"
+#include "nsIDOMThreads.h"
+
// Other includes
#include "jsapi.h"
-#include "nsCOMPtr.h"
#include "nsStringGlue.h"
-#include "nsTArray.h"
+#include "nsTPtrArray.h"
#include "prmon.h"
-class nsDOMWorker;
+class nsDOMWorkerThread;
class nsIDocument;
class nsIScriptContext;
class nsIScriptError;
class nsIScriptGlobalObject;
-class nsDOMWorkerPool
+/**
+ * The pool is almost always touched only on the main thread.
+ */
+class nsDOMWorkerPool : public nsDOMWorkerBase,
+ public nsIDOMWorkerPool,
+ public nsIClassInfo
{
+ friend class nsDOMThreadService;
+ friend class nsDOMWorkerFunctions;
+ friend class nsDOMWorkerPoolWeakRef;
+ friend class nsDOMWorkerScriptLoader;
+ friend class nsDOMWorkerStreamObserver;
+ friend class nsDOMWorkerThread;
+ friend class nsReportErrorRunnable;
+ friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
+
public:
- nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
- nsIDocument* aDocument);
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMWORKERPOOL
+ NS_DECL_NSICLASSINFO
+
+ nsDOMWorkerPool(nsIDocument* aDocument);
- NS_IMETHOD_(nsrefcnt) AddRef();
- NS_IMETHOD_(nsrefcnt) Release();
+ // For nsDOMWorkerBase
+ virtual nsDOMWorkerPool* Pool() {
+ return this;
+ }
+ nsIDocument* ParentDocument();
nsIScriptContext* ScriptContext();
- nsIScriptGlobalObject* ScriptGlobalObject() {
- return mParentGlobal;
- }
-
- nsIDocument* ParentDocument() {
- return mParentDocument;
- }
+private:
+ virtual ~nsDOMWorkerPool();
nsresult Init();
- void Cancel();
- void Suspend();
- void Resume();
+ // For nsDOMWorkerBase
+ virtual nsresult HandleMessage(const nsAString& aMessage,
+ nsDOMWorkerBase* aSourceThread);
+
+ // For nsDOMWorkerBase
+ virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
- nsresult NoteWorker(nsDOMWorker* aWorker);
- void NoteDyingWorker(nsDOMWorker* aWorker);
+ void HandleError(nsIScriptError* aError,
+ nsDOMWorkerThread* aSource);
+
+ void NoteDyingWorker(nsDOMWorkerThread* aWorker);
+
+ void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+ void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+ void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
PRMonitor* Monitor() {
return mMonitor;
}
-private:
- virtual ~nsDOMWorkerPool();
-
- void GetWorkers(nsTArray<nsDOMWorker*>& aArray);
+ // Weak reference to the window that created and owns this pool.
+ nsISupports* mParentGlobal;
- nsAutoRefCnt mRefCnt;
-
- // Reference to the window that created and owns this pool.
- nsCOMPtr<nsIScriptGlobalObject> mParentGlobal;
-
- // Reference to the document that created this pool.
- nsCOMPtr<nsIDocument> mParentDocument;
+ // Weak reference to the document that created this pool.
+ nsIDocument* mParentDocument;
// Weak array of workers. The idea is that workers can be garbage collected
// independently of the owning pool and other workers.
- nsTArray<nsDOMWorker*> mWorkers;
+ nsTPtrArray<nsDOMWorkerThread> mWorkers;
+
+ // An error handler function, may be null.
+ nsCOMPtr<nsIDOMWorkerErrorListener> mErrorListener;
// Monitor for suspending and resuming workers.
PRMonitor* mMonitor;
-
- PRPackedBool mCanceled;
- PRPackedBool mSuspended;
};
#endif /* __NSDOMWORKERPOOL_H__ */
--- a/dom/src/threads/nsDOMWorkerScriptLoader.cpp
+++ b/dom/src/threads/nsDOMWorkerScriptLoader.cpp
@@ -60,41 +60,88 @@
// DOMWorker includes
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerSecurityManager.h"
#include "nsDOMThreadService.h"
#include "nsDOMWorkerTimeout.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
-nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker)
-: nsDOMWorkerFeature(aWorker),
+nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader()
+: mWorker(nsnull),
mTarget(nsnull),
+ mCx(NULL),
mScriptCount(0),
- mCanceled(PR_FALSE)
+ mCanceled(PR_FALSE),
+ mTrackedByWorker(PR_FALSE)
{
// Created on worker thread.
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- NS_ASSERTION(aWorker, "Null worker!");
}
-NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
- nsIRunnable,
+nsDOMWorkerScriptLoader::~nsDOMWorkerScriptLoader()
+{
+ // Can't touch mWorker's lock
+ if (!mCanceled) {
+ // Destroyed on worker thread, unless canceled (and then who knows!).
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ if (mTrackedByWorker) {
+ jsrefcount suspendDepth = 0;
+ if (mCx) {
+ suspendDepth = JS_SuspendRequest(mCx);
+ }
+
+ nsAutoLock lock(mWorker->Lock());
+ #ifdef DEBUG
+ PRBool removed =
+ #endif
+ mWorker->mScriptLoaders.RemoveElement(this);
+ NS_ASSERTION(removed, "Something is wrong here!");
+
+ if (mCx) {
+ JS_ResumeRequest(mCx, suspendDepth);
+ }
+ }
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerScriptLoader, nsRunnable,
nsIStreamLoaderObserver)
nsresult
-nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
+nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
+ JSContext* aCx,
const nsTArray<nsString>& aURLs)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aWorker, "Null worker!");
NS_ASSERTION(aCx, "Null context!");
+ NS_ASSERTION(!mWorker, "Not designed to be used more than once!");
+
+ mWorker = aWorker;
+ mCx = aCx;
+
mTarget = NS_GetCurrentThread();
NS_ASSERTION(mTarget, "This should never be null!");
+ {
+ JSAutoSuspendRequest asr(aCx);
+
+ nsAutoLock lock(mWorker->Lock());
+
+ if (mWorker->IsCanceled()) {
+ return NS_ERROR_ABORT;
+ }
+
+ mTrackedByWorker = nsnull != mWorker->mScriptLoaders.AppendElement(this);
+ NS_ASSERTION(mTrackedByWorker, "Failed to add loader to worker's array!");
+ }
+
if (mCanceled) {
return NS_ERROR_ABORT;
}
mScriptCount = aURLs.Length();
if (!mScriptCount) {
return NS_ERROR_INVALID_ARG;
}
@@ -121,47 +168,60 @@ nsDOMWorkerScriptLoader::LoadScripts(JSC
success = newInfo->scriptObj.Hold(aCx);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
}
// Don't want timeouts, etc., from queuing up while we're waiting on the
// network or compiling.
AutoSuspendWorkerEvents aswe(this);
- nsresult rv = DoRunLoop(aCx);
+ nsresult rv = DoRunLoop();
+
+ {
+ JSAutoSuspendRequest asr(aCx);
+ nsAutoLock lock(mWorker->Lock());
+#ifdef DEBUG
+ PRBool removed =
+#endif
+ mWorker->mScriptLoaders.RemoveElement(this);
+ NS_ASSERTION(removed, "Something is wrong here!");
+ mTrackedByWorker = PR_FALSE;
+ }
+
if (NS_FAILED(rv)) {
return rv;
}
// Verify that all scripts downloaded and compiled.
- rv = VerifyScripts(aCx);
+ rv = VerifyScripts();
if (NS_FAILED(rv)) {
return rv;
}
- rv = ExecuteScripts(aCx);
+ rv = ExecuteScripts();
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
nsresult
-nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx,
+nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker,
+ JSContext* aCx,
const nsString& aURL)
{
nsAutoTArray<nsString, 1> url;
url.AppendElement(aURL);
- return LoadScripts(aCx, url);
+ return LoadScripts(aWorker, aCx, url);
}
nsresult
-nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
+nsDOMWorkerScriptLoader::DoRunLoop()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
volatile PRBool done = PR_FALSE;
mDoneRunnable = new ScriptLoaderDone(this, &done);
NS_ENSURE_TRUE(mDoneRunnable, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = NS_DispatchToMainThread(this);
@@ -169,33 +229,31 @@ nsDOMWorkerScriptLoader::DoRunLoop(JSCon
if (!(done || mCanceled)) {
// Since we're going to lock up this thread we might as well allow the
// thread service to schedule another worker on a new thread.
nsDOMThreadService* threadService = nsDOMThreadService::get();
PRBool changed = NS_SUCCEEDED(threadService->ChangeThreadPoolMaxThreads(1));
while (!(done || mCanceled)) {
- JSAutoSuspendRequest asr(aCx);
+ JSAutoSuspendRequest asr(mCx);
NS_ProcessNextEvent(mTarget);
}
if (changed) {
threadService->ChangeThreadPoolMaxThreads(-1);
}
}
return mCanceled ? NS_ERROR_ABORT : NS_OK;
}
nsresult
-nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
+nsDOMWorkerScriptLoader::VerifyScripts()
{
- NS_ASSERTION(aCx, "Shouldn't be null!");
-
nsresult rv = NS_OK;
for (PRUint32 index = 0; index < mScriptCount; index++) {
ScriptLoadInfo& loadInfo = mLoadInfos[index];
NS_ASSERTION(loadInfo.done, "Inconsistent state!");
if (NS_SUCCEEDED(loadInfo.result) && loadInfo.scriptObj) {
continue;
@@ -211,70 +269,52 @@ nsDOMWorkerScriptLoader::VerifyScripts(J
// since that's the code we set when some other script had a problem and the
// rest were canceled.
if (NS_SUCCEEDED(loadInfo.result) || loadInfo.result == NS_BINDING_ABORTED) {
continue;
}
// Ok, this is the script that caused us to fail.
- JSAutoRequest ar(aCx);
-
- // Only throw an error if there is no other pending exception.
- if (!JS_IsExceptionPending(aCx)) {
- const char* message;
- switch (loadInfo.result) {
- case NS_ERROR_MALFORMED_URI:
- message = "Malformed script URI: %s";
- break;
- case NS_ERROR_FILE_NOT_FOUND:
- message = "Script file not found: %s";
- break;
- default:
- message = "Failed to load script: %s (nsresult = 0x%x)";
- break;
- }
+ // Only throw an error there is no other pending exception.
+ if (!JS_IsExceptionPending(mCx)) {
NS_ConvertUTF16toUTF8 url(loadInfo.url);
- JS_ReportError(aCx, message, url.get(), loadInfo.result);
+ JS_ReportError(mCx, "Failed to compile script: %s", url.get());
}
break;
}
return rv;
}
nsresult
-nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
+nsDOMWorkerScriptLoader::ExecuteScripts()
{
- NS_ASSERTION(aCx, "Shouldn't be null!");
-
// Now execute all the scripts.
for (PRUint32 index = 0; index < mScriptCount; index++) {
ScriptLoadInfo& loadInfo = mLoadInfos[index];
- JSAutoRequest ar(aCx);
-
JSScript* script =
- static_cast<JSScript*>(JS_GetPrivate(aCx, loadInfo.scriptObj));
+ static_cast<JSScript*>(JS_GetPrivate(mCx, loadInfo.scriptObj));
NS_ASSERTION(script, "This shouldn't ever be null!");
JSObject* global = mWorker->mGlobal ?
mWorker->mGlobal :
- JS_GetGlobalObject(aCx);
+ JS_GetGlobalObject(mCx);
NS_ENSURE_STATE(global);
// Because we may have nested calls to this function we don't want the
// execution to automatically report errors. We let them propagate instead.
uint32 oldOpts =
- JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+ JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
jsval val;
- PRBool success = JS_ExecuteScript(aCx, global, script, &val);
+ PRBool success = JS_ExecuteScript(mCx, global, script, &val);
- JS_SetOptions(aCx, oldOpts);
+ JS_SetOptions(mCx, oldOpts);
if (!success) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
@@ -403,21 +443,18 @@ nsDOMWorkerScriptLoader::OnStreamComplet
}
nsresult
nsDOMWorkerScriptLoader::RunInternal()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Things we need to make all this work...
- nsCOMPtr<nsIDocument> parentDoc = mWorker->Pool()->ParentDocument();
- if (!parentDoc) {
- // Must have been canceled.
- return NS_ERROR_ABORT;
- }
+ nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
+ NS_ASSERTION(parentDoc, "Null parent document?!");
// All of these can potentially be null, but that should be ok. We'll either
// succeed without them or fail below.
nsIURI* parentBaseURI = parentDoc->GetBaseURI();
nsCOMPtr<nsILoadGroup> loadGroup(parentDoc->GetDocumentLoadGroup());
nsCOMPtr<nsIIOService> ios(do_GetIOService());
for (PRUint32 index = 0; index < mScriptCount; index++) {
@@ -430,17 +467,19 @@ nsDOMWorkerScriptLoader::RunInternal()
parentBaseURI);
if (NS_FAILED(rv)) {
return rv;
}
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
- rv = secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, 0);
+ rv =
+ secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri,
+ nsIScriptSecurityManager::ALLOW_CHROME);
if (NS_FAILED(rv)) {
return rv;
}
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
uri,
parentDoc->NodePrincipal(),
@@ -569,17 +608,18 @@ nsDOMWorkerScriptLoader::OnStreamComplet
}
else {
// This will help callers figure out what their script url resolved to in
// case of errors.
loadInfo.url.Assign(NS_ConvertUTF8toUTF16(filename));
}
nsRefPtr<ScriptCompiler> compiler =
- new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj);
+ new ScriptCompiler(this, mCx, loadInfo.scriptText, filename,
+ loadInfo.scriptObj);
NS_ASSERTION(compiler, "Out of memory!");
if (!compiler) {
return rv = NS_ERROR_OUT_OF_MEMORY;
}
rv = mTarget->Dispatch(compiler, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
@@ -618,24 +658,24 @@ nsDOMWorkerScriptLoader::NotifyDone()
mDoneRunnable = nsnull;
}
void
nsDOMWorkerScriptLoader::SuspendWorkerEvents()
{
NS_ASSERTION(mWorker, "No worker yet!");
- mWorker->SuspendFeatures();
+ mWorker->SuspendTimeouts();
}
void
nsDOMWorkerScriptLoader::ResumeWorkerEvents()
{
NS_ASSERTION(mWorker, "No worker yet!");
- mWorker->ResumeFeatures();
+ mWorker->ResumeTimeouts();
}
nsDOMWorkerScriptLoader::
ScriptLoaderRunnable::ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader)
: mRevoked(PR_FALSE),
mLoader(aLoader)
{
nsAutoLock lock(aLoader->Lock());
@@ -654,35 +694,35 @@ ScriptLoaderRunnable::~ScriptLoaderRunna
#ifdef DEBUG
PRBool removed =
#endif
mLoader->mPendingRunnables.RemoveElement(this);
NS_ASSERTION(removed, "Someone has changed the array!");
}
}
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerScriptLoader::ScriptLoaderRunnable,
- nsIRunnable)
-
void
nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke()
{
mRevoked = PR_TRUE;
}
nsDOMWorkerScriptLoader::
ScriptCompiler::ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
+ JSContext* aCx,
const nsString& aScriptText,
const nsCString& aFilename,
nsAutoJSObjectHolder& aScriptObj)
: ScriptLoaderRunnable(aLoader),
+ mCx(aCx),
mScriptText(aScriptText),
mFilename(aFilename),
mScriptObj(aScriptObj)
{
+ NS_ASSERTION(aCx, "Null context!");
NS_ASSERTION(!aScriptText.IsEmpty(), "No script to compile!");
NS_ASSERTION(aScriptObj.IsHeld(), "Should be held!");
}
NS_IMETHODIMP
nsDOMWorkerScriptLoader::ScriptCompiler::Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
@@ -690,44 +730,41 @@ nsDOMWorkerScriptLoader::ScriptCompiler:
if (mRevoked) {
return NS_OK;
}
NS_ASSERTION(!mScriptObj, "Already have a script object?!");
NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!");
NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!");
- JSContext* cx = nsDOMThreadService::GetCurrentContext();
- NS_ENSURE_STATE(cx);
+ JSAutoRequest ar(mCx);
- JSAutoRequest ar(cx);
-
- JSObject* global = JS_GetGlobalObject(cx);
+ JSObject* global = JS_GetGlobalObject(mCx);
NS_ENSURE_STATE(global);
// Because we may have nested calls to this function we don't want the
// execution to automatically report errors. We let them propagate instead.
uint32 oldOpts =
- JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
+ JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
JSScript* script =
- JS_CompileUCScriptForPrincipals(cx, global, principal,
+ JS_CompileUCScriptForPrincipals(mCx, global, principal,
reinterpret_cast<const jschar*>
(mScriptText.BeginReading()),
mScriptText.Length(), mFilename.get(), 1);
- JS_SetOptions(cx, oldOpts);
+ JS_SetOptions(mCx, oldOpts);
if (!script) {
return NS_ERROR_FAILURE;
}
- mScriptObj = JS_NewScriptObject(cx, script);
+ mScriptObj = JS_NewScriptObject(mCx, script);
NS_ENSURE_STATE(mScriptObj);
return NS_OK;
}
nsDOMWorkerScriptLoader::
ScriptLoaderDone::ScriptLoaderDone(nsDOMWorkerScriptLoader* aLoader,
volatile PRBool* aDoneFlag)
--- a/dom/src/threads/nsDOMWorkerScriptLoader.h
+++ b/dom/src/threads/nsDOMWorkerScriptLoader.h
@@ -35,35 +35,34 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERSCRIPTLOADER_H__
#define __NSDOMWORKERSCRIPTLOADER_H__
// Bases
-#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
#include "nsIStreamLoader.h"
// Interfaces
#include "nsIChannel.h"
#include "nsIURI.h"
// Other includes
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsAutoJSObjectHolder.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "prlock.h"
-#include "nsDOMWorker.h"
-
-class nsIThread;
+// DOMWorker includes
+#include "nsDOMWorkerThread.h"
/**
* This class takes a list of script URLs, downloads the scripts, compiles the
* scripts, and then finally executes them. Due to platform limitations all
* network operations must happen on the main thread so this object sends events
* back and forth from the worker thread to the main thread. The flow goes like
* this:
*
@@ -82,45 +81,47 @@ class nsIThread;
* downloads completed.
* 7. (Worker thread) After all loads complete and all compilation succeeds
* the scripts are executed in the order that the URLs were
* given to LoadScript(s).
*
* Currently if *anything* after 2 fails then we cancel any pending loads and
* bail out entirely.
*/
-class nsDOMWorkerScriptLoader : public nsDOMWorkerFeature,
- public nsIRunnable,
+class nsDOMWorkerScriptLoader : public nsRunnable,
public nsIStreamLoaderObserver
{
friend class AutoSuspendWorkerEvents;
+ friend class nsDOMWorkerFunctions;
+ friend class nsDOMWorkerThread;
friend class ScriptLoaderRunnable;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
NS_DECL_NSISTREAMLOADEROBSERVER
- nsDOMWorkerScriptLoader(nsDOMWorker* aWorker);
+ nsDOMWorkerScriptLoader();
- nsresult LoadScripts(JSContext* aCx,
+ nsresult LoadScripts(nsDOMWorkerThread* aWorker,
+ JSContext* aCx,
const nsTArray<nsString>& aURLs);
- nsresult LoadScript(JSContext* aCx,
- const nsString& aURL);
+ nsresult LoadScript(nsDOMWorkerThread* aWorker,
+ JSContext* aCx,
+ const nsString& aURL);
- virtual void Cancel();
+ void Cancel();
private:
- ~nsDOMWorkerScriptLoader() { }
-
+ ~nsDOMWorkerScriptLoader();
- nsresult DoRunLoop(JSContext* aCx);
- nsresult VerifyScripts(JSContext* aCx);
- nsresult ExecuteScripts(JSContext* aCx);
+ nsresult DoRunLoop();
+ nsresult VerifyScripts();
+ nsresult ExecuteScripts();
nsresult RunInternal();
nsresult OnStreamCompleteInternal(nsIStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
PRUint32 aStringLen,
const PRUint8* aString);
@@ -129,21 +130,18 @@ private:
void SuspendWorkerEvents();
void ResumeWorkerEvents();
PRLock* Lock() {
return mWorker->Lock();
}
- class ScriptLoaderRunnable : public nsIRunnable
+ class ScriptLoaderRunnable : public nsRunnable
{
- public:
- NS_DECL_ISUPPORTS
-
protected:
// Meant to be inherited.
ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader);
virtual ~ScriptLoaderRunnable();
public:
void Revoke();
@@ -155,21 +153,23 @@ private:
};
class ScriptCompiler : public ScriptLoaderRunnable
{
public:
NS_DECL_NSIRUNNABLE
ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
+ JSContext* aCx,
const nsString& aScriptText,
const nsCString& aFilename,
nsAutoJSObjectHolder& aScriptObj);
private:
+ JSContext* mCx;
nsString mScriptText;
nsCString mFilename;
nsAutoJSObjectHolder& mScriptObj;
};
class ScriptLoaderDone : public ScriptLoaderRunnable
{
public:
@@ -200,22 +200,25 @@ private:
nsString scriptText;
PRBool done;
nsresult result;
nsCOMPtr<nsIURI> finalURI;
nsCOMPtr<nsIChannel> channel;
nsAutoJSObjectHolder scriptObj;
};
+ nsDOMWorkerThread* mWorker;
nsIThread* mTarget;
+ JSContext* mCx;
nsRefPtr<ScriptLoaderDone> mDoneRunnable;
PRUint32 mScriptCount;
nsTArray<ScriptLoadInfo> mLoadInfos;
+ PRPackedBool mCanceled;
+ PRPackedBool mTrackedByWorker;
+
// Protected by mWorker's lock!
nsTArray<ScriptLoaderRunnable*> mPendingRunnables;
-
- PRPackedBool mCanceled;
};
#endif /* __NSDOMWORKERSCRIPTLOADER_H__ */
--- a/dom/src/threads/nsDOMWorkerTimeout.cpp
+++ b/dom/src/threads/nsDOMWorkerTimeout.cpp
@@ -42,19 +42,17 @@
#include "nsIJSContextStack.h"
#include "nsIJSRuntimeService.h"
#include "nsITimer.h"
#include "nsIXPConnect.h"
// Other includes
#include "nsContentUtils.h"
#include "nsJSUtils.h"
-#include "nsThreadUtils.h"
#include "pratom.h"
-#include "prtime.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
#define CONSTRUCTOR_ENSURE_TRUE(_cond, _rv) \
PR_BEGIN_MACRO \
@@ -70,25 +68,25 @@
static const char* kSetIntervalStr = "setInterval";
static const char* kSetTimeoutStr = "setTimeout";
nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
jsval* aArgv,
nsresult* aRv)
: mCallback(nsnull),
mCallbackArgs(nsnull),
- mCallbackArgsLength(0),
- mRuntime(NULL)
+ mCallbackArgsLength(0)
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
- *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
+ JSRuntime* rt;
+ *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(*aRv,);
- PRBool success = JS_AddNamedRootRT(mRuntime, &mCallback,
+ PRBool success = JS_AddNamedRootRT(rt, &mCallback,
"nsDOMWorkerTimeout Callback Object");
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
mCallback = aArgv[0];
// We want enough space for an extra lateness arg.
mCallbackArgsLength = aArgc > 2 ? aArgc - 1 : 1;
@@ -99,32 +97,32 @@ nsDOMWorkerTimeout::FunctionCallback::Fu
NS_ERROR("Out of memory!");
*aRv = NS_ERROR_OUT_OF_MEMORY;
return;
}
for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) {
mCallbackArgs[i] = aArgv[i + 2];
- success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[i],
+ success = JS_AddNamedRootRT(rt, &mCallbackArgs[i],
"nsDOMWorkerTimeout Callback Arg");
if (NS_UNLIKELY(!success)) {
// Set this to i so that the destructor only unroots the right number of
// values.
mCallbackArgsLength = i;
NS_WARNING("Failed to add root!");
*aRv = NS_ERROR_FAILURE;
return;
}
}
// Take care of the last arg.
mCallbackArgs[mCallbackArgsLength - 1] = 0;
- success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[mCallbackArgsLength - 1],
+ success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1],
"nsDOMWorkerTimeout Callback Final Arg");
if (NS_UNLIKELY(!success)) {
// Decrement this so that the destructor only unroots the right number of
// values.
mCallbackArgsLength -= 1;
NS_WARNING("Failed to add root!");
*aRv = NS_ERROR_FAILURE;
@@ -134,20 +132,27 @@ nsDOMWorkerTimeout::FunctionCallback::Fu
*aRv = NS_OK;
}
nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
{
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
if (mCallback) {
- for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
- JS_RemoveRootRT(mRuntime, &mCallbackArgs[i]);
+ JSRuntime* rt;
+ nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
+
+ NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
+
+ if (NS_SUCCEEDED(rv)) {
+ for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
+ JS_RemoveRootRT(rt, &mCallbackArgs[i]);
+ }
+ JS_RemoveRootRT(rt, &mCallback);
}
- JS_RemoveRootRT(mRuntime, &mCallback);
}
delete [] mCallbackArgs;
}
nsresult
nsDOMWorkerTimeout::FunctionCallback::Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx)
@@ -168,29 +173,29 @@ nsDOMWorkerTimeout::FunctionCallback::Ru
return NS_OK;
}
nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
jsval* aArgv,
JSContext* aCx,
nsresult* aRv)
: mExpression(nsnull),
- mLineNumber(0),
- mRuntime(NULL)
+ mLineNumber(0)
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
JSString* expr = JS_ValueToString(aCx, aArgv[0]);
*aRv = expr ? NS_OK : NS_ERROR_FAILURE;
NS_ENSURE_SUCCESS(*aRv,);
- *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
+ JSRuntime* rt;
+ *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(*aRv,);
- PRBool success = JS_AddNamedRootRT(mRuntime, &mExpression,
+ PRBool success = JS_AddNamedRootRT(rt, &mExpression,
"nsDOMWorkerTimeout Expression");
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
mExpression = expr;
// Get the calling location.
const char* fileName;
PRUint32 lineNumber;
@@ -202,44 +207,66 @@ nsDOMWorkerTimeout::ExpressionCallback::
*aRv = NS_OK;
}
nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
{
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
if (mExpression) {
- JS_RemoveRootRT(mRuntime, &mExpression);
+ JSRuntime* rt;
+ nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
+
+ NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
+
+ if (NS_SUCCEEDED(rv)) {
+ JS_RemoveRootRT(rt, &mExpression);
+ }
}
}
nsresult
nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx)
{
NS_ERROR("Not yet implemented!");
return NS_ERROR_NOT_IMPLEMENTED;
}
-nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorker* aWorker,
+nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
PRUint32 aId)
-: nsDOMWorkerFeature(aWorker, aId),
+: mWorker(aWorker),
mInterval(0),
+ mIsInterval(PR_FALSE),
+ mId(aId),
mSuspendSpinlock(0),
- mSuspendInterval(0),
- mIsInterval(PR_FALSE),
mIsSuspended(PR_FALSE),
- mSuspendedBeforeStart(PR_FALSE),
- mStarted(PR_FALSE)
+ mSuspendInterval(0)
+#ifdef DEBUG
+, mFiredOrCanceled(PR_FALSE)
+#endif
{
+ MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
NS_ASSERTION(mWorker, "Need a worker here!");
}
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerTimeout, nsDOMWorkerFeature,
- nsITimerCallback)
+nsDOMWorkerTimeout::~nsDOMWorkerTimeout()
+{
+ MOZ_COUNT_DTOR(nsDOMWorkerTimeout);
+
+ // If we have a timer then we assume we added ourselves to the thread's list.
+ if (mTimer) {
+ NS_ASSERTION(mFiredOrCanceled || mWorker->IsCanceled(),
+ "Timeout should have fired or been canceled!");
+
+ mWorker->RemoveTimeout(this);
+ }
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeout, nsITimerCallback)
nsresult
nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
PRBool aIsInterval)
{
NS_ASSERTION(aCx, "Null pointer!");
NS_ASSERTION(aArgv, "Null pointer!");
@@ -262,18 +289,16 @@ nsDOMWorkerTimeout::Init(JSContext* aCx,
else {
// If no interval was specified, treat this like a timeout, to avoid
// setting an interval of 0 milliseconds.
aIsInterval = PR_FALSE;
}
mInterval = interval;
- mIsInterval = aIsInterval;
-
mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC;
nsresult rv;
switch (JS_TypeOfValue(aCx, aArgv[0])) {
case JSTYPE_FUNCTION:
mCallback = new FunctionCallback(aArgc, aArgv, &rv);
NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
@@ -285,64 +310,65 @@ nsDOMWorkerTimeout::Init(JSContext* aCx,
mCallback = new ExpressionCallback(aArgc, aArgv, aCx, &rv);
NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
break;
default:
JS_ReportError(aCx, "useless %s call (missing quotes around argument?)",
aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
-
+
// Return an error that nsGlobalWindow can recognize and turn into NS_OK.
return NS_ERROR_INVALID_ARG;
}
+ PRInt32 type;
+ if (aIsInterval) {
+ type = nsITimer::TYPE_REPEATING_SLACK;
+ }
+ else {
+ type = nsITimer::TYPE_ONE_SHOT;
+ }
+ mIsInterval = aIsInterval;
+
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsIEventTarget* target =
static_cast<nsIEventTarget*>(nsDOMThreadService::get());
rv = timer->SetTarget(target);
NS_ENSURE_SUCCESS(rv, rv);
+ rv = timer->InitWithCallback(this, interval, type);
+ NS_ENSURE_SUCCESS(rv, rv);
+
mTimer.swap(timer);
- return NS_OK;
-}
-nsresult
-nsDOMWorkerTimeout::Start()
-{
- if (IsSuspended()) {
- NS_ASSERTION(mSuspendedBeforeStart, "Bad state!");
- return NS_OK;
+ if (!mWorker->AddTimeout(this)) {
+ // Must have been canceled.
+ mTimer->Cancel();
+ mTimer = nsnull;
+ return NS_ERROR_ABORT;
}
- PRInt32 type;
- if (mIsInterval) {
- type = nsITimer::TYPE_REPEATING_SLACK;
- }
- else {
- type = nsITimer::TYPE_ONE_SHOT;
- }
-
- nsresult rv = mTimer->InitWithCallback(this, mInterval, type);
- NS_ENSURE_SUCCESS(rv, rv);
-
- mStarted = PR_TRUE;
return NS_OK;
}
nsresult
nsDOMWorkerTimeout::Run()
{
NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
LOG(("Worker [0x%p] running timeout [0x%p] with id %u",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
+#ifdef DEBUG
+ mFiredOrCanceled = PR_TRUE;
+#endif
+
JSContext* cx;
nsresult rv =
nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoRequest ar(cx);
rv = mCallback->Run(this, cx);
@@ -360,87 +386,71 @@ nsDOMWorkerTimeout::Run()
void
nsDOMWorkerTimeout::Cancel()
{
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
LOG(("Worker [0x%p] canceling timeout [0x%p] with id %u",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
+#ifdef DEBUG
+ mFiredOrCanceled = PR_TRUE;
+#endif
+
{
AutoSpinlock lock(this);
if (IsSuspendedNoLock()) {
mIsSuspended = PR_FALSE;
// This should kill us when all is said and done.
mSuspendedRef = nsnull;
}
}
// This call to Cancel should kill us.
mTimer->Cancel();
}
void
-nsDOMWorkerTimeout::Suspend()
+nsDOMWorkerTimeout::Suspend(PRTime aNow)
{
-#ifdef DEBUG
- if (mStarted) {
- NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- }
-#endif
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
AutoSpinlock lock(this);
- NS_ASSERTION(!IsSuspendedNoLock(), "Bad state!");
-
- mIsSuspended = PR_TRUE;
- mSuspendedRef = this;
-
- if (!mStarted) {
- mSuspendedBeforeStart = PR_TRUE;
- return;
+ if (!mIsSuspended) {
+ mIsSuspended = PR_TRUE;
+ mSuspendedRef = this;
}
mTimer->Cancel();
- mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - PR_Now())) /
+ mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
(PRTime)PR_USEC_PER_MSEC;
LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId,
mSuspendInterval));
}
void
-nsDOMWorkerTimeout::Resume()
+nsDOMWorkerTimeout::Resume(PRTime aNow)
{
-#ifdef DEBUG
- if (!mSuspendedBeforeStart) {
- NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- }
-#endif
-
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
AutoSpinlock lock(this);
NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!");
- if (mSuspendedBeforeStart) {
- NS_ASSERTION(!mSuspendInterval, "Bad state!");
- mSuspendedBeforeStart = PR_FALSE;
- mSuspendInterval = mInterval;
- mStarted = PR_TRUE;
- }
-
- mTargetTime = PR_Now() + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
+ mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
#ifdef DEBUG
nsresult rv =
#endif
mTimer->InitWithCallback(this, mSuspendInterval, nsITimer::TYPE_ONE_SHOT);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init timer!");
}
@@ -484,25 +494,29 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTi
nsresult rv = aTimer->GetType(&type);
NS_ENSURE_SUCCESS(rv, rv);
// We only care about one-shot timers here because that may be the one that
// we set from Resume().
if (type == nsITimer::TYPE_ONE_SHOT) {
AutoSpinlock lock(this);
if (mIsSuspended) {
- mIsSuspended = PR_FALSE;
- mSuspendedRef = nsnull;
if (mIsInterval) {
+ //LOG(("Timeout [0x%p] resuming normal interval (%u) with id %u",
+ //static_cast<void*>(this), mInterval, mId));
+
// This is the first fire since we resumed. Set our interval back to the
// real interval.
mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
rv = aTimer->InitWithCallback(this, mInterval,
nsITimer::TYPE_REPEATING_SLACK);
NS_ENSURE_SUCCESS(rv, rv);
}
+
+ mIsSuspended = PR_FALSE;
+ mSuspendedRef = nsnull;
}
}
nsDOMThreadService::get()->TimeoutReady(this);
return NS_OK;
}
--- a/dom/src/threads/nsDOMWorkerTimeout.h
+++ b/dom/src/threads/nsDOMWorkerTimeout.h
@@ -42,71 +42,70 @@
// Interfaces
#include "nsITimer.h"
// Other includes
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
+#include "prclist.h"
// DOMWorker includes
-#include "nsDOMWorker.h"
+#include "nsDOMWorkerThread.h"
/**
* The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created
- * by an nsDOMWorker (or one of its JS context functions) and immediately takes
- * a strong reference to the worker that created it. It does this so that the
- * worker can't be collected while a timeout is outstanding. However, the worker
- * needs a weak reference to the timeout so that it can be canceled if the
- * worker is canceled (in the event that the page falls out of the fastback
+ * by an nsDOMWorkerThread (or one of its JS context functions) and immediately
+ * takes a strong reference to the worker that created it. It does this so that
+ * the worker can't be collected while a timeout is outstanding. However, the
+ * worker needs a weak reference to the timeout so that it can be canceled if
+ * the worker is canceled (in the event that the page falls out of the fastback
* cache or the application is exiting, for instance). The only thing that holds
* the timeout alive is it's mTimer via the nsITimerCallback interface. If the
* timer is single-shot and has run already or if the timer is canceled then
* this object should die.
*/
-class nsDOMWorkerTimeout : public nsDOMWorkerFeature,
+class nsDOMWorkerTimeout : public PRCList,
public nsITimerCallback
{
public:
- NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
- nsDOMWorkerTimeout(nsDOMWorker* aWorker,
- PRUint32 aId);
+ nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId);
+ ~nsDOMWorkerTimeout();
- nsresult Init(JSContext* aCx,
- PRUint32 aArgc,
- jsval* aArgv,
+ nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
PRBool aIsInterval);
- nsresult Start();
-
nsresult Run();
- virtual void Cancel();
- virtual void Suspend();
- virtual void Resume();
+ void Cancel();
+ void Suspend(PRTime aNow);
+ void Resume(PRTime aNow);
PRIntervalTime GetInterval() {
return mInterval;
}
- nsDOMWorker* GetWorker() {
+ nsDOMWorkerThread* GetWorker() {
return mWorker;
}
+ PRUint32 GetId() {
+ return mId;
+ }
+
PRBool IsSuspended() {
AutoSpinlock lock(this);
return IsSuspendedNoLock();
}
private:
- ~nsDOMWorkerTimeout() { }
-
void AcquireSpinlock();
void ReleaseSpinlock();
PRBool IsSuspendedNoLock() {
return mIsSuspended;
}
class AutoSpinlock
@@ -133,58 +132,58 @@ private:
virtual ~CallbackBase() { }
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx) = 0;
};
class FunctionCallback : public CallbackBase
{
public:
- FunctionCallback(PRUint32 aArgc,
- jsval* aArgv,
- nsresult* aRv);
+ FunctionCallback(PRUint32 aArgc, jsval* aArgv, nsresult* aRv);
virtual ~FunctionCallback();
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx);
protected:
jsval mCallback;
jsval* mCallbackArgs;
PRUint32 mCallbackArgsLength;
- JSRuntime* mRuntime;
};
-
+
class ExpressionCallback : public CallbackBase
{
public:
- ExpressionCallback(PRUint32 aArgc,
- jsval* aArgv,
- JSContext* aCx,
+ ExpressionCallback(PRUint32 aArgc, jsval* aArgv, JSContext* aCx,
nsresult* aRv);
virtual ~ExpressionCallback();
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx);
protected:
JSString* mExpression;
nsString mFileName;
PRUint32 mLineNumber;
- JSRuntime* mRuntime;
};
+ // Hold the worker alive!
+ nsRefPtr<nsDOMWorkerThread> mWorker;
+
// Hold this object alive!
nsCOMPtr<nsITimer> mTimer;
PRUint32 mInterval;
+ PRBool mIsInterval;
PRTime mTargetTime;
nsAutoPtr<CallbackBase> mCallback;
+ PRUint32 mId;
+
PRInt32 mSuspendSpinlock;
+ PRBool mIsSuspended;
PRUint32 mSuspendInterval;
nsRefPtr<nsDOMWorkerTimeout> mSuspendedRef;
- PRPackedBool mIsInterval;
- PRPackedBool mIsSuspended;
- PRPackedBool mSuspendedBeforeStart;
- PRPackedBool mStarted;
+#ifdef DEBUG
+ PRBool mFiredOrCanceled;
+#endif
};
#endif /* __NSDOMWORKERTIMEOUT_H__ */
--- a/dom/src/threads/nsDOMWorkerXHR.cpp
+++ b/dom/src/threads/nsDOMWorkerXHR.cpp
@@ -51,17 +51,16 @@
#include "nsContentUtils.h"
#include "nsIClassInfoImpl.h"
#include "nsJSUtils.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
-#include "nsDOMWorkerEvents.h"
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerXHRProxy.h"
// The list of event types that we support. This list and the defines based on
// it determine the sizes of the listener arrays in nsDOMWorkerXHRProxy. Make
// sure that any event types shared by both the XHR and Upload objects are
// together at the beginning of the list. Any changes made to this list may
// affect sMaxUploadEventTypes, so make sure that it is adjusted accordingly or
@@ -87,330 +86,339 @@ const PRUint32 nsDOMWorkerXHREventTarget
const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes =
LISTENER_TYPE_READYSTATECHANGE;
// Enforce the invariant that the upload object supports no more event types
// than the xhr object.
PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >=
nsDOMWorkerXHREventTarget::sMaxUploadEventTypes);
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHREventTarget,
- nsDOMWorkerMessageHandler,
- nsIXMLHttpRequestEventTarget)
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget,
+ nsIDOMEventTarget,
+ nsIXMLHttpRequestEventTarget)
PRUint32
nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString)
{
for (PRUint32 index = 0; index < sMaxXHREventTypes; index++) {
if (aString.EqualsASCII(sListenerTypes[index])) {
return index;
}
}
return PR_UINT32_MAX;
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnabort);
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
-
- nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+ nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ABORT);
listener.forget(aOnabort);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
-
- return SetOnXListener(type, aOnabort);
+ return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
{
NS_ENSURE_ARG_POINTER(aOnerror);
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
-
- nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+ nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ERROR);
listener.forget(aOnerror);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
-
- return SetOnXListener(type, aOnerror);
+ return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnload);
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
-
- nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+ nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_LOAD);
listener.forget(aOnload);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
-
- return SetOnXListener(type, aOnload);
+ return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnloadstart);
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
-
- nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+ nsCOMPtr<nsIDOMEventListener> listener =
+ GetOnXListener(LISTENER_TYPE_LOADSTART);
listener.forget(aOnloadstart);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
-
- return SetOnXListener(type, aOnloadstart);
+ return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnprogress);
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
-
- nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
+ nsCOMPtr<nsIDOMEventListener> listener =
+ GetOnXListener(LISTENER_TYPE_PROGRESS);
listener.forget(aOnprogress);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType,
+ nsIDOMEventListener* aListener,
+ PRBool aUseCapture)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ PRUint32 type = GetListenerTypeFromString(aType);
+ if (type > sMaxXHREventTypes) {
+ // Silently ignore junk events.
+ return NS_OK;
+ }
+
+ return SetEventListener(type, aListener, PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::RemoveEventListener(const nsAString& aType,
+ nsIDOMEventListener* aListener,
+ PRBool aUseCapture)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ PRUint32 type = GetListenerTypeFromString(aType);
+ if (type > sMaxXHREventTypes) {
+ // Silently ignore junk events.
+ return NS_OK;
+ }
+
+ return UnsetEventListener(type, aListener);
+}
+
+/* ec702b78-c30f-439f-9a9b-a5dae17ee0fc */
+#define NS_IPRIVATEWORKERXHREVENT_IID \
+{ \
+ 0xec702b78, \
+ 0xc30f, \
+ 0x439f, \
+ { 0x9a, 0x9b, 0xa5, 0xda, 0xe1, 0x7e, 0xe0, 0xfc } \
+}
+
+class nsIPrivateWorkerXHREvent : public nsIDOMEvent
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRIVATEWORKERXHREVENT_IID)
+ virtual PRBool PreventDefaultCalled() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateWorkerXHREvent,
+ NS_IPRIVATEWORKERXHREVENT_IID)
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
+#define NS_FORWARD_NSIDOMEVENT_SPECIAL \
+ NS_IMETHOD GetType(nsAString& aType) \
+ { return mEvent->GetType(aType); } \
+ NS_IMETHOD GetTarget(nsIDOMEventTarget** aTarget) \
+ { return mEvent->GetTarget(aTarget); } \
+ NS_IMETHOD GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) \
+ { return mEvent->GetCurrentTarget(aCurrentTarget); } \
+ NS_IMETHOD GetEventPhase(PRUint16* aEventPhase) \
+ { return mEvent->GetEventPhase(aEventPhase); } \
+ NS_IMETHOD GetBubbles(PRBool* aBubbles) \
+ { return mEvent->GetBubbles(aBubbles); } \
+ NS_IMETHOD GetCancelable(PRBool* aCancelable) \
+ { return mEvent->GetCancelable(aCancelable); } \
+ NS_IMETHOD GetTimeStamp(DOMTimeStamp* aTimeStamp) \
+ { return mEvent->GetTimeStamp(aTimeStamp); } \
+ NS_IMETHOD StopPropagation() \
+ { return mEvent->StopPropagation(); }
+
+class nsDOMWorkerXHREventWrapper : public nsIPrivateWorkerXHREvent
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIDOMEVENT_SPECIAL
+
+ nsDOMWorkerXHREventWrapper(nsIDOMEvent* aEvent)
+ : mEvent(aEvent), mPreventDefaultCalled(PR_FALSE) {
+ NS_ASSERTION(aEvent, "Null pointer!");
+ }
+
+ NS_IMETHOD PreventDefault() {
+ mPreventDefaultCalled = PR_TRUE;
+ return mEvent->PreventDefault();
+ }
- return SetOnXListener(type, aOnprogress);
+ NS_IMETHOD InitEvent(const nsAString& aEventType, PRBool aCanBubble,
+ PRBool aCancelable) {
+ mPreventDefaultCalled = PR_FALSE;
+ return mEvent->InitEvent(aEventType, aCanBubble, aCancelable);
+ }
+
+ // nsIPrivateWorkerXHREvent
+ virtual PRBool PreventDefaultCalled() {
+ return mPreventDefaultCalled;
+ }
+
+private:
+ nsCOMPtr<nsIDOMEvent> mEvent;
+ PRBool mPreventDefaultCalled;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventWrapper,
+ nsIDOMEvent,
+ nsIPrivateWorkerXHREvent)
+
+NS_IMETHODIMP
+nsDOMWorkerXHREventTarget::DispatchEvent(nsIDOMEvent* aEvent,
+ PRBool* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aEvent);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsCOMPtr<nsIPrivateWorkerXHREvent> wrapper(do_QueryInterface(aEvent));
+ if (!wrapper) {
+ wrapper = new nsDOMWorkerXHREventWrapper(aEvent);
+ NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ nsresult rv = HandleWorkerEvent(wrapper);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *_retval = wrapper->PreventDefaultCalled();
+ return NS_OK;
}
nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR)
: mWorkerXHR(aWorkerXHR)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- NS_ASSERTION(aWorkerXHR, "Null pointer!");
+ NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!");
}
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
- nsIXMLHttpRequestUpload)
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
+ nsIXMLHttpRequestUpload,
+ nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget,
nsIXMLHttpRequestEventTarget,
nsIXMLHttpRequestUpload)
-NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHRUpload)
+NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload)
-NS_IMETHODIMP
-nsDOMWorkerXHRUpload::AddEventListener(const nsAString& aType,
+nsresult
+nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
- PRBool aUseCapture)
+ PRBool aOnXListener)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- NS_ENSURE_ARG_POINTER(aListener);
-
- if (mWorkerXHR->mWorker->IsCanceled()) {
+ if (mWorkerXHR->mCanceled) {
return NS_ERROR_ABORT;
}
- nsresult rv = nsDOMWorkerXHREventTarget::AddEventListener(aType, aListener,
- aUseCapture);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mWorkerXHR->mXHRProxy->UploadEventListenerAdded();
- if (NS_FAILED(rv)) {
- NS_WARNING("UploadEventListenerAdded failed!");
- RemoveEventListener(aType, aListener, aUseCapture);
- return rv;
- }
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHRUpload::RemoveEventListener(const nsAString& aType,
- nsIDOMEventListener* aListener,
- PRBool aUseCapture)
-{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- NS_ENSURE_ARG_POINTER(aListener);
-
- if (mWorkerXHR->mWorker->IsCanceled()) {
- return NS_ERROR_ABORT;
- }
-
- return nsDOMWorkerXHREventTarget::RemoveEventListener(aType, aListener,
- aUseCapture);
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHRUpload::DispatchEvent(nsIDOMEvent* aEvent,
- PRBool* _retval)
-{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- NS_ENSURE_ARG_POINTER(aEvent);
-
- if (mWorkerXHR->mWorker->IsCanceled()) {
- return NS_ERROR_ABORT;
- }
-
- return nsDOMWorkerXHREventTarget::DispatchEvent(aEvent, _retval);
+ return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener,
+ PR_TRUE);
}
nsresult
-nsDOMWorkerXHRUpload::SetOnXListener(const nsAString& aType,
- nsIDOMEventListener* aListener)
+nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
if (mWorkerXHR->mCanceled) {
return NS_ERROR_ABORT;
}
- PRUint32 type = GetListenerTypeFromString(aType);
- if (type > sMaxUploadEventTypes) {
- // Silently ignore junk events.
- return NS_OK;
+ return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE);
+}
+
+nsresult
+nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent)
+{
+ if (mWorkerXHR->mCanceled) {
+ return NS_ERROR_ABORT;
}
- return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
+ return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE);
}
-nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorker* aWorker)
-: nsDOMWorkerFeature(aWorker),
- mWrappedNative(nsnull),
- mCanceled(PR_FALSE)
+already_AddRefed<nsIDOMEventListener>
+nsDOMWorkerXHRUpload::GetOnXListener(PRUint32 aType)
+{
+ if (mWorkerXHR->mCanceled) {
+ return nsnull;
+ }
+
+ return mWorkerXHR->mXHRProxy->GetOnXListener(aType, PR_TRUE);
+}
+
+nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorkerThread* aWorker)
+: mWorker(aWorker),
+ mCanceled(PR_TRUE)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aWorker, "Must have a worker!");
}
-// Tricky! We use the AddRef/Release method of nsDOMWorkerFeature (to make sure
-// we properly remove ourselves from the worker array) but inherit the QI of
-// nsDOMWorkerXHREventTarget.
-NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
-NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
+nsDOMWorkerXHR::~nsDOMWorkerXHR()
+{
+ if (!mCanceled) {
+ mWorker->RemoveXHR(this);
+ }
+}
-NS_IMPL_QUERY_INTERFACE_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
- nsIXMLHttpRequest,
- nsIXPCScriptable)
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
+ nsIXMLHttpRequest,
+ nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget,
nsIXMLHttpRequestEventTarget,
nsIXMLHttpRequest)
-NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHR)
-
-#define XPC_MAP_CLASSNAME nsDOMWorkerXHR
-#define XPC_MAP_QUOTED_CLASSNAME "XMLHttpRequest"
-#define XPC_MAP_WANT_POSTCREATE
-#define XPC_MAP_WANT_TRACE
-#define XPC_MAP_WANT_FINALIZE
-
-#define XPC_MAP_FLAGS \
- nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \
- nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY | \
- nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
-
-#include "xpc_map_end.h"
-
-NS_IMETHODIMP
-nsDOMWorkerXHR::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
- JSTracer* aTracer,
- JSObject* /*aObj */)
-{
- if (!mCanceled) {
- nsDOMWorkerMessageHandler::Trace(aTracer);
- if (mUpload) {
- mUpload->Trace(aTracer);
- }
- }
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHR::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
- JSContext* /* aCx */,
- JSObject* /* aObj */)
-{
- nsDOMWorkerMessageHandler::ClearAllListeners();
- if (mUpload) {
- mUpload->ClearAllListeners();
- }
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMWorkerXHR::PostCreate(nsIXPConnectWrappedNative* aWrapper,
- JSContext* /* aCx */,
- JSObject* /* aObj */)
-{
- mWrappedNative = aWrapper;
- return NS_OK;
-}
+NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR)
nsresult
nsDOMWorkerXHR::Init()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ if (!mWorker->AddXHR(this)) {
+ // Must have been canceled.
+ return NS_ERROR_ABORT;
+ }
+ mCanceled = PR_FALSE;
+
nsRefPtr<nsDOMWorkerXHRProxy> proxy = new nsDOMWorkerXHRProxy(this);
NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = proxy->Init();
NS_ENSURE_SUCCESS(rv, rv);
proxy.swap(mXHRProxy);
return NS_OK;
@@ -433,53 +441,82 @@ nsDOMWorkerXHR::Cancel()
mCanceled = PR_TRUE;
mUpload = nsnull;
}
if (mXHRProxy) {
mXHRProxy->Destroy();
}
+ mWorker->RemoveXHR(this);
mWorker = nsnull;
}
nsresult
-nsDOMWorkerXHR::SetOnXListener(const nsAString& aType,
- nsIDOMEventListener* aListener)
+nsDOMWorkerXHR::SetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aOnXListener)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ if (mCanceled) {
+ return NS_ERROR_ABORT;
+ }
+
+ return mXHRProxy->AddEventListener(aType, aListener, aOnXListener, PR_FALSE);
+}
+
+nsresult
+nsDOMWorkerXHR::UnsetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mCanceled) {
return NS_ERROR_ABORT;
}
- PRUint32 type = GetListenerTypeFromString(aType);
- if (type > sMaxXHREventTypes) {
- // Silently ignore junk events.
- return NS_OK;
+ return mXHRProxy->RemoveEventListener(aType, aListener, PR_FALSE);
+}
+
+nsresult
+nsDOMWorkerXHR::HandleWorkerEvent(nsIDOMEvent* aEvent)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ if (mCanceled) {
+ return NS_ERROR_ABORT;
}
- return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
+ return mXHRProxy->HandleWorkerEvent(aEvent, PR_FALSE);
+}
+
+already_AddRefed<nsIDOMEventListener>
+nsDOMWorkerXHR::GetOnXListener(PRUint32 aType)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ if (mCanceled) {
+ return nsnull;
+ }
+
+ return mXHRProxy->GetOnXListener(aType, PR_FALSE);
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
NS_ENSURE_ARG_POINTER(aChannel);
*aChannel = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetResponseXML(nsIDOMDocument** aResponseXML)
{
- NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
NS_ENSURE_ARG_POINTER(aResponseXML);
*aResponseXML = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetResponseText(nsAString& aResponseText)
{
@@ -811,17 +848,17 @@ nsDOMWorkerXHR::Init(nsIPrincipal* aPrin
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- nsRefPtr<nsDOMWorker> worker = mWorker;
+ nsRefPtr<nsDOMWorkerThread> worker = mWorker;
if (!worker) {
return NS_ERROR_ABORT;
}
nsAutoLock lock(worker->Lock());
if (mCanceled) {
return NS_ERROR_ABORT;
@@ -836,34 +873,42 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequ
NS_ADDREF(*aUpload = mUpload);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange)
{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ if (mCanceled) {
+ return NS_ERROR_ABORT;
+ }
+
NS_ENSURE_ARG_POINTER(aOnreadystatechange);
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
+ nsCOMPtr<nsIDOMEventListener> listener =
+ mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE);
- nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
listener.forget(aOnreadystatechange);
-
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange)
{
- nsAutoString type;
- type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- return SetOnXListener(type, aOnreadystatechange);
+ if (mCanceled) {
+ return NS_ERROR_ABORT;
+ }
+
+ return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE,
+ aOnreadystatechange, PR_TRUE, PR_FALSE);
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetWithCredentials(PRBool* aWithCredentials)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mCanceled) {
--- a/dom/src/threads/nsDOMWorkerXHR.h
+++ b/dom/src/threads/nsDOMWorkerXHR.h
@@ -35,123 +35,143 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERXHR_H__
#define __NSDOMWORKERXHR_H__
// Bases
+#include "nsIXMLHttpRequest.h"
#include "nsIClassInfo.h"
-#include "nsIXMLHttpRequest.h"
-#include "nsIXPCScriptable.h"
+
+// Interfaces
// Other includes
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "prlock.h"
// DOMWorker includes
-#include "nsDOMWorker.h"
-#include "nsDOMWorkerMacros.h"
-#include "nsDOMWorkerXHRProxy.h"
+#include "nsDOMWorkerThread.h"
// Convenience defines for event *indexes* in the sListenerTypes array.
#define LISTENER_TYPE_ABORT 0
#define LISTENER_TYPE_ERROR 1
#define LISTENER_TYPE_LOAD 2
#define LISTENER_TYPE_LOADSTART 3
#define LISTENER_TYPE_PROGRESS 4
#define LISTENER_TYPE_READYSTATECHANGE 5
-class nsIXPConnectWrappedNative;
+class nsDOMWorkerXHR;
+class nsDOMWorkerXHREvent;
+class nsDOMWorkerXHRProxy;
-class nsDOMWorkerXHREventTarget : public nsDOMWorkerMessageHandler,
- public nsIXMLHttpRequestEventTarget
+class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget
{
public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTTARGET
NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET
static const char* const sListenerTypes[];
static const PRUint32 sMaxXHREventTypes;
static const PRUint32 sMaxUploadEventTypes;
static PRUint32 GetListenerTypeFromString(const nsAString& aString);
+ virtual nsresult SetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aOnXListener) = 0;
+
+ virtual nsresult UnsetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener) = 0;
+
+ virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent) = 0;
+
+ virtual already_AddRefed<nsIDOMEventListener>
+ GetOnXListener(PRUint32 aType) = 0;
+
protected:
virtual ~nsDOMWorkerXHREventTarget() { }
};
-class nsDOMWorkerXHRUpload;
+class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
+ public nsIXMLHttpRequestUpload,
+ public nsIClassInfo
+{
+ friend class nsDOMWorkerXHR;
+
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerXHREventTarget::)
+ NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
+ NS_DECL_NSIXMLHTTPREQUESTUPLOAD
+ NS_DECL_NSICLASSINFO
+
+ nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
+
+ virtual nsresult SetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aOnXListener);
+
+ virtual nsresult UnsetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener);
+
+ virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
+
+ virtual already_AddRefed<nsIDOMEventListener>
+ GetOnXListener(PRUint32 aType);
+
+protected:
+ virtual ~nsDOMWorkerXHRUpload() { }
+
+ nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
+};
class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
- public nsDOMWorkerFeature,
public nsIXMLHttpRequest,
- public nsIXPCScriptable
+ public nsIClassInfo
{
friend class nsDOMWorkerXHREvent;
friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
friend class nsDOMWorkerXHRProxy;
friend class nsDOMWorkerXHRUpload;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIXMLHTTPREQUEST
- NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
- NS_DECL_NSICLASSINFO_GETINTERFACES
- NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
- nsDOMWorkerXHR(nsDOMWorker* aWorker);
+ nsDOMWorkerXHR(nsDOMWorkerThread* aWorker);
nsresult Init();
- virtual void Cancel();
+ void Cancel();
+
+ virtual nsresult SetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aOnXListener);
- virtual nsresult SetOnXListener(const nsAString& aType,
- nsIDOMEventListener* aListener);
+ virtual nsresult UnsetEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener);
+
+ virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
-private:
- virtual ~nsDOMWorkerXHR() { }
+ virtual already_AddRefed<nsIDOMEventListener>
+ GetOnXListener(PRUint32 aType);
+
+protected:
+ virtual ~nsDOMWorkerXHR();
PRLock* Lock() {
return mWorker->Lock();
}
- nsIXPConnectWrappedNative* GetWrappedNative() {
- return mWrappedNative;
- }
-
+ nsRefPtr<nsDOMWorkerThread> mWorker;
nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
nsRefPtr<nsDOMWorkerXHRUpload> mUpload;
- nsIXPConnectWrappedNative* mWrappedNative;
-
volatile PRBool mCanceled;
};
-class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
- public nsIXMLHttpRequestUpload
-{
- friend class nsDOMWorkerXHR;
-
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_NSIDOMEVENTTARGET
- NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
- NS_DECL_NSIXMLHTTPREQUESTUPLOAD
- NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
- NS_DECL_NSICLASSINFO_GETINTERFACES
-
- nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
-
- virtual nsresult SetOnXListener(const nsAString& aType,
- nsIDOMEventListener* aListener);
-
-protected:
- virtual ~nsDOMWorkerXHRUpload() { }
-
- nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
-};
-
#endif /* __NSDOMWORKERXHR_H__ */
--- a/dom/src/threads/nsDOMWorkerXHRProxy.cpp
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp
@@ -54,22 +54,18 @@
#include "nsIClassInfoImpl.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsXMLHttpRequest.h"
#include "prinrval.h"
#include "prthread.h"
// DOMWorker includes
-#include "nsDOMThreadService.h"
-#include "nsDOMWorker.h"
-#include "nsDOMWorkerEvents.h"
-#include "nsDOMWorkerMacros.h"
#include "nsDOMWorkerPool.h"
-#include "nsDOMWorkerXHR.h"
+#include "nsDOMWorkerThread.h"
#include "nsDOMWorkerXHRProxiedFunctions.h"
#define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes
#define MAX_UPLOAD_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxUploadEventTypes
#define RUN_PROXIED_FUNCTION(_name, _args) \
PR_BEGIN_MACRO \
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); \
@@ -108,17 +104,17 @@
using namespace nsDOMWorkerProxiedXHRFunctions;
class nsResultReturningRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable,
- nsDOMWorker* aWorker)
+ nsDOMWorkerThread* aWorker)
: mTarget(aTarget), mRunnable(aRunnable), mWorker(aWorker),
mResult(NS_OK), mDone(PR_FALSE) { }
nsresult Dispatch() {
if (!mWorker) {
// Must have been canceled, bail out.
return NS_ERROR_ABORT;
}
@@ -156,42 +152,355 @@ public:
mDone = PR_TRUE;
return mResult;
}
private:
nsCOMPtr<nsIEventTarget> mTarget;
nsCOMPtr<nsIRunnable> mRunnable;
- nsRefPtr<nsDOMWorker> mWorker;
+ nsRefPtr<nsDOMWorkerThread> mWorker;
nsresult mResult;
volatile PRBool mDone;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsResultReturningRunnable, nsIRunnable)
+class nsDOMWorkerXHREvent : public nsIRunnable,
+ public nsIDOMProgressEvent,
+ public nsIClassInfo
+{
+ friend class nsDOMWorkerXHRProxy;
+ friend class nsDOMWorkerXHREventTargetProxy;
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+ NS_DECL_NSIDOMEVENT
+ NS_DECL_NSIDOMPROGRESSEVENT
+ NS_DECL_NSICLASSINFO
+
+ nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
+
+ nsresult Init(PRUint32 aType,
+ const nsAString& aTypeString,
+ nsIDOMEvent* aEvent);
+
+ nsresult Init(nsIXMLHttpRequest* aXHR);
+
+ void EventHandled();
+
+protected:
+ nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
+ nsCOMPtr<nsIDOMEventTarget> mTarget;
+ nsString mTypeString;
+ PRUint32 mType;
+ PRUint16 mEventPhase;
+ DOMTimeStamp mTimeStamp;
+ nsString mResponseText;
+ nsCString mStatusText;
+ nsresult mStatus;
+ PRInt32 mReadyState;
+ PRUint64 mLoaded;
+ PRUint64 mTotal;
+ PRInt32 mChannelID;
+ PRPackedBool mBubbles;
+ PRPackedBool mCancelable;
+ PRPackedBool mUploadEvent;
+ PRPackedBool mProgressEvent;
+ PRPackedBool mLengthComputable;
+};
+
+nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
+: mXHRProxy(aXHRProxy),
+ mType(PR_UINT32_MAX),
+ mEventPhase(0),
+ mTimeStamp(0),
+ mStatus(NS_OK),
+ mReadyState(0),
+ mLoaded(0),
+ mTotal(0),
+ mChannelID(-1),
+ mBubbles(PR_FALSE),
+ mCancelable(PR_FALSE),
+ mUploadEvent(PR_FALSE),
+ mProgressEvent(PR_FALSE),
+ mLengthComputable(PR_FALSE)
+{
+ NS_ASSERTION(aXHRProxy, "Can't be null!");
+}
+
+NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerXHREvent)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerXHREvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEvent)
+ NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerXHREvent, nsIDOMEvent)
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetInterfaces(PRUint32* aCount,
+ nsIID*** aArray)
+{
+ PRUint32 count = *aCount = mProgressEvent ? 2 : 1;
+
+ *aArray = (nsIID**)nsMemory::Alloc(sizeof(nsIID*) * count);
+
+ if (mProgressEvent) {
+ (*aArray)[--count] =
+ (nsIID*)nsMemory::Clone(&NS_GET_IID(nsIDOMProgressEvent), sizeof(nsIID));
+ }
+
+ (*aArray)[--count] =
+ (nsIID *)nsMemory::Clone(&NS_GET_IID(nsIDOMEvent), sizeof(nsIID));
+
+ NS_ASSERTION(!count, "Bad math!");
+ return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerXHREvent)
+
+nsresult
+nsDOMWorkerXHREvent::Init(PRUint32 aType,
+ const nsAString& aTypeString,
+ nsIDOMEvent* aEvent)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aEvent, "Don't pass null here!");
+
+ nsresult rv;
+
+ mType = aType;
+ mTypeString.Assign(aTypeString);
+
+ mChannelID = mXHRProxy->ChannelID();
+
+ nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(aEvent));
+ if (progressEvent) {
+ mProgressEvent = PR_TRUE;
+
+ PRBool lengthComputable;
+ rv = progressEvent->GetLengthComputable(&lengthComputable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mLengthComputable = lengthComputable ? PR_TRUE : PR_FALSE;
+
+ rv = progressEvent->GetLoaded(&mLoaded);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = progressEvent->GetTotal(&mTotal);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIDOMEventTarget> mainThreadTarget;
+ rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(mainThreadTarget));
+ if (upload) {
+ mUploadEvent = PR_TRUE;
+ mTarget =
+ static_cast<nsDOMWorkerXHREventTarget*>(mXHRProxy->mWorkerXHR->mUpload);
+ }
+ else {
+ mUploadEvent = PR_FALSE;
+ mTarget = mXHRProxy->mWorkerXHR;
+ }
+
+ PRBool boolVal;
+ rv = aEvent->GetBubbles(&boolVal);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mBubbles = boolVal ? PR_TRUE : PR_FALSE;
+
+ rv = aEvent->GetCancelable(&boolVal);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCancelable = boolVal ? PR_TRUE : PR_FALSE;
+
+ rv = aEvent->GetTimeStamp(&mTimeStamp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Init(mXHRProxy->mXHR);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+nsDOMWorkerXHREvent::Init(nsIXMLHttpRequest* aXHR)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aXHR, "Don't pass null here!");
+
+ nsresult rv = aXHR->GetResponseText(mResponseText);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aXHR->GetStatusText(mStatusText);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aXHR->GetStatus(&mStatus);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aXHR->GetReadyState(&mReadyState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+void
+nsDOMWorkerXHREvent::EventHandled()
+{
+ // Prevent reference cycles by releasing these here.
+ mXHRProxy = nsnull;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::Run()
+{
+ nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent);
+
+ EventHandled();
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetType(nsAString& aType)
+{
+ aType.Assign(mTypeString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetTarget(nsIDOMEventTarget** aTarget)
+{
+ NS_ENSURE_ARG_POINTER(aTarget);
+ NS_ADDREF(*aTarget = mTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget)
+{
+ NS_ENSURE_ARG_POINTER(aCurrentTarget);
+ NS_ADDREF(*aCurrentTarget = mTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetEventPhase(PRUint16* aEventPhase)
+{
+ NS_ENSURE_ARG_POINTER(aEventPhase);
+ *aEventPhase = mEventPhase;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetBubbles(PRBool* aBubbles)
+{
+ NS_ENSURE_ARG_POINTER(aBubbles);
+ *aBubbles = mBubbles;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetCancelable(PRBool* aCancelable)
+{
+ NS_ENSURE_ARG_POINTER(aCancelable);
+ *aCancelable = mCancelable;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetTimeStamp(DOMTimeStamp* aTimeStamp)
+{
+ NS_ENSURE_ARG_POINTER(aTimeStamp);
+ *aTimeStamp = mTimeStamp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::StopPropagation()
+{
+ NS_WARNING("StopPropagation doesn't do anything here!");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::PreventDefault()
+{
+ NS_WARNING("PreventDefault doesn't do anything yet!");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::InitEvent(const nsAString& aEventTypeArg,
+ PRBool aCanBubbleArg,
+ PRBool aCancelableArg)
+{
+ NS_WARNING("InitEvent doesn't do anything here!");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetLengthComputable(PRBool* aLengthComputable)
+{
+ NS_ENSURE_ARG_POINTER(aLengthComputable);
+ *aLengthComputable = mLengthComputable;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetLoaded(PRUint64* aLoaded)
+{
+ NS_ENSURE_ARG_POINTER(aLoaded);
+ *aLoaded = mLoaded;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::GetTotal(PRUint64* aTotal)
+{
+ NS_ENSURE_ARG_POINTER(aTotal);
+ *aTotal = mTotal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerXHREvent::InitProgressEvent(const nsAString_internal& aTypeArg,
+ PRBool aCanBubbleArg,
+ PRBool aCancelableArg,
+ PRBool aLengthComputableArg,
+ PRUint64 aLoadedArg,
+ PRUint64 aTotalArg)
+{
+ NS_WARNING("InitProgressEvent doesn't do anything here!");
+ return NS_OK;
+}
+
class nsDOMWorkerXHRLastProgressOrLoadEvent : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
nsDOMWorkerXHRLastProgressOrLoadEvent(nsDOMWorkerXHRProxy* aProxy)
: mProxy(aProxy) {
NS_ASSERTION(aProxy, "Null pointer!");
}
NS_IMETHOD Run() {
nsRefPtr<nsDOMWorkerXHREvent> lastProgressOrLoadEvent;
if (!mProxy->mCanceled) {
nsAutoLock lock(mProxy->mWorkerXHR->Lock());
mProxy->mLastProgressOrLoadEvent.swap(lastProgressOrLoadEvent);
- if (mProxy->mCanceled) {
- return NS_ERROR_ABORT;
- }
}
if (lastProgressOrLoadEvent) {
return lastProgressOrLoadEvent->Run();
}
return NS_OK;
}
@@ -315,17 +624,17 @@ nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(MAX_XHR_LISTENER_TYPE >= MAX_UPLOAD_LISTENER_TYPE,
"Upload should support a subset of XHR's event types!");
}
nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy()
{
if (mOwnedByXHR) {
- mWorkerXHRWN = nsnull;
+ mWorkerXHR->Release();
}
else if (mXHR) {
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
NS_ASSERTION(mainThread, "This isn't supposed to fail!");
// This will release immediately if we're on the main thread.
NS_ProxyRelease(mainThread, mXHR);
@@ -338,16 +647,28 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMWorke
nsresult
nsDOMWorkerXHRProxy::Init()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_FALSE(mXHR, NS_ERROR_ALREADY_INITIALIZED);
+ PRBool success = mXHRListeners.SetLength(MAX_XHR_LISTENER_TYPE);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ success = mXHROnXListeners.SetLength(MAX_XHR_LISTENER_TYPE);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ success = mUploadListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+ success = mUploadOnXListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE);
+ NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
mMainThread = do_GetMainThread();
NS_ENSURE_TRUE(mMainThread, NS_ERROR_UNEXPECTED);
nsRefPtr<nsResultReturningRunnable> runnable =
new nsResultReturningRunnable(mMainThread, this, mWorkerXHR->mWorker);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = runnable->Dispatch();
@@ -367,41 +688,41 @@ nsDOMWorkerXHRProxy::GetXMLHttpRequest()
return mCanceled ? nsnull : mXHR;
}
nsresult
nsDOMWorkerXHRProxy::Destroy()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ mCanceled = PR_TRUE;
+
+ ClearEventListeners();
+
{
nsAutoLock lock(mWorkerXHR->Lock());
-
- mCanceled = PR_TRUE;
-
mLastProgressOrLoadEvent = nsnull;
- mLastXHREvent = nsnull;
}
if (mXHR) {
DestroyInternal();
}
- NS_ASSERTION(!(mLastProgressOrLoadEvent && mLastXHREvent), "Going to leak!");
+ mLastXHREvent = nsnull;
return NS_OK;
}
nsresult
nsDOMWorkerXHRProxy::InitInternal()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!");
- nsDOMWorker* worker = mWorkerXHR->mWorker;
+ nsDOMWorkerThread* worker = mWorkerXHR->mWorker;
nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
if (worker->IsCanceled()) {
return NS_ERROR_ABORT;
}
nsIPrincipal* nodePrincipal = pool->ParentDocument()->NodePrincipal();
nsIScriptContext* scriptContext = pool->ScriptContext();
@@ -421,21 +742,21 @@ nsDOMWorkerXHRProxy::InitInternal()
nsCOMPtr<nsIXMLHttpRequestUpload> upload;
rv = xhr->GetUpload(getter_AddRefs(upload));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsDOMWorkerXHREvent> nullEvent = new nsDOMWorkerXHREvent(this);
NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY);
- rv = nullEvent->SnapshotXHRState(xhr);
+ rv = nullEvent->Init(xhr);
NS_ENSURE_SUCCESS(rv, rv);
+ mLastXHREvent.swap(nullEvent);
- nullEvent->EventHandled();
- mLastXHREvent.swap(nullEvent);
+ mLastXHREvent->EventHandled();
xhrConcrete->SetRequestObserver(this);
// We now own mXHR and it owns upload.
xhr.swap(mXHR);
// Weak refs.
mUpload = upload;
@@ -471,16 +792,17 @@ nsDOMWorkerXHRProxy::DestroyInternal()
if (syncFinishedRunnable) {
syncFinishedRunnable->Dispatch();
}
}
NS_ASSERTION(!mOwnedByXHR, "Should have flipped already!");
NS_ASSERTION(!mSyncFinishedRunnable, "Should have fired this already!");
+ NS_ASSERTION(!mLastProgressOrLoadEvent, "Should have killed this already!");
// mXHR could be null if Init fails.
if (mXHR) {
AddRemoveXHRListeners(PR_FALSE);
mXHR->Release();
mXHR = nsnull;
@@ -489,171 +811,347 @@ nsDOMWorkerXHRProxy::DestroyInternal()
}
void
nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
{
nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
NS_ASSERTION(xhrTarget, "This shouldn't fail!");
- EventListenerFunction addRemoveEventListener =
- aAdd ?
- &nsIDOMEventTarget::AddEventListener :
- &nsIDOMEventTarget::RemoveEventListener;
+ EventListenerFunction function = aAdd ?
+ &nsIDOMEventTarget::AddEventListener :
+ &nsIDOMEventTarget::RemoveEventListener;
nsAutoString eventName;
PRUint32 index = 0;
if (mWantUploadListeners) {
nsCOMPtr<nsIDOMEventTarget> uploadTarget(do_QueryInterface(mUpload));
NS_ASSERTION(uploadTarget, "This shouldn't fail!");
for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
- (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
- (uploadTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
+ (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
+ (uploadTarget.get()->*function)(eventName, this, PR_FALSE);
}
}
for (; index < MAX_XHR_LISTENER_TYPE; index++) {
eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
- (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
+ (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
}
}
void
nsDOMWorkerXHRProxy::FlipOwnership()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Flip!
mOwnedByXHR = !mOwnedByXHR;
// If mWorkerXHR has no outstanding refs from JS then we are about to die.
// Hold an extra ref here to make sure that we live through this call.
nsRefPtr<nsDOMWorkerXHRProxy> kungFuDeathGrip(this);
if (mOwnedByXHR) {
- mWorkerXHRWN = mWorkerXHR->GetWrappedNative();
- NS_ASSERTION(mWorkerXHRWN, "Null pointer!");
+ mWorkerXHR->AddRef();
mXHR->Release();
}
else {
mXHR->AddRef();
- mWorkerXHRWN = nsnull;
+ mWorkerXHR->Release();
}
}
nsresult
-nsDOMWorkerXHRProxy::UploadEventListenerAdded()
+nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aOnXListener,
+ PRBool aUploadListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
- // If this is the first time we're setting an upload listener then we have to
- // hit the main thread to attach the upload listeners. Otherwise there's
- // nothing to do here.
- if (mWantUploadListeners) {
+ if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
+ aType >= MAX_XHR_LISTENER_TYPE) {
+ // Silently fail on junk events.
return NS_OK;
}
- nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
- new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
- NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
+ ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
+ mXHRListeners[aType];
+ WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
+ mXHROnXListeners[aType];
+
+ {
+ nsAutoLock lock(mWorkerXHR->Lock());
+
+ if (mCanceled) {
+ return NS_ERROR_ABORT;
+ }
+
+#ifdef DEBUG
+ if (!aListener) {
+ NS_ASSERTION(aOnXListener, "Shouldn't pass a null listener!");
+ }
+#endif
- nsRefPtr<nsResultReturningRunnable> runnable =
- new nsResultReturningRunnable(mMainThread, attachRunnable,
- mWorkerXHR->mWorker);
- NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+ if (aOnXListener) {
+ // Remove the old one from the array if it exists.
+ if (onXListener) {
+#ifdef DEBUG
+ PRBool removed =
+#endif
+ listeners.RemoveElement(onXListener);
+ NS_ASSERTION(removed, "Should still be in the array!");
+ }
- nsresult rv = runnable->Dispatch();
- if (NS_FAILED(rv)) {
- return rv;
+ if (!aListener) {
+ onXListener = nsnull;
+ return NS_OK;
+ }
+
+ onXListener = new nsDOMWorkerXHRWrappedListener(aListener);
+ NS_ENSURE_TRUE(onXListener, NS_ERROR_OUT_OF_MEMORY);
+
+ aListener = onXListener;
+ }
+
+ Listener* added = listeners.AppendElement(aListener);
+ NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
}
- NS_ASSERTION(mWantUploadListeners, "Should have set this!");
+ // If this is the first time we're setting an upload listener then we have to
+ // hit the main thread to attach the upload listeners.
+ if (aUploadListener && aListener && !mWantUploadListeners) {
+ nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
+ new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
+ NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
+
+ nsRefPtr<nsResultReturningRunnable> runnable =
+ new nsResultReturningRunnable(mMainThread, attachRunnable,
+ mWorkerXHR->mWorker);
+ NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = runnable->Dispatch();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(mWantUploadListeners, "Should have set this!");
+ }
+
return NS_OK;
}
nsresult
+nsDOMWorkerXHRProxy::RemoveEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aUploadListener)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aListener, "Null pointer!");
+
+ if (mCanceled) {
+ return NS_ERROR_ABORT;
+ }
+
+ if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
+ aType >= MAX_XHR_LISTENER_TYPE) {
+ // Silently fail on junk events.
+ return NS_OK;
+ }
+
+ ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
+ mXHRListeners[aType];
+
+ nsAutoLock lock(mWorkerXHR->Lock());
+
+ listeners.RemoveElement(aListener);
+
+ return NS_OK;
+}
+
+already_AddRefed<nsIDOMEventListener>
+nsDOMWorkerXHRProxy::GetOnXListener(PRUint32 aType,
+ PRBool aUploadListener)
+{
+ if (mCanceled) {
+ return nsnull;
+ }
+
+ if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
+ aType >= MAX_XHR_LISTENER_TYPE) {
+ // Silently fail on junk events.
+ return nsnull;
+ }
+
+ WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
+ mXHROnXListeners[aType];
+
+ nsAutoLock lock(mWorkerXHR->Lock());
+
+ nsCOMPtr<nsIDOMEventListener> listener = onXListener->Inner();
+ return listener.forget();
+}
+
+nsresult
nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
PRBool aUploadEvent)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aEvent, "Should not be null!");
- {
- nsAutoLock lock(mWorkerXHR->Lock());
-
- if (mCanceled ||
- (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
- return NS_OK;
- }
-
- mLastXHREvent = aEvent;
+ if (mCanceled ||
+ (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
+ return NS_OK;
}
- nsIDOMEvent* event = static_cast<nsDOMWorkerEvent*>(aEvent);
- return HandleEventInternal(aEvent->mXHREventType, event, aUploadEvent);
+ mLastXHREvent = aEvent;
+
+ return HandleEventInternal(aEvent->mType, aEvent, aUploadEvent);
+}
+
+nsresult
+nsDOMWorkerXHRProxy::HandleWorkerEvent(nsIDOMEvent* aEvent,
+ PRBool aUploadEvent)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aEvent, "Should not be null!");
+
+ nsString typeString;
+ nsresult rv = aEvent->GetType(typeString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 maxType = aUploadEvent ? MAX_UPLOAD_LISTENER_TYPE :
+ MAX_XHR_LISTENER_TYPE;
+
+ PRUint32 type =
+ nsDOMWorkerXHREventTarget::GetListenerTypeFromString(typeString);
+ if (type >= maxType) {
+ // Silently fail on junk events.
+ return NS_OK;
+ }
+
+ return HandleEventInternal(type, aEvent, aUploadEvent);
}
nsresult
nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType,
nsIDOMEvent* aEvent,
PRBool aUploadListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aEvent, "Should not be null!");
#ifdef DEBUG
if (aUploadListener) {
NS_ASSERTION(aType < MAX_UPLOAD_LISTENER_TYPE, "Bad type!");
- NS_ASSERTION(mWorkerXHR->mUpload, "No upload object!");
}
else {
NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
}
#endif
if (mCanceled) {
return NS_ERROR_ABORT;
}
- nsIDOMEventTarget* target = aUploadListener ?
- static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR->mUpload) :
- static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR);
+ ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
+ mXHRListeners[aType];
+
+ nsAutoTArray<Listener, 10> listenerCopy;
+ PRUint32 count;
+
+ {
+ nsAutoLock lock(mWorkerXHR->Lock());
+
+ count = listeners.Length();
+ if (!count) {
+ return NS_OK;
+ }
+
+ Listener* copied = listenerCopy.AppendElements(listeners);
+ NS_ENSURE_TRUE(copied, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ for (PRUint32 index = 0; index < count; index++) {
+ NS_ASSERTION(listenerCopy[index], "Null listener?!");
+ listenerCopy[index]->HandleEvent(aEvent);
+ }
+
+ return NS_OK;
+}
+
+void
+nsDOMWorkerXHRProxy::ClearEventListeners()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
- return target->DispatchEvent(aEvent, nsnull);
+ nsTArray<ListenerArray> doomedArrays;
+ PRBool ok = doomedArrays.SetLength(MAX_XHR_LISTENER_TYPE +
+ MAX_UPLOAD_LISTENER_TYPE);
+ NS_ENSURE_TRUE(ok,);
+
+ nsTArray<WrappedListener> doomedListeners;
+ ok = doomedListeners.SetLength(MAX_XHR_LISTENER_TYPE +
+ MAX_UPLOAD_LISTENER_TYPE);
+ NS_ENSURE_TRUE(ok,);
+
+ {
+ PRUint32 listenerIndex, doomedIndex;
+
+ nsAutoLock lock(mWorkerXHR->Lock());
+
+ for (listenerIndex = 0, doomedIndex = 0;
+ listenerIndex < MAX_UPLOAD_LISTENER_TYPE;
+ listenerIndex++, doomedIndex++) {
+ mUploadListeners[listenerIndex].
+ SwapElements(doomedArrays[doomedIndex]);
+ mUploadOnXListeners[listenerIndex].
+ swap(doomedListeners[doomedIndex]);
+ }
+
+ for (listenerIndex = 0;
+ listenerIndex < MAX_XHR_LISTENER_TYPE;
+ listenerIndex++, doomedIndex++) {
+ mXHRListeners[listenerIndex].
+ SwapElements(doomedArrays[doomedIndex]);
+ mXHROnXListeners[listenerIndex].
+ swap(doomedListeners[doomedIndex]);
+ }
+ }
+
+ // Destructors for the nsTArrays actually kill the listeners outside of the
+ // lock.
}
PRBool
-nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType,
+nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType,
nsIDOMEvent* aEvent)
{
-#ifdef DEBUG
- PRUint32 type = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(aType);
- NS_ASSERTION(type < MAX_XHR_LISTENER_TYPE, "Bad type!");
-#endif
+ NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
- if (mWorkerXHR->HasListeners(aType)) {
+ if (mXHRListeners[aType].Length()) {
return PR_TRUE;
}
PRBool checkUploadListeners = PR_FALSE;
if (aEvent) {
nsCOMPtr<nsIDOMEventTarget> target;
if (NS_SUCCEEDED(aEvent->GetTarget(getter_AddRefs(target)))) {
nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(target));
checkUploadListeners = !!upload;
}
}
else {
checkUploadListeners = PR_TRUE;
}
- if (checkUploadListeners && mWorkerXHR->mUpload->HasListeners(aType)) {
+ if (checkUploadListeners && mUploadListeners[aType].Length()) {
return PR_TRUE;
}
return PR_FALSE;
}
// nsIDOMEventListener
NS_IMETHODIMP
@@ -668,39 +1166,30 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOME
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 type =
nsDOMWorkerXHREventTarget::GetListenerTypeFromString(typeString);
if (type >= MAX_XHR_LISTENER_TYPE) {
return NS_OK;
}
+ // When Abort is called on nsXMLHttpRequest (either from a proxied Abort call
+ // or from DestroyInternal) the OnStopRequest call is not run synchronously.
+ // Thankfully an abort event *is* fired synchronously so we can flip our
+ // ownership around and fire the sync finished runnable if we're running in
+ // sync mode.
+ if (type == LISTENER_TYPE_ABORT && mCanceled) {
+ OnStopRequest(nsnull, nsnull, NS_ERROR_ABORT);
+ }
+
if (mCanceled) {
- // When Abort is called on nsXMLHttpRequest (either from a proxied Abort
- // call or from DestroyInternal) the OnStopRequest call is not run
- // synchronously. Thankfully an abort event *is* fired synchronously so we
- // can flip our ownership around and fire the sync finished runnable if
- // we're running in sync mode.
- if (type == LISTENER_TYPE_ABORT) {
- OnStopRequest(nsnull, nsnull, NS_ERROR_ABORT);
- }
-
- // Always bail out if we're canceled.
return NS_ERROR_ABORT;
}
- PRBool ignoreEvent = !HasListenersForType(typeString, aEvent);
- if (ignoreEvent && mSyncRequest) {
- // Only ignore the event if it isn't final.
- ignoreEvent = type != LISTENER_TYPE_ABORT &&
- type != LISTENER_TYPE_ERROR &&
- type != LISTENER_TYPE_LOAD;
- }
-
- if (ignoreEvent) {
+ if (!HasListenersForType(type, aEvent)) {
return NS_OK;
}
nsRefPtr<nsDOMWorkerXHREvent> newEvent = new nsDOMWorkerXHREvent(this);
NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
rv = newEvent->Init(type, typeString, aEvent);
NS_ENSURE_SUCCESS(rv, rv);
@@ -708,21 +1197,16 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOME
nsCOMPtr<nsIRunnable> runnable(newEvent);
if (type == LISTENER_TYPE_LOAD || type == LISTENER_TYPE_PROGRESS) {
runnable = new nsDOMWorkerXHRLastProgressOrLoadEvent(this);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
{
nsAutoLock lock(mWorkerXHR->Lock());
-
- if (mCanceled) {
- return NS_ERROR_ABORT;
- }
-
mLastProgressOrLoadEvent.swap(newEvent);
if (newEvent) {
// Already had a saved progress/load event so no need to generate
// another. Bail out rather than dispatching runnable.
return NS_OK;
}
}
--- a/dom/src/threads/nsDOMWorkerXHRProxy.h
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.h
@@ -40,44 +40,48 @@
#define __NSDOMWORKERXHRPROXY_H__
// Bases
#include "nsThreadUtils.h"
#include "nsIDOMEventListener.h"
#include "nsIRequestObserver.h"
// Other includes
-#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
+// DOMWorker includes
+#include "nsDOMWorkerXHR.h"
+
class nsIJSXMLHttpRequest;
class nsIThread;
class nsIVariant;
class nsIXMLHttpRequest;
-class nsIXMLHttpRequestUpload;
-class nsIXPConnectWrappedNative;
-class nsDOMWorkerXHR;
class nsDOMWorkerXHREvent;
class nsDOMWorkerXHRFinishSyncXHRRunnable;
class nsDOMWorkerXHRWrappedListener;
class nsXMLHttpRequest;
class nsDOMWorkerXHRProxy : public nsIRunnable,
public nsIDOMEventListener,
public nsIRequestObserver
{
friend class nsDOMWorkerXHRAttachUploadListenersRunnable;
friend class nsDOMWorkerXHREvent;
friend class nsDOMWorkerXHRFinishSyncXHRRunnable;
friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
friend class nsDOMWorkerXHR;
friend class nsDOMWorkerXHRUpload;
+ typedef nsCOMPtr<nsIDOMEventListener> Listener;
+ typedef nsTArray<Listener> ListenerArray;
+
+ typedef nsRefPtr<nsDOMWorkerXHRWrappedListener> WrappedListener;
+
typedef nsresult (NS_STDCALL nsIDOMEventTarget::*EventListenerFunction)
(const nsAString&, nsIDOMEventListener*, PRBool);
public:
typedef nsAutoTArray<nsCOMPtr<nsIRunnable>, 5> SyncEventQueue;
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
@@ -109,25 +113,38 @@ protected:
nsresult InitInternal();
void DestroyInternal();
nsresult Destroy();
void AddRemoveXHRListeners(PRBool aAdd);
void FlipOwnership();
- nsresult UploadEventListenerAdded();
+ nsresult AddEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aOnXListener,
+ PRBool aUploadListener);
- nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
- PRBool aUploadEvent);
+ nsresult RemoveEventListener(PRUint32 aType,
+ nsIDOMEventListener* aListener,
+ PRBool aUploadListener);
+
+ already_AddRefed<nsIDOMEventListener> GetOnXListener(PRUint32 aType,
+ PRBool aUploadListener);
+
+ nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, PRBool aUploadEvent);
+
+ nsresult HandleWorkerEvent(nsIDOMEvent* aEvent, PRBool aUploadEvent);
nsresult HandleEventInternal(PRUint32 aType,
nsIDOMEvent* aEvent,
PRBool aUploadEvent);
+ void ClearEventListeners();
+
// Methods of nsIXMLHttpRequest that we implement
nsresult GetAllResponseHeaders(char** _retval);
nsresult GetResponseHeader(const nsACString& aHeader,
nsACString& _retval);
nsresult Send(nsIVariant* aBody);
nsresult SendAsBinary(const nsAString& aBody);
nsresult GetResponseText(nsAString& _retval);
nsresult GetStatusText(nsACString& _retval);
@@ -140,35 +157,39 @@ protected:
nsresult SetMultipart(PRBool aMultipart);
nsresult GetWithCredentials(PRBool* aWithCredentials);
nsresult SetWithCredentials(PRBool aWithCredentials);
nsresult RunSyncEventLoop();
// aEvent is used to see if we should check upload listeners as well. If left
// unset we always check upload listeners.
- PRBool HasListenersForType(const nsAString& aType,
- nsIDOMEvent* aEvent = nsnull);
+ PRBool HasListenersForType(PRUint32 aType, nsIDOMEvent* aEvent = nsnull);
// May be weak or strong, check mOwnedByXHR.
nsDOMWorkerXHR* mWorkerXHR;
- nsCOMPtr<nsIXPConnectWrappedNative> mWorkerXHRWN;
// May be weak or strong, check mOwnedByXHR.
nsIXMLHttpRequest* mXHR;
// Always weak!
nsXMLHttpRequest* mConcreteXHR;
nsIXMLHttpRequestUpload* mUpload;
nsCOMPtr<nsIThread> mMainThread;
nsRefPtr<nsDOMWorkerXHREvent> mLastXHREvent;
nsRefPtr<nsDOMWorkerXHREvent> mLastProgressOrLoadEvent;
+ nsTArray<ListenerArray> mXHRListeners;
+ nsTArray<WrappedListener> mXHROnXListeners;
+
+ nsTArray<ListenerArray> mUploadListeners;
+ nsTArray<WrappedListener> mUploadOnXListeners;
+
SyncEventQueue* mSyncEventQueue;
PRInt32 mChannelID;
// Only touched on the worker thread!
nsCOMPtr<nsIThread> mSyncXHRThread;
// Touched on more than one thread, protected by the worker's lock.
--- a/dom/src/threads/test/Makefile.in
+++ b/dom/src/threads/test/Makefile.in
@@ -42,38 +42,26 @@ srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = dom/src/threads/tests
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
- test_importScripts.html \
importScripts_worker.js \
importScripts_worker_imported1.js \
importScripts_worker_imported2.js \
importScripts_worker_imported3.js \
importScripts_worker_imported4.js \
- test_longThread.html \
- longThread_worker.js \
- test_recursion.html \
- recursion_worker.js \
- test_regExpStatics.html \
- regExpStatics_worker.js \
+ test_importScripts.html \
test_simpleThread.html \
- simpleThread_worker.js \
test_threadErrors.html \
- threadErrors_worker1.js \
- threadErrors_worker2.js \
- threadErrors_worker3.js \
- threadErrors_worker4.js \
test_threadTimeouts.html \
- threadTimeouts_worker.js \
+ test_longThread.html \
+ test_recursion.html \
+ test_regExpStatics.html \
test_xhr.html \
- xhr_worker.js \
testXHR.txt \
- test_fibonacci.html \
- fibonacci_worker.js \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/dom/src/threads/test/importScripts_worker.js
+++ b/dom/src/threads/test/importScripts_worker.js
@@ -1,30 +1,30 @@
-onmessage = function(event) {
- switch (event.data) {
+function messageListener(message, source) {
+ switch (message) {
case 'start':
- importScripts("importScripts_worker_imported2.js");
+ loadScripts("importScripts_worker_imported2.js");
importedScriptFunction2();
tryBadScripts();
- postMessage('started');
+ source.postMessage('started');
break;
case 'stop':
tryBadScripts();
- postMessage('stopped');
+ postMessageToPool('stopped');
break;
default:
- throw new Error("Bad message: " + event.data);
+ throw new Error("Bad message: " + message);
break;
}
}
// This caused security exceptions in the past, make sure it doesn't!
var constructor = {}.constructor;
-importScripts("importScripts_worker_imported1.js");
+loadScripts("importScripts_worker_imported1.js");
// Try to call a function defined in the imported script.
importedScriptFunction();
function tryBadScripts() {
var badScripts = [
// Has a syntax error
"importScripts_worker_imported3.js",
@@ -35,17 +35,17 @@ function tryBadScripts() {
// Not a valid url
"http://flippety::foo_js ftw"
];
for (var i = 0; i < badScripts.length; i++) {
var caughtException = false;
var url = badScripts[i];
try {
- importScripts(url);
+ loadScripts(url);
}
catch (e) {
caughtException = true;
}
if (!caughtException) {
throw "Bad script didn't throw exception: " + url;
}
}
--- a/dom/src/threads/test/test_importScripts.html
+++ b/dom/src/threads/test/test_importScripts.html
@@ -13,38 +13,37 @@ Tests of DOM Worker Threads (Bug 437152)
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- var worker = new Worker("importScripts_worker.js");
-
- worker.onmessage = function(event) {
- switch (event.data) {
+ var pool = navigator.newWorkerPool();
+ pool.messageListener = function(message, source) {
+ switch (message) {
case "started":
- worker.postMessage("stop");
+ source.postMessage("stop");
break;
case "stopped":
SimpleTest.finish();
break;
default:
ok(false, "Unexpected message:" + message);
SimpleTest.finish();
}
};
- worker.onerror = function(event) {
- ok(false, "Worker had an error:" + event.data);
+ pool.errorListener = function(error, source) {
+ ok(false, "Worker had an error:" + error);
SimpleTest.finish();
}
+ var worker = pool.createWorkerFromURL("importScripts_worker.js");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();
-
</script>
</pre>
</body>
</html>
--- a/dom/src/threads/test/test_longThread.html
+++ b/dom/src/threads/test/test_longThread.html
@@ -13,43 +13,70 @@ Tests of DOM Worker Threads (Bug 437152)
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- const numThreads = 5;
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ //prefs.setIntPref("javascript.options.gczeal", 2);
+
+ var workerScript =
+ "function messageListener(message, source) { " +
+ " switch (message) { " +
+ " case 'start': " +
+ " /* do a ton of stuff! */ " +
+ " for (var i = 0; i < 10000000; i++) { } " +
+ " dump('done!\\n'); " +
+ " /* pass message to source */ " +
+ " source.postMessage('done'); " +
+ " break; " +
+ " default: " +
+ " throw 'Bad message: ' + message; " +
+ " } " +
+ "} " +
+ "";
+
+ var pool = navigator.newWorkerPool();
+ ok(pool, "Couldn't get worker pool");
+
+ const numThreads = 10;
var doneThreads = 0;
- function onmessage(event) {
- switch (event.data) {
+ pool.messageListener = function(message, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ switch (message) {
case "done":
if (++doneThreads == numThreads) {
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
break;
default:
ok(false, "Unexpected message");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
- }
+ };
- function onerror(event) {
+ pool.errorListener = function(error, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
- }
+ };
for (var i = 0; i < numThreads; i++) {
- var worker = new Worker("longThread_worker.js");
- worker.onmessage = onmessage;
- worker.onerror = onerror;
+ var worker = pool.createWorker(workerScript);
worker.postMessage("start");
}
SimpleTest.waitForExplicitFinish();
-
</script>
</pre>
</body>
</html>
--- a/dom/src/threads/test/test_recursion.html
+++ b/dom/src/threads/test/test_recursion.html
@@ -12,26 +12,41 @@ Tests of DOM Worker Threads
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- var worker = new Worker("recursion_worker.js");
+ function workerScript() {
+ function recurse() {
+ recurse();
+ }
- worker.onerror = function(event) {
- dump(event.data + "\n");
- is(event.data,
- '[JavaScript Error: "too much recursion" {file: "http://localhost:8888' +
- '/tests/dom/src/threads/tests/recursion_worker.js" line: 2}]');
+ this.messageListener = function(message, source) {
+ switch (message) {
+ case "start":
+ recurse();
+ break;
+ default:
+ }
+ }
+ }
+
+ var pool = navigator.newWorkerPool();
+
+ pool.errorListener = function(error, source) {
+ is(error.message,
+ '[JavaScript Error: "too much recursion" {file: "DOMWorker inline ' +
+ 'script" line: 4}]');
SimpleTest.finish();
}
+ var worker = pool.createWorker("(" + workerScript + ")();");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>
--- a/dom/src/threads/test/test_regExpStatics.html
+++ b/dom/src/threads/test/test_regExpStatics.html
@@ -14,32 +14,60 @@ Tests of DOM Worker Threads RegExp stati
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
const WORKER_COUNT = 25;
+ function workerScript() {
+ var runCount = 0;
+ var timeout;
+
+ this.messageListener = function(message, source) {
+ run();
+ timeout = setTimeout(run, 0);
+ timeout = setTimeout(run, 5000);
+ };
+
+ function run() {
+ if (RegExp.$1) {
+ throw "RegExp.$1 already set!";
+ cancelTimeout(timeout);
+ }
+
+ var match = /a(sd)f/("asdf");
+ if (!RegExp.$1) {
+ throw "RegExp.$1 didn't get set!";
+ cancelTimeout(timeout);
+ }
+
+ if (++runCount == 3) {
+ postMessageToPool("done");
+ }
+ }
+ }
+
+ var pool = navigator.newWorkerPool();
+
var doneWorkers = 0;
- function onmessage(event) {
+ pool.messageListener = function(message, source) {
if (++doneWorkers == WORKER_COUNT) {
SimpleTest.finish();
}
- }
+ };
- function onerror(event) {
- ok(false, "Worker had an error: " + event.data);
+ pool.errorListener = function(error, source) {
+ ok(false, "Worker had an error: " + error);
SimpleTest.finish();
};
for (var i = 0; i < WORKER_COUNT; i++) {
- var worker = new Worker("regExpStatics_worker.js");
- worker.onmessage = onmessage;
- worker.onerror = onerror;
+ var worker = pool.createWorker("(" + workerScript + ")();");
worker.postMessage("start");
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
--- a/dom/src/threads/test/test_simpleThread.html
+++ b/dom/src/threads/test/test_simpleThread.html
@@ -13,53 +13,85 @@ Tests of DOM Worker Threads (Bug 437152)
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- var worker = new Worker("simpleThread_worker.js");
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ //prefs.setIntPref("javascript.options.gczeal", 2);
- worker.addEventListener("message",function(event) {
- is(event.target, worker);
- switch (event.data) {
+ var workerScript =
+ "function messageListener(message, source) { " +
+ " switch (message) { " +
+ " case 'no-op': " +
+ " break; " +
+ " case 'start': " +
+ " /* do a ton of stuff! */ " +
+ " for (var i = 0; i < 1000; i++) { } " +
+ " /* pass message to source */ " +
+ " source.postMessage('started'); " +
+ " break; " +
+ " case 'stop': " +
+ " /* do some more stuff! */ " +
+ " for (var i = 0; i < 1000; i++) { } " +
+ " /* pass message to self */ " +
+ " threadContext.thisThread.postMessage('no-op'); " +
+ " /* pass message to pool */ " +
+ " postMessageToPool('stopped'); " +
+ " break; " +
+ " default: " +
+ " throw 'Bad message: ' + message; " +
+ " } " +
+ "} " +
+ "";
+
+ var pool = navigator.newWorkerPool();
+ ok(pool, "Couldn't get worker pool");
+
+ pool.messageListener = function(message, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ switch (message) {
case "no-op":
break;
case "started":
- is(gotError, true);
- worker.postMessage("no-op");
- worker.postMessage("stop");
+ // pass message to self
+ pool.postMessage("no-op");
+ // pass message to source
+ source.postMessage("stop");
break;
case "stopped":
+ // pass message to worker
worker.postMessage("no-op");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
break;
default:
- ok(false, "Unexpected message:" + event.data);
+ ok(false, "Unexpected message");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
- }, false);
-
- var gotError = false;
- worker.onerror = function(event) {
- is(event.target, worker);
- is(event.data,
- '[JavaScript Error: "uncaught exception: Bad message: asdf"]');
- gotError = true;
-
- worker.onerror = function(otherEvent) {
- ok(false, "Worker had an error:" + otherEvent.data);
- SimpleTest.finish();
- }
};
- worker.postMessage("asdf");
+ pool.errorListener = function(error, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ ok(false, "Worker had an error");
+ prefs.setIntPref("javascript.options.gczeal", 0);
+ SimpleTest.finish();
+ };
+
+ var worker = pool.createWorker(workerScript);
+ ok(worker, "Couldn't make worker");
+
+ pool.postMessage("no-op");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();
-
</script>
</pre>
</body>
</html>
--- a/dom/src/threads/test/test_threadErrors.html
+++ b/dom/src/threads/test/test_threadErrors.html
@@ -13,47 +13,86 @@ Tests of DOM Worker Threads (Bug 437152)
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- const expectedErrorCount = 4;
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ //prefs.setIntPref("javascript.options.gczeal", 2);
+
+ var badWorkerScripts = [
+ // Syntax error
+ "function messageListener(message, source) { " +
+ " for (var i = 0; i < 10) { } " +
+ "} " +
+ "",
+
+ // Bad function error
+ "function messageListener(message, source) { " +
+ " foopy(); " +
+ "} " +
+ "",
- function messageListener(event) {
- ok(false, "Unexpected message: " + event.data);
+ // Unhandled exception in body
+ "function messageListener(message, source) { " +
+ "} " +
+ "throw new Error('Bah!'); " +
+ "",
+
+ // Throwing message listener
+ "function messageListener(message, source) { " +
+ " throw 'Bad message: ' + message; " +
+ "} " +
+ ""
+ ];
+
+ var expectedErrorCount = badWorkerScripts.length;
+
+ var pool = navigator.newWorkerPool();
+
+ pool.messageListener = function(message, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+ prefs.setIntPref("javascript.options.gczeal", 0);
+ ok(false, "Unexpected message");
SimpleTest.finish();
};
var actualErrorCount = 0;
var failedWorkers = [];
- function errorListener(event) {
- if (failedWorkers.indexOf(event.target) != -1) {
+ pool.errorListener = function(error, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+ if (failedWorkers.indexOf(source) != -1) {
+ dump("Already seen worker: " + source + "\n");
ok(false, "Seen an extra error from this worker");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
return;
}
- failedWorkers.push(event.target);
+ failedWorkers.push(source);
actualErrorCount++;
if (actualErrorCount == expectedErrorCount) {
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
- for (var i = 1; i <= expectedErrorCount; i++) {
- var worker = new Worker("threadErrors_worker" + i + ".js");
- worker.onmessage = messageListener;
- worker.onerror = errorListener;
+ for (var i = 0; i < expectedErrorCount; i++) {
+ var worker = pool.createWorker(badWorkerScripts[i]);
worker.postMessage("Hi");
}
SimpleTest.waitForExplicitFinish();
-
</script>
</pre>
</body>
</html>
--- a/dom/src/threads/test/test_threadTimeouts.html
+++ b/dom/src/threads/test/test_threadTimeouts.html
@@ -13,43 +13,93 @@ Tests of DOM Worker Threads (Bug 437152)
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- var worker = new Worker("threadTimeouts_worker.js");
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ //prefs.setIntPref("javascript.options.gczeal", 2);
- worker.onmessage = function(event) {
- is(event.target, worker);
- switch (event.data) {
+ var workerScript =
+ "var gTimeoutId; " +
+ "var gTimeoutCount = 0; " +
+ "var gIntervalCount = 0; " +
+ "" +
+ "function timeoutFunc() { " +
+ " if (++gTimeoutCount > 1) { " +
+ " throw new Error('Timeout called more than once!'); " +
+ " } " +
+ " postMessageToPool('timeoutFinished'); " +
+ "} " +
+ "" +
+ "function intervalFunc() { " +
+ " if (++gIntervalCount == 2) { " +
+ " postMessageToPool('intervalFinished'); " +
+ " } " +
+ "} " +
+ "" +
+ "function messageListener(message, source) { " +
+ " switch (message) { " +
+ " case 'startTimeout': " +
+ " gTimeoutId = setTimeout(timeoutFunc, 5000); " +
+ " clearTimeout(gTimeoutId); " +
+ " gTimeoutId = setTimeout(timeoutFunc, 5000); " +
+ " break; " +
+ " case 'startInterval': " +
+ " gTimeoutId = setInterval(intervalFunc, 5000); " +
+ " break; " +
+ " case 'cancelInterval': " +
+ " clearInterval(gTimeoutId); " +
+ " postMessageToPool('intervalCanceled'); " +
+ " break; " +
+ " default: " +
+ " throw 'Bad message: ' + message; " +
+ " } " +
+ "} " +
+ "";
+
+ var pool = navigator.newWorkerPool();
+ ok(pool, "Couldn't get worker pool");
+
+ pool.messageListener = function(message, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ switch (message) {
case "timeoutFinished":
- event.target.postMessage("startInterval");
+ source.postMessage("startInterval");
break;
case "intervalFinished":
- event.target.postMessage("cancelInterval");
+ source.postMessage("cancelInterval");
break;
case "intervalCanceled":
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
break;
default:
ok(false, "Unexpected message");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
- worker.onerror = function(event) {
- is(event.target, worker);
- ok(false, "Worker had an error: " + event.data);
+ pool.errorListener = function(error, source) {
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ ok(false, "Worker had an error");
+ prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
};
+ var worker = pool.createWorker(workerScript);
+ ok(worker, "Couldn't make worker");
+
worker.postMessage("startTimeout");
SimpleTest.waitForExplicitFinish();
-
</script>
</pre>
</body>
</html>
--- a/dom/src/threads/test/test_xhr.html
+++ b/dom/src/threads/test/test_xhr.html
@@ -13,21 +13,76 @@ Tests of DOM Worker Threads XHR(Bug 4504
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads XHR (Bug 450452)</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
- var worker = new Worker("xhr_worker.js");
+ function workerScript() {
+ var xhr = new XMLHttpRequest();
+
+ function onload(event) {
+ if (event.target.status != 200) {
+ var message = { type: "error",
+ error: event.target.status };
+ postMessageToPool(message.toSource());
+ }
+
+ var message = { type: "load",
+ data: xhr.responseText };
+ postMessageToPool(message.toSource());
+ }
+
+ xhr.onload = onload;
+ xhr.addEventListener("load", onload, false);
+ xhr.removeEventListener("load", onload, false);
+ if (!xhr.onload) {
+ var message = { type: "error",
+ error: "Lost message listener!" };
+ postMessageToPool(message.toSource());
+ }
+
+ xhr.addEventListener("error", function(event) {
+ var message = { type: "error",
+ error: event.target.status };
+ postMessageToPool(message.toSource());
+ }, false);
- worker.onmessage = function(event) {
- is(event.target, worker);
- var args = eval(event.data);
+ function onprogress(event) {
+ var message = { type: "progress",
+ current: event.loaded,
+ total: event.total };
+ postMessageToPool(message.toSource());
+ }
+ xhr.addEventListener("progress", onprogress, false);
+
+ xhr.addEventListener("foopety", function(event) {}, false);
+ xhr.removeEventListener("doopety", function(event) {}, false);
+
+ var upload = xhr.upload;
+ upload.onprogress = function(event) { };
+ upload.addEventListener("readystatechange", function(event) { }, false);
+ upload.removeEventListener("readystatechange", function(event) { }, false);
+ upload.addEventListener("load", function(event) { }, false);
+ upload.removeEventListener("readystatechange", function(event) { }, false);
+
+ this.messageListener = function(message, source) {
+ if (xhr.readystate > 0) {
+ throw "XHR already running!";
+ }
+ xhr.open("GET", message);
+ xhr.send(null);
+ }
+ }
+
+ var pool = navigator.newWorkerPool();
+ pool.messageListener = function(message, source) {
+ var args = eval(message);
switch (args.type) {
case "progress": {
ok(parseInt(args.current) <= parseInt(args.total));
} break;
case "error": {
ok(false, "XHR error: " + args.error);
} break;
case "load": {
@@ -37,22 +92,22 @@ Tests of DOM Worker Threads XHR(Bug 4504
} break;
default: {
ok(false, "Unexpected message");
SimpleTest.finish();
}
}
};
- worker.onerror = function(event) {
- is(event.target, worker);
- ok(false, "Worker had an error:" + event.data);
+ pool.errorListener = function(error, source) {
+ ok(false, "Worker had an error:" + error);
SimpleTest.finish();
}
+ var worker = pool.createWorker("(" + workerScript + ")();");
worker.postMessage("testXHR.txt");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -739,19 +739,16 @@ GetScopeOfObject(JSObject* obj)
#ifdef DEBUG
void DEBUG_CheckForComponentsInScope(XPCCallContext& ccx, JSObject* obj,
JSBool OKIfNotInitialized)
{
if(OKIfNotInitialized)
return;
- if(!(JS_GetOptions(ccx) & JSOPTION_PRIVATE_IS_NSISUPPORTS))
- return;
-
const char* name = ccx.GetRuntime()->GetStringName(XPCJSRuntime::IDX_COMPONENTS);
jsval prop;
if(JS_LookupProperty(ccx, obj, name, &prop) && !JSVAL_IS_PRIMITIVE(prop))
return;
// This is pretty much always bad. It usually means that native code is
// making a callback to an interface implemented in JavaScript, but the
// document where the JS object was created has already been cleared and the