diff --git a/dom/public/idl/base/nsIDOMNavigator.idl b/dom/public/idl/base/nsIDOMNavigator.idl index 7217013f570f..5e1d18599648 100644 --- a/dom/public/idl/base/nsIDOMNavigator.idl +++ b/dom/public/idl/base/nsIDOMNavigator.idl @@ -39,9 +39,7 @@ #include "domstubs.idl" -interface nsIDOMWorkerPool; - -[scriptable, uuid(c206f746-52e2-47dd-8ccc-ce76ccda6c6d)] +[scriptable, uuid(777bd8a1-38c1-4b12-ba8f-ff6c2eb8c56b)] interface nsIDOMNavigator : nsISupports { readonly attribute DOMString appCodeName; @@ -65,8 +63,6 @@ interface nsIDOMNavigator : nsISupports boolean javaEnabled(); boolean taintEnabled(); - nsIDOMWorkerPool newWorkerPool(); - // XXX This one's tough, would nsISupports preference(in DOMString // pref /*, ... */); work? diff --git a/dom/public/idl/threads/Makefile.in b/dom/public/idl/threads/Makefile.in index 75a214299e55..d7d8e507f430 100644 --- a/dom/public/idl/threads/Makefile.in +++ b/dom/public/idl/threads/Makefile.in @@ -47,6 +47,6 @@ MODULE = dom XPIDL_MODULE = dom_threads GRE_MODULE = 1 -XPIDLSRCS = nsIDOMThreads.idl +XPIDLSRCS = nsIDOMWorkers.idl include $(topsrcdir)/config/rules.mk diff --git a/dom/public/idl/threads/nsIDOMThreads.idl b/dom/public/idl/threads/nsIDOMThreads.idl deleted file mode 100644 index a901c2eef293..000000000000 --- a/dom/public/idl/threads/nsIDOMThreads.idl +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is worker threads. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Vladimir Vukicevic (Original Author) - * Ben Turner - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsISupports.idl" - -interface nsIScriptError; - -[scriptable, function, uuid(e50ca05d-1381-4abb-a021-02eb720cfc38)] -interface nsIDOMWorkerMessageListener : nsISupports -{ - /** - * An nsIDOMWorkerThread receives the onMessage callback when another - * worker posts a message to it. - * - * @param aMessage (in DOMString) - * The message sent from another worker. - * @param aSource (in nsISupports) - * The worker that sent the message. Useful for a quick response. - */ - void onMessage(in DOMString aMessage, - in nsISupports aSource); -}; - -[scriptable, function, uuid(9df8422e-25dd-43f4-b9b9-709f9e074647)] -interface nsIDOMWorkerErrorListener : nsISupports -{ - /** - * An nsIDOMWorkerPool receives the onError callback when one of its child - * workers has a parse error or an unhandled exception. - * - * @param aMessage (in nsIScriptError) - * Details about the error that occurred. See nsIScriptError. - * @param aSource (in nsISupports) - * The worker that sent the message. Depending on the specific error in - * question it may not be possible to use this object (in the case of a - * parse error, for instance, aSource will be unusable). - */ - void onError(in nsIScriptError aError, - in nsISupports aSource); -}; - -[scriptable, uuid(6f19f3ff-2aaa-4504-9b71-dca3c191efed)] -interface nsIDOMWorkerThread : nsISupports -{ - /** - * Sends a message to the worker. - * - * @param aMessage (in DOMString) - * The message to send. - */ - void postMessage(in DOMString aMessage); -}; - -[scriptable, uuid(45312e93-8a3e-4493-9bd9-272a6c23a16c)] -interface nsIDOMWorkerPool : nsISupports -{ - /** - * Sends a message to the pool. - * - * @param aMessage (in DOMString) - * The message to send.. - */ - void postMessage(in DOMString aMessage); - - /** - * The nsIDOMWorkerMessageListener which handles messages for this worker. - * - * Developers should set this attribute to a proper object before another - * worker begins sending messages to ensure that all messages are received. - */ - attribute nsIDOMWorkerMessageListener messageListener; - - /** - * The nsIDOMWorkerErrorListener which handles errors in child threads. - * - * Developers should set this attribute to a proper object before calling - * createWorker in order to catch parse errors as well as runtime exceptions. - */ - attribute nsIDOMWorkerErrorListener errorListener; - - /** - * Create a new worker object by evaluating the given script. - * - * @param aSourceScript (in DOMString) - * The script to compile. See below for details on the scope in which - * the script will run. - */ - nsIDOMWorkerThread createWorker(in DOMString aSourceScript); - - /** - * Create a new worker object by evaluating the given script. - * - * @param aSourceURL (in AString) - * The script url to load and compile. See below for details on the - * scope in which the script will run. - */ - nsIDOMWorkerThread createWorkerFromURL(in AString aSourceURL); -}; - -[scriptable, uuid(0f2a52ea-afc9-49e6-86dd-2d0cb65b5dd5)] -interface nsIDOMThreadService : nsISupports -{ - /** - * Creates a new DOM worker pool. - */ - nsIDOMWorkerPool createPool(); -}; - -[scriptable, uuid(fcf387be-a7e3-4283-8bc5-06bfe13c5e8c)] -interface nsIDOMWorkerThreadContext : nsISupports -{ - readonly attribute nsIDOMWorkerThread thisThread; -}; diff --git a/dom/public/idl/threads/nsIDOMWorkers.idl b/dom/public/idl/threads/nsIDOMWorkers.idl new file mode 100644 index 000000000000..62786095c7f8 --- /dev/null +++ b/dom/public/idl/threads/nsIDOMWorkers.idl @@ -0,0 +1,98 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * From http://www.whatwg.org/specs/web-workers/current-work + */ + +#include "nsIDOMEvent.idl" +#include "nsIDOMEventTarget.idl" + +interface nsIDOMEventListener; + +[scriptable, uuid(6c32d0c5-6bfa-438b-ad44-be0df80cd4a8)] +interface nsIWorkerMessagePort : nsISupports +{ + void postMessage(in DOMString aMessage); +}; + +[scriptable, uuid(508f2d49-e9a0-4fe8-bd33-321820173b4a)] +interface nsIWorkerMessageEvent : nsIDOMEvent +{ + readonly attribute DOMString data; + readonly attribute DOMString origin; + + readonly attribute nsISupports source; + + void initMessageEvent(in DOMString aTypeArg, + in boolean aCanBubbleArg, + in boolean aCancelableArg, + in DOMString aDataArg, + in DOMString aOriginArg, + in nsISupports aSourceArg); +}; + +[scriptable, uuid(3e54b65e-5d25-453e-8d3f-258766fbf6fd)] +interface nsIWorkerGlobalScope : nsISupports +{ + readonly attribute nsIWorkerGlobalScope self; +}; + +[scriptable, uuid(b10cfe72-91b9-45c6-ab13-33f89c2d0e56)] +interface nsIWorkerScope : nsIWorkerGlobalScope +{ + void postMessage(in DOMString aMessage, + [optional] in nsIWorkerMessagePort aMessagePort); + + attribute nsIDOMEventListener onmessage; +}; + +[scriptable, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)] +interface nsIAbstractWorker : nsIDOMEventTarget +{ + attribute nsIDOMEventListener onerror; +}; + +[scriptable, uuid(479c9476-0bc2-4211-8656-6627dfd82b1c)] +interface nsIWorker : nsIAbstractWorker +{ + void postMessage(in DOMString aMessage, + [optional] in nsIWorkerMessagePort aMessagePort); + + attribute nsIDOMEventListener onmessage; +}; diff --git a/dom/public/nsDOMClassInfoID.h b/dom/public/nsDOMClassInfoID.h index a5258e57d46e..8d7ec9aee449 100644 --- a/dom/public/nsDOMClassInfoID.h +++ b/dom/public/nsDOMClassInfoID.h @@ -458,6 +458,8 @@ enum nsDOMClassInfoID { eDOMClassInfo_MathMLElement_id, #endif + eDOMClassInfo_Worker_id, + // This one better be the last one in this list eDOMClassInfoIDCount }; diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index 4285a530e2e1..38da98874805 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -457,6 +457,9 @@ #include "nsIDOMGeoPosition.h" #include "nsIDOMGeoPositionError.h" +// Workers +#include "nsDOMWorker.h" + #include "nsDOMFile.h" #include "nsIDOMFileException.h" @@ -1299,6 +1302,9 @@ static nsDOMClassInfoData sClassInfoData[] = { 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();| @@ -1321,6 +1327,20 @@ static const nsContractIDMapData kConstructorMap[] = "@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; @@ -3552,6 +3572,12 @@ nsDOMClassInfo::Init() 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); @@ -4966,6 +4992,17 @@ FindConstructorContractID(PRInt32 aDOMClassInfoID) 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, @@ -4976,7 +5013,16 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner, if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { const char *contractid = FindConstructorContractID(name_struct->mDOMClassInfoID); - native = do_CreateInstance(contractid, &rv); + if (contractid) { + native = do_CreateInstance(contractid, &rv); + } + else { + nsDOMConstructorFunc func = + FindConstructorFunc(name_struct->mDOMClassInfoID); + if (func) { + rv = func(getter_AddRefs(native)); + } + } } else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) { native = do_CreateInstance(name_struct->mCID, &rv); } else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) { @@ -5194,7 +5240,8 @@ private: { return (aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor && - FindConstructorContractID(aNameStruct->mDOMClassInfoID)) || + (FindConstructorContractID(aNameStruct->mDOMClassInfoID) || + FindConstructorFunc(aNameStruct->mDOMClassInfoID))) || (aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo && aNameStruct->mData->mConstructorCID) || aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructor || diff --git a/dom/src/base/nsDOMClassInfo.h b/dom/src/base/nsDOMClassInfo.h index f03297212905..fc825252d1e2 100644 --- a/dom/src/base/nsDOMClassInfo.h +++ b/dom/src/base/nsDOMClassInfo.h @@ -61,6 +61,7 @@ struct nsDOMClassInfoData; typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc) (nsDOMClassInfoData* aData); +typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject); struct nsDOMClassInfoData { diff --git a/dom/src/base/nsDOMScriptObjectFactory.cpp b/dom/src/base/nsDOMScriptObjectFactory.cpp index 9f29eecd9db4..bdd514979d4a 100644 --- a/dom/src/base/nsDOMScriptObjectFactory.cpp +++ b/dom/src/base/nsDOMScriptObjectFactory.cpp @@ -65,6 +65,7 @@ #ifdef MOZ_XUL #include "nsXULPrototypeCache.h" #endif +#include "nsThreadUtils.h" static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); @@ -395,6 +396,10 @@ 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: diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index e7f7141e514f..66712a3947c0 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -113,7 +113,6 @@ #include "nsIDOMPkcs11.h" #include "nsIDOMOfflineResourceList.h" #include "nsIDOMGeoGeolocation.h" -#include "nsIDOMThreads.h" #include "nsDOMString.h" #include "nsIEmbeddingSiteWindow2.h" #include "nsThreadUtils.h" @@ -226,6 +225,10 @@ static PRUint32 gSerialCounter = 0; 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 @@ -655,9 +658,21 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) // We could have failed the first time through trying // to create the entropy collector, so we should // try to get one until we succeed. - if (gRefCnt++ == 0 || !gEntropyCollector) { + + 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) { CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector); } + #ifdef DEBUG printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt, static_cast(static_cast(this)), @@ -1667,6 +1682,11 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, 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 @@ -3828,24 +3848,25 @@ nsGlobalWindow::GetFullScreen(PRBool* aFullScreen) return NS_OK; } +PRBool +nsGlobalWindow::DOMWindowDumpEnabled() +{ +#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; +#endif +} + 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. - - // if pref doesn't exist, disable dump output. - PRBool enable_dump = - nsContentUtils::GetBoolPref("browser.dom.window.dump.enabled"); - - if (!enable_dump) { - return NS_OK; - } + if (!DOMWindowDumpEnabled()) { + return NS_OK; } -#endif char *cstr = ToNewUTF8String(aStr); @@ -9443,18 +9464,3 @@ NS_IMETHODIMP nsNavigator::GetGeolocation(nsIDOMGeoGeolocation **_retval) NS_IF_ADDREF(*_retval = mGeolocation); return NS_OK; } - -NS_IMETHODIMP -nsNavigator::NewWorkerPool(nsIDOMWorkerPool** _retval) -{ - nsCOMPtr threadService = - nsDOMThreadService::GetOrInitService(); - NS_ENSURE_TRUE(threadService, NS_ERROR_OUT_OF_MEMORY); - - nsCOMPtr newPool; - nsresult rv = threadService->CreatePool(getter_AddRefs(newPool)); - NS_ENSURE_SUCCESS(rv, rv); - - newPool.forget(_retval); - return NS_OK; -} diff --git a/dom/src/base/nsGlobalWindow.h b/dom/src/base/nsGlobalWindow.h index 4dca29c2ed9d..95fd573d96b8 100644 --- a/dom/src/base/nsGlobalWindow.h +++ b/dom/src/base/nsGlobalWindow.h @@ -434,6 +434,7 @@ public: CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey, nsScriptObjectHolder& aHandler); + static PRBool DOMWindowDumpEnabled(); protected: // Object Management diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 6408eccc8376..eb529236eee2 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -2373,6 +2373,11 @@ nsJSContext::InitContext(nsIScriptGlobalObject *aGlobalObject) return NS_OK; } + nsCxPusher cxPusher; + if (!cxPusher.Push(mContext)) { + return NS_ERROR_FAILURE; + } + nsIXPConnect *xpc = nsContentUtils::XPConnect(); JSObject *global = ::JS_GetGlobalObject(mContext); diff --git a/dom/src/threads/Makefile.in b/dom/src/threads/Makefile.in index d4730a81abe9..8eb69c6fc9f8 100644 --- a/dom/src/threads/Makefile.in +++ b/dom/src/threads/Makefile.in @@ -51,11 +51,13 @@ FORCE_STATIC_LIB = 1 REQUIRES = \ caps \ content \ + docshell \ gfx \ js \ layout \ locale \ necko \ + plugin \ pref \ string \ thebes \ @@ -66,11 +68,12 @@ REQUIRES = \ CPPSRCS = \ nsDOMThreadService.cpp \ - nsDOMWorkerBase.cpp \ + nsDOMWorker.cpp \ + nsDOMWorkerEvents.cpp \ + nsDOMWorkerMessageHandler.cpp \ nsDOMWorkerPool.cpp \ nsDOMWorkerScriptLoader.cpp \ nsDOMWorkerSecurityManager.cpp \ - nsDOMWorkerThread.cpp \ nsDOMWorkerTimeout.cpp \ nsDOMWorkerXHR.cpp \ nsDOMWorkerXHRProxy.cpp \ @@ -87,5 +90,3 @@ DIRS += test endif include $(topsrcdir)/config/rules.mk - -#CXXFLAGS += $(WARNINGS_AS_ERRORS) diff --git a/dom/src/threads/nsAutoJSObjectHolder.h b/dom/src/threads/nsAutoJSObjectHolder.h index c0aaa35ce0fb..c6753bdfef6a 100644 --- a/dom/src/threads/nsAutoJSObjectHolder.h +++ b/dom/src/threads/nsAutoJSObjectHolder.h @@ -147,7 +147,11 @@ public: * Pretend to be a JSObject*. Assert if not held. */ JSObject* operator=(JSObject* aOther) { - NS_ASSERTION(mHeld, "Not rooted!"); +#ifdef DEBUG + if (aOther) { + NS_ASSERTION(mHeld, "Not rooted!"); + } +#endif return mObj = aOther; } diff --git a/dom/src/threads/nsDOMThreadService.cpp b/dom/src/threads/nsDOMThreadService.cpp index 58f091211f47..b9519d463eb4 100644 --- a/dom/src/threads/nsDOMThreadService.cpp +++ b/dom/src/threads/nsDOMThreadService.cpp @@ -41,7 +41,6 @@ // Interfaces #include "nsIComponentManager.h" -#include "nsIConsoleService.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIEventTarget.h" @@ -55,6 +54,7 @@ #include "nsISupportsPriority.h" #include "nsIThreadPool.h" #include "nsIXPConnect.h" +#include "nsPIDOMWindow.h" // Other includes #include "nsAutoLock.h" @@ -71,6 +71,10 @@ #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" @@ -105,14 +109,6 @@ PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >= THREADPOOL_MAX_THREADS); // 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; @@ -155,103 +151,31 @@ private: }; /** - * 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. + * This class is used as to post an error to the worker's outer handler. */ -class nsReportErrorRunnable : public nsRunnable -{ -public: - nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker) - : mError(aError), mWorker(aWorker) { } - - NS_IMETHOD Run() { - nsresult rv; - - nsCOMPtr 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 pool = mWorker->Pool(); - - LOG(("Posting error '%s' to pool [0x%p]", - NS_LossyConvertUTF16toASCII(message).get(), - static_cast(pool.get()))); - - pool->HandleError(mError, mWorker); - } - return NS_OK; - } - -private: - // XXX Maybe this should be an nsIException... - nsCOMPtr mError; - - // Have to carry a strong ref since this is used as a parameter to the - // onError callback. - nsRefPtr mWorker; -}; - -/** - * Need this to expose an nsIScriptError to content JS (implement nsIClassInfo - * with DOM_OBJECT flag set. - */ -class nsDOMWorkerScriptError : public nsIClassInfo +class nsReportErrorRunnable : public nsIRunnable { public: NS_DECL_ISUPPORTS - NS_DECL_NSICLASSINFO - nsDOMWorkerScriptError(nsIScriptError* aError) - : mScriptError(this, aError) { } + nsReportErrorRunnable(nsDOMWorker* aWorker, nsIWorkerMessageEvent* aEvent) + : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mEvent(aEvent) { } -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) { } - - protected: - nsDOMWorkerScriptError* mParent; - nsCOMPtr mError; - }; - - InnerScriptError mScriptError; +private: + nsRefPtr mWorker; + nsCOMPtr mWorkerWN; + nsCOMPtr mEvent; }; -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(&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) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable) /** * Used to post an expired timeout to the correct worker. @@ -283,7 +207,7 @@ class nsDOMWorkerRunnable : public nsRunnable friend class nsDOMThreadService; public: - nsDOMWorkerRunnable(nsDOMWorkerThread* aWorker) + nsDOMWorkerRunnable(nsDOMWorker* aWorker) : mWorker(aWorker) { } virtual ~nsDOMWorkerRunnable() { @@ -291,16 +215,6 @@ public: 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 mainThread(do_GetMainThread()); - NS_ProxyRelease(mainThread, supports); } void PutRunnable(nsIRunnable* aRunnable) { @@ -314,6 +228,9 @@ public: } 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!"); @@ -321,6 +238,8 @@ public: 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 @@ -335,6 +254,7 @@ public: 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); @@ -382,7 +302,7 @@ protected: } // Set at construction - nsRefPtr mWorker; + nsRefPtr mWorker; // Protected by mMonitor nsDeque mRunnables; @@ -395,7 +315,7 @@ protected: JSBool DOMWorkerOperationCallback(JSContext* aCx) { - nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx); + nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx); // Want a strong ref here to make sure that the monitor we wait on won't go // away. @@ -482,7 +402,7 @@ DOMWorkerErrorReporter(JSContext* aCx, { NS_ASSERTION(!NS_IsMainThread(), "Huh?!"); - nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx); + nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx); if (worker->IsCanceled()) { // We don't want to report errors from canceled workers. It's very likely @@ -511,21 +431,30 @@ DOMWorkerErrorReporter(JSContext* aCx, column, aReport->flags, "DOM Worker javascript"); NS_ENSURE_SUCCESS(rv,); - nsRefPtr domError = - new nsDOMWorkerScriptError(errorObject); - NS_ENSURE_TRUE(domError,); + nsCString finalMessage; + rv = errorObject->ToString(finalMessage); + NS_ENSURE_SUCCESS(rv,); - nsCOMPtr scriptError(do_QueryInterface(domError)); - NS_ENSURE_TRUE(scriptError,); + nsRefPtr event(new nsDOMWorkerMessageEvent()); + NS_ENSURE_TRUE(event,); - nsCOMPtr mainThread(do_GetMainThread()); - NS_ENSURE_TRUE(mainThread,); + rv = event->InitMessageEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE, + NS_ConvertUTF8toUTF16(finalMessage), + EmptyString(), nsnull); + NS_ENSURE_SUCCESS(rv,); - nsCOMPtr runnable = - new nsReportErrorRunnable(scriptError, worker); + event->SetTarget(worker); + + nsCOMPtr runnable(new nsReportErrorRunnable(worker, event)); NS_ENSURE_TRUE(runnable,); - rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + nsRefPtr 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); NS_ENSURE_SUCCESS(rv,); } @@ -558,10 +487,9 @@ nsDOMThreadService::~nsDOMThreadService() } } -NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget, +NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget, nsIObserver, - nsIThreadPoolListener, - nsIDOMThreadService) + nsIThreadPoolListener) nsresult nsDOMThreadService::Init() @@ -597,6 +525,9 @@ nsDOMThreadService::Init() 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 runtimeSvc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1")); NS_ENSURE_TRUE(runtimeSvc, NS_ERROR_FAILURE); @@ -622,7 +553,7 @@ nsDOMThreadService::Init() } /* static */ -already_AddRefed +already_AddRefed nsDOMThreadService::GetOrInitService() { if (!gDOMThreadService) { @@ -635,7 +566,7 @@ nsDOMThreadService::GetOrInitService() service.swap(gDOMThreadService); } - nsCOMPtr service(gDOMThreadService); + nsRefPtr service(gDOMThreadService); return service.forget(); } @@ -646,6 +577,24 @@ 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(PR_GetThreadPrivate(gJSContextIndex)); + } + + return cx; +} + /* static */ void nsDOMThreadService::Shutdown() @@ -687,10 +636,15 @@ nsDOMThreadService::Cleanup() // 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(nsDOMWorkerThread* aWorker, +nsDOMThreadService::Dispatch(nsDOMWorker* aWorker, nsIRunnable* aRunnable) { NS_ASSERTION(aWorker, "Null pointer!"); @@ -755,7 +709,7 @@ nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable) // No need to be in the monitor here because we should already be in it. #ifdef DEBUG - nsRefPtr& debugWorker = aRunnable->mWorker; + nsRefPtr& debugWorker = aRunnable->mWorker; nsRefPtr runnable; NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) && @@ -766,19 +720,6 @@ nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable) 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() @@ -810,42 +751,64 @@ nsDOMThreadService::CreateJSContext() return cx.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 +already_AddRefed +nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject, + PRBool aRemove) +{ + NS_ASSERTION(aGlobalObject, "Null pointer!"); + + nsAutoMonitor mon(mMonitor); + + nsRefPtr pool; + mPools.Get(aGlobalObject, getter_AddRefs(pool)); + + if (aRemove) { + mPools.Remove(aGlobalObject); + } + + return pool.forget(); +} void nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject)); + NS_ASSERTION(aGlobalObject, "Null pointer!"); + + nsRefPtr pool = GetPoolForGlobal(aGlobalObject, PR_TRUE); + if (pool) { + pool->Cancel(); + } } void nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject)); + NS_ASSERTION(aGlobalObject, "Null pointer!"); + + nsRefPtr pool = GetPoolForGlobal(aGlobalObject, PR_FALSE); + if (pool) { + pool->Suspend(); + } } void nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject)); + NS_ASSERTION(aGlobalObject, "Null pointer!"); + + nsRefPtr pool = GetPoolForGlobal(aGlobalObject, PR_FALSE); + if (pool) { + pool->Resume(); + } } void -nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool) +nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aPool, "Null pointer!"); - NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!"); - mPools.RemoveElement(aPool); + nsAutoMonitor mon(mMonitor); + mPools.Remove(aPool->ScriptGlobalObject()); } void @@ -863,6 +826,8 @@ 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); @@ -912,7 +877,6 @@ nsDOMThreadService::Dispatch(nsIRunnable* aEvent, // 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; @@ -1011,31 +975,69 @@ nsDOMThreadService::OnThreadShuttingDown() return NS_OK; } -/** - * See nsIDOMThreadService - */ -NS_IMETHODIMP -nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval) +nsresult +nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker, + nsIScriptGlobalObject* aGlobalObject) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aWorker, "Null pointer!"); + NS_ASSERTION(aGlobalObject, "Null pointer!"); - NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + if (NS_IsMainThread()) { + nsCOMPtr domWindow(do_QueryInterface(aGlobalObject)); + NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE); - nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller(); - NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED); + nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ? + domWindow->GetCurrentInnerWindow() : + domWindow.get(); + NS_ENSURE_STATE(innerWindow); - nsCOMPtr callingDocument(do_QueryInterface(domDocument)); - NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE); + nsCOMPtr newGlobal(do_QueryInterface(innerWindow)); + NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE); - nsRefPtr pool(new nsDOMWorkerPool(callingDocument)); - NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY); + aGlobalObject = newGlobal; + } - nsresult rv = pool->Init(); + nsRefPtr pool; + { + nsAutoMonitor mon(mMonitor); + + if (!mThreadPool) { + // Shutting down! + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; + } + + mPools.Get(aGlobalObject, getter_AddRefs(pool)); + } + + nsresult rv; + + if (!pool) { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsCOMPtr domWindow(do_QueryInterface(aGlobalObject)); + NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE); + + nsIDOMDocument* domDocument = domWindow->GetExtantDocument(); + NS_ENSURE_STATE(domDocument); + + nsCOMPtr document(do_QueryInterface(domDocument)); + NS_ENSURE_STATE(document); + + pool = new nsDOMWorkerPool(aGlobalObject, document); + NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY); + + rv = pool->Init(); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoMonitor mon(mMonitor); + + PRBool success = mPools.Put(aGlobalObject, pool); + NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY); + } + + rv = pool->NoteWorker(aWorker); NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(!mPools.Contains(pool), "Um?!"); - mPools.AppendElement(pool); - - NS_ADDREF(*_retval = pool); + aWorker->SetPool(pool); return NS_OK; } diff --git a/dom/src/threads/nsDOMThreadService.h b/dom/src/threads/nsDOMThreadService.h index 78e8095a5e89..827abac2e495 100644 --- a/dom/src/threads/nsDOMThreadService.h +++ b/dom/src/threads/nsDOMThreadService.h @@ -44,7 +44,6 @@ #include "nsIEventTarget.h" #include "nsIObserver.h" #include "nsIThreadPool.h" -#include "nsIDOMThreads.h" // Other includes #include "jsapi.h" @@ -59,9 +58,9 @@ extern PRLogModuleInfo* gDOMThreadsLog; #endif +class nsDOMWorker; class nsDOMWorkerPool; class nsDOMWorkerRunnable; -class nsDOMWorkerThread; class nsDOMWorkerTimeout; class nsIJSRuntimeService; class nsIScriptGlobalObject; @@ -71,9 +70,9 @@ class nsIXPCSecurityManager; class nsDOMThreadService : public nsIEventTarget, public nsIObserver, - public nsIThreadPoolListener, - public nsIDOMThreadService + public nsIThreadPoolListener { + friend class nsDOMWorker; friend class nsDOMWorkerPool; friend class nsDOMWorkerRunnable; friend class nsDOMWorkerThread; @@ -82,20 +81,25 @@ class nsDOMThreadService : public nsIEventTarget, 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 GetOrInitService(); + static already_AddRefed 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(); @@ -116,24 +120,29 @@ private: static void Shutdown(); - nsresult Dispatch(nsDOMWorkerThread* aWorker, + nsresult Dispatch(nsDOMWorker* aWorker, nsIRunnable* aRunnable); void WorkerComplete(nsDOMWorkerRunnable* aRunnable); - void WaitForCanceledWorker(nsDOMWorkerThread* aWorker); - static JSContext* CreateJSContext(); - void NoteDyingPool(nsDOMWorkerPool* aPool); + already_AddRefed + GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject, + PRBool aRemove); + + void NoteEmptyPool(nsDOMWorkerPool* aPool); void TimeoutReady(nsDOMWorkerTimeout* aTimeout); + nsresult RegisterWorker(nsDOMWorker* aWorker, + nsIScriptGlobalObject* aGlobalObject); + // Our internal thread pool. nsCOMPtr mThreadPool; - // Weak references, only ever touched on the main thread! - nsTPtrArray mPools; + // Maps nsIScriptGlobalObject* to nsDOMWorkerPool. + nsRefPtrHashtable mPools; // mMonitor protects all access to mWorkersInProgress and // mCreationsInProgress. diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp new file mode 100644 index 000000000000..0a1d579aaefd --- /dev/null +++ b/dom/src/threads/nsDOMWorker.cpp @@ -0,0 +1,1331 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsDOMWorker.h" + +#include "nsIDOMEvent.h" +#include "nsIEventTarget.h" +#include "nsIJSRuntimeService.h" +#include "nsIXPConnect.h" + +#ifdef MOZ_SHARK +#include "jsdbgapi.h" +#endif +#include "nsAutoLock.h" +#include "nsContentUtils.h" +#include "nsDOMClassInfoID.h" +#include "nsGlobalWindow.h" +#include "nsJSUtils.h" +#include "nsThreadUtils.h" + +#include "nsDOMThreadService.h" +#include "nsDOMWorkerEvents.h" +#include "nsDOMWorkerPool.h" +#include "nsDOMWorkerScriptLoader.h" +#include "nsDOMWorkerTimeout.h" +#include "nsDOMWorkerXHR.h" + +class nsDOMWorkerFunctions +{ +public: + // Same as window.dump(). + static JSBool Dump(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval); + + // Same as window.setTimeout(). + static JSBool SetTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval) { + return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_FALSE); + } + + // Same as window.setInterval(). + static JSBool SetInterval(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval) { + return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_TRUE); + } + + // Used for both clearTimeout() and clearInterval(). + static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval); + + static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval); + + static JSBool NewXMLHttpRequest(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval); + + static JSBool NewWorker(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval); + +private: + // Internal helper for SetTimeout and SetInterval. + static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc, + jsval* aArgv, jsval* aRval, PRBool aIsInterval); +}; + +JSBool +nsDOMWorkerFunctions::Dump(JSContext* aCx, + JSObject* /* aObj */, + uintN aArgc, + jsval* aArgv, + jsval* /* aRval */) +{ + if (!nsGlobalWindow::DOMWindowDumpEnabled()) { + return JS_TRUE; + } + + JSString* str; + if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) { + nsDependentJSString string(str); + fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr); + fflush(stderr); + } + return JS_TRUE; +} + +JSBool +nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx, + JSObject* /* aObj */, + uintN aArgc, + jsval* aArgv, + jsval* aRval, + PRBool aIsInterval) +{ + nsDOMWorker* worker = static_cast(JS_GetContextPrivate(aCx)); + NS_ASSERTION(worker, "This should be set by the DOM thread service!"); + + if (worker->IsCanceled()) { + return JS_FALSE; + } + + PRUint32 id = worker->NextTimeoutId(); + + nsRefPtr timeout = new nsDOMWorkerTimeout(worker, id); + if (!timeout) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + nsresult rv = timeout->Init(aCx, aArgc, aArgv, aIsInterval); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to initialize timeout!"); + return JS_FALSE; + } + + rv = worker->AddFeature(timeout, aCx); + if (NS_FAILED(rv)) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + rv = timeout->Start(); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to start timeout!"); + return JS_FALSE; + } + + *aRval = INT_TO_JSVAL(id); + return JS_TRUE; +} + +JSBool +nsDOMWorkerFunctions::KillTimeout(JSContext* aCx, + JSObject* /* aObj */, + uintN aArgc, + jsval* aArgv, + jsval* /* aRval */) +{ + nsDOMWorker* worker = static_cast(JS_GetContextPrivate(aCx)); + NS_ASSERTION(worker, "This should be set by the DOM thread service!"); + + if (worker->IsCanceled()) { + return JS_FALSE; + } + + if (!aArgc) { + JS_ReportError(aCx, "Function requires at least 1 parameter"); + return JS_FALSE; + } + + uint32 id; + if (!JS_ValueToECMAUint32(aCx, aArgv[0], &id)) { + JS_ReportError(aCx, "First argument must be a timeout id"); + return JS_FALSE; + } + + worker->CancelTimeoutWithId(PRUint32(id)); + return JS_TRUE; +} + +JSBool +nsDOMWorkerFunctions::LoadScripts(JSContext* aCx, + JSObject* /* aObj */, + uintN aArgc, + jsval* aArgv, + jsval* /* aRval */) +{ + nsDOMWorker* worker = static_cast(JS_GetContextPrivate(aCx)); + NS_ASSERTION(worker, "This should be set by the DOM thread service!"); + + if (worker->IsCanceled()) { + return JS_FALSE; + } + + if (!aArgc) { + JS_ReportError(aCx, "Function must have at least one argument!"); + return JS_FALSE; + } + + nsAutoTArray urls; + + if (!urls.SetCapacity((PRUint32)aArgc)) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + for (uintN index = 0; index < aArgc; index++) { + jsval val = aArgv[index]; + + if (!JSVAL_IS_STRING(val)) { + JS_ReportError(aCx, "Argument %d must be a string", index); + return JS_FALSE; + } + + JSString* str = JS_ValueToString(aCx, val); + if (!str) { + JS_ReportError(aCx, "Couldn't convert argument %d to a string", index); + return JS_FALSE; + } + + nsString* newURL = urls.AppendElement(); + NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!"); + + newURL->Assign(nsDependentJSString(str)); + } + + nsRefPtr loader = + new nsDOMWorkerScriptLoader(worker); + if (!loader) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + nsresult rv = worker->AddFeature(loader, aCx); + if (NS_FAILED(rv)) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + rv = loader->LoadScripts(aCx, urls); + if (NS_FAILED(rv)) { + if (!JS_IsExceptionPending(aCx)) { + JS_ReportError(aCx, "Failed to load scripts"); + } + return JS_FALSE; + } + + return JS_TRUE; +} + +JSBool +nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx, + JSObject* aObj, + uintN aArgc, + jsval* /* aArgv */, + jsval* aRval) +{ + nsDOMWorker* worker = static_cast(JS_GetContextPrivate(aCx)); + NS_ASSERTION(worker, "This should be set by the DOM thread service!"); + + if (worker->IsCanceled()) { + return JS_FALSE; + } + + if (aArgc) { + JS_ReportError(aCx, "XMLHttpRequest constructor takes no arguments!"); + return JS_FALSE; + } + + nsRefPtr xhr = new nsDOMWorkerXHR(worker); + if (!xhr) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + nsresult rv = xhr->Init(); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to construct XMLHttpRequest!"); + return JS_FALSE; + } + + rv = worker->AddFeature(xhr, aCx); + if (NS_FAILED(rv)) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + nsCOMPtr xhrWrapped; + rv = xpc->WrapNative(aCx, aObj, static_cast(xhr), + NS_GET_IID(nsISupports), getter_AddRefs(xhrWrapped)); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to wrap XMLHttpRequest!"); + return JS_FALSE; + } + + JSObject* xhrJSObj; + rv = xhrWrapped->GetJSObject(&xhrJSObj); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to get JSObject from wrapper!"); + return JS_FALSE; + } + + *aRval = OBJECT_TO_JSVAL(xhrJSObj); + return JS_TRUE; +} + +JSBool +nsDOMWorkerFunctions::NewWorker(JSContext* aCx, + JSObject* aObj, + uintN aArgc, + jsval* aArgv, + jsval* aRval) +{ + nsDOMWorker* worker = static_cast(JS_GetContextPrivate(aCx)); + NS_ASSERTION(worker, "This should be set by the DOM thread service!"); + + if (worker->IsCanceled()) { + return JS_FALSE; + } + + if (!aArgc) { + JS_ReportError(aCx, "Worker constructor must have an argument!"); + return JS_FALSE; + } + + nsRefPtr pool = worker->Pool(); + if (!pool) { + JS_ReportError(aCx, "Couldn't get pool from worker!"); + return JS_FALSE; + } + + // This pointer is protected by our pool, but it is *not* threadsafe and must + // not be used in any way other than to pass it along to the Initialize call. + nsIScriptGlobalObject* owner = pool->ScriptGlobalObject(); + if (!owner) { + JS_ReportError(aCx, "Couldn't get owner from pool!"); + return JS_FALSE; + } + + nsCOMPtr wrappedWorker = + worker->GetWrappedNative(); + if (!wrappedWorker) { + JS_ReportError(aCx, "Couldn't get wrapped native of worker!"); + return JS_FALSE; + } + + nsRefPtr newWorker = new nsDOMWorker(worker, wrappedWorker); + if (!newWorker) { + JS_ReportOutOfMemory(aCx); + return JS_FALSE; + } + + nsresult rv = newWorker->InitializeInternal(owner, aCx, aObj, aArgc, aArgv); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Couldn't initialize new worker!"); + return JS_FALSE; + } + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + nsCOMPtr workerWrapped; + rv = xpc->WrapNative(aCx, aObj, static_cast(newWorker), + NS_GET_IID(nsISupports), getter_AddRefs(workerWrapped)); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to wrap new worker!"); + return JS_FALSE; + } + + JSObject* workerJSObj; + rv = workerWrapped->GetJSObject(&workerJSObj); + if (NS_FAILED(rv)) { + JS_ReportError(aCx, "Failed to get JSObject from wrapper!"); + return JS_FALSE; + } + + *aRval = OBJECT_TO_JSVAL(workerJSObj); + return JS_TRUE; +} + +JSFunctionSpec gDOMWorkerFunctions[] = { + { "dump", nsDOMWorkerFunctions::Dump, 1, 0, 0 }, + { "setTimeout", nsDOMWorkerFunctions::SetTimeout, 1, 0, 0 }, + { "clearTimeout", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 }, + { "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0, 0 }, + { "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 }, + { "importScripts", nsDOMWorkerFunctions::LoadScripts, 1, 0, 0 }, + { "XMLHttpRequest", nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0, 0 }, + { "Worker", nsDOMWorkerFunctions::NewWorker, 1, 0, 0 }, +#ifdef MOZ_SHARK + { "startShark", js_StartShark, 0, 0, 0 }, + { "stopShark", js_StopShark, 0, 0, 0 }, + { "connectShark", js_ConnectShark, 0, 0, 0 }, + { "disconnectShark", js_DisconnectShark, 0, 0, 0 }, +#endif + { nsnull, nsnull, 0, 0, 0 } +}; + +class nsDOMWorkerScope : public nsIWorkerScope, + public nsIDOMEventTarget, + public nsIXPCScriptable, + public nsIClassInfo +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWORKERGLOBALSCOPE + NS_DECL_NSIWORKERSCOPE + NS_DECL_NSIDOMEVENTTARGET + NS_DECL_NSIXPCSCRIPTABLE + NS_DECL_NSICLASSINFO + + nsDOMWorkerScope(nsDOMWorker* aWorker) + : mWorker(aWorker) { + NS_ASSERTION(aWorker, "Null pointer!"); + } + +private: + nsDOMWorker* mWorker; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS5(nsDOMWorkerScope, nsIWorkerScope, + nsIWorkerGlobalScope, + nsIDOMEventTarget, + nsIXPCScriptable, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER4(nsDOMWorkerScope, nsIWorkerScope, + nsIWorkerGlobalScope, + nsIDOMEventTarget, + nsIXPCScriptable) + +NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerScope) +NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerScope) + +// Need to return a scriptable helper so that XPConnect can get our +// nsIXPCScriptable flags properly (to not enumerate QI, for instance). +NS_IMETHODIMP +nsDOMWorkerScope::GetHelperForLanguage(PRUint32 aLanguage, + nsISupports** _retval) +{ + if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) { + NS_ADDREF(*_retval = NS_ISUPPORTS_CAST(nsIWorkerScope*, this)); + } + else { + *_retval = nsnull; + } + return NS_OK; +} + +// Use the xpc_map_end.h macros to generate the nsIXPCScriptable methods we want +// for the scope. + +#define XPC_MAP_CLASSNAME nsDOMWorkerScope +#define XPC_MAP_QUOTED_CLASSNAME "DedicatedWorkerGlobalScope" + +#define XPC_MAP_FLAGS \ + nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \ + nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \ + nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY | \ + nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \ + nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY | \ + nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES + +#include "xpc_map_end.h" + +NS_IMETHODIMP +nsDOMWorkerScope::GetSelf(nsIWorkerGlobalScope** aSelf) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aSelf); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + NS_ADDREF(*aSelf = this); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerScope::PostMessage(const nsAString& aMessage, + nsIWorkerMessagePort* aMessagePort) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + if (aMessagePort) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + return mWorker->PostMessageInternal(aMessage, PR_FALSE); +} + +NS_IMETHODIMP +nsDOMWorkerScope::GetOnmessage(nsIDOMEventListener** aOnmessage) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aOnmessage); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + nsCOMPtr listener = + mWorker->mInnerHandler->GetOnXListener(NS_LITERAL_STRING("message")); + listener.forget(aOnmessage); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerScope::SetOnmessage(nsIDOMEventListener* aOnmessage) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + return mWorker->mInnerHandler->SetOnXListener(NS_LITERAL_STRING("message"), + aOnmessage); +} + +NS_IMETHODIMP +nsDOMWorkerScope::AddEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + PRBool aUseCapture) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + return mWorker->mInnerHandler->AddEventListener(aType, aListener, + aUseCapture); +} + +NS_IMETHODIMP +nsDOMWorkerScope::RemoveEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + PRBool aUseCapture) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + return mWorker->mInnerHandler->RemoveEventListener(aType, aListener, + aUseCapture); +} + +NS_IMETHODIMP +nsDOMWorkerScope::DispatchEvent(nsIDOMEvent* aEvent, + PRBool* _retval) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + return mWorker->mInnerHandler->DispatchEvent(aEvent, _retval); +} + +class nsWorkerHoldingRunnable : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + + nsWorkerHoldingRunnable(nsDOMWorker* aWorker) + : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()) { } + + NS_IMETHOD Run() { + return NS_OK; + } + +protected: + virtual ~nsWorkerHoldingRunnable() { } + + nsRefPtr mWorker; + +private: + nsCOMPtr mWorkerWN; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsWorkerHoldingRunnable, nsIRunnable) + +class nsDOMFireEventRunnable : public nsWorkerHoldingRunnable +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + nsDOMFireEventRunnable(nsDOMWorker* aWorker, + nsDOMWorkerEvent* aEvent, + PRBool aToInner) + : nsWorkerHoldingRunnable(aWorker), mEvent(aEvent), mToInner(aToInner) + { + NS_ASSERTION(aWorker && aEvent, "Null pointer!"); + } + + NS_IMETHOD Run() { +#ifdef DEBUG + if (NS_IsMainThread()) { + NS_ASSERTION(!mToInner, "Should only run outer events on main thread!"); + NS_ASSERTION(!mWorker->mParent, "Worker shouldn't have a parent!"); + } + else { + JSContext* cx = nsDOMThreadService::GetCurrentContext(); + nsDOMWorker* currentWorker = (nsDOMWorker*)JS_GetContextPrivate(cx); + NS_ASSERTION(currentWorker, "Must have a worker here!"); + + nsDOMWorker* targetWorker = mToInner ? mWorker.get() : mWorker->mParent; + NS_ASSERTION(currentWorker == targetWorker, "Wrong worker!"); + } +#endif + if (mWorker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + nsCOMPtr target = mToInner ? + static_cast(mWorker->GetInnerScope()) : + static_cast(mWorker); + + NS_ASSERTION(target, "Null target!"); + NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); + + mEvent->SetTarget(target); + return target->DispatchEvent(mEvent, nsnull); + } + +protected: + nsRefPtr mEvent; + PRBool mToInner; +}; + +NS_IMPL_ISUPPORTS_INHERITED0(nsDOMFireEventRunnable, nsWorkerHoldingRunnable) + +// Standard NS_IMPL_THREADSAFE_ADDREF without the logging stuff (since this +// class is made to be inherited anyway). +NS_IMETHODIMP_(nsrefcnt) +nsDOMWorkerFeature::AddRef() +{ + NS_ASSERTION(mRefCnt >= 0, "Illegal refcnt!"); + return PR_AtomicIncrement((PRInt32*)&mRefCnt); +} + +// Custom NS_IMPL_THREADSAFE_RELEASE. Checks the mFreeToDie flag before calling +// delete. If the flag is false then the feature still lives in the worker's +// list and must be removed. We rely on the fact that the RemoveFeature method +// calls AddRef and Release after setting the mFreeToDie flag so we won't leak. +NS_IMETHODIMP_(nsrefcnt) +nsDOMWorkerFeature::Release() +{ + NS_ASSERTION(mRefCnt, "Double release!"); + nsrefcnt count = PR_AtomicDecrement((PRInt32*)&mRefCnt); + if (count == 0) { + if (mFreeToDie) { + mRefCnt = 1; + delete this; + } + else { + mWorker->RemoveFeature(this, nsnull); + } + } + return count; +} + +NS_IMPL_QUERY_INTERFACE0(nsDOMWorkerFeature) + +class nsDOMWorkerClassInfo : public nsIClassInfo +{ +public: + NS_DECL_NSICLASSINFO + + NS_IMETHOD_(nsrefcnt) AddRef() { + return 2; + } + + NS_IMETHOD_(nsrefcnt) Release() { + return 1; + } + + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr); +}; + +NS_IMPL_QUERY_INTERFACE1(nsDOMWorkerClassInfo, nsIClassInfo) + +// Keep this list in sync with the list in nsDOMClassInfo.cpp! +NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerClassInfo, nsIWorker, + nsIAbstractWorker, + nsIDOMEventTarget) + +NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerClassInfo) + +static nsDOMWorkerClassInfo sDOMWorkerClassInfo; + +nsDOMWorker::nsDOMWorker(nsDOMWorker* aParent, + nsIXPConnectWrappedNative* aParentWN) +: mParent(aParent), + mParentWN(aParentWN), + mCallbackCount(0), + mLock(nsnull), + mInnerScope(nsnull), + mGlobal(NULL), + mNextTimeoutId(0), + mFeatureSuspendDepth(0), + mWrappedNative(nsnull), + mCanceled(PR_FALSE), + mSuspended(PR_FALSE), + mCompileAttempted(PR_FALSE) +{ +#ifdef DEBUG + PRBool mainThread = NS_IsMainThread(); + NS_ASSERTION(aParent ? !mainThread : mainThread, "Wrong thread!"); +#endif +} + +nsDOMWorker::~nsDOMWorker() +{ + if (mPool) { + mPool->NoteDyingWorker(this); + } + + if (mLock) { + nsAutoLock::DestroyLock(mLock); + } + + NS_ASSERTION(!mFeatures.Length(), "Live features!"); +} + +/* static */ nsresult +nsDOMWorker::NewWorker(nsISupports** aNewObject) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsCOMPtr newWorker = + NS_ISUPPORTS_CAST(nsIWorker*, new nsDOMWorker(nsnull, nsnull)); + NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY); + + newWorker.forget(aNewObject); + return NS_OK; +} + +NS_IMPL_THREADSAFE_ADDREF(nsDOMWorker) +NS_IMPL_THREADSAFE_RELEASE(nsDOMWorker) + +NS_INTERFACE_MAP_BEGIN(nsDOMWorker) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWorker) + NS_INTERFACE_MAP_ENTRY(nsIWorker) + NS_INTERFACE_MAP_ENTRY(nsIAbstractWorker) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) + NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) + NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer) + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { + foundInterface = static_cast(&sDOMWorkerClassInfo); + } else +NS_INTERFACE_MAP_END + +// Use the xpc_map_end.h macros to generate the nsIXPCScriptable methods we want +// for the worker. + +#define XPC_MAP_CLASSNAME nsDOMWorker +#define XPC_MAP_QUOTED_CLASSNAME "Worker" +#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 +nsDOMWorker::PostCreate(nsIXPConnectWrappedNative* aWrapper, + JSContext* /* aCx */, + JSObject* /* aObj */) +{ + mWrappedNative = aWrapper; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorker::Trace(nsIXPConnectWrappedNative* /* aWrapper */, + JSTracer* aTracer, + JSObject* /*aObj */) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!IsCanceled()) { + if (mGlobal) { + JS_SET_TRACING_DETAILS(aTracer, nsnull, this, 0); + JS_CallTracer(aTracer, mGlobal, JSTRACE_OBJECT); + } + // We should never get null handlers here if our call to Initialize succeeded. + NS_ASSERTION(mInnerHandler && mOuterHandler, "Shouldn't be possible!"); + + mInnerHandler->Trace(aTracer); + mOuterHandler->Trace(aTracer); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorker::Finalize(nsIXPConnectWrappedNative* /* aWrapper */, + JSContext* aCx, + JSObject* /* aObj */) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // Don't leave dangling JSObject pointers in our handlers! + mInnerHandler->ClearAllListeners(); + mOuterHandler->ClearAllListeners(); + + // Clear our wrapped native now that it has died. + mWrappedNative = nsnull; + + // We no longer need to keep our inner scope. + mGlobal = NULL; + mInnerScope = nsnull; + + // And we can let our parent die now too. + mParent = nsnull; + mParentWN = nsnull; + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorker::Initialize(nsISupports* aOwner, + JSContext* aCx, + JSObject* aObj, + PRUint32 aArgc, + jsval* aArgv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aOwner); + + nsCOMPtr globalObj(do_QueryInterface(aOwner)); + NS_ENSURE_TRUE(globalObj, NS_NOINTERFACE); + + return InitializeInternal(globalObj, aCx, aObj, aArgc, aArgv); +} + +nsresult +nsDOMWorker::InitializeInternal(nsIScriptGlobalObject* aOwner, + JSContext* aCx, + JSObject* aObj, + PRUint32 aArgc, + jsval* aArgv) +{ + NS_ENSURE_TRUE(aArgc, NS_ERROR_INVALID_ARG); + NS_ENSURE_ARG_POINTER(aArgv); + NS_ENSURE_TRUE(JSVAL_IS_STRING(aArgv[0]), NS_ERROR_INVALID_ARG); + + JSString* str = JS_ValueToString(aCx, aArgv[0]); + NS_ENSURE_STATE(str); + + mScriptURL.Assign(nsDependentJSString(str)); + NS_ENSURE_FALSE(mScriptURL.IsEmpty(), NS_ERROR_INVALID_ARG); + + mLock = nsAutoLock::NewLock("nsDOMWorker::mLock"); + NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY); + + mInnerHandler = new nsDOMWorkerMessageHandler(); + NS_ENSURE_TRUE(mInnerHandler, NS_ERROR_OUT_OF_MEMORY); + + mOuterHandler = new nsDOMWorkerMessageHandler(); + NS_ENSURE_TRUE(mOuterHandler, NS_ERROR_OUT_OF_MEMORY); + + NS_ASSERTION(!mGlobal, "Already got a global?!"); + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + nsCOMPtr thisWrapped; + nsresult rv = xpc->WrapNative(aCx, aObj, static_cast(this), + NS_GET_IID(nsISupports), + getter_AddRefs(thisWrapped)); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(mWrappedNative, "Post-create hook should have set this!"); + + // This is pretty cool - all we have to do to get our script executed is to + // pass a no-op runnable to the thread service and it will make sure we have + // a context and global object. + nsCOMPtr runnable(new nsWorkerHoldingRunnable(this)); + NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); + + nsRefPtr threadService = + nsDOMThreadService::GetOrInitService(); + NS_ENSURE_STATE(threadService); + + rv = threadService->RegisterWorker(this, aOwner); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(mPool, "RegisterWorker should have set our pool!"); + + rv = threadService->Dispatch(this, runnable); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +nsDOMWorker::Cancel() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + mCanceled = PR_TRUE; + + CancelFeatures(); +} + +void +nsDOMWorker::Suspend() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mSuspended, "Suspended more than once!"); + mSuspended = PR_TRUE; + + SuspendFeatures(); +} + +void +nsDOMWorker::Resume() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mSuspended, "Not suspended!"); + mSuspended = PR_FALSE; + + ResumeFeatures(); +} + +nsresult +nsDOMWorker::PostMessageInternal(const nsAString& aMessage, + PRBool aToInner) +{ + nsRefPtr message = new nsDOMWorkerMessageEvent(); + NS_ENSURE_TRUE(message, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"), + PR_FALSE, PR_FALSE, aMessage, + EmptyString(), nsnull); + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr runnable = + new nsDOMFireEventRunnable(this, message, aToInner); + NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); + + // If aToInner is true then we want to target the runnable at this worker's + // thread. Otherwise we need to target the parent's thread. + nsDOMWorker* target = aToInner ? this : mParent; + + // If this is a top-level worker then target the main thread. Otherwise use + // the thread service to find the target's thread. + if (!target) { + nsCOMPtr mainThread; + rv = NS_GetMainThread(getter_AddRefs(mainThread)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + rv = nsDOMThreadService::get()->Dispatch(target, runnable); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +PRBool +nsDOMWorker::SetGlobalForContext(JSContext* aCx) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (!CompileGlobalObject(aCx)) { + return PR_FALSE; + } + + JS_SetGlobalObject(aCx, mGlobal); + return PR_TRUE; +} + +PRBool +nsDOMWorker::CompileGlobalObject(JSContext* aCx) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mGlobal) { + return PR_TRUE; + } + + if (mCompileAttempted) { + // Don't try to recompile a bad script. + return PR_FALSE; + } + mCompileAttempted = PR_TRUE; + + NS_ASSERTION(!mScriptURL.IsEmpty(), "Must have a url here!"); + + JSAutoRequest ar(aCx); + + NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!"); + + nsRefPtr scope = new nsDOMWorkerScope(this); + NS_ENSURE_TRUE(scope, NS_ERROR_OUT_OF_MEMORY); + + nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIWorkerScope*, scope); + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + nsCOMPtr globalWrapper; + nsresult rv = + xpc->InitClassesWithNewWrappedGlobal(aCx, scopeSupports, + NS_GET_IID(nsISupports), + nsIXPConnect::INIT_JS_STANDARD_CLASSES, + getter_AddRefs(globalWrapper)); + NS_ENSURE_SUCCESS(rv, PR_FALSE); + + JSObject* global; + rv = globalWrapper->GetJSObject(&global); + NS_ENSURE_SUCCESS(rv, PR_FALSE); + + NS_ASSERTION(JS_GetGlobalObject(aCx) == global, "Global object mismatch!"); + + // XXX Fix this! + PRBool success = JS_DeleteProperty(aCx, global, "Components"); + NS_ENSURE_TRUE(success, PR_FALSE); + + // Set up worker thread functions + success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions); + NS_ENSURE_TRUE(success, PR_FALSE); + + // From here on out we have to remember to null mGlobal and mInnerScope if + // something fails! + mGlobal = global; + mInnerScope = scope; + + nsRefPtr loader = + new nsDOMWorkerScriptLoader(this); + NS_ASSERTION(loader, "Out of memory!"); + if (!loader) { + mGlobal = NULL; + mInnerScope = nsnull; + return PR_FALSE; + } + + rv = AddFeature(loader, aCx); + if (NS_FAILED(rv)) { + mGlobal = NULL; + mInnerScope = nsnull; + return PR_FALSE; + } + + rv = loader->LoadScript(aCx, mScriptURL); + + JS_ReportPendingException(aCx); + + if (NS_FAILED(rv)) { + mGlobal = NULL; + mInnerScope = nsnull; + return PR_FALSE; + } + + return PR_TRUE; +} + +void +nsDOMWorker::SetPool(nsDOMWorkerPool* aPool) +{ + NS_ASSERTION(!mPool, "Shouldn't ever set pool more than once!"); + mPool = aPool; +} + +already_AddRefed +nsDOMWorker::GetWrappedNative() +{ + nsCOMPtr wrappedNative = mWrappedNative; + NS_ASSERTION(wrappedNative, "Null wrapped native!"); + return wrappedNative.forget(); +} + +nsresult +nsDOMWorker::AddFeature(nsDOMWorkerFeature* aFeature, + JSContext* aCx) +{ + NS_ASSERTION(aFeature, "Null pointer!"); + + PRBool shouldSuspend; + + { + // aCx may be null. + JSAutoSuspendRequest asr(aCx); + + nsAutoLock lock(mLock); + + nsDOMWorkerFeature** newFeature = mFeatures.AppendElement(aFeature); + NS_ENSURE_TRUE(newFeature, NS_ERROR_OUT_OF_MEMORY); + + aFeature->FreeToDie(PR_FALSE); + shouldSuspend = mFeatureSuspendDepth > 0; + } + + if (shouldSuspend) { + aFeature->Suspend(); + } + + return NS_OK; +} + +void +nsDOMWorker::RemoveFeature(nsDOMWorkerFeature* aFeature, + JSContext* aCx) +{ + NS_ASSERTION(aFeature, "Null pointer!"); + + // This *must* be a nsRefPtr so that we call Release after setting FreeToDie. + nsRefPtr feature(aFeature); + { + // aCx may be null. + JSAutoSuspendRequest asr(aCx); + + nsAutoLock lock(mLock); + +#ifdef DEBUG + PRBool removed = +#endif + mFeatures.RemoveElement(aFeature); + NS_ASSERTION(removed, "Feature not in the list!"); + + aFeature->FreeToDie(PR_TRUE); + } +} + +void +nsDOMWorker::CancelTimeoutWithId(PRUint32 aId) +{ + nsRefPtr foundFeature; + { + nsAutoLock lock(mLock); + PRUint32 count = mFeatures.Length(); + for (PRUint32 index = 0; index < count; index++) { + nsDOMWorkerFeature*& feature = mFeatures[index]; + if (feature->HasId() && feature->GetId() == aId) { + foundFeature = feature; + feature->FreeToDie(PR_TRUE); + mFeatures.RemoveElementAt(index); + break; + } + } + } + + if (foundFeature) { + foundFeature->Cancel(); + } +} + +void +nsDOMWorker::SuspendFeatures() +{ + nsAutoTArray, 20> features; + { + nsAutoLock lock(mLock); + + // We don't really have to worry about overflow here because the only way + // to do this is through recursive script loading, which uses the stack. We + // would exceed our stack limit long before this counter. + NS_ASSERTION(mFeatureSuspendDepth < PR_UINT32_MAX, "Shouldn't happen!"); + if (++mFeatureSuspendDepth != 1) { + // Allow nested suspending of timeouts. + return; + } + +#ifdef DEBUG + nsRefPtr* newFeatures = +#endif + features.AppendElements(mFeatures); + NS_WARN_IF_FALSE(newFeatures, "Out of memory!"); + } + + PRUint32 count = features.Length(); + for (PRUint32 i = 0; i < count; i++) { + features[i]->Suspend(); + } +} + +void +nsDOMWorker::ResumeFeatures() +{ + nsAutoTArray, 20> features; + { + nsAutoLock lock(mLock); + + NS_ASSERTION(mFeatureSuspendDepth > 0, "Shouldn't happen!"); + if (--mFeatureSuspendDepth != 0) { + return; + } + + features.AppendElements(mFeatures); + } + + PRUint32 count = features.Length(); + for (PRUint32 i = 0; i < count; i++) { + features[i]->Resume(); + } +} + +void +nsDOMWorker::CancelFeatures() +{ + NS_ASSERTION(IsCanceled(), "More items can still be added!"); + + PRUint32 count, index; + + nsAutoTArray, 20> features; + { + nsAutoLock lock(mLock); + + count = mFeatures.Length(); + for (index = 0; index < count; index++) { + nsDOMWorkerFeature*& feature = mFeatures[index]; + +#ifdef DEBUG + nsRefPtr* newFeature = +#endif + features.AppendElement(feature); + NS_ASSERTION(newFeature, "Out of memory!"); + + feature->FreeToDie(PR_TRUE); + } + + mFeatures.Clear(); + } + + count = features.Length(); + for (index = 0; index < count; index++) { + features[index]->Cancel(); + } +} + +already_AddRefed +nsDOMWorker::GetParent() +{ + nsRefPtr parent(mParent); + return parent.forget(); +} + +/** + * See nsIWorker + */ +NS_IMETHODIMP +nsDOMWorker::PostMessage(const nsAString& aMessage, + nsIWorkerMessagePort* aMessagePort) +{ + if (aMessagePort) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + return PostMessageInternal(aMessage, PR_TRUE); +} + +/** + * See nsIWorker + */ +NS_IMETHODIMP +nsDOMWorker::GetOnerror(nsIDOMEventListener** aOnerror) +{ + NS_ENSURE_ARG_POINTER(aOnerror); + + nsCOMPtr listener = + mOuterHandler->GetOnXListener(NS_LITERAL_STRING("error")); + + listener.forget(aOnerror); + return NS_OK; +} + +/** + * See nsIWorker + */ +NS_IMETHODIMP +nsDOMWorker::SetOnerror(nsIDOMEventListener* aOnerror) +{ + return mOuterHandler->SetOnXListener(NS_LITERAL_STRING("error"), aOnerror); +} + +/** + * See nsIWorker + */ +NS_IMETHODIMP +nsDOMWorker::GetOnmessage(nsIDOMEventListener** aOnmessage) +{ + NS_ENSURE_ARG_POINTER(aOnmessage); + + nsCOMPtr listener = + mOuterHandler->GetOnXListener(NS_LITERAL_STRING("message")); + + listener.forget(aOnmessage); + return NS_OK; +} + +/** + * See nsIWorker + */ +NS_IMETHODIMP +nsDOMWorker::SetOnmessage(nsIDOMEventListener* aOnmessage) +{ + return mOuterHandler->SetOnXListener(NS_LITERAL_STRING("message"), + aOnmessage); +} diff --git a/dom/src/threads/nsDOMWorker.h b/dom/src/threads/nsDOMWorker.h new file mode 100644 index 000000000000..c170a398b6cd --- /dev/null +++ b/dom/src/threads/nsDOMWorker.h @@ -0,0 +1,254 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __NSDOMWORKER_H__ +#define __NSDOMWORKER_H__ + +#include "nsIDOMEventTarget.h" +#include "nsIDOMWorkers.h" +#include "nsIJSNativeInitializer.h" +#include "nsIXPCScriptable.h" + +#include "jsapi.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsTPtrArray.h" +#include "prlock.h" + +#include "nsDOMWorkerMessageHandler.h" + +class nsDOMWorkerMessageHandler; +class nsDOMWorkerPool; +class nsDOMWorkerScope; +class nsDOMWorkerTimeout; +class nsICancelable; +class nsIDOMEventListener; +class nsIEventTarget; +class nsIScriptGlobalObject; +class nsIXPConnectWrappedNative; + +class nsDOMWorkerFeature; + +class nsDOMWorker : public nsIWorker, + public nsIJSNativeInitializer, + public nsIXPCScriptable +{ + friend class nsDOMWorkerFeature; + friend class nsDOMWorkerFunctions; + friend class nsDOMWorkerRefPtr; + friend class nsDOMWorkerScope; + friend class nsDOMWorkerScriptLoader; + friend class nsDOMWorkerTimeout; + friend class nsDOMWorkerXHR; + + friend JSBool DOMWorkerOperationCallback(JSContext* aCx); + friend void DOMWorkerErrorReporter(JSContext* aCx, + const char* aMessage, + JSErrorReport* aReport); + +#ifdef DEBUG + // For fun assertions. + friend class nsDOMFireEventRunnable; +#endif + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIABSTRACTWORKER + NS_DECL_NSIWORKER + NS_FORWARD_SAFE_NSIDOMEVENTTARGET(mOuterHandler) + NS_DECL_NSIXPCSCRIPTABLE + + static nsresult NewWorker(nsISupports** aNewObject); + + nsDOMWorker(nsDOMWorker* aParent, + nsIXPConnectWrappedNative* aParentWN); + + NS_IMETHOD Initialize(nsISupports* aOwner, + JSContext* aCx, + JSObject* aObj, + PRUint32 aArgc, + jsval* aArgv); + + nsresult InitializeInternal(nsIScriptGlobalObject* aOwner, + JSContext* aCx, + JSObject* aObj, + PRUint32 aArgc, + jsval* aArgv); + + void Cancel(); + void Suspend(); + void Resume(); + + PRBool IsCanceled() { + return mCanceled; + } + + PRBool IsSuspended() { + return mSuspended; + } + + PRBool SetGlobalForContext(JSContext* aCx); + + void SetPool(nsDOMWorkerPool* aPool); + + nsDOMWorkerPool* Pool() { + return mPool; + } + + PRLock* Lock() { + return mLock; + } + + already_AddRefed GetWrappedNative(); + already_AddRefed GetParent(); + + nsDOMWorkerScope* GetInnerScope() { + return mInnerScope; + } + +private: + ~nsDOMWorker(); + + nsresult PostMessageInternal(const nsAString& aMessage, + PRBool aToInner); + + PRBool CompileGlobalObject(JSContext* aCx); + + PRUint32 NextTimeoutId() { + return mNextTimeoutId++; + } + + nsresult AddFeature(nsDOMWorkerFeature* aFeature, + JSContext* aCx); + void RemoveFeature(nsDOMWorkerFeature* aFeature, + JSContext* aCx); + void CancelTimeoutWithId(PRUint32 aId); + void SuspendFeatures(); + void ResumeFeatures(); + void CancelFeatures(); + +private: + + // mParent will live as long as mParentWN but only mParentWN will keep the JS + // reflection alive, so we only hold one strong reference to mParentWN. + nsDOMWorker* mParent; + nsCOMPtr mParentWN; + + PRUint32 mCallbackCount; + + PRLock* mLock; + + nsRefPtr mInnerHandler; + nsRefPtr mOuterHandler; + + nsRefPtr mPool; + + nsDOMWorkerScope* mInnerScope; + JSObject* mGlobal; + + PRUint32 mNextTimeoutId; + + nsTArray mFeatures; + PRUint32 mFeatureSuspendDepth; + + nsString mScriptURL; + + nsIXPConnectWrappedNative* mWrappedNative; + + PRPackedBool mCanceled; + PRPackedBool mSuspended; + PRPackedBool mCompileAttempted; +}; + +/** + * A worker "feature" holds the worker alive yet can be canceled, paused, and + * resumed by the worker. It is up to each derived class to implement these + * methods. This class uses a custom implementation of Release in order to + * ensure no races between Cancel and object destruction can occur, so derived + * classes must use the ISUPPORTS_INHERITED macros. + * + * To use this class you should inherit it and use the ISUPPORTS_INHERITED + * macros. Then add or remove an instance to the worker using the + * AddFeature/RemoveFeature functions. + */ +class nsDOMWorkerFeature : public nsISupports +{ + friend class nsDOMWorker; + +public: + NS_DECL_ISUPPORTS + + nsDOMWorkerFeature(nsDOMWorker* aWorker) + : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mId(0), + mHasId(PR_FALSE), mFreeToDie(PR_TRUE) { } + + nsDOMWorkerFeature(nsDOMWorker* aWorker, PRUint32 aId) + : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mId(aId), + mHasId(PR_TRUE), mFreeToDie(PR_TRUE) { } + + virtual void Cancel() = 0; + virtual void Suspend() { } + virtual void Resume() { } + + PRUint32 GetId() { + return mId; + } + + PRBool HasId() { + return mHasId; + } + +protected: + virtual ~nsDOMWorkerFeature() { } + +private: + void FreeToDie(PRBool aFreeToDie) { + mFreeToDie = aFreeToDie; + } + +protected: + nsRefPtr mWorker; + nsCOMPtr mWorkerWN; + PRUint32 mId; + +private: + PRPackedBool mHasId; + PRPackedBool mFreeToDie; +}; + +#endif /* __NSDOMWORKER_H__ */ diff --git a/dom/src/threads/nsDOMWorkerEvents.cpp b/dom/src/threads/nsDOMWorkerEvents.cpp new file mode 100644 index 000000000000..74b59e7540fc --- /dev/null +++ b/dom/src/threads/nsDOMWorkerEvents.cpp @@ -0,0 +1,422 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsDOMWorkerEvents.h" + +#include "nsIXMLHttpRequest.h" + +#include "nsThreadUtils.h" + +#include "nsDOMWorkerMessageHandler.h" +#include "nsDOMWorkerXHR.h" +#include "nsDOMWorkerXHRProxy.h" + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMWorkerPrivateEvent, + NS_IDOMWORKERPRIVATEEVENT_IID) + +nsDOMWorkerPrivateEvent::nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent) +: mEvent(aEvent), + mPreventDefaultCalled(PR_FALSE) +{ + NS_ASSERTION(aEvent, "Null pointer!"); +} + +NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMWorkerPrivateEvent, nsIDOMEvent, + nsIDOMWorkerPrivateEvent, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerPrivateEvent, nsIDOMEvent, + nsIDOMWorkerPrivateEvent) + +NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPrivateEvent) + +NS_IMETHODIMP +nsDOMWorkerPrivateEvent::PreventDefault() +{ + mPreventDefaultCalled = PR_TRUE; + return mEvent->PreventDefault(); +} + +NS_IMETHODIMP +nsDOMWorkerPrivateEvent::InitEvent(const nsAString& aEventType, + PRBool aCanBubble, + PRBool aCancelable) +{ + mPreventDefaultCalled = PR_FALSE; + return mEvent->InitEvent(aEventType, aCanBubble, aCancelable); +} + +PRBool +nsDOMWorkerPrivateEvent::PreventDefaultCalled() +{ + return mPreventDefaultCalled; +} + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerEvent, nsIDOMEvent, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerEvent, nsIDOMEvent) + +NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerEvent) + +NS_IMETHODIMP +nsDOMWorkerEvent::GetType(nsAString& aType) +{ + aType.Assign(mType); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::GetTarget(nsIDOMEventTarget** aTarget) +{ + NS_ENSURE_ARG_POINTER(aTarget); + NS_IF_ADDREF(*aTarget = mTarget); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) +{ + NS_ENSURE_ARG_POINTER(aCurrentTarget); + NS_IF_ADDREF(*aCurrentTarget = mTarget); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::GetEventPhase(PRUint16* aEventPhase) +{ + NS_ENSURE_ARG_POINTER(aEventPhase); + *aEventPhase = mEventPhase; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::GetBubbles(PRBool* aBubbles) +{ + NS_ENSURE_ARG_POINTER(aBubbles); + *aBubbles = mBubbles; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::GetCancelable(PRBool* aCancelable) +{ + NS_ENSURE_ARG_POINTER(aCancelable); + *aCancelable = mCancelable; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::GetTimeStamp(DOMTimeStamp* aTimeStamp) +{ + NS_ENSURE_ARG_POINTER(aTimeStamp); + *aTimeStamp = mTimeStamp; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::StopPropagation() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::PreventDefault() +{ + mPreventDefaultCalled = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerEvent::InitEvent(const nsAString& aEventTypeArg, + PRBool aCanBubbleArg, + PRBool aCancelableArg) +{ + NS_ENSURE_FALSE(aEventTypeArg.IsEmpty(), NS_ERROR_INVALID_ARG); + + mType.Assign(aEventTypeArg); + mBubbles = aCanBubbleArg; + mCancelable = aCancelableArg; + mPreventDefaultCalled = PR_FALSE; + mTimeStamp = PR_Now(); + return NS_OK; +} + +NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerMessageEvent, nsDOMWorkerEvent, + nsIWorkerMessageEvent) + +NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerMessageEvent, nsIDOMEvent, + nsIWorkerMessageEvent) + +NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerMessageEvent) + +NS_IMETHODIMP +nsDOMWorkerMessageEvent::GetData(nsAString& aData) +{ + aData.Assign(mData); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerMessageEvent::GetOrigin(nsAString& aOrigin) +{ + aOrigin.Assign(mOrigin); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerMessageEvent::GetSource(nsISupports** aSource) +{ + NS_ENSURE_ARG_POINTER(aSource); + NS_IF_ADDREF(*aSource = mSource); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerMessageEvent::InitMessageEvent(const nsAString& aTypeArg, + PRBool aCanBubbleArg, + PRBool aCancelableArg, + const nsAString& aDataArg, + const nsAString& aOriginArg, + nsISupports* aSourceArg) +{ + mData.Assign(aDataArg); + mOrigin.Assign(aOriginArg); + mSource = aSourceArg; + return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); +} + +nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy) +: mXHRProxy(aXHRProxy), + mXHREventType(PR_UINT32_MAX), + mStatus(NS_OK), + mReadyState(0), + mLoaded(0), + mTotal(0), + mChannelID(-1), + mUploadEvent(PR_FALSE), + mProgressEvent(PR_FALSE), + mLengthComputable(PR_FALSE) +{ + NS_ASSERTION(aXHRProxy, "Can't be null!"); +} + +NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerEvent) +NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerEvent) + +NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent) + NS_INTERFACE_MAP_ENTRY(nsIRunnable) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent) +NS_INTERFACE_MAP_END_INHERITING(nsDOMWorkerEvent) + +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; +} + +nsresult +nsDOMWorkerXHREvent::Init(PRUint32 aXHREventType, + const nsAString& aType, + nsIDOMEvent* aEvent) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aEvent, "Don't pass null here!"); + + mXHREventType = aXHREventType; + mChannelID = mXHRProxy->ChannelID(); + + mTarget = static_cast(mXHRProxy->mWorkerXHR); + NS_ENSURE_TRUE(mTarget, NS_ERROR_UNEXPECTED); + + mWorkerWN = mXHRProxy->mWorkerXHR->mWorker->GetWrappedNative(); + NS_ENSURE_STATE(mWorkerWN); + + nsCOMPtr mainThreadTarget; + nsresult rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(mainThreadTarget); + + nsCOMPtr upload(do_QueryInterface(mainThreadTarget)); + if (upload) { + mUploadEvent = PR_TRUE; + mTarget = + static_cast(mXHRProxy->mWorkerXHR->mUpload); + } + else { + mUploadEvent = PR_FALSE; + mTarget = static_cast(mXHRProxy->mWorkerXHR); + } + NS_ASSERTION(mTarget, "Null target!"); + + PRBool bubbles; + rv = aEvent->GetBubbles(&bubbles); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool cancelable; + rv = aEvent->GetCancelable(&cancelable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aEvent->GetTimeStamp(&mTimeStamp); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aEvent->GetEventPhase(&mEventPhase); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(mEventPhase == nsIDOMEvent::AT_TARGET, "Unsupported phase!"); + + nsCOMPtr progressEvent(do_QueryInterface(aEvent)); + if (progressEvent) { + mProgressEvent = PR_TRUE; + + PRBool lengthComputable; + rv = progressEvent->GetLengthComputable(&lengthComputable); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint64 loaded; + rv = progressEvent->GetLoaded(&loaded); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint64 total; + rv = progressEvent->GetTotal(&total); + NS_ENSURE_SUCCESS(rv, rv); + + rv = InitProgressEvent(aType, bubbles, cancelable, lengthComputable, loaded, + total); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + mProgressEvent = PR_FALSE; + + rv = nsDOMWorkerEvent::InitEvent(aType, bubbles, cancelable); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = SnapshotXHRState(mXHRProxy->mXHR); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +nsDOMWorkerXHREvent::SnapshotXHRState(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::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) +{ + mLengthComputable = aLengthComputableArg; + mLoaded = aLoadedArg; + mTotal = aTotalArg; + return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); +} diff --git a/dom/src/threads/nsDOMWorkerEvents.h b/dom/src/threads/nsDOMWorkerEvents.h new file mode 100644 index 000000000000..e2fdde5d176c --- /dev/null +++ b/dom/src/threads/nsDOMWorkerEvents.h @@ -0,0 +1,183 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __NSDOMWORKEREVENTS_H__ +#define __NSDOMWORKEREVENTS_H__ + +#include "nsIClassInfo.h" +#include "nsIDOMEvent.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMProgressEvent.h" +#include "nsIDOMWorkers.h" +#include "nsIRunnable.h" + +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsStringGlue.h" + +#include "nsDOMWorkerMacros.h" + +class nsDOMWorkerXHRProxy; +class nsIXMLHttpRequest; +class nsIXPConnectWrappedNative; + +/* 4d5794d6-98ab-4a6b-ad5a-8ed1fa1d4839 */ +#define NS_IDOMWORKERPRIVATEEVENT_IID \ +{ \ + 0x4d5794d6, \ + 0x98ab, \ + 0x4a6b, \ + { 0xad, 0x5a, 0x8e, 0xd1, 0xfa, 0x1d, 0x48, 0x39 } \ +} + +class nsIDOMWorkerPrivateEvent : public nsIDOMEvent +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMWORKERPRIVATEEVENT_IID) + virtual PRBool PreventDefaultCalled() = 0; +}; + +class nsDOMWorkerPrivateEvent : public nsIDOMWorkerPrivateEvent, + public nsIClassInfo +{ +public: + NS_DECL_ISUPPORTS + NS_FORWARD_NSIDOMEVENT_SPECIAL + NS_DECL_NSICLASSINFO + + nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent); + + NS_IMETHOD PreventDefault(); + + NS_IMETHOD InitEvent(const nsAString& aEventType, + PRBool aCanBubble, + PRBool aCancelable); + + virtual PRBool PreventDefaultCalled(); + +private: + nsCOMPtr mEvent; + PRBool mPreventDefaultCalled; +}; + +class nsDOMWorkerEvent : public nsIDOMEvent, + public nsIClassInfo +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENT + NS_DECL_NSICLASSINFO + + nsDOMWorkerEvent() + : mEventPhase(nsIDOMEvent::AT_TARGET), mTimeStamp(0), mBubbles(PR_FALSE), + mCancelable(PR_FALSE), mPreventDefaultCalled(PR_FALSE) { } + + void SetTarget(nsIDOMEventTarget* aTarget) { + mTarget = aTarget; + } + +protected: + virtual ~nsDOMWorkerEvent() { } + + nsString mType; + nsCOMPtr mTarget; + PRUint16 mEventPhase; + DOMTimeStamp mTimeStamp; + PRPackedBool mBubbles; + PRPackedBool mCancelable; + PRPackedBool mPreventDefaultCalled; +}; + +class nsDOMWorkerMessageEvent : public nsDOMWorkerEvent, + public nsIWorkerMessageEvent +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::) + NS_DECL_NSIWORKERMESSAGEEVENT + NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerEvent::) + NS_DECL_NSICLASSINFO_GETINTERFACES + +protected: + nsString mData; + nsString mOrigin; + nsCOMPtr mSource; +}; + +class nsDOMWorkerXHREvent : public nsDOMWorkerEvent, + public nsIRunnable, + public nsIDOMProgressEvent +{ + friend class nsDOMWorkerXHRProxy; + friend class nsDOMWorkerXHREventTargetProxy; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE + NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::) + NS_DECL_NSIDOMPROGRESSEVENT + NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerEvent::) + NS_DECL_NSICLASSINFO_GETINTERFACES + + nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy); + + nsresult Init(PRUint32 aXHREventType, + const nsAString& aType, + nsIDOMEvent* aEvent); + + nsresult SnapshotXHRState(nsIXMLHttpRequest* aXHR); + + void EventHandled(); + +protected: + nsRefPtr mXHRProxy; + nsCOMPtr mWorkerWN; + PRUint32 mXHREventType; + nsString mResponseText; + nsCString mStatusText; + nsresult mStatus; + PRInt32 mReadyState; + PRUint64 mLoaded; + PRUint64 mTotal; + PRInt32 mChannelID; + PRPackedBool mUploadEvent; + PRPackedBool mProgressEvent; + PRPackedBool mLengthComputable; +}; + +#endif /* __NSDOMWORKEREVENTS_H__ */ diff --git a/dom/src/threads/nsDOMWorkerThread.h b/dom/src/threads/nsDOMWorkerMacros.h similarity index 63% rename from dom/src/threads/nsDOMWorkerThread.h rename to dom/src/threads/nsDOMWorkerMacros.h index 61a4939f1f8e..3c042f1f197d 100644 --- a/dom/src/threads/nsDOMWorkerThread.h +++ b/dom/src/threads/nsDOMWorkerMacros.h @@ -12,7 +12,7 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is worker threads. + * The Original Code is Web Workers. * * The Initial Developer of the Original Code is * Mozilla Corporation. @@ -20,8 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Vladimir Vukicevic (Original Author) - * Ben Turner + * Ben Turner (Original Author) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -37,28 +36,10 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef __NSDOMWORKERTHREAD_H__ -#define __NSDOMWORKERTHREAD_H__ +#ifndef __NSDOMWORKERMACROS_H__ +#define __NSDOMWORKERMACROS_H__ -// Bases -#include "nsDOMWorkerBase.h" -#include "nsIClassInfo.h" -#include "nsIDOMThreads.h" - -// Other includes -#include "jsapi.h" -#include "nsAutoJSObjectHolder.h" -#include "nsCOMPtr.h" -#include "nsStringGlue.h" -#include "nsTArray.h" -#include "nsThreadUtils.h" -#include "prclist.h" -#include "prlock.h" - -// DOMWorker includes -#include "nsDOMThreadService.h" - -// Macro to generate nsIClassInfo methods for these threadsafe DOM classes +// Macro to generate nsIClassInfo methods for these threadsafe DOM classes #define NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(_class) \ NS_IMETHODIMP \ _class::GetInterfaces(PRUint32* _count, nsIID*** _array) \ @@ -66,14 +47,15 @@ _class::GetInterfaces(PRUint32* _count, nsIID*** _array) \ return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \ } \ -#define NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class) \ +#define NS_IMPL_THREADSAFE_DOM_CI_HELPER(_class) \ NS_IMETHODIMP \ _class::GetHelperForLanguage(PRUint32 _language, nsISupports** _retval) \ { \ *_retval = nsnull; \ return NS_OK; \ -} \ - \ +} + +#define NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class) \ NS_IMETHODIMP \ _class::GetContractID(char** _contractID) \ { \ @@ -117,96 +99,52 @@ _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \ #define NS_IMPL_THREADSAFE_DOM_CI(_class) \ NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(_class) \ +NS_IMPL_THREADSAFE_DOM_CI_HELPER(_class) \ NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class) -class nsDOMWorkerPool; -class nsDOMWorkerScriptLoader; -class nsDOMWorkerTimeout; -class nsDOMWorkerXHR; +#define NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(_to) \ + NS_IMETHOD GetHelperForLanguage(PRUint32 aLanguage, nsISupports** _retval) \ + { return _to GetHelperForLanguage(aLanguage, _retval); } \ + NS_IMETHOD GetContractID(char** aContractID) \ + { return _to GetContractID(aContractID); } \ + NS_IMETHOD GetClassDescription(char** aClassDescription) \ + { return _to GetClassDescription(aClassDescription); } \ + NS_IMETHOD GetClassID(nsCID** aClassID) \ + { return _to GetClassID(aClassID); } \ + NS_IMETHOD GetImplementationLanguage(PRUint32* aImplementationLanguage) \ + { return _to GetImplementationLanguage(aImplementationLanguage); } \ + NS_IMETHOD GetFlags(PRUint32* aFlags) \ + { return _to GetFlags(aFlags); } \ + NS_IMETHOD GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) \ + { return _to GetClassIDNoAlloc(aClassIDNoAlloc); } -class nsDOMWorkerThread : public nsDOMWorkerBase, - public nsIDOMWorkerThread, - public nsIClassInfo -{ - friend class nsDOMCreateJSContextRunnable; - friend class nsDOMWorkerFunctions; - friend class nsDOMWorkerPool; - friend class nsDOMWorkerRunnable; - friend class nsDOMWorkerScriptLoader; - friend class nsDOMWorkerTimeout; - friend class nsDOMWorkerXHR; +#define NS_DECL_NSICLASSINFO_GETINTERFACES \ + NS_IMETHOD GetInterfaces(PRUint32* aCount, nsIID*** aArray); - friend JSBool DOMWorkerOperationCallback(JSContext* aCx); +#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(); } -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMWORKERTHREAD - NS_DECL_NSICLASSINFO +// 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(); } - nsDOMWorkerThread(nsDOMWorkerPool* aPool, - const nsAString& aSource, - PRBool aSourceIsURL); - - virtual nsDOMWorkerPool* Pool() { - NS_ASSERTION(!IsCanceled(), "Don't touch Pool after we've been canceled!"); - return mPool; - } - -private: - virtual ~nsDOMWorkerThread(); - - nsresult Init(); - - // For nsDOMWorkerBase - virtual nsresult HandleMessage(const nsAString& aMessage, - nsDOMWorkerBase* aSourceThread); - - // For nsDOMWorkerBase - virtual nsresult DispatchMessage(nsIRunnable* aRunnable); - - virtual void Cancel(); - virtual void Suspend(); - virtual void Resume(); - - PRBool SetGlobalForContext(JSContext* aCx); - PRBool CompileGlobalObject(JSContext* aCx); - - inline nsDOMWorkerTimeout* FirstTimeout(); - inline nsDOMWorkerTimeout* NextTimeout(nsDOMWorkerTimeout* aTimeout); - - PRBool AddTimeout(nsDOMWorkerTimeout* aTimeout); - void RemoveTimeout(nsDOMWorkerTimeout* aTimeout); - void ClearTimeouts(); - void CancelTimeout(PRUint32 aId); - void SuspendTimeouts(); - void ResumeTimeouts(); - - void CancelScriptLoaders(); - - PRBool AddXHR(nsDOMWorkerXHR* aXHR); - void RemoveXHR(nsDOMWorkerXHR* aXHR); - void CancelXHRs(); - - PRLock* Lock() { - return mLock; - } - - nsDOMWorkerPool* mPool; - nsString mSource; - nsString mSourceURL; - - nsAutoJSObjectHolder mGlobal; - PRBool mCompiled; - - PRUint32 mCallbackCount; - - PRUint32 mNextTimeoutId; - - PRLock* mLock; - PRCList mTimeouts; - - nsTArray mScriptLoaders; - nsTArray mXHRs; -}; - -#endif /* __NSDOMWORKERTHREAD_H__ */ +#endif /* __NSDOMWORKERMACROS_H__ */ diff --git a/dom/src/threads/nsDOMWorkerMessageHandler.cpp b/dom/src/threads/nsDOMWorkerMessageHandler.cpp new file mode 100644 index 000000000000..953b158051b3 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerMessageHandler.cpp @@ -0,0 +1,353 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsDOMWorkerMessageHandler.h" + +#include "nsIDOMEvent.h" +#include "nsIXPConnect.h" + +#include "nsContentUtils.h" + +#include "nsDOMThreadService.h" +#include "nsDOMWorkerEvents.h" + +NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerEventListenerBase) +NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerEventListenerBase) + +nsresult +nsDOMWorkerWeakEventListener::Init(nsIDOMEventListener* aListener) +{ + NS_ENSURE_ARG_POINTER(aListener); + + nsCOMPtr wrappedJS(do_QueryInterface(aListener)); + NS_ENSURE_TRUE(wrappedJS, NS_NOINTERFACE); + + JSObject* obj; + nsresult rv = wrappedJS->GetJSObject(&obj); + NS_ENSURE_SUCCESS(rv, rv); + + mObj = obj; + + return NS_OK; +} + +already_AddRefed +nsDOMWorkerWeakEventListener::GetListener() +{ + JSContext* cx = nsDOMThreadService::GetCurrentContext(); + NS_ENSURE_TRUE(cx, nsnull); + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + nsCOMPtr listener; + nsresult rv = xpc->WrapJS(cx, mObj, NS_GET_IID(nsIDOMEventListener), + getter_AddRefs(listener)); + NS_ENSURE_SUCCESS(rv, nsnull); + + return listener.forget(); +} + +nsDOMWorkerWrappedWeakEventListener:: +nsDOMWorkerWrappedWeakEventListener(nsDOMWorkerWeakEventListener* aInner) +: mInner(aInner) +{ + NS_ASSERTION(aInner, "Null pointer!"); +} + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerMessageHandler, + nsIDOMEventTarget, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerMessageHandler, + nsIDOMEventTarget) + +NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerMessageHandler) + +const nsDOMWorkerMessageHandler::ListenerCollection* +nsDOMWorkerMessageHandler::GetListenerCollection(const nsAString& aType) const +{ + PRUint32 count = mCollections.Length(); + for (PRUint32 index = 0; index < count; index++) { + const ListenerCollection& collection = mCollections[index]; + if (collection.type.Equals(aType)) { + return &collection; + } + } + return nsnull; +} + +void +nsDOMWorkerMessageHandler::GetListenersForType(const nsAString& aType, + ListenerArray& _retval) const +{ + _retval.Clear(); + + const ListenerCollection* collection = GetListenerCollection(aType); + if (collection) { + PRUint32 count = collection->listeners.Length(); + + if (!_retval.SetLength(count)) { + NS_WARNING("Out of memory!"); + return; + } + + for (PRUint32 index = 0; index < count; index++) { + nsCOMPtr listener = + collection->listeners[index]->GetListener(); + _retval[index].swap(listener); + } + } +} + +nsresult +nsDOMWorkerMessageHandler::SetOnXListener(const nsAString& aType, + nsIDOMEventListener* aListener) +{ + nsRefPtr wrappedListener; + + ListenerCollection* collection = + const_cast(GetListenerCollection(aType)); + +#ifdef DEBUG + PRBool removed; +#endif + + if (collection) { + wrappedListener.swap(collection->onXListener); + if (wrappedListener) { +#ifdef DEBUG + removed = +#endif + collection->listeners.RemoveElement(wrappedListener); + NS_ASSERTION(removed, "Element wasn't in the list!"); + } + } + + if (!aListener) { + if (collection && !collection->listeners.Length()) { +#ifdef DEBUG + removed = +#endif + mCollections.RemoveElement(*collection); + NS_ASSERTION(removed, "Element wasn't in the list!"); + } + return NS_OK; + } + + nsRefPtr weakListener = + new nsDOMWorkerWeakEventListener(); + NS_ENSURE_TRUE(weakListener, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = weakListener->Init(aListener); + NS_ENSURE_SUCCESS(rv, rv); + + wrappedListener = new nsDOMWorkerWrappedWeakEventListener(weakListener); + NS_ENSURE_TRUE(wrappedListener, NS_ERROR_OUT_OF_MEMORY); + + if (!collection) { + collection = mCollections.AppendElement(aType); + NS_ENSURE_TRUE(collection, NS_ERROR_OUT_OF_MEMORY); + } + + WeakListener* newListener = + collection->listeners.AppendElement(wrappedListener); + NS_ENSURE_TRUE(newListener, NS_ERROR_OUT_OF_MEMORY); + + wrappedListener.swap(collection->onXListener); + return NS_OK; +} + +already_AddRefed +nsDOMWorkerMessageHandler::GetOnXListener(const nsAString& aType) const +{ + const ListenerCollection* collection = GetListenerCollection(aType); + if (collection && collection->onXListener) { + return collection->onXListener->GetListener(); + } + + return nsnull; +} + +void +nsDOMWorkerMessageHandler::ClearListeners(const nsAString& aType) +{ + PRUint32 count = mCollections.Length(); + for (PRUint32 index = 0; index < count; index++) { + if (mCollections[index].type.Equals(aType)) { + mCollections.RemoveElementAt(index); + return; + } + } +} + +PRBool +nsDOMWorkerMessageHandler::HasListeners(const nsAString& aType) +{ + const ListenerCollection* collection = GetListenerCollection(aType); + return collection && collection->listeners.Length(); +} + +void +nsDOMWorkerMessageHandler::ClearAllListeners() +{ + mCollections.Clear(); +} + +void +nsDOMWorkerMessageHandler::Trace(JSTracer* aTracer) +{ + PRUint32 cCount = mCollections.Length(); + for (PRUint32 cIndex = 0; cIndex < cCount; cIndex++) { + const ListenerCollection& collection = mCollections[cIndex]; + PRUint32 lCount = collection.listeners.Length(); + for (PRUint32 lIndex = 0; lIndex < lCount; lIndex++) { + JSObject* obj = collection.listeners[lIndex]->GetJSObject(); + NS_ASSERTION(obj, "Null object!"); + JS_SET_TRACING_DETAILS(aTracer, nsnull, this, 0); + JS_CallTracer(aTracer, obj, JSTRACE_OBJECT); + } + } +} + +/** + * See nsIDOMEventTarget + */ +NS_IMETHODIMP +nsDOMWorkerMessageHandler::AddEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + PRBool aUseCapture) +{ + ListenerCollection* collection = + const_cast(GetListenerCollection(aType)); + + if (!collection) { + collection = mCollections.AppendElement(aType); + NS_ENSURE_TRUE(collection, NS_ERROR_OUT_OF_MEMORY); + } + + nsRefPtr weakListener = + new nsDOMWorkerWeakEventListener(); + NS_ENSURE_TRUE(weakListener, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = weakListener->Init(aListener); + NS_ENSURE_SUCCESS(rv, rv); + + WeakListener* newListener = collection->listeners.AppendElement(weakListener); + NS_ENSURE_TRUE(newListener, NS_ERROR_OUT_OF_MEMORY); + + return NS_OK; +} + +/** + * See nsIDOMEventTarget + */ +NS_IMETHODIMP +nsDOMWorkerMessageHandler::RemoveEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + PRBool aUseCapture) +{ + ListenerCollection* collection = + const_cast(GetListenerCollection(aType)); + + if (collection) { + PRUint32 count = collection->listeners.Length(); + for (PRUint32 index = 0; index < count; index++) { + WeakListener& weakListener = collection->listeners[index]; + if (weakListener == collection->onXListener) { + continue; + } + nsCOMPtr listener = weakListener->GetListener(); + if (listener == aListener) { + collection->listeners.RemoveElementAt(index); + break; + } + } + + if (!collection->listeners.Length()) { +#ifdef DEBUG + PRBool removed = +#endif + mCollections.RemoveElement(*collection); + NS_ASSERTION(removed, "Somehow this wasn't in the list!"); + } + } + + return NS_OK; +} + +/** + * See nsIDOMEventTarget + */ +NS_IMETHODIMP +nsDOMWorkerMessageHandler::DispatchEvent(nsIDOMEvent* aEvent, + PRBool* _retval) +{ + NS_ENSURE_ARG_POINTER(aEvent); + + nsCOMPtr event; + + if (_retval) { + event = do_QueryInterface(aEvent); + if (!event) { + event = new nsDOMWorkerPrivateEvent(aEvent); + NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); + } + aEvent = event; + } + + nsAutoString type; + nsresult rv = aEvent->GetType(type); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoTArray listeners; + GetListenersForType(type, listeners); + + PRUint32 count = listeners.Length(); + for (PRUint32 index = 0; index < count; index++) { + const Listener& listener = listeners[index]; + NS_ASSERTION(listener, "Null listener in array!"); + + listener->HandleEvent(aEvent); + } + + if (_retval) { + *_retval = event->PreventDefaultCalled(); + } + + return NS_OK; +} diff --git a/dom/src/threads/nsDOMWorkerMessageHandler.h b/dom/src/threads/nsDOMWorkerMessageHandler.h new file mode 100644 index 000000000000..beda17085032 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerMessageHandler.h @@ -0,0 +1,161 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __NSDOMWORKERMESSAGEHANDLER_H__ +#define __NSDOMWORKERMESSAGEHANDLER_H__ + +#include "nsIClassInfo.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMWorkers.h" + +#include "nsIProgrammingLanguage.h" + +#include "jsapi.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsIClassInfoImpl.h" +#include "nsStringGlue.h" +#include "nsTArray.h" +#include "nsIWeakReference.h" + +class nsDOMWorkerEventListenerBase +{ +public: + NS_IMETHOD_(nsrefcnt) AddRef(); + NS_IMETHOD_(nsrefcnt) Release(); + + virtual already_AddRefed GetListener() = 0; + virtual JSObject* GetJSObject() = 0; + +protected: + virtual ~nsDOMWorkerEventListenerBase() { } + + nsAutoRefCnt mRefCnt; +}; + +class nsDOMWorkerWeakEventListener : public nsDOMWorkerEventListenerBase +{ +public: + nsDOMWorkerWeakEventListener() + : mObj(NULL) { } + + nsresult Init(nsIDOMEventListener* aListener); + + already_AddRefed GetListener(); + + virtual JSObject* GetJSObject() { + return mObj; + } + +private: + JSObject* mObj; +}; + +class nsDOMWorkerWrappedWeakEventListener : public nsDOMWorkerEventListenerBase +{ +public: + nsDOMWorkerWrappedWeakEventListener(nsDOMWorkerWeakEventListener* aInner); + + already_AddRefed GetListener() { + return mInner->GetListener(); + } + + virtual JSObject* GetJSObject() { + return mInner->GetJSObject(); + } + +private: + nsRefPtr mInner; +}; + +class nsDOMWorkerMessageHandler : public nsIDOMEventTarget, + public nsIClassInfo +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTTARGET + NS_DECL_NSICLASSINFO + + virtual nsresult SetOnXListener(const nsAString& aType, + nsIDOMEventListener* aListener); + + already_AddRefed + GetOnXListener(const nsAString& aType) const; + + void ClearListeners(const nsAString& aType); + + PRBool HasListeners(const nsAString& aType); + + void ClearAllListeners(); + + void Trace(JSTracer* aTracer); + +protected: + virtual ~nsDOMWorkerMessageHandler() { } + +private: + + typedef nsCOMPtr Listener; + typedef nsTArray ListenerArray; + + typedef nsRefPtr WeakListener; + typedef nsTArray WeakListenerArray; + + struct ListenerCollection { + PRBool operator==(const ListenerCollection& aOther) const { + return this == &aOther; + } + + ListenerCollection(const nsAString& aType) + : type(aType) { } + + nsString type; + WeakListenerArray listeners; + nsRefPtr onXListener; + }; + + const ListenerCollection* GetListenerCollection(const nsAString& aType) const; + + void GetListenersForType(const nsAString& aType, + ListenerArray& _retval) const; + + nsTArray mCollections; +}; + +#endif /* __NSDOMWORKERMESSAGEHANDLER_H__ */ diff --git a/dom/src/threads/nsDOMWorkerPool.cpp b/dom/src/threads/nsDOMWorkerPool.cpp index 554141a05de2..bba2979b56fc 100644 --- a/dom/src/threads/nsDOMWorkerPool.cpp +++ b/dom/src/threads/nsDOMWorkerPool.cpp @@ -58,69 +58,37 @@ // DOMWorker includes #include "nsDOMThreadService.h" -#include "nsDOMWorkerThread.h" +#include "nsDOMWorker.h" #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args) -#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) +nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject, + nsIDocument* aDocument) +: mParentGlobal(aGlobalObject), + mParentDocument(aDocument), + mMonitor(nsnull), + mCanceled(PR_FALSE), + mSuspended(PR_FALSE) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aGlobalObject, "Must have a global object!"); 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_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool, - nsIClassInfo) - -NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool) - -NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool) +NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPool) +NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool) nsresult nsDOMWorkerPool::Init() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsIScriptGlobalObject* globalObject = - mParentDocument->GetScriptGlobalObject(); - NS_ENSURE_STATE(globalObject); - - nsCOMPtr 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 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); @@ -128,208 +96,147 @@ nsDOMWorkerPool::Init() } nsresult -nsDOMWorkerPool::HandleMessage(const nsAString& aMessage, - nsDOMWorkerBase* aSource) +nsDOMWorkerPool::NoteWorker(nsDOMWorker* aWorker) { - nsCOMPtr messageListener = - nsDOMWorkerBase::GetMessageListener(); - if (!messageListener) { - LOG(("Message received on a worker with no listener!")); - return NS_OK; + 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 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 mainThread(do_GetMainThread()); - NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE); - - nsresult rv = mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); + if (suspendWorker) { + aWorker->Suspend(); + } return NS_OK; } void -nsDOMWorkerPool::HandleError(nsIScriptError* aError, - nsDOMWorkerThread* aSource) +nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aWorker, "Null pointer!"); - if (mErrorListener) { - mErrorListener->OnError(aError, - NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource)); + PRBool removeFromThreadService = PR_FALSE; + + { + nsAutoMonitor mon(mMonitor); + + NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!"); + mWorkers.RemoveElement(aWorker); + + if (!mCanceled && !mWorkers.Length()) { + removeFromThreadService = PR_TRUE; + } + } + + if (removeFromThreadService) { + nsRefPtr kungFuDeathGrip(this); + nsDOMThreadService::get()->NoteEmptyPool(this); } } void -nsDOMWorkerPool::NoteDyingWorker(nsDOMWorkerThread* aWorker) +nsDOMWorkerPool::GetWorkers(nsTArray& aArray) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + aArray.Clear(); - NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!"); - mWorkers.RemoveElement(aWorker); + nsAutoMonitor mon(mMonitor); +#ifdef DEBUG + nsDOMWorker** newWorkers = +#endif + aArray.AppendElements(mWorkers); + NS_WARN_IF_FALSE(newWorkers, "Out of memory!"); } void -nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) +nsDOMWorkerPool::Cancel() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mCanceled, "Canceled more than once!"); - nsCOMPtr globalSupports(do_QueryInterface(aGlobalObject)); - NS_ASSERTION(globalSupports, "Null pointer?!"); + { + nsAutoMonitor mon(mMonitor); - if (globalSupports == mParentGlobal) { - LOOP_OVER_WORKERS(Cancel, ()); - mWorkers.Clear(); - if (IsSuspended()) { - nsAutoMonitor mon(mMonitor); + mCanceled = PR_TRUE; + + nsAutoTArray 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::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) +nsDOMWorkerPool::Suspend() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsCOMPtr globalSupports(do_QueryInterface(aGlobalObject)); - NS_ASSERTION(globalSupports, "Null pointer?!"); + nsAutoTArray workers; + { + nsAutoMonitor mon(mMonitor); - if (globalSupports == mParentGlobal) { - LOOP_OVER_WORKERS(Suspend, ()); - Suspend(); + NS_ASSERTION(!mSuspended, "Suspended more than once!"); + mSuspended = PR_TRUE; + + GetWorkers(workers); + } + + PRUint32 count = workers.Length(); + for (PRUint32 index = 0; index < count; index++) { + workers[index]->Suspend(); } } void -nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) +nsDOMWorkerPool::Resume() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsCOMPtr globalSupports(do_QueryInterface(aGlobalObject)); - NS_ASSERTION(globalSupports, "Null pointer?!"); + nsAutoTArray workers; + { + nsAutoMonitor mon(mMonitor); - if (globalSupports == mParentGlobal) { - LOOP_OVER_WORKERS(Resume, ()); - Resume(); + NS_ASSERTION(mSuspended, "Not suspended!"); + mSuspended = PR_FALSE; + GetWorkers(workers); + } + + 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 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 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 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 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; + return mParentGlobal->GetContext(); } diff --git a/dom/src/threads/nsDOMWorkerPool.h b/dom/src/threads/nsDOMWorkerPool.h index 18135a311eec..d13776464349 100644 --- a/dom/src/threads/nsDOMWorkerPool.h +++ b/dom/src/threads/nsDOMWorkerPool.h @@ -40,94 +40,73 @@ #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 "nsTPtrArray.h" +#include "nsTArray.h" #include "prmon.h" -class nsDOMWorkerThread; +class nsDOMWorker; class nsIDocument; class nsIScriptContext; class nsIScriptError; class nsIScriptGlobalObject; -/** - * The pool is almost always touched only on the main thread. - */ -class nsDOMWorkerPool : public nsDOMWorkerBase, - public nsIDOMWorkerPool, - public nsIClassInfo +class nsDOMWorkerPool { - 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: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMWORKERPOOL - NS_DECL_NSICLASSINFO + nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject, + nsIDocument* aDocument); - nsDOMWorkerPool(nsIDocument* aDocument); + NS_IMETHOD_(nsrefcnt) AddRef(); + NS_IMETHOD_(nsrefcnt) Release(); - // For nsDOMWorkerBase - virtual nsDOMWorkerPool* Pool() { - return this; - } - - nsIDocument* ParentDocument(); nsIScriptContext* ScriptContext(); -private: - virtual ~nsDOMWorkerPool(); + nsIScriptGlobalObject* ScriptGlobalObject() { + return mParentGlobal; + } + + nsIDocument* ParentDocument() { + return mParentDocument; + } nsresult Init(); - // For nsDOMWorkerBase - virtual nsresult HandleMessage(const nsAString& aMessage, - nsDOMWorkerBase* aSourceThread); + void Cancel(); + void Suspend(); + void Resume(); - // For nsDOMWorkerBase - virtual nsresult DispatchMessage(nsIRunnable* aRunnable); - - void HandleError(nsIScriptError* aError, - nsDOMWorkerThread* aSource); - - void NoteDyingWorker(nsDOMWorkerThread* aWorker); - - void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject); - void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject); - void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject); + nsresult NoteWorker(nsDOMWorker* aWorker); + void NoteDyingWorker(nsDOMWorker* aWorker); PRMonitor* Monitor() { return mMonitor; } - // Weak reference to the window that created and owns this pool. - nsISupports* mParentGlobal; +private: + virtual ~nsDOMWorkerPool(); - // Weak reference to the document that created this pool. - nsIDocument* mParentDocument; + void GetWorkers(nsTArray& aArray); + + nsAutoRefCnt mRefCnt; + + // Reference to the window that created and owns this pool. + nsCOMPtr mParentGlobal; + + // Reference to the document that created this pool. + nsCOMPtr mParentDocument; // Weak array of workers. The idea is that workers can be garbage collected // independently of the owning pool and other workers. - nsTPtrArray mWorkers; - - // An error handler function, may be null. - nsCOMPtr mErrorListener; + nsTArray mWorkers; // Monitor for suspending and resuming workers. PRMonitor* mMonitor; + + PRPackedBool mCanceled; + PRPackedBool mSuspended; }; #endif /* __NSDOMWORKERPOOL_H__ */ diff --git a/dom/src/threads/nsDOMWorkerScriptLoader.cpp b/dom/src/threads/nsDOMWorkerScriptLoader.cpp index c5eb1dcbaff8..4cd5ffdc098c 100644 --- a/dom/src/threads/nsDOMWorkerScriptLoader.cpp +++ b/dom/src/threads/nsDOMWorkerScriptLoader.cpp @@ -65,78 +65,31 @@ #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args) -nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader() -: mWorker(nsnull), +nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker) +: nsDOMWorkerFeature(aWorker), mTarget(nsnull), - mCx(NULL), mScriptCount(0), - mCanceled(PR_FALSE), - mTrackedByWorker(PR_FALSE) + mCanceled(PR_FALSE) { // Created on worker thread. NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aWorker, "Null worker!"); } -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, +NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature, + nsIRunnable, nsIStreamLoaderObserver) nsresult -nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker, - JSContext* aCx, +nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx, const nsTArray& 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; } @@ -173,30 +126,18 @@ nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker, // network or compiling. AutoSuspendWorkerEvents aswe(this); - 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; - } - + nsresult rv = DoRunLoop(aCx); if (NS_FAILED(rv)) { return rv; } // Verify that all scripts downloaded and compiled. - rv = VerifyScripts(); + rv = VerifyScripts(aCx); if (NS_FAILED(rv)) { return rv; } - rv = ExecuteScripts(); + rv = ExecuteScripts(aCx); if (NS_FAILED(rv)) { return rv; } @@ -205,18 +146,17 @@ nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker, } nsresult -nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker, - JSContext* aCx, +nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx, const nsString& aURL) { nsAutoTArray url; url.AppendElement(aURL); - return LoadScripts(aWorker, aCx, url); + return LoadScripts(aCx, url); } nsresult -nsDOMWorkerScriptLoader::DoRunLoop() +nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); @@ -234,7 +174,7 @@ nsDOMWorkerScriptLoader::DoRunLoop() PRBool changed = NS_SUCCEEDED(threadService->ChangeThreadPoolMaxThreads(1)); while (!(done || mCanceled)) { - JSAutoSuspendRequest asr(mCx); + JSAutoSuspendRequest asr(aCx); NS_ProcessNextEvent(mTarget); } @@ -247,8 +187,10 @@ nsDOMWorkerScriptLoader::DoRunLoop() } nsresult -nsDOMWorkerScriptLoader::VerifyScripts() +nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx) { + NS_ASSERTION(aCx, "Shouldn't be null!"); + nsresult rv = NS_OK; for (PRUint32 index = 0; index < mScriptCount; index++) { @@ -274,10 +216,24 @@ nsDOMWorkerScriptLoader::VerifyScripts() // Ok, this is the script that caused us to fail. - // Only throw an error there is no other pending exception. - if (!JS_IsExceptionPending(mCx)) { + 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; + } NS_ConvertUTF16toUTF8 url(loadInfo.url); - JS_ReportError(mCx, "Failed to compile script: %s", url.get()); + JS_ReportError(aCx, message, url.get(), loadInfo.result); } break; } @@ -286,30 +242,34 @@ nsDOMWorkerScriptLoader::VerifyScripts() } nsresult -nsDOMWorkerScriptLoader::ExecuteScripts() +nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx) { + 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(JS_GetPrivate(mCx, loadInfo.scriptObj)); + static_cast(JS_GetPrivate(aCx, loadInfo.scriptObj)); NS_ASSERTION(script, "This shouldn't ever be null!"); JSObject* global = mWorker->mGlobal ? mWorker->mGlobal : - JS_GetGlobalObject(mCx); + JS_GetGlobalObject(aCx); 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(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT); + JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT); jsval val; - PRBool success = JS_ExecuteScript(mCx, global, script, &val); + PRBool success = JS_ExecuteScript(aCx, global, script, &val); - JS_SetOptions(mCx, oldOpts); + JS_SetOptions(aCx, oldOpts); if (!success) { return NS_ERROR_FAILURE; @@ -448,8 +408,11 @@ nsDOMWorkerScriptLoader::RunInternal() NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); // Things we need to make all this work... - nsIDocument* parentDoc = mWorker->Pool()->ParentDocument(); - NS_ASSERTION(parentDoc, "Null parent document?!"); + nsCOMPtr parentDoc = mWorker->Pool()->ParentDocument(); + if (!parentDoc) { + // Must have been canceled. + return NS_ERROR_ABORT; + } // All of these can potentially be null, but that should be ok. We'll either // succeed without them or fail below. @@ -472,9 +435,7 @@ nsDOMWorkerScriptLoader::RunInternal() nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); - rv = - secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, - nsIScriptSecurityManager::ALLOW_CHROME); + rv = secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, 0); if (NS_FAILED(rv)) { return rv; } @@ -613,8 +574,7 @@ nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader, } nsRefPtr compiler = - new ScriptCompiler(this, mCx, loadInfo.scriptText, filename, - loadInfo.scriptObj); + new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj); NS_ASSERTION(compiler, "Out of memory!"); if (!compiler) { return rv = NS_ERROR_OUT_OF_MEMORY; @@ -663,14 +623,14 @@ void nsDOMWorkerScriptLoader::SuspendWorkerEvents() { NS_ASSERTION(mWorker, "No worker yet!"); - mWorker->SuspendTimeouts(); + mWorker->SuspendFeatures(); } void nsDOMWorkerScriptLoader::ResumeWorkerEvents() { NS_ASSERTION(mWorker, "No worker yet!"); - mWorker->ResumeTimeouts(); + mWorker->ResumeFeatures(); } nsDOMWorkerScriptLoader:: @@ -699,6 +659,9 @@ ScriptLoaderRunnable::~ScriptLoaderRunnable() } } +NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerScriptLoader::ScriptLoaderRunnable, + nsIRunnable) + void nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke() { @@ -707,17 +670,14 @@ nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke() 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!"); } @@ -735,31 +695,34 @@ nsDOMWorkerScriptLoader::ScriptCompiler::Run() NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!"); NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!"); - JSAutoRequest ar(mCx); + JSContext* cx = nsDOMThreadService::GetCurrentContext(); + NS_ENSURE_STATE(cx); - JSObject* global = JS_GetGlobalObject(mCx); + JSAutoRequest ar(cx); + + JSObject* global = JS_GetGlobalObject(cx); 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(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT); + JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT); JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal(); JSScript* script = - JS_CompileUCScriptForPrincipals(mCx, global, principal, + JS_CompileUCScriptForPrincipals(cx, global, principal, reinterpret_cast (mScriptText.BeginReading()), mScriptText.Length(), mFilename.get(), 1); - JS_SetOptions(mCx, oldOpts); + JS_SetOptions(cx, oldOpts); if (!script) { return NS_ERROR_FAILURE; } - mScriptObj = JS_NewScriptObject(mCx, script); + mScriptObj = JS_NewScriptObject(cx, script); NS_ENSURE_STATE(mScriptObj); return NS_OK; diff --git a/dom/src/threads/nsDOMWorkerScriptLoader.h b/dom/src/threads/nsDOMWorkerScriptLoader.h index eb19fc039f64..e6ff88bc6d67 100644 --- a/dom/src/threads/nsDOMWorkerScriptLoader.h +++ b/dom/src/threads/nsDOMWorkerScriptLoader.h @@ -40,7 +40,7 @@ #define __NSDOMWORKERSCRIPTLOADER_H__ // Bases -#include "nsThreadUtils.h" +#include "nsIRunnable.h" #include "nsIStreamLoader.h" // Interfaces @@ -56,8 +56,9 @@ #include "nsTArray.h" #include "prlock.h" -// DOMWorker includes -#include "nsDOMWorkerThread.h" +#include "nsDOMWorker.h" + +class nsIThread; /** * This class takes a list of script URLs, downloads the scripts, compiles the @@ -86,12 +87,11 @@ * Currently if *anything* after 2 fails then we cancel any pending loads and * bail out entirely. */ -class nsDOMWorkerScriptLoader : public nsRunnable, +class nsDOMWorkerScriptLoader : public nsDOMWorkerFeature, + public nsIRunnable, public nsIStreamLoaderObserver { friend class AutoSuspendWorkerEvents; - friend class nsDOMWorkerFunctions; - friend class nsDOMWorkerThread; friend class ScriptLoaderRunnable; public: @@ -99,24 +99,23 @@ public: NS_DECL_NSIRUNNABLE NS_DECL_NSISTREAMLOADEROBSERVER - nsDOMWorkerScriptLoader(); + nsDOMWorkerScriptLoader(nsDOMWorker* aWorker); - nsresult LoadScripts(nsDOMWorkerThread* aWorker, - JSContext* aCx, + nsresult LoadScripts(JSContext* aCx, const nsTArray& aURLs); - nsresult LoadScript(nsDOMWorkerThread* aWorker, - JSContext* aCx, - const nsString& aURL); + nsresult LoadScript(JSContext* aCx, + const nsString& aURL); - void Cancel(); + virtual void Cancel(); private: - ~nsDOMWorkerScriptLoader(); + ~nsDOMWorkerScriptLoader() { } - nsresult DoRunLoop(); - nsresult VerifyScripts(); - nsresult ExecuteScripts(); + + nsresult DoRunLoop(JSContext* aCx); + nsresult VerifyScripts(JSContext* aCx); + nsresult ExecuteScripts(JSContext* aCx); nsresult RunInternal(); @@ -135,8 +134,11 @@ private: return mWorker->Lock(); } - class ScriptLoaderRunnable : public nsRunnable + class ScriptLoaderRunnable : public nsIRunnable { + public: + NS_DECL_ISUPPORTS + protected: // Meant to be inherited. ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader); @@ -158,13 +160,11 @@ private: 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; @@ -205,20 +205,17 @@ private: nsAutoJSObjectHolder scriptObj; }; - nsDOMWorkerThread* mWorker; nsIThread* mTarget; - JSContext* mCx; nsRefPtr mDoneRunnable; PRUint32 mScriptCount; nsTArray mLoadInfos; - PRPackedBool mCanceled; - PRPackedBool mTrackedByWorker; - // Protected by mWorker's lock! nsTArray mPendingRunnables; + + PRPackedBool mCanceled; }; #endif /* __NSDOMWORKERSCRIPTLOADER_H__ */ diff --git a/dom/src/threads/nsDOMWorkerThread.cpp b/dom/src/threads/nsDOMWorkerThread.cpp deleted file mode 100644 index 1c36670917a1..000000000000 --- a/dom/src/threads/nsDOMWorkerThread.cpp +++ /dev/null @@ -1,984 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is worker threads. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Vladimir Vukicevic (Original Author) - * Ben Turner - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsDOMWorkerThread.h" - -// Interfaces -#include "nsIDOMClassInfo.h" -#include "nsIJSContextStack.h" -#include "nsIJSRuntimeService.h" -#include "nsIScriptContext.h" -#include "nsIXPConnect.h" - -// Other includes -#ifdef MOZ_SHARK -#include "jsdbgapi.h" -#endif -#include "nsAutoLock.h" -#include "nsContentUtils.h" -#include "nsJSUtils.h" -#include "nsJSEnvironment.h" -#include "nsThreadUtils.h" - -// DOMWorker includes -#include "nsDOMWorkerPool.h" -#include "nsDOMWorkerScriptLoader.h" -#include "nsDOMWorkerSecurityManager.h" -#include "nsDOMThreadService.h" -#include "nsDOMWorkerTimeout.h" -#include "nsDOMWorkerXHR.h" - -#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args) - -// XXX Could make these functions of nsDOMWorkerThread instead. -class nsDOMWorkerFunctions -{ -public: - // Same as window.dump(). - static JSBool Dump(JSContext* aCx, JSObject* aObj, uintN aArgc, jsval* aArgv, - jsval* aRval); - - // Debug-only version of window.dump(), like the JS component loader has. - static JSBool DebugDump(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval); - - // Same as nsIDOMWorkerThread::PostMessage - static JSBool PostMessage(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval); - - // Same as window.setTimeout(). - static JSBool SetTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval) { - return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_FALSE); - } - - // Same as window.setInterval(). - static JSBool SetInterval(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval) { - return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_TRUE); - } - - // Used for both clearTimeout() and clearInterval(). - static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval); - - static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval); - - static JSBool NewXMLHttpRequest(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval); - -private: - // Internal helper for SetTimeout and SetInterval. - static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc, - jsval* aArgv, jsval* aRval, PRBool aIsInterval); -}; - -JSBool -nsDOMWorkerFunctions::Dump(JSContext* aCx, - JSObject* /* aObj */, - uintN aArgc, - jsval* aArgv, - jsval* /* aRval */) -{ - // XXX Expose this to the JS console? Only if that DOM pref is set? - - JSString* str; - if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) { - nsDependentJSString string(str); - fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr); - fflush(stderr); - } - return JS_TRUE; -} - -JSBool -nsDOMWorkerFunctions::DebugDump(JSContext* aCx, - JSObject* aObj, - uintN aArgc, - jsval* aArgv, - jsval* aRval) -{ -#ifdef DEBUG - return nsDOMWorkerFunctions::Dump(aCx, aObj, aArgc, aArgv, aRval); -#else - return JS_TRUE; -#endif -} - -JSBool -nsDOMWorkerFunctions::PostMessage(JSContext* aCx, - JSObject* /* aObj */, - uintN aArgc, - jsval* aArgv, - jsval* /* aRval */) -{ - nsDOMWorkerThread* worker = - static_cast(JS_GetContextPrivate(aCx)); - NS_ASSERTION(worker, "This should be set by the DOM thread service!"); - - if (worker->IsCanceled()) { - return JS_FALSE; - } - - nsRefPtr pool = worker->Pool(); - NS_ASSERTION(pool, "Shouldn't ever be null!"); - - nsresult rv; - - JSString* str; - if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) { - rv = pool->PostMessageInternal(nsDependentJSString(str), worker); - } - else { - rv = pool->PostMessageInternal(EmptyString(), worker); - } - - if (NS_FAILED(rv)) { - JS_ReportError(aCx, "Failed to post message!"); - return JS_FALSE; - } - - return JS_TRUE; -} - -JSBool -nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx, - JSObject* /* aObj */, - uintN aArgc, - jsval* aArgv, - jsval* aRval, - PRBool aIsInterval) -{ - nsDOMWorkerThread* worker = - static_cast(JS_GetContextPrivate(aCx)); - NS_ASSERTION(worker, "This should be set by the DOM thread service!"); - - if (worker->IsCanceled()) { - return JS_FALSE; - } - - PRUint32 id = ++worker->mNextTimeoutId; - - nsAutoPtr - timeout(new nsDOMWorkerTimeout(worker, id)); - if (!timeout) { - JS_ReportOutOfMemory(aCx); - return JS_FALSE; - } - - nsresult rv = timeout->Init(aCx, aArgc, aArgv, aIsInterval); - if (NS_FAILED(rv)) { - JS_ReportError(aCx, "Failed to initialize timeout!"); - return JS_FALSE; - } - - timeout.forget(); - - *aRval = INT_TO_JSVAL(id); - return JS_TRUE; -} - -JSBool -nsDOMWorkerFunctions::KillTimeout(JSContext* aCx, - JSObject* /* aObj */, - uintN aArgc, - jsval* aArgv, - jsval* /* aRval */) -{ - nsDOMWorkerThread* worker = - static_cast(JS_GetContextPrivate(aCx)); - NS_ASSERTION(worker, "This should be set by the DOM thread service!"); - - // A canceled worker should have already killed all timeouts. - if (worker->IsCanceled()) { - return JS_TRUE; - } - - if (!aArgc) { - JS_ReportError(aCx, "Function requires at least 1 parameter"); - return JS_FALSE; - } - - uint32 id; - if (!JS_ValueToECMAUint32(aCx, aArgv[0], &id)) { - JS_ReportError(aCx, "First argument must be a timeout id"); - return JS_FALSE; - } - - worker->CancelTimeout(PRUint32(id)); - return JS_TRUE; -} - -JSBool -nsDOMWorkerFunctions::LoadScripts(JSContext* aCx, - JSObject* /* aObj */, - uintN aArgc, - jsval* aArgv, - jsval* /* aRval */) -{ - nsDOMWorkerThread* worker = - static_cast(JS_GetContextPrivate(aCx)); - NS_ASSERTION(worker, "This should be set by the DOM thread service!"); - - if (worker->IsCanceled()) { - return JS_FALSE; - } - - if (!aArgc) { - JS_ReportError(aCx, "Function must have at least one argument!"); - return JS_FALSE; - } - - nsAutoTArray urls; - - if (!urls.SetCapacity((PRUint32)aArgc)) { - JS_ReportOutOfMemory(aCx); - return JS_FALSE; - } - - for (uintN index = 0; index < aArgc; index++) { - jsval val = aArgv[index]; - - if (!JSVAL_IS_STRING(val)) { - JS_ReportError(aCx, "Argument %d must be a string", index); - return JS_FALSE; - } - - JSString* str = JS_ValueToString(aCx, val); - if (!str) { - JS_ReportError(aCx, "Couldn't convert argument %d to a string", index); - return JS_FALSE; - } - - nsString* newURL = urls.AppendElement(); - NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!"); - - newURL->Assign(nsDependentJSString(str)); - } - - nsRefPtr loader = new nsDOMWorkerScriptLoader(); - if (!loader) { - JS_ReportOutOfMemory(aCx); - return JS_FALSE; - } - - nsresult rv = loader->LoadScripts(worker, aCx, urls); - if (NS_FAILED(rv)) { - return JS_FALSE; - } - - return JS_TRUE; -} - -JSBool -nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx, - JSObject* aObj, - uintN aArgc, - jsval* /* aArgv */, - jsval* aRval) -{ - nsDOMWorkerThread* worker = - static_cast(JS_GetContextPrivate(aCx)); - NS_ASSERTION(worker, "This should be set by the DOM thread service!"); - - if (worker->IsCanceled()) { - return JS_FALSE; - } - - if (aArgc) { - JS_ReportError(aCx, "Constructor takes no arguments!"); - return JS_FALSE; - } - - nsRefPtr xhr = new nsDOMWorkerXHR(worker); - if (!xhr) { - JS_ReportOutOfMemory(aCx); - return JS_FALSE; - } - - nsresult rv = xhr->Init(); - if (NS_FAILED(rv)) { - JS_ReportError(aCx, "Failed to construct XHR!"); - return JS_FALSE; - } - - nsCOMPtr xhrSupports; - xhr->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(xhrSupports)); - NS_ASSERTION(xhrSupports, "Impossible!"); - - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - - nsCOMPtr xhrWrapped; - rv = xpc->WrapNative(aCx, aObj, xhrSupports, NS_GET_IID(nsIXMLHttpRequest), - getter_AddRefs(xhrWrapped)); - if (NS_FAILED(rv)) { - JS_ReportError(aCx, "Failed to wrap XHR!"); - return JS_FALSE; - } - - JSObject* xhrJSObj; - rv = xhrWrapped->GetJSObject(&xhrJSObj); - if (NS_FAILED(rv)) { - JS_ReportError(aCx, "Failed to get JSObject!"); - return JS_FALSE; - } - - *aRval = OBJECT_TO_JSVAL(xhrJSObj); - return JS_TRUE; -} - -JSFunctionSpec gDOMWorkerFunctions[] = { - { "dump", nsDOMWorkerFunctions::Dump, 1, 0, 0 }, - { "debug", nsDOMWorkerFunctions::DebugDump, 1, 0, 0 }, - { "postMessageToPool", nsDOMWorkerFunctions::PostMessage, 1, 0, 0 }, - { "setTimeout", nsDOMWorkerFunctions::SetTimeout, 1, 0, 0 }, - { "clearTimeout", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 }, - { "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0, 0 }, - { "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 }, - { "loadScripts", nsDOMWorkerFunctions::LoadScripts, 1, 0, 0 }, - { "XMLHttpRequest", nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0, 0 }, -#ifdef MOZ_SHARK - { "startShark", js_StartShark, 0, 0, 0 }, - { "stopShark", js_StopShark, 0, 0, 0 }, - { "connectShark", js_ConnectShark, 0, 0, 0 }, - { "disconnectShark", js_DisconnectShark, 0, 0, 0 }, -#endif - { nsnull, nsnull, 0, 0, 0 } -}; - -/** - * An nsISupports that holds a weak ref to the worker. The worker owns the - * thread context so we don't have to worry about nulling this out ever. - */ -class nsDOMWorkerThreadWeakRef : public nsIDOMWorkerThread, - public nsIClassInfo -{ -public: - NS_DECL_ISUPPORTS - NS_FORWARD_NSIDOMWORKERTHREAD(mWorker->) - NS_FORWARD_NSICLASSINFO(mWorker->) - - nsDOMWorkerThreadWeakRef(nsDOMWorkerThread* aWorker) - : mWorker(aWorker) { } - -protected: - nsDOMWorkerThread* mWorker; -}; - -NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadWeakRef, nsIDOMWorkerThread, - nsIClassInfo) - -/** - * The 'threadContext' object for a worker's JS global object. - */ -class nsDOMWorkerThreadContext : public nsIDOMWorkerThreadContext, - public nsIClassInfo -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMWORKERTHREADCONTEXT - NS_DECL_NSICLASSINFO - - nsDOMWorkerThreadContext(nsDOMWorkerThread* aWorker) - : mWorker(aWorker) { } - -protected: - nsDOMWorkerThread* mWorker; - nsCOMPtr mWeakRef; -}; - -NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadContext, - nsIDOMWorkerThreadContext, - nsIClassInfo) - -NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThreadContext, - nsIDOMWorkerThreadContext) - -NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThreadContext) - -NS_IMETHODIMP -nsDOMWorkerThreadContext::GetThisThread(nsIDOMWorkerThread** aThisThread) -{ - if (!mWeakRef) { - mWeakRef = new nsDOMWorkerThreadWeakRef(mWorker); - NS_ENSURE_TRUE(mWeakRef, NS_ERROR_OUT_OF_MEMORY); - } - - NS_ADDREF(*aThisThread = mWeakRef); - return NS_OK; -} - -nsDOMWorkerThread::nsDOMWorkerThread(nsDOMWorkerPool* aPool, - const nsAString& aSource, - PRBool aSourceIsURL) -: mPool(aPool), - mCompiled(PR_FALSE), - mCallbackCount(0), - mNextTimeoutId(0), - mLock(nsnull) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (aSourceIsURL) { - mSourceURL.Assign(aSource); - NS_ASSERTION(!mSourceURL.IsEmpty(), "Empty source url!"); - } - else { - mSource.Assign(aSource); - NS_ASSERTION(!mSource.IsEmpty(), "Empty source string!"); - } - - PR_INIT_CLIST(&mTimeouts); -} - -nsDOMWorkerThread::~nsDOMWorkerThread() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!IsCanceled()) { - nsRefPtr pool = Pool(); - pool->NoteDyingWorker(this); - } - - ClearTimeouts(); - - if (mLock) { - nsAutoLock::DestroyLock(mLock); - } -} - -NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThread, nsIDOMWorkerThread, - nsIClassInfo) -NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThread, nsIDOMWorkerThread) - -NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThread) - -nsresult -nsDOMWorkerThread::Init() -{ - mLock = nsAutoLock::NewLock("nsDOMWorkerThread::mLock"); - NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY); - - NS_ASSERTION(!mGlobal, "Already got a global?!"); - - JSRuntime* rt; - nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool success = mGlobal.Hold(rt); - NS_ENSURE_TRUE(success, NS_ERROR_FAILURE); - - // This is pretty cool - all we have to do to get our script executed is to - // pass a no-op runnable to the thread service and it will make sure we have - // a context and global object. - nsCOMPtr runnable(new nsRunnable()); - NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); - - rv = nsDOMThreadService::get()->Dispatch(this, runnable); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -// From nsDOMWorkerBase -nsresult -nsDOMWorkerThread::HandleMessage(const nsAString& aMessage, - nsDOMWorkerBase* aSource) -{ - nsCOMPtr messageListener = GetMessageListener(); - if (!messageListener) { - LOG(("Message received on a worker with no listener!")); - return NS_OK; - } - - // We have to call this manually because XPConnect will replace our error - // reporter with its own and we won't properly notify the pool of any - // unhandled exceptions... - - JSContext* cx; - nsresult rv = - nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx); - NS_ENSURE_SUCCESS(rv, rv); - - JSAutoRequest ar(cx); - - if (JS_IsExceptionPending(cx)) { - JS_ClearPendingException(cx); - } - - // Get a JS string for the message. - JSString* message = JS_NewUCStringCopyN(cx, (jschar*)aMessage.BeginReading(), - aMessage.Length()); - NS_ENSURE_TRUE(message, NS_ERROR_FAILURE); - - // Root it - jsval messageVal = STRING_TO_JSVAL(message); - nsAutoGCRoot rootedMessage(&messageVal, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - - nsCOMPtr source; - aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source)); - NS_ASSERTION(source, "Impossible!"); - - // Wrap the source thread. - nsCOMPtr wrappedThread; - rv = xpc->WrapNative(cx, mGlobal, source, NS_GET_IID(nsISupports), - getter_AddRefs(wrappedThread)); - NS_ENSURE_SUCCESS(rv, rv); - - JSObject* sourceThread; - rv = wrappedThread->GetJSObject(&sourceThread); - NS_ENSURE_SUCCESS(rv, rv); - - // Set up our arguments. - jsval argv[2] = { - STRING_TO_JSVAL(message), - OBJECT_TO_JSVAL(sourceThread) - }; - - // Get the listener object out of our wrapped listener. - nsCOMPtr wrappedListener = - do_QueryInterface(messageListener); - NS_ENSURE_TRUE(wrappedListener, NS_ERROR_NO_INTERFACE); - - JSObject* listener; - rv = wrappedListener->GetJSObject(&listener); - NS_ENSURE_SUCCESS(rv, rv); - - // And call it. - jsval rval; - PRBool success = JS_CallFunctionValue(cx, mGlobal, OBJECT_TO_JSVAL(listener), - 2, argv, &rval); - if (!success && JS_IsExceptionPending(cx)) { - // Make sure any pending exceptions are converted to errors for the pool. - JS_ReportPendingException(cx); - } - - return NS_OK; -} - -// From nsDOMWorkerBase -nsresult -nsDOMWorkerThread::DispatchMessage(nsIRunnable* aRunnable) -{ - nsresult rv = nsDOMThreadService::get()->Dispatch(this, aRunnable); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -nsDOMWorkerThread::Cancel() -{ - nsDOMWorkerBase::Cancel(); - - // Do this before waiting on the thread service below! - CancelScriptLoaders(); - CancelXHRs(); - - // If we're suspended there's a good chance that we're already paused waiting - // on the pool's monitor. Waiting on the thread service's lock will deadlock. - if (!IsSuspended()) { - nsDOMThreadService::get()->WaitForCanceledWorker(this); - } - - ClearTimeouts(); -} - -void -nsDOMWorkerThread::Suspend() -{ - nsDOMWorkerBase::Suspend(); - SuspendTimeouts(); -} - -void -nsDOMWorkerThread::Resume() -{ - nsDOMWorkerBase::Resume(); - ResumeTimeouts(); -} - -PRBool -nsDOMWorkerThread::SetGlobalForContext(JSContext* aCx) -{ - PRBool success = CompileGlobalObject(aCx); - if (!success) { - return PR_FALSE; - } - - JS_SetGlobalObject(aCx, mGlobal); - return PR_TRUE; -} - -PRBool -nsDOMWorkerThread::CompileGlobalObject(JSContext* aCx) -{ - if (mGlobal) { - return PR_TRUE; - } - - if (mCompiled) { - // Don't try to recompile a bad script. - return PR_FALSE; - } - - mCompiled = PR_TRUE; - - JSAutoRequest ar(aCx); - - JSObject* global = JS_NewObject(aCx, nsnull, nsnull, nsnull); - NS_ENSURE_TRUE(global, PR_FALSE); - - NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!"); - - // This call will root global. - PRBool success = JS_InitStandardClasses(aCx, global); - NS_ENSURE_TRUE(success, PR_FALSE); - - // Set up worker thread functions - success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions); - NS_ENSURE_TRUE(success, PR_FALSE); - - nsRefPtr - context(new nsDOMWorkerThreadContext(this)); - NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY); - - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - nsresult rv = xpc->InitClasses(aCx, global); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - - // XXX Fix this! - success = JS_DeleteProperty(aCx, global, "Components"); - NS_ENSURE_TRUE(success, PR_FALSE); - - nsCOMPtr contextWrapper; - rv = xpc->WrapNative(aCx, global, - NS_ISUPPORTS_CAST(nsIDOMWorkerThreadContext*, context), - NS_GET_IID(nsIDOMWorkerThreadContext), - getter_AddRefs(contextWrapper)); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - - JSObject* contextObj; - rv = contextWrapper->GetJSObject(&contextObj); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - - // Set up a name for our worker object - success = JS_DefineProperty(aCx, global, "threadContext", - OBJECT_TO_JSVAL(contextObj), nsnull, nsnull, - JSPROP_ENUMERATE); - NS_ENSURE_TRUE(success, PR_FALSE); - - jsval val; - - // From here on out we have to remember to null mGlobal if something fails! - mGlobal = global; - - if (mSource.IsEmpty()) { - NS_ASSERTION(!mSourceURL.IsEmpty(), "Must have a url here!"); - - nsRefPtr loader = new nsDOMWorkerScriptLoader(); - NS_ASSERTION(loader, "Out of memory!"); - if (!loader) { - mGlobal = NULL; - return PR_FALSE; - } - - rv = loader->LoadScript(this, aCx, mSourceURL); - JS_ReportPendingException(aCx); - if (NS_FAILED(rv)) { - mGlobal = NULL; - return PR_FALSE; - } - } - else { - NS_ASSERTION(!mSource.IsEmpty(), "No source text!"); - - JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal(); - - // Evaluate and execute the script - success = JS_EvaluateUCScriptForPrincipals(aCx, global, principal, - reinterpret_cast - (mSource.get()), - mSource.Length(), - "DOMWorker inline script", 1, - &val); - if (!success) { - mGlobal = NULL; - return PR_FALSE; - } - } - - // See if the message listener function was defined. - nsCOMPtr listener; - if (JS_LookupProperty(aCx, global, "messageListener", &val) && - JSVAL_IS_OBJECT(val) && - NS_SUCCEEDED(xpc->WrapJS(aCx, JSVAL_TO_OBJECT(val), - NS_GET_IID(nsIDOMWorkerMessageListener), - getter_AddRefs(listener)))) { - SetMessageListener(listener); - } - - return PR_TRUE; -} - -nsDOMWorkerTimeout* -nsDOMWorkerThread::FirstTimeout() -{ - // Only called within the lock! - PRCList* first = PR_LIST_HEAD(&mTimeouts); - return first == &mTimeouts ? - nsnull : - static_cast(first); -} - -nsDOMWorkerTimeout* -nsDOMWorkerThread::NextTimeout(nsDOMWorkerTimeout* aTimeout) -{ - // Only called within the lock! - nsDOMWorkerTimeout* next = - static_cast(PR_NEXT_LINK(aTimeout)); - return next == &mTimeouts ? nsnull : next; -} - -PRBool -nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout) -{ - // This should only ever be called on the worker thread... but there's no way - // to really assert that since we're using a thread pool. - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTimeout, "Null pointer!"); - - PRIntervalTime newInterval = aTimeout->GetInterval(); - - if (IsSuspended()) { - aTimeout->Suspend(PR_Now()); - } - - nsAutoLock lock(mLock); - - if (IsCanceled()) { - return PR_FALSE; - } - - // XXX Currently stored in the order that they should execute (like the window - // timeouts are) but we don't flush all expired timeouts the same way that - // the window does... Either we should or this is unnecessary. - for (nsDOMWorkerTimeout* timeout = FirstTimeout(); - timeout; - timeout = NextTimeout(timeout)) { - if (timeout->GetInterval() > newInterval) { - PR_INSERT_BEFORE(aTimeout, timeout); - return PR_TRUE; - } - } - - PR_APPEND_LINK(aTimeout, &mTimeouts); - return PR_TRUE; -} - -void -nsDOMWorkerThread::RemoveTimeout(nsDOMWorkerTimeout* aTimeout) -{ - nsAutoLock lock(mLock); - - PR_REMOVE_LINK(aTimeout); -} - -void -nsDOMWorkerThread::ClearTimeouts() -{ - nsAutoTArray, 20> timeouts; - { - nsAutoLock lock(mLock); - for (nsDOMWorkerTimeout* timeout = FirstTimeout(); - timeout; - timeout = NextTimeout(timeout)) { - timeouts.AppendElement(timeout); - } - } - - PRUint32 count = timeouts.Length(); - for (PRUint32 i = 0; i < count; i++) { - timeouts[i]->Cancel(); - } -} - -void -nsDOMWorkerThread::CancelTimeout(PRUint32 aId) -{ - nsRefPtr foundTimeout; - { - nsAutoLock lock(mLock); - for (nsDOMWorkerTimeout* timeout = FirstTimeout(); - timeout; - timeout = NextTimeout(timeout)) { - if (timeout->GetId() == aId) { - foundTimeout = timeout; - break; - } - } - } - - if (foundTimeout) { - foundTimeout->Cancel(); - } -} - -void -nsDOMWorkerThread::SuspendTimeouts() -{ - nsAutoTArray, 20> timeouts; - { - nsAutoLock lock(mLock); - for (nsDOMWorkerTimeout* timeout = FirstTimeout(); - timeout; - timeout = NextTimeout(timeout)) { - timeouts.AppendElement(timeout); - } - } - - PRTime now = PR_Now(); - - PRUint32 count = timeouts.Length(); - for (PRUint32 i = 0; i < count; i++) { - timeouts[i]->Suspend(now); - } -} - -void -nsDOMWorkerThread::ResumeTimeouts() -{ - nsAutoTArray, 20> timeouts; - { - nsAutoLock lock(mLock); - for (nsDOMWorkerTimeout* timeout = FirstTimeout(); - timeout; - timeout = NextTimeout(timeout)) { - NS_ASSERTION(timeout->IsSuspended(), "Should be suspended!"); - timeouts.AppendElement(timeout); - } - } - - PRTime now = PR_Now(); - - PRUint32 count = timeouts.Length(); - for (PRUint32 i = 0; i < count; i++) { - timeouts[i]->Resume(now); - } -} - -void -nsDOMWorkerThread::CancelScriptLoaders() -{ - nsAutoTArray loaders; - - // Must call cancel on the loaders outside the lock! - { - nsAutoLock lock(mLock); - loaders.AppendElements(mScriptLoaders); - - // Don't clear mScriptLoaders, they'll remove themselves as they get - // destroyed. - } - - PRUint32 loaderCount = loaders.Length(); - for (PRUint32 index = 0; index < loaderCount; index++) { - loaders[index]->Cancel(); - } -} - -PRBool -nsDOMWorkerThread::AddXHR(nsDOMWorkerXHR* aXHR) -{ - nsAutoLock lock(mLock); - - if (IsCanceled()) { - return PR_FALSE; - } - -#ifdef DEBUG - PRBool contains = mXHRs.Contains(aXHR); - NS_ASSERTION(!contains, "Adding an XHR twice!"); -#endif - - nsDOMWorkerXHR** newElement = mXHRs.AppendElement(aXHR); - NS_ENSURE_TRUE(newElement, PR_FALSE); - - return PR_TRUE; -} - -void -nsDOMWorkerThread::RemoveXHR(nsDOMWorkerXHR* aXHR) -{ - nsAutoLock lock(mLock); -#ifdef DEBUG - PRBool removed = -#endif - mXHRs.RemoveElement(aXHR); - NS_WARN_IF_FALSE(removed, "Removed an XHR that was never added?!"); -} - -void -nsDOMWorkerThread::CancelXHRs() -{ - nsAutoTArray xhrs; - - // Must call Cancel outside the lock! - { - nsAutoLock lock(mLock); - xhrs.AppendElements(mXHRs); - } - - PRUint32 xhrCount = xhrs.Length(); - for (PRUint32 index = 0; index < xhrCount; index++) { - xhrs[index]->Cancel(); - } -} - -NS_IMETHODIMP -nsDOMWorkerThread::PostMessage(const nsAString& aMessage) -{ - nsresult rv = PostMessageInternal(aMessage); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} diff --git a/dom/src/threads/nsDOMWorkerTimeout.cpp b/dom/src/threads/nsDOMWorkerTimeout.cpp index d04f561ae794..a067b1331c9c 100644 --- a/dom/src/threads/nsDOMWorkerTimeout.cpp +++ b/dom/src/threads/nsDOMWorkerTimeout.cpp @@ -47,7 +47,9 @@ // Other includes #include "nsContentUtils.h" #include "nsJSUtils.h" +#include "nsThreadUtils.h" #include "pratom.h" +#include "prtime.h" // DOMWorker includes #include "nsDOMThreadService.h" @@ -73,15 +75,15 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc, nsresult* aRv) : mCallback(nsnull), mCallbackArgs(nsnull), - mCallbackArgsLength(0) + mCallbackArgsLength(0), + mRuntime(NULL) { MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback); - JSRuntime* rt; - *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt); + *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime); NS_ENSURE_SUCCESS(*aRv,); - PRBool success = JS_AddNamedRootRT(rt, &mCallback, + PRBool success = JS_AddNamedRootRT(mRuntime, &mCallback, "nsDOMWorkerTimeout Callback Object"); CONSTRUCTOR_ENSURE_TRUE(success, *aRv); @@ -102,7 +104,7 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc, for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) { mCallbackArgs[i] = aArgv[i + 2]; - success = JS_AddNamedRootRT(rt, &mCallbackArgs[i], + success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[i], "nsDOMWorkerTimeout Callback Arg"); if (NS_UNLIKELY(!success)) { // Set this to i so that the destructor only unroots the right number of @@ -117,7 +119,7 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc, // Take care of the last arg. mCallbackArgs[mCallbackArgsLength - 1] = 0; - success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1], + success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[mCallbackArgsLength - 1], "nsDOMWorkerTimeout Callback Final Arg"); if (NS_UNLIKELY(!success)) { // Decrement this so that the destructor only unroots the right number of @@ -137,17 +139,10 @@ nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback() MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback); if (mCallback) { - 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); + for (PRUint32 i = 0; i < mCallbackArgsLength; i++) { + JS_RemoveRootRT(mRuntime, &mCallbackArgs[i]); } + JS_RemoveRootRT(mRuntime, &mCallback); } delete [] mCallbackArgs; @@ -178,7 +173,8 @@ nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc, JSContext* aCx, nsresult* aRv) : mExpression(nsnull), - mLineNumber(0) + mLineNumber(0), + mRuntime(NULL) { MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback); @@ -186,11 +182,10 @@ nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc, *aRv = expr ? NS_OK : NS_ERROR_FAILURE; NS_ENSURE_SUCCESS(*aRv,); - JSRuntime* rt; - *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt); + *aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime); NS_ENSURE_SUCCESS(*aRv,); - PRBool success = JS_AddNamedRootRT(rt, &mExpression, + PRBool success = JS_AddNamedRootRT(mRuntime, &mExpression, "nsDOMWorkerTimeout Expression"); CONSTRUCTOR_ENSURE_TRUE(success, *aRv); @@ -212,14 +207,7 @@ nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback() MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback); if (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); - } + JS_RemoveRootRT(mRuntime, &mExpression); } } @@ -231,37 +219,22 @@ nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout, return NS_ERROR_NOT_IMPLEMENTED; } -nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, +nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorker* aWorker, PRUint32 aId) -: mWorker(aWorker), +: nsDOMWorkerFeature(aWorker, aId), mInterval(0), - mIsInterval(PR_FALSE), - mId(aId), mSuspendSpinlock(0), + mSuspendInterval(0), + mIsInterval(PR_FALSE), mIsSuspended(PR_FALSE), - mSuspendInterval(0) -#ifdef DEBUG -, mFiredOrCanceled(PR_FALSE) -#endif + mSuspendedBeforeStart(PR_FALSE), + mStarted(PR_FALSE) { - MOZ_COUNT_CTOR(nsDOMWorkerTimeout); NS_ASSERTION(mWorker, "Need a worker here!"); } -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) +NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerTimeout, nsDOMWorkerFeature, + nsITimerCallback) nsresult nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv, @@ -294,6 +267,8 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv, mInterval = interval; + mIsInterval = aIsInterval; + mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC; nsresult rv; @@ -315,20 +290,11 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv, 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 timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); @@ -338,18 +304,30 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv, 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; +} - if (!mWorker->AddTimeout(this)) { - // Must have been canceled. - mTimer->Cancel(); - mTimer = nsnull; - return NS_ERROR_ABORT; +nsresult +nsDOMWorkerTimeout::Start() +{ + if (IsSuspended()) { + NS_ASSERTION(mSuspendedBeforeStart, "Bad state!"); + return NS_OK; } + 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; } @@ -360,10 +338,6 @@ nsDOMWorkerTimeout::Run() LOG(("Worker [0x%p] running timeout [0x%p] with id %u", static_cast(mWorker.get()), static_cast(this), mId)); -#ifdef DEBUG - mFiredOrCanceled = PR_TRUE; -#endif - JSContext* cx; nsresult rv = nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx); @@ -391,10 +365,6 @@ nsDOMWorkerTimeout::Cancel() LOG(("Worker [0x%p] canceling timeout [0x%p] with id %u", static_cast(mWorker.get()), static_cast(this), mId)); -#ifdef DEBUG - mFiredOrCanceled = PR_TRUE; -#endif - { AutoSpinlock lock(this); @@ -410,21 +380,29 @@ nsDOMWorkerTimeout::Cancel() } void -nsDOMWorkerTimeout::Suspend(PRTime aNow) +nsDOMWorkerTimeout::Suspend() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mTimer, "Impossible to get here without a timer!"); +#ifdef DEBUG + if (mStarted) { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + } +#endif AutoSpinlock lock(this); - if (!mIsSuspended) { - mIsSuspended = PR_TRUE; - mSuspendedRef = this; + NS_ASSERTION(!IsSuspendedNoLock(), "Bad state!"); + + mIsSuspended = PR_TRUE; + mSuspendedRef = this; + + if (!mStarted) { + mSuspendedBeforeStart = PR_TRUE; + return; } mTimer->Cancel(); - mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) / + mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - PR_Now())) / (PRTime)PR_USEC_PER_MSEC; LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)", @@ -433,9 +411,14 @@ nsDOMWorkerTimeout::Suspend(PRTime aNow) } void -nsDOMWorkerTimeout::Resume(PRTime aNow) +nsDOMWorkerTimeout::Resume() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +#ifdef DEBUG + if (!mSuspendedBeforeStart) { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + } +#endif + NS_ASSERTION(mTimer, "Impossible to get here without a timer!"); LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u", @@ -445,7 +428,14 @@ nsDOMWorkerTimeout::Resume(PRTime aNow) NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!"); - mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC; + if (mSuspendedBeforeStart) { + NS_ASSERTION(!mSuspendInterval, "Bad state!"); + mSuspendedBeforeStart = PR_FALSE; + mSuspendInterval = mInterval; + mStarted = PR_TRUE; + } + + mTargetTime = PR_Now() + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC; #ifdef DEBUG nsresult rv = @@ -499,10 +489,9 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTimer) 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(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; @@ -511,9 +500,6 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTimer) nsITimer::TYPE_REPEATING_SLACK); NS_ENSURE_SUCCESS(rv, rv); } - - mIsSuspended = PR_FALSE; - mSuspendedRef = nsnull; } } diff --git a/dom/src/threads/nsDOMWorkerTimeout.h b/dom/src/threads/nsDOMWorkerTimeout.h index 28492ddbf9d1..098d3975810c 100644 --- a/dom/src/threads/nsDOMWorkerTimeout.h +++ b/dom/src/threads/nsDOMWorkerTimeout.h @@ -47,60 +47,61 @@ #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsStringGlue.h" -#include "prclist.h" // DOMWorker includes -#include "nsDOMWorkerThread.h" +#include "nsDOMWorker.h" /** * The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created - * 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 + * 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 * 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 PRCList, +class nsDOMWorkerTimeout : public nsDOMWorkerFeature, public nsITimerCallback { public: - NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSITIMERCALLBACK - nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId); - ~nsDOMWorkerTimeout(); + nsDOMWorkerTimeout(nsDOMWorker* aWorker, + PRUint32 aId); - nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv, + nsresult Init(JSContext* aCx, + PRUint32 aArgc, + jsval* aArgv, PRBool aIsInterval); + nsresult Start(); + nsresult Run(); - void Cancel(); - void Suspend(PRTime aNow); - void Resume(PRTime aNow); + virtual void Cancel(); + virtual void Suspend(); + virtual void Resume(); PRIntervalTime GetInterval() { return mInterval; } - nsDOMWorkerThread* GetWorker() { + nsDOMWorker* GetWorker() { return mWorker; } - PRUint32 GetId() { - return mId; - } - PRBool IsSuspended() { AutoSpinlock lock(this); return IsSuspendedNoLock(); } private: + ~nsDOMWorkerTimeout() { } + void AcquireSpinlock(); void ReleaseSpinlock(); @@ -137,7 +138,9 @@ private: 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); @@ -145,12 +148,15 @@ private: 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, @@ -159,31 +165,26 @@ private: JSString* mExpression; nsString mFileName; PRUint32 mLineNumber; + JSRuntime* mRuntime; }; - // Hold the worker alive! - nsRefPtr mWorker; - // Hold this object alive! nsCOMPtr mTimer; PRUint32 mInterval; - PRBool mIsInterval; PRTime mTargetTime; nsAutoPtr mCallback; - PRUint32 mId; - PRInt32 mSuspendSpinlock; - PRBool mIsSuspended; PRUint32 mSuspendInterval; nsRefPtr mSuspendedRef; -#ifdef DEBUG - PRBool mFiredOrCanceled; -#endif + PRPackedBool mIsInterval; + PRPackedBool mIsSuspended; + PRPackedBool mSuspendedBeforeStart; + PRPackedBool mStarted; }; #endif /* __NSDOMWORKERTIMEOUT_H__ */ diff --git a/dom/src/threads/nsDOMWorkerXHR.cpp b/dom/src/threads/nsDOMWorkerXHR.cpp index 4aa3d410194b..4c68ca853d9a 100644 --- a/dom/src/threads/nsDOMWorkerXHR.cpp +++ b/dom/src/threads/nsDOMWorkerXHR.cpp @@ -56,6 +56,7 @@ // DOMWorker includes #include "nsDOMThreadService.h" +#include "nsDOMWorkerEvents.h" #include "nsDOMWorkerPool.h" #include "nsDOMWorkerXHRProxy.h" @@ -91,9 +92,9 @@ const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes = PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >= nsDOMWorkerXHREventTarget::sMaxUploadEventTypes); -NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget, - nsIDOMEventTarget, - nsIXMLHttpRequestEventTarget) +NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHREventTarget, + nsDOMWorkerMessageHandler, + nsIXMLHttpRequestEventTarget) PRUint32 nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString) @@ -109,9 +110,13 @@ nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString) NS_IMETHODIMP nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ENSURE_ARG_POINTER(aOnabort); - nsCOMPtr listener = GetOnXListener(LISTENER_TYPE_ABORT); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]); + + nsCOMPtr listener = GetOnXListener(type); listener.forget(aOnabort); return NS_OK; @@ -120,15 +125,24 @@ nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort) NS_IMETHODIMP nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort) { - return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]); + + return SetOnXListener(type, aOnabort); } NS_IMETHODIMP nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror) { NS_ENSURE_ARG_POINTER(aOnerror); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - nsCOMPtr listener = GetOnXListener(LISTENER_TYPE_ERROR); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]); + + nsCOMPtr listener = GetOnXListener(type); listener.forget(aOnerror); return NS_OK; @@ -137,15 +151,24 @@ nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror) NS_IMETHODIMP nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror) { - return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]); + + return SetOnXListener(type, aOnerror); } NS_IMETHODIMP nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ENSURE_ARG_POINTER(aOnload); - nsCOMPtr listener = GetOnXListener(LISTENER_TYPE_LOAD); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]); + + nsCOMPtr listener = GetOnXListener(type); listener.forget(aOnload); return NS_OK; @@ -154,16 +177,24 @@ nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload) NS_IMETHODIMP nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload) { - return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]); + + return SetOnXListener(type, aOnload); } NS_IMETHODIMP nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ENSURE_ARG_POINTER(aOnloadstart); - nsCOMPtr listener = - GetOnXListener(LISTENER_TYPE_LOADSTART); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]); + + nsCOMPtr listener = GetOnXListener(type); listener.forget(aOnloadstart); return NS_OK; @@ -172,16 +203,24 @@ nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart) NS_IMETHODIMP nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart) { - return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]); + + return SetOnXListener(type, aOnloadstart); } NS_IMETHODIMP nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ENSURE_ARG_POINTER(aOnprogress); - nsCOMPtr listener = - GetOnXListener(LISTENER_TYPE_PROGRESS); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]); + + nsCOMPtr listener = GetOnXListener(type); listener.forget(aOnprogress); return NS_OK; @@ -190,230 +229,183 @@ nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress) NS_IMETHODIMP nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress) { - return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE); -} + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); -NS_IMETHODIMP -nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - PRBool aUseCapture) -{ - NS_ENSURE_ARG_POINTER(aListener); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]); - 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) - -#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(); - } - - 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 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 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; + return SetOnXListener(type, aOnprogress); } nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR) : mWorkerXHR(aWorkerXHR) { - NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!"); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aWorkerXHR, "Null pointer!"); } -NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget, - nsIXMLHttpRequestUpload, - nsIClassInfo) +NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget, + nsIXMLHttpRequestUpload) NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget, nsIXMLHttpRequestEventTarget, nsIXMLHttpRequestUpload) -NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload) +NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHRUpload) -nsresult -nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType, +NS_IMETHODIMP +nsDOMWorkerXHRUpload::AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, - PRBool aOnXListener) + PRBool aUseCapture) { - if (mWorkerXHR->mCanceled) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aListener); + + if (mWorkerXHR->mWorker->IsCanceled()) { return NS_ERROR_ABORT; } - return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener, - PR_TRUE); + 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); } nsresult -nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType, - nsIDOMEventListener* aListener) +nsDOMWorkerXHRUpload::SetOnXListener(const nsAString& aType, + nsIDOMEventListener* aListener) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + if (mWorkerXHR->mCanceled) { return NS_ERROR_ABORT; } - return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE); -} - -nsresult -nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent) -{ - if (mWorkerXHR->mCanceled) { - return NS_ERROR_ABORT; + PRUint32 type = GetListenerTypeFromString(aType); + if (type > sMaxUploadEventTypes) { + // Silently ignore junk events. + return NS_OK; } - return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE); + return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener); } -already_AddRefed -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) +nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorker* aWorker) +: nsDOMWorkerFeature(aWorker), + mWrappedNative(nsnull), + mCanceled(PR_FALSE) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aWorker, "Must have a worker!"); } -nsDOMWorkerXHR::~nsDOMWorkerXHR() -{ - if (!mCanceled) { - mWorker->RemoveXHR(this); - } -} +// 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) -NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget, - nsIXMLHttpRequest, - nsIClassInfo) +NS_IMPL_QUERY_INTERFACE_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget, + nsIXMLHttpRequest, + nsIXPCScriptable) NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget, nsIXMLHttpRequestEventTarget, nsIXMLHttpRequest) -NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR) +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; +} 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 proxy = new nsDOMWorkerXHRProxy(this); NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY); @@ -446,14 +438,12 @@ nsDOMWorkerXHR::Cancel() mXHRProxy->Destroy(); } - mWorker->RemoveXHR(this); mWorker = nsnull; } nsresult -nsDOMWorkerXHR::SetEventListener(PRUint32 aType, - nsIDOMEventListener* aListener, - PRBool aOnXListener) +nsDOMWorkerXHR::SetOnXListener(const nsAString& aType, + nsIDOMEventListener* aListener) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); @@ -461,49 +451,20 @@ nsDOMWorkerXHR::SetEventListener(PRUint32 aType, 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 mXHRProxy->HandleWorkerEvent(aEvent, PR_FALSE); -} - -already_AddRefed -nsDOMWorkerXHR::GetOnXListener(PRUint32 aType) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - if (mCanceled) { - return nsnull; - } - - return mXHRProxy->GetOnXListener(aType, PR_FALSE); + return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener); } NS_IMETHODIMP nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aChannel); *aChannel = nsnull; return NS_OK; @@ -512,6 +473,8 @@ nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel) NS_IMETHODIMP nsDOMWorkerXHR::GetResponseXML(nsIDOMDocument** aResponseXML) { + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aResponseXML); *aResponseXML = nsnull; return NS_OK; @@ -853,7 +816,7 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - nsRefPtr worker = mWorker; + nsRefPtr worker = mWorker; if (!worker) { return NS_ERROR_ABORT; } @@ -878,32 +841,24 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload) NS_IMETHODIMP nsDOMWorkerXHR::GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange) { - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - if (mCanceled) { - return NS_ERROR_ABORT; - } - NS_ENSURE_ARG_POINTER(aOnreadystatechange); - nsCOMPtr listener = - mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]); + nsCOMPtr listener = GetOnXListener(type); listener.forget(aOnreadystatechange); + return NS_OK; } NS_IMETHODIMP nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange) { - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + nsAutoString type; + type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]); - if (mCanceled) { - return NS_ERROR_ABORT; - } - - return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE, - aOnreadystatechange, PR_TRUE, PR_FALSE); + return SetOnXListener(type, aOnreadystatechange); } NS_IMETHODIMP diff --git a/dom/src/threads/nsDOMWorkerXHR.h b/dom/src/threads/nsDOMWorkerXHR.h index 8c101e7cab22..fbd9fdc74c2c 100644 --- a/dom/src/threads/nsDOMWorkerXHR.h +++ b/dom/src/threads/nsDOMWorkerXHR.h @@ -40,10 +40,9 @@ #define __NSDOMWORKERXHR_H__ // Bases -#include "nsIXMLHttpRequest.h" #include "nsIClassInfo.h" - -// Interfaces +#include "nsIXMLHttpRequest.h" +#include "nsIXPCScriptable.h" // Other includes #include "nsAutoPtr.h" @@ -52,7 +51,9 @@ #include "prlock.h" // DOMWorker includes -#include "nsDOMWorkerThread.h" +#include "nsDOMWorker.h" +#include "nsDOMWorkerMacros.h" +#include "nsDOMWorkerXHRProxy.h" // Convenience defines for event *indexes* in the sListenerTypes array. #define LISTENER_TYPE_ABORT 0 @@ -62,15 +63,14 @@ #define LISTENER_TYPE_PROGRESS 4 #define LISTENER_TYPE_READYSTATECHANGE 5 -class nsDOMWorkerXHR; -class nsDOMWorkerXHREvent; -class nsDOMWorkerXHRProxy; +class nsIXPConnectWrappedNative; -class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget +class nsDOMWorkerXHREventTarget : public nsDOMWorkerMessageHandler, + public nsIXMLHttpRequestEventTarget { public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMEVENTTARGET + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::) NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET static const char* const sListenerTypes[]; @@ -79,58 +79,16 @@ public: 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 - GetOnXListener(PRUint32 aType) = 0; - protected: virtual ~nsDOMWorkerXHREventTarget() { } }; -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 - GetOnXListener(PRUint32 aType); - -protected: - virtual ~nsDOMWorkerXHRUpload() { } - - nsRefPtr mWorkerXHR; -}; +class nsDOMWorkerXHRUpload; class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget, + public nsDOMWorkerFeature, public nsIXMLHttpRequest, - public nsIClassInfo + public nsIXPCScriptable { friend class nsDOMWorkerXHREvent; friend class nsDOMWorkerXHRLastProgressOrLoadEvent; @@ -140,38 +98,60 @@ class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget, public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIXMLHTTPREQUEST - NS_DECL_NSICLASSINFO + NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::) + NS_DECL_NSICLASSINFO_GETINTERFACES + NS_DECL_NSIXPCSCRIPTABLE - nsDOMWorkerXHR(nsDOMWorkerThread* aWorker); + nsDOMWorkerXHR(nsDOMWorker* aWorker); nsresult Init(); - void Cancel(); + virtual 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); - - virtual already_AddRefed - GetOnXListener(PRUint32 aType); - -protected: - virtual ~nsDOMWorkerXHR(); +private: + virtual ~nsDOMWorkerXHR() { } PRLock* Lock() { return mWorker->Lock(); } - nsRefPtr mWorker; + nsIXPConnectWrappedNative* GetWrappedNative() { + return mWrappedNative; + } + nsRefPtr mXHRProxy; nsRefPtr 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 mWorkerXHR; +}; + #endif /* __NSDOMWORKERXHR_H__ */ diff --git a/dom/src/threads/nsDOMWorkerXHRProxy.cpp b/dom/src/threads/nsDOMWorkerXHRProxy.cpp index 161e82460327..d1ee4316dd54 100644 --- a/dom/src/threads/nsDOMWorkerXHRProxy.cpp +++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp @@ -59,8 +59,12 @@ #include "prthread.h" // DOMWorker includes +#include "nsDOMThreadService.h" +#include "nsDOMWorker.h" +#include "nsDOMWorkerEvents.h" +#include "nsDOMWorkerMacros.h" #include "nsDOMWorkerPool.h" -#include "nsDOMWorkerThread.h" +#include "nsDOMWorkerXHR.h" #include "nsDOMWorkerXHRProxiedFunctions.h" #define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes @@ -109,7 +113,7 @@ public: NS_DECL_ISUPPORTS nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable, - nsDOMWorkerThread* aWorker) + nsDOMWorker* aWorker) : mTarget(aTarget), mRunnable(aRunnable), mWorker(aWorker), mResult(NS_OK), mDone(PR_FALSE) { } @@ -157,329 +161,13 @@ public: private: nsCOMPtr mTarget; nsCOMPtr mRunnable; - nsRefPtr mWorker; + nsRefPtr 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 mXHRProxy; - nsCOMPtr 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 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 mainThreadTarget; - rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr upload(do_QueryInterface(mainThreadTarget)); - if (upload) { - mUploadEvent = PR_TRUE; - mTarget = - static_cast(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: @@ -496,6 +184,9 @@ public: if (!mProxy->mCanceled) { nsAutoLock lock(mProxy->mWorkerXHR->Lock()); mProxy->mLastProgressOrLoadEvent.swap(lastProgressOrLoadEvent); + if (mProxy->mCanceled) { + return NS_ERROR_ABORT; + } } if (lastProgressOrLoadEvent) { @@ -629,7 +320,7 @@ nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR) nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy() { if (mOwnedByXHR) { - mWorkerXHR->Release(); + mWorkerXHRWN = nsnull; } else if (mXHR) { nsCOMPtr mainThread; @@ -652,18 +343,6 @@ nsDOMWorkerXHRProxy::Init() 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); @@ -693,20 +372,20 @@ 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(); } - mLastXHREvent = nsnull; + NS_ASSERTION(!(mLastProgressOrLoadEvent && mLastXHREvent), "Going to leak!"); return NS_OK; } @@ -717,7 +396,7 @@ nsDOMWorkerXHRProxy::InitInternal() NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!"); - nsDOMWorkerThread* worker = mWorkerXHR->mWorker; + nsDOMWorker* worker = mWorkerXHR->mWorker; nsRefPtr pool = worker->Pool(); if (worker->IsCanceled()) { @@ -747,11 +426,11 @@ nsDOMWorkerXHRProxy::InitInternal() nsRefPtr nullEvent = new nsDOMWorkerXHREvent(this); NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY); - rv = nullEvent->Init(xhr); + rv = nullEvent->SnapshotXHRState(xhr); NS_ENSURE_SUCCESS(rv, rv); - mLastXHREvent.swap(nullEvent); - mLastXHREvent->EventHandled(); + nullEvent->EventHandled(); + mLastXHREvent.swap(nullEvent); xhrConcrete->SetRequestObserver(this); @@ -797,7 +476,6 @@ nsDOMWorkerXHRProxy::DestroyInternal() 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) { @@ -816,9 +494,10 @@ nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd) nsCOMPtr xhrTarget(do_QueryInterface(mXHR)); NS_ASSERTION(xhrTarget, "This shouldn't fail!"); - EventListenerFunction function = aAdd ? - &nsIDOMEventTarget::AddEventListener : - &nsIDOMEventTarget::RemoveEventListener; + EventListenerFunction addRemoveEventListener = + aAdd ? + &nsIDOMEventTarget::AddEventListener : + &nsIDOMEventTarget::RemoveEventListener; nsAutoString eventName; PRUint32 index = 0; @@ -829,14 +508,14 @@ nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd) for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) { eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]); - (xhrTarget.get()->*function)(eventName, this, PR_FALSE); - (uploadTarget.get()->*function)(eventName, this, PR_FALSE); + (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE); + (uploadTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE); } } for (; index < MAX_XHR_LISTENER_TYPE; index++) { eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]); - (xhrTarget.get()->*function)(eventName, this, PR_FALSE); + (xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE); } } @@ -853,146 +532,46 @@ nsDOMWorkerXHRProxy::FlipOwnership() nsRefPtr kungFuDeathGrip(this); if (mOwnedByXHR) { - mWorkerXHR->AddRef(); + mWorkerXHRWN = mWorkerXHR->GetWrappedNative(); + NS_ASSERTION(mWorkerXHRWN, "Null pointer!"); mXHR->Release(); } else { mXHR->AddRef(); - mWorkerXHR->Release(); + mWorkerXHRWN = nsnull; } } nsresult -nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType, - nsIDOMEventListener* aListener, - PRBool aOnXListener, - PRBool aUploadListener) +nsDOMWorkerXHRProxy::UploadEventListenerAdded() { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - 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]; - 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 - - 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!"); - } - - 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); - } - // 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 attachRunnable = - new nsDOMWorkerXHRAttachUploadListenersRunnable(this); - NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY); - - nsRefPtr 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. + // hit the main thread to attach the upload listeners. Otherwise there's + // nothing to do here. + if (mWantUploadListeners) { return NS_OK; } - ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] : - mXHRListeners[aType]; + nsRefPtr attachRunnable = + new nsDOMWorkerXHRAttachUploadListenersRunnable(this); + NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY); - nsAutoLock lock(mWorkerXHR->Lock()); + nsRefPtr runnable = + new nsResultReturningRunnable(mMainThread, attachRunnable, + mWorkerXHR->mWorker); + NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); - listeners.RemoveElement(aListener); + nsresult rv = runnable->Dispatch(); + if (NS_FAILED(rv)) { + return rv; + } + NS_ASSERTION(mWantUploadListeners, "Should have set this!"); return NS_OK; } -already_AddRefed -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 listener = onXListener->Inner(); - return listener.forget(); -} - nsresult nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, PRBool aUploadEvent) @@ -1000,38 +579,19 @@ nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aEvent, "Should not be null!"); - if (mCanceled || - (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) { - return NS_OK; + { + nsAutoLock lock(mWorkerXHR->Lock()); + + if (mCanceled || + (aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) { + return NS_OK; + } + + mLastXHREvent = aEvent; } - 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); + nsIDOMEvent* event = static_cast(aEvent); + return HandleEventInternal(aEvent->mXHREventType, event, aUploadEvent); } nsresult @@ -1045,6 +605,7 @@ nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType, #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!"); @@ -1055,82 +616,23 @@ nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType, return NS_ERROR_ABORT; } - ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] : - mXHRListeners[aType]; + nsIDOMEventTarget* target = aUploadListener ? + static_cast(mWorkerXHR->mUpload) : + static_cast(mWorkerXHR); - nsAutoTArray 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!"); - - nsTArray doomedArrays; - PRBool ok = doomedArrays.SetLength(MAX_XHR_LISTENER_TYPE + - MAX_UPLOAD_LISTENER_TYPE); - NS_ENSURE_TRUE(ok,); - - nsTArray 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. + return target->DispatchEvent(aEvent, nsnull); } PRBool -nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType, +nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType, nsIDOMEvent* aEvent) { - NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!"); +#ifdef DEBUG + PRUint32 type = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(aType); + NS_ASSERTION(type < MAX_XHR_LISTENER_TYPE, "Bad type!"); +#endif - if (mXHRListeners[aType].Length()) { + if (mWorkerXHR->HasListeners(aType)) { return PR_TRUE; } @@ -1146,7 +648,7 @@ nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType, checkUploadListeners = PR_TRUE; } - if (checkUploadListeners && mUploadListeners[aType].Length()) { + if (checkUploadListeners && mWorkerXHR->mUpload->HasListeners(aType)) { return PR_TRUE; } @@ -1171,20 +673,29 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent) 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; } - if (!HasListenersForType(type, aEvent)) { + 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) { return NS_OK; } @@ -1202,6 +713,11 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent) { nsAutoLock lock(mWorkerXHR->Lock()); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + mLastProgressOrLoadEvent.swap(newEvent); if (newEvent) { diff --git a/dom/src/threads/nsDOMWorkerXHRProxy.h b/dom/src/threads/nsDOMWorkerXHRProxy.h index 9f85f2c037d7..391c56261313 100644 --- a/dom/src/threads/nsDOMWorkerXHRProxy.h +++ b/dom/src/threads/nsDOMWorkerXHRProxy.h @@ -45,17 +45,18 @@ #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; @@ -72,11 +73,6 @@ class nsDOMWorkerXHRProxy : public nsIRunnable, friend class nsDOMWorkerXHR; friend class nsDOMWorkerXHRUpload; - typedef nsCOMPtr Listener; - typedef nsTArray ListenerArray; - - typedef nsRefPtr WrappedListener; - typedef nsresult (NS_STDCALL nsIDOMEventTarget::*EventListenerFunction) (const nsAString&, nsIDOMEventListener*, PRBool); @@ -118,28 +114,15 @@ protected: void AddRemoveXHRListeners(PRBool aAdd); void FlipOwnership(); - nsresult AddEventListener(PRUint32 aType, - nsIDOMEventListener* aListener, - PRBool aOnXListener, - PRBool aUploadListener); + nsresult UploadEventListenerAdded(); - nsresult RemoveEventListener(PRUint32 aType, - nsIDOMEventListener* aListener, - PRBool aUploadListener); - - already_AddRefed GetOnXListener(PRUint32 aType, - PRBool aUploadListener); - - nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, PRBool aUploadEvent); - - nsresult HandleWorkerEvent(nsIDOMEvent* aEvent, PRBool aUploadEvent); + nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* 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, @@ -162,10 +145,12 @@ protected: // aEvent is used to see if we should check upload listeners as well. If left // unset we always check upload listeners. - PRBool HasListenersForType(PRUint32 aType, nsIDOMEvent* aEvent = nsnull); + PRBool HasListenersForType(const nsAString& aType, + nsIDOMEvent* aEvent = nsnull); // May be weak or strong, check mOwnedByXHR. nsDOMWorkerXHR* mWorkerXHR; + nsCOMPtr mWorkerXHRWN; // May be weak or strong, check mOwnedByXHR. nsIXMLHttpRequest* mXHR; @@ -179,12 +164,6 @@ protected: nsRefPtr mLastXHREvent; nsRefPtr mLastProgressOrLoadEvent; - nsTArray mXHRListeners; - nsTArray mXHROnXListeners; - - nsTArray mUploadListeners; - nsTArray mUploadOnXListeners; - SyncEventQueue* mSyncEventQueue; PRInt32 mChannelID; diff --git a/dom/src/threads/test/Makefile.in b/dom/src/threads/test/Makefile.in index c1db91fa44a6..a8f4b602ebda 100644 --- a/dom/src/threads/test/Makefile.in +++ b/dom/src/threads/test/Makefile.in @@ -47,20 +47,32 @@ 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_importScripts.html \ - test_simpleThread.html \ - test_threadErrors.html \ - test_threadTimeouts.html \ test_longThread.html \ + longThread_worker.js \ test_recursion.html \ + recursion_worker.js \ test_regExpStatics.html \ + regExpStatics_worker.js \ + 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_xhr.html \ + xhr_worker.js \ testXHR.txt \ + test_fibonacci.html \ + fibonacci_worker.js \ $(NULL) libs:: $(_TEST_FILES) diff --git a/dom/src/threads/test/fibonacci_worker.js b/dom/src/threads/test/fibonacci_worker.js new file mode 100644 index 000000000000..591cf7594d8e --- /dev/null +++ b/dom/src/threads/test/fibonacci_worker.js @@ -0,0 +1,28 @@ +var results = []; + +function messageHandler(event) { + results.push(parseInt(event.data)); + if (results.length == 2) { + postMessage(results[0] + results[1]); + } +} + +function errorHandler(event) { + throw event.data; +} + +onmessage = function(event) { + var n = parseInt(event.data); + + if (n == 0 || n == 1) { + postMessage(n); + return; + } + + for (var i = 1; i <= 2; i++) { + var worker = new Worker("fibonacci_worker.js"); + worker.onmessage = messageHandler; + worker.onerror = errorHandler; + worker.postMessage(n - i); + } +} diff --git a/dom/src/threads/test/importScripts_worker.js b/dom/src/threads/test/importScripts_worker.js index fcae6f54e259..c6c93ce472b9 100644 --- a/dom/src/threads/test/importScripts_worker.js +++ b/dom/src/threads/test/importScripts_worker.js @@ -1,17 +1,17 @@ -function messageListener(message, source) { - switch (message) { +onmessage = function(event) { + switch (event.data) { case 'start': - loadScripts("importScripts_worker_imported2.js"); + importScripts("importScripts_worker_imported2.js"); importedScriptFunction2(); tryBadScripts(); - source.postMessage('started'); + postMessage('started'); break; case 'stop': tryBadScripts(); - postMessageToPool('stopped'); + postMessage('stopped'); break; default: - throw new Error("Bad message: " + message); + throw new Error("Bad message: " + event.data); break; } } @@ -19,7 +19,7 @@ function messageListener(message, source) { // This caused security exceptions in the past, make sure it doesn't! var constructor = {}.constructor; -loadScripts("importScripts_worker_imported1.js"); +importScripts("importScripts_worker_imported1.js"); // Try to call a function defined in the imported script. importedScriptFunction(); @@ -40,7 +40,7 @@ function tryBadScripts() { var caughtException = false; var url = badScripts[i]; try { - loadScripts(url); + importScripts(url); } catch (e) { caughtException = true; diff --git a/dom/src/threads/test/longThread_worker.js b/dom/src/threads/test/longThread_worker.js new file mode 100644 index 000000000000..d3afbd43ec43 --- /dev/null +++ b/dom/src/threads/test/longThread_worker.js @@ -0,0 +1,10 @@ +onmessage = function(event) { + switch (event.data) { + case "start": + for (var i = 0; i < 10000000; i++) { }; + postMessage("done"); + break; + default: + throw "Bad message: " + event.data; + } +}; diff --git a/dom/src/threads/test/recursion_worker.js b/dom/src/threads/test/recursion_worker.js new file mode 100644 index 000000000000..5b5fc53528cb --- /dev/null +++ b/dom/src/threads/test/recursion_worker.js @@ -0,0 +1,14 @@ +function recurse() { + recurse(); +} + +onmessage = function(event) { + switch (event.data) { + case "start": + recurse(); + throw "Never should have gotten here!"; + break; + default: + throw "Bad message: " + event.data; + } +} diff --git a/dom/src/threads/test/regExpStatics_worker.js b/dom/src/threads/test/regExpStatics_worker.js new file mode 100644 index 000000000000..dc3a87a7e33a --- /dev/null +++ b/dom/src/threads/test/regExpStatics_worker.js @@ -0,0 +1,26 @@ +var runCount = 0; +var timeout; + +onmessage = function() { + 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) { + postMessage("done"); + } +} + diff --git a/dom/src/threads/test/simpleThread_worker.js b/dom/src/threads/test/simpleThread_worker.js new file mode 100644 index 000000000000..8b6250906265 --- /dev/null +++ b/dom/src/threads/test/simpleThread_worker.js @@ -0,0 +1,20 @@ +function messageListener(event) { + switch (event.data) { + case "no-op": + break; + case "start": + for (var i = 0; i < 1000; i++) { } + postMessage("started"); + break; + case "stop": + for (var i = 0; i < 1000; i++) { } + self.postMessage('no-op'); + postMessage("stopped"); + self.removeEventListener("message", messageListener, false); + break; + default: + throw 'Bad message: ' + event.data; + } +} + +addEventListener("message", messageListener, false); diff --git a/dom/src/threads/test/test_fibonacci.html b/dom/src/threads/test/test_fibonacci.html new file mode 100644 index 000000000000..b4a30457dba9 --- /dev/null +++ b/dom/src/threads/test/test_fibonacci.html @@ -0,0 +1,43 @@ + + + + + Test for DOM Worker Threads with Fibonacci + + + + + +DOM Worker Threads Fibonacci +

+ +
+
+
+ + + diff --git a/dom/src/threads/test/test_importScripts.html b/dom/src/threads/test/test_importScripts.html index 8e4846bacb9b..f5209682e7a2 100644 --- a/dom/src/threads/test/test_importScripts.html +++ b/dom/src/threads/test/test_importScripts.html @@ -18,11 +18,12 @@ Tests of DOM Worker Threads (Bug 437152)
 
 
diff --git a/dom/src/threads/test/test_longThread.html b/dom/src/threads/test/test_longThread.html index f073eaa8ec77..ace3f106b3d0 100644 --- a/dom/src/threads/test/test_longThread.html +++ b/dom/src/threads/test/test_longThread.html @@ -18,63 +18,36 @@ Tests of DOM Worker Threads (Bug 437152)
 
 
diff --git a/dom/src/threads/test/test_recursion.html b/dom/src/threads/test/test_recursion.html index 3ba3b7f85648..716a69207df5 100644 --- a/dom/src/threads/test/test_recursion.html +++ b/dom/src/threads/test/test_recursion.html @@ -17,31 +17,16 @@ Tests of DOM Worker Threads
 
 
diff --git a/dom/src/threads/test/test_threadErrors.html b/dom/src/threads/test/test_threadErrors.html index 48ff828952dd..48f5f1b75f2d 100644 --- a/dom/src/threads/test/test_threadErrors.html +++ b/dom/src/threads/test/test_threadErrors.html @@ -18,79 +18,40 @@ Tests of DOM Worker Threads (Bug 437152)
 
 
diff --git a/dom/src/threads/test/test_threadTimeouts.html b/dom/src/threads/test/test_threadTimeouts.html index 8df88d60bd9b..a2cc6d13db86 100644 --- a/dom/src/threads/test/test_threadTimeouts.html +++ b/dom/src/threads/test/test_threadTimeouts.html @@ -18,86 +18,36 @@ Tests of DOM Worker Threads (Bug 437152)
 
 
diff --git a/dom/src/threads/test/test_xhr.html b/dom/src/threads/test/test_xhr.html index adc4fac8083c..2c2c865e1e23 100644 --- a/dom/src/threads/test/test_xhr.html +++ b/dom/src/threads/test/test_xhr.html @@ -18,66 +18,11 @@ Tests of DOM Worker Threads XHR(Bug 450452 )