From 4f737aafd386b7f4510d6ca6dd715bdecf2ce4f2 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Wed, 24 Sep 2008 19:48:07 -0700 Subject: [PATCH] Bug 450452 - "Implement XHR ('minus X') for worker threads". r+sr=jst. --- content/base/public/nsIXMLHttpRequest.idl | 90 +- content/base/src/nsXMLHttpRequest.cpp | 23 +- content/base/src/nsXMLHttpRequest.h | 14 +- dom/src/base/nsDOMClassInfo.cpp | 1 - dom/src/base/nsDOMScriptObjectFactory.cpp | 103 +- dom/src/base/nsDOMScriptObjectFactory.h | 13 +- dom/src/base/nsGlobalWindow.cpp | 8 + dom/src/threads/Makefile.in | 9 + dom/src/threads/nsDOMThreadService.cpp | 7 + dom/src/threads/nsDOMThreadService.h | 2 + dom/src/threads/nsDOMWorkerBase.cpp | 14 +- dom/src/threads/nsDOMWorkerPool.cpp | 23 +- dom/src/threads/nsDOMWorkerPool.h | 6 +- dom/src/threads/nsDOMWorkerScriptLoader.cpp | 10 +- dom/src/threads/nsDOMWorkerThread.cpp | 120 +- dom/src/threads/nsDOMWorkerThread.h | 9 +- dom/src/threads/nsDOMWorkerTimeout.cpp | 15 +- dom/src/threads/nsDOMWorkerXHR.cpp | 915 +++++++++++++++ dom/src/threads/nsDOMWorkerXHR.h | 167 +++ .../threads/nsDOMWorkerXHRProxiedFunctions.h | 248 ++++ dom/src/threads/nsDOMWorkerXHRProxy.cpp | 1043 +++++++++++++++++ dom/src/threads/nsDOMWorkerXHRProxy.h | 173 +++ dom/src/threads/test/Makefile.in | 2 + dom/src/threads/test/testXHR.txt | 1 + dom/src/threads/test/test_xhr.html | 114 ++ js/src/xpconnect/src/xpcprivate.h | 6 +- js/src/xpconnect/src/xpcwrappedjsclass.cpp | 34 +- 27 files changed, 3012 insertions(+), 158 deletions(-) create mode 100644 dom/src/threads/nsDOMWorkerXHR.cpp create mode 100644 dom/src/threads/nsDOMWorkerXHR.h create mode 100644 dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h create mode 100644 dom/src/threads/nsDOMWorkerXHRProxy.cpp create mode 100644 dom/src/threads/nsDOMWorkerXHRProxy.h create mode 100644 dom/src/threads/test/testXHR.txt create mode 100644 dom/src/threads/test/test_xhr.html diff --git a/content/base/public/nsIXMLHttpRequest.idl b/content/base/public/nsIXMLHttpRequest.idl index 7509476f2ba..94ecd91e765 100644 --- a/content/base/public/nsIXMLHttpRequest.idl +++ b/content/base/public/nsIXMLHttpRequest.idl @@ -101,7 +101,7 @@ interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget { * you're aware of all the security implications. And then think twice about * it. */ -[scriptable, uuid(acda85ab-d06c-4176-b834-6d129ca97ca3)] +[scriptable, uuid(ae8f1468-cd7f-4aea-9c40-a3f085db9369)] interface nsIXMLHttpRequest : nsISupports { /** @@ -334,78 +334,11 @@ interface nsIXMLHttpRequest : nsISupports [noscript] void init(in nsIPrincipal principal, in nsIScriptContext scriptContext, in nsPIDOMWindow ownerWindow); -}; -[scriptable, uuid(6e127bd2-b4c1-4a82-be0d-012bd24efb37)] -interface nsIXMLHttpRequestUploadGetter : nsISupports { /** * Upload process can be tracked by adding event listener to |upload|. */ readonly attribute nsIXMLHttpRequestUpload upload; -}; - -[scriptable, uuid(261676b4-d508-43bf-b099-74635a0ee2e9)] -interface nsIJSXMLHttpRequest : nsISupports { - /** - * Meant to be a script-only mechanism for setting a load event listener. - * The attribute is expected to be JavaScript function object. When - * the load event occurs, the function is invoked. - * This attribute should not be used from native code!! - * - * After the initial response, all event listeners will be cleared. - * // XXXbz what does that mean, exactly? - * - * Call open() before setting an onload listener. - * - * Mozilla only. - */ - attribute nsIDOMEventListener onload; - - /** - * Meant to be a script-only mechanism for setting an error event listener. - * The attribute is expected to be JavaScript function object. When - * the error event occurs, the function is invoked. - * This attribute should not be used from native code!! - * - * After the initial response, all event listeners will be cleared. - * // XXXbz what does that mean, exactly? - * - * Call open() before setting an onerror listener. - * - * Mozilla only. - */ - attribute nsIDOMEventListener onerror; - - /** - * Meant to be a script-only mechanism for setting a progress event listener. - * The attribute is expected to be JavaScript function object. When - * the error event occurs, the function is invoked. - * This attribute should not be used from native code!! - * This event listener may be called multiple times during the open request. - * - * After the initial response, all event listeners will be cleared. - * // XXXbz what does that mean, exactly? - * - * This event listener must be set BEFORE calling open(). - * - * Mozilla only. - */ - attribute nsIDOMEventListener onprogress; - - /** - * Meant to be a script-only mechanism for setting an upload progress event - * listener. - * This attribute should not be used from native code!! - * This event listener may be called multiple times during the upload.. - * - * After the initial response, all event listeners will be cleared. - * // XXXbz what does that mean, exactly? - * - * This event listener must be set BEFORE calling open(). - * - * Mozilla only. - */ - attribute nsIDOMEventListener onuploadprogress; /** * Meant to be a script-only mechanism for setting a callback function. @@ -421,6 +354,27 @@ interface nsIJSXMLHttpRequest : nsISupports { attribute nsIDOMEventListener onreadystatechange; }; +/** + * DEPRECATED. + */ +[scriptable, uuid(261676b4-d508-43bf-b099-74635a0ee2e9)] +interface nsIJSXMLHttpRequest : nsISupports { + /** + * Meant to be a script-only mechanism for setting an upload progress event + * listener. + * This attribute should not be used from native code!! + * This event listener may be called multiple times during the upload.. + * + * After the initial response, all event listeners will be cleared. + * // XXXbz what does that mean, exactly? + * + * This event listener must be set BEFORE calling open(). + * + * Mozilla only. + */ + attribute nsIDOMEventListener onuploadprogress; +}; + %{ C++ #define NS_XMLHTTPREQUEST_CID \ { /* d164e770-4157-11d4-9a42-000064657374 */ \ diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 33257c0d318..9ca28e194bc 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -611,8 +611,9 @@ nsXMLHttpRequestUpload::GetContextForEventHandlers(nsIScriptContext** aContext) ///////////////////////////////////////////// nsXMLHttpRequest::nsXMLHttpRequest() - : mState(XML_HTTP_REQUEST_UNINITIALIZED), mUploadTransferred(0), - mUploadTotal(0), mUploadComplete(PR_TRUE), mErrorLoad(PR_FALSE) + : mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED), + mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE), + mErrorLoad(PR_FALSE), mFirstStartRequestSeen(PR_FALSE) { nsLayoutStatics::AddRef(); } @@ -727,6 +728,12 @@ nsXMLHttpRequest::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj, return NS_OK; } +void +nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver) +{ + mRequestObserver = aObserver; +} + NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest, @@ -776,7 +783,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest) NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest) NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest) - NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUploadGetter) NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener) NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) @@ -1565,6 +1571,11 @@ IsSameOrBaseChannel(nsIRequest* aPossibleBase, nsIChannel* aChannel) NS_IMETHODIMP nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { + if (!mFirstStartRequestSeen && mRequestObserver) { + mFirstStartRequestSeen = PR_TRUE; + mRequestObserver->OnStartRequest(request, ctxt); + } + if (!IsSameOrBaseChannel(request, mChannel)) { return NS_OK; } @@ -1785,6 +1796,12 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult mState &= ~XML_HTTP_REQUEST_SYNCLOOPING; + if (mRequestObserver && mState & XML_HTTP_REQUEST_GOT_FINAL_STOP) { + NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!"); + mFirstStartRequestSeen = PR_FALSE; + mRequestObserver->OnStopRequest(request, ctxt, status); + } + return rv; } diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index c3a2e924034..9dc9e690f80 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -165,8 +165,7 @@ class nsXMLHttpRequest : public nsXHREventTarget, public nsIProgressEventSink, public nsIInterfaceRequestor, public nsSupportsWeakReference, - public nsIJSNativeInitializer, - public nsIXMLHttpRequestUploadGetter + public nsIJSNativeInitializer { public: nsXMLHttpRequest(); @@ -180,17 +179,12 @@ public: // nsIJSXMLHttpRequest NS_IMETHOD GetOnuploadprogress(nsIDOMEventListener** aOnuploadprogress); NS_IMETHOD SetOnuploadprogress(nsIDOMEventListener* aOnuploadprogress); - NS_IMETHOD GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange); - NS_IMETHOD SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange); NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsXHREventTarget::) // nsIDOMEventListener NS_DECL_NSIDOMEVENTLISTENER - // nsIXMLHttpRequestUploadGetter - NS_DECL_NSIXMLHTTPREQUESTUPLOADGETTER - // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); @@ -250,6 +244,8 @@ public: // This is called by the factory constructor. nsresult Init(); + void SetRequestObserver(nsIRequestObserver* aObserver); + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest, nsXHREventTarget) @@ -345,6 +341,8 @@ protected: nsCOMPtr mChannelEventSink; nsCOMPtr mProgressEventSink; + nsIRequestObserver* mRequestObserver; + PRUint32 mState; // List of potentially dangerous headers explicitly set using @@ -357,6 +355,8 @@ protected: PRPackedBool mUploadComplete; PRPackedBool mErrorLoad; + + PRPackedBool mFirstStartRequestSeen; }; // helper class to expose a progress DOM Event diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index 26bbff55cef..6926a1808d3 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -3385,7 +3385,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequest) DOM_CLASSINFO_MAP_ENTRY(nsIJSXMLHttpRequest) DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestEventTarget) - DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestUploadGetter) DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) DOM_CLASSINFO_MAP_ENTRY(nsIInterfaceRequestor) DOM_CLASSINFO_MAP_END diff --git a/dom/src/base/nsDOMScriptObjectFactory.cpp b/dom/src/base/nsDOMScriptObjectFactory.cpp index 0d5bcb71872..9f29eecd9db 100644 --- a/dom/src/base/nsDOMScriptObjectFactory.cpp +++ b/dom/src/base/nsDOMScriptObjectFactory.cpp @@ -68,6 +68,8 @@ static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); +nsIExceptionProvider* gExceptionProvider = nsnull; + nsDOMScriptObjectFactory::nsDOMScriptObjectFactory() : mLoadedAllLanguages(PR_FALSE) { @@ -78,18 +80,25 @@ nsDOMScriptObjectFactory::nsDOMScriptObjectFactory() : observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE); } - nsCOMPtr xs = - do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID); + nsCOMPtr provider(new nsDOMExceptionProvider()); + if (provider) { + nsCOMPtr xs = + do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID); - if (xs) { - xs->RegisterExceptionProvider(this, NS_ERROR_MODULE_DOM); - xs->RegisterExceptionProvider(this, NS_ERROR_MODULE_DOM_RANGE); + if (xs) { + xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM); + xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_RANGE); #ifdef MOZ_SVG - xs->RegisterExceptionProvider(this, NS_ERROR_MODULE_SVG); + xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_SVG); #endif - xs->RegisterExceptionProvider(this, NS_ERROR_MODULE_DOM_XPATH); - xs->RegisterExceptionProvider(this, NS_ERROR_MODULE_XPCONNECT); + xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_XPATH); + xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_XPCONNECT); + } + + NS_ASSERTION(!gExceptionProvider, "Registered twice?!"); + provider.swap(gExceptionProvider); } + // And pre-create the javascript language. NS_CreateJSRuntime(getter_AddRefs(mLanguageArray[NS_STID_INDEX(nsIProgrammingLanguage::JAVASCRIPT)])); } @@ -97,7 +106,6 @@ nsDOMScriptObjectFactory::nsDOMScriptObjectFactory() : NS_INTERFACE_MAP_BEGIN(nsDOMScriptObjectFactory) NS_INTERFACE_MAP_ENTRY(nsIDOMScriptObjectFactory) NS_INTERFACE_MAP_ENTRY(nsIObserver) - NS_INTERFACE_MAP_ENTRY(nsIExceptionProvider) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMScriptObjectFactory) NS_INTERFACE_MAP_END @@ -287,17 +295,26 @@ nsDOMScriptObjectFactory::Observe(nsISupports *aSubject, nsGlobalWindow::ShutDown(); nsDOMClassInfo::ShutDown(); - nsCOMPtr xs = - do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID); + if (gExceptionProvider) { + nsCOMPtr xs = + do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID); - if (xs) { - xs->UnregisterExceptionProvider(this, NS_ERROR_MODULE_DOM); - xs->UnregisterExceptionProvider(this, NS_ERROR_MODULE_DOM_RANGE); + if (xs) { + xs->UnregisterExceptionProvider(gExceptionProvider, + NS_ERROR_MODULE_DOM); + xs->UnregisterExceptionProvider(gExceptionProvider, + NS_ERROR_MODULE_DOM_RANGE); #ifdef MOZ_SVG - xs->UnregisterExceptionProvider(this, NS_ERROR_MODULE_SVG); + xs->UnregisterExceptionProvider(gExceptionProvider, + NS_ERROR_MODULE_SVG); #endif - xs->UnregisterExceptionProvider(this, NS_ERROR_MODULE_DOM_XPATH); - xs->UnregisterExceptionProvider(this, NS_ERROR_MODULE_XPCONNECT); + xs->UnregisterExceptionProvider(gExceptionProvider, + NS_ERROR_MODULE_DOM_XPATH); + xs->UnregisterExceptionProvider(gExceptionProvider, + NS_ERROR_MODULE_XPCONNECT); + } + + NS_RELEASE(gExceptionProvider); } } @@ -325,30 +342,6 @@ CreateXPConnectException(nsresult aResult, nsIException *aDefaultException, return NS_OK; } -NS_IMETHODIMP -nsDOMScriptObjectFactory::GetException(nsresult result, - nsIException *aDefaultException, - nsIException **_retval) -{ - switch (NS_ERROR_GET_MODULE(result)) - { - case NS_ERROR_MODULE_DOM_RANGE: - return NS_NewRangeException(result, aDefaultException, _retval); -#ifdef MOZ_SVG - case NS_ERROR_MODULE_SVG: - return NS_NewSVGException(result, aDefaultException, _retval); -#endif - case NS_ERROR_MODULE_DOM_XPATH: - return NS_NewXPathException(result, aDefaultException, _retval); - case NS_ERROR_MODULE_XPCONNECT: - return CreateXPConnectException(result, aDefaultException, _retval); - case NS_ERROR_MODULE_DOM_FILE: - return NS_NewFileException(result, aDefaultException, _retval); - default: - return NS_NewDOMException(result, aDefaultException, _retval); - } -} - NS_IMETHODIMP nsDOMScriptObjectFactory::RegisterDOMClassInfo(const char *aName, nsDOMClassInfoExternalConstructorFnc aConstructorFptr, @@ -394,3 +387,31 @@ nsresult NS_GetScriptRuntimeByID(PRUint32 aScriptTypeID, return rv; return factory->GetScriptRuntimeByID(aScriptTypeID, aLanguage); } + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMExceptionProvider, nsIExceptionProvider) + +NS_IMETHODIMP +nsDOMExceptionProvider::GetException(nsresult result, + nsIException *aDefaultException, + nsIException **_retval) +{ + switch (NS_ERROR_GET_MODULE(result)) + { + case NS_ERROR_MODULE_DOM_RANGE: + return NS_NewRangeException(result, aDefaultException, _retval); +#ifdef MOZ_SVG + case NS_ERROR_MODULE_SVG: + return NS_NewSVGException(result, aDefaultException, _retval); +#endif + case NS_ERROR_MODULE_DOM_XPATH: + return NS_NewXPathException(result, aDefaultException, _retval); + case NS_ERROR_MODULE_XPCONNECT: + return CreateXPConnectException(result, aDefaultException, _retval); + case NS_ERROR_MODULE_DOM_FILE: + return NS_NewFileException(result, aDefaultException, _retval); + default: + return NS_NewDOMException(result, aDefaultException, _retval); + } + NS_NOTREACHED("Not reached"); + return NS_ERROR_UNEXPECTED; +} diff --git a/dom/src/base/nsDOMScriptObjectFactory.h b/dom/src/base/nsDOMScriptObjectFactory.h index ac1fe120136..8da912641b1 100644 --- a/dom/src/base/nsDOMScriptObjectFactory.h +++ b/dom/src/base/nsDOMScriptObjectFactory.h @@ -57,8 +57,7 @@ #include "nsIScriptGlobalObject.h" // for misplaced NS_STID_ macros. class nsDOMScriptObjectFactory : public nsIDOMScriptObjectFactory, - public nsIObserver, - public nsIExceptionProvider + public nsIObserver { public: nsDOMScriptObjectFactory(); @@ -68,9 +67,6 @@ public: // nsIObserver NS_DECL_NSIOBSERVER - // nsIExceptionProvider - NS_DECL_NSIEXCEPTIONPROVIDER - // nsIDOMScriptObjectFactory NS_IMETHOD GetScriptRuntime(const nsAString &aLanguageName, nsIScriptRuntime **aLanguage); @@ -100,3 +96,10 @@ protected: PRBool mLoadedAllLanguages; nsCOMPtr mLanguageArray[NS_STID_ARRAY_UBOUND]; }; + +class nsDOMExceptionProvider : public nsIExceptionProvider +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIEXCEPTIONPROVIDER +}; diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 2a1b08e2df5..15e77e78100 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -867,6 +867,14 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope) // Kill all of the workers for this window. nsDOMThreadService* dts = nsDOMThreadService::get(); if (dts) { + nsIScriptContext *scx = GetContextInternal(); + + JSContext *cx = scx ? (JSContext *)scx->GetNativeContext() : nsnull; + + // Have to suspend this request here because CancelWorkersForGlobal will + // lock until the worker has died and that could cause a deadlock. + JSAutoSuspendRequest asr(cx); + dts->CancelWorkersForGlobal(static_cast(this)); } diff --git a/dom/src/threads/Makefile.in b/dom/src/threads/Makefile.in index cae2810bc97..d4730a81abe 100644 --- a/dom/src/threads/Makefile.in +++ b/dom/src/threads/Makefile.in @@ -51,11 +51,14 @@ FORCE_STATIC_LIB = 1 REQUIRES = \ caps \ content \ + gfx \ js \ layout \ + locale \ necko \ pref \ string \ + thebes \ widget \ xpcom \ xpconnect \ @@ -69,10 +72,14 @@ CPPSRCS = \ nsDOMWorkerSecurityManager.cpp \ nsDOMWorkerThread.cpp \ nsDOMWorkerTimeout.cpp \ + nsDOMWorkerXHR.cpp \ + nsDOMWorkerXHRProxy.cpp \ $(NULL) LOCAL_INCLUDES = \ -I$(topsrcdir)/dom/src/base \ + -I$(topsrcdir)/content/base/src \ + -I$(topsrcdir)/content/events/src \ $(NULL) ifdef ENABLE_TESTS @@ -80,3 +87,5 @@ DIRS += test endif include $(topsrcdir)/config/rules.mk + +#CXXFLAGS += $(WARNINGS_AS_ERRORS) diff --git a/dom/src/threads/nsDOMThreadService.cpp b/dom/src/threads/nsDOMThreadService.cpp index 463fd7ce2d4..47a5394e9b6 100644 --- a/dom/src/threads/nsDOMThreadService.cpp +++ b/dom/src/threads/nsDOMThreadService.cpp @@ -478,6 +478,13 @@ DOMWorkerErrorReporter(JSContext* aCx, nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx); + if (worker->IsCanceled()) { + // We don't want to report errors from canceled workers. It's very likely + // that we only returned an error in the first place because the worker was + // already canceled. + return; + } + nsresult rv; nsCOMPtr errorObject = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); diff --git a/dom/src/threads/nsDOMThreadService.h b/dom/src/threads/nsDOMThreadService.h index de287e65073..78e8095a5e8 100644 --- a/dom/src/threads/nsDOMThreadService.h +++ b/dom/src/threads/nsDOMThreadService.h @@ -78,6 +78,8 @@ class nsDOMThreadService : public nsIEventTarget, friend class nsDOMWorkerRunnable; friend class nsDOMWorkerThread; friend class nsDOMWorkerTimeout; + friend class nsDOMWorkerXHR; + friend class nsDOMWorkerXHRProxy; friend class nsLayoutStatics; public: diff --git a/dom/src/threads/nsDOMWorkerBase.cpp b/dom/src/threads/nsDOMWorkerBase.cpp index 9a6a832ccfc..12b10d18b0a 100644 --- a/dom/src/threads/nsDOMWorkerBase.cpp +++ b/dom/src/threads/nsDOMWorkerBase.cpp @@ -92,12 +92,16 @@ public: getter_AddRefs(targetIsPool)); #endif - LOG(("Posting message '%s' from %s [0x%p] to %s [0x%p]", - utf8Message.get(), sourceIsPool ? poolStr : workerStr, - static_cast(mSource.get()), targetIsPool ? poolStr : workerStr, - static_cast(mTarget.get()))); + if (!(mTarget->IsCanceled() || mSource->IsCanceled())) { + LOG(("Posting message '%s' from %s [0x%p] to %s [0x%p]", + utf8Message.get(), + sourceIsPool ? poolStr : workerStr, + static_cast(mSource.get()), + targetIsPool ? poolStr : workerStr, + static_cast(mTarget.get()))); - mTarget->HandleMessage(mMessage, mSource); + mTarget->HandleMessage(mMessage, mSource); + } return NS_OK; } diff --git a/dom/src/threads/nsDOMWorkerPool.cpp b/dom/src/threads/nsDOMWorkerPool.cpp index 732f674d683..554141a05de 100644 --- a/dom/src/threads/nsDOMWorkerPool.cpp +++ b/dom/src/threads/nsDOMWorkerPool.cpp @@ -40,6 +40,7 @@ #include "nsDOMWorkerPool.h" // Interfaces +#include "nsIDocument.h" #include "nsIDOMClassInfo.h" #include "nsIJSContextStack.h" #include "nsIScriptContext.h" @@ -102,15 +103,8 @@ nsDOMWorkerPool::Init() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - // GetCurrentJSContext () can return a null context... We shouldn't - // ever see that here. - JSContext* cx = nsContentUtils::GetCurrentJSContext(); - NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED); - - nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx); - NS_ENSURE_STATE(scriptContext); - - nsIScriptGlobalObject* globalObject = scriptContext->GetGlobalObject(); + nsIScriptGlobalObject* globalObject = + mParentDocument->GetScriptGlobalObject(); NS_ENSURE_STATE(globalObject); nsCOMPtr domWindow(do_QueryInterface(globalObject)); @@ -237,13 +231,22 @@ nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject) } nsIDocument* -nsDOMWorkerPool::GetParentDocument() +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) { diff --git a/dom/src/threads/nsDOMWorkerPool.h b/dom/src/threads/nsDOMWorkerPool.h index 45cba1d466a..18135a311ee 100644 --- a/dom/src/threads/nsDOMWorkerPool.h +++ b/dom/src/threads/nsDOMWorkerPool.h @@ -53,6 +53,7 @@ class nsDOMWorkerThread; class nsIDocument; +class nsIScriptContext; class nsIScriptError; class nsIScriptGlobalObject; @@ -84,6 +85,9 @@ public: return this; } + nsIDocument* ParentDocument(); + nsIScriptContext* ScriptContext(); + private: virtual ~nsDOMWorkerPool(); @@ -109,8 +113,6 @@ private: return mMonitor; } - nsIDocument* GetParentDocument(); - // Weak reference to the window that created and owns this pool. nsISupports* mParentGlobal; diff --git a/dom/src/threads/nsDOMWorkerScriptLoader.cpp b/dom/src/threads/nsDOMWorkerScriptLoader.cpp index ff08eb41c3c..c5eb1dcbaff 100644 --- a/dom/src/threads/nsDOMWorkerScriptLoader.cpp +++ b/dom/src/threads/nsDOMWorkerScriptLoader.cpp @@ -126,7 +126,13 @@ nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker, { 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!"); } @@ -442,7 +448,7 @@ nsDOMWorkerScriptLoader::RunInternal() NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); // Things we need to make all this work... - nsIDocument* parentDoc = mWorker->Pool()->GetParentDocument(); + nsIDocument* parentDoc = mWorker->Pool()->ParentDocument(); NS_ASSERTION(parentDoc, "Null parent document?!"); // All of these can potentially be null, but that should be ok. We'll either @@ -578,7 +584,7 @@ nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader, return rv = NS_ERROR_UNEXPECTED; } - nsIDocument* parentDoc = mWorker->Pool()->GetParentDocument(); + nsIDocument* parentDoc = mWorker->Pool()->ParentDocument(); NS_ASSERTION(parentDoc, "Null parent document?!"); // Use the regular nsScriptLoader for this grunt work! Should be just fine diff --git a/dom/src/threads/nsDOMWorkerThread.cpp b/dom/src/threads/nsDOMWorkerThread.cpp index e72f26af1ad..1c36670917a 100644 --- a/dom/src/threads/nsDOMWorkerThread.cpp +++ b/dom/src/threads/nsDOMWorkerThread.cpp @@ -62,6 +62,7 @@ #include "nsDOMWorkerSecurityManager.h" #include "nsDOMThreadService.h" #include "nsDOMWorkerTimeout.h" +#include "nsDOMWorkerXHR.h" #define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args) @@ -100,6 +101,9 @@ public: 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, @@ -303,6 +307,63 @@ nsDOMWorkerFunctions::LoadScripts(JSContext* aCx, 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 }, @@ -312,6 +373,7 @@ JSFunctionSpec gDOMWorkerFunctions[] = { { "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 }, @@ -551,6 +613,7 @@ nsDOMWorkerThread::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. @@ -717,7 +780,7 @@ nsDOMWorkerThread::NextTimeout(nsDOMWorkerTimeout* aTimeout) return next == &mTimeouts ? nsnull : next; } -void +PRBool nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout) { // This should only ever be called on the worker thread... but there's no way @@ -733,6 +796,10 @@ nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout) 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. @@ -741,11 +808,12 @@ nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout) timeout = NextTimeout(timeout)) { if (timeout->GetInterval() > newInterval) { PR_INSERT_BEFORE(aTimeout, timeout); - return; + return PR_TRUE; } } PR_APPEND_LINK(aTimeout, &mTimeouts); + return PR_TRUE; } void @@ -859,6 +927,54 @@ nsDOMWorkerThread::CancelScriptLoaders() } } +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) { diff --git a/dom/src/threads/nsDOMWorkerThread.h b/dom/src/threads/nsDOMWorkerThread.h index 59001d698a0..b75726b7023 100644 --- a/dom/src/threads/nsDOMWorkerThread.h +++ b/dom/src/threads/nsDOMWorkerThread.h @@ -117,6 +117,7 @@ _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \ class nsDOMWorkerPool; class nsDOMWorkerScriptLoader; class nsDOMWorkerTimeout; +class nsDOMWorkerXHR; class nsDOMWorkerThread : public nsDOMWorkerBase, public nsIDOMWorkerThread, @@ -128,6 +129,7 @@ class nsDOMWorkerThread : public nsDOMWorkerBase, friend class nsDOMWorkerRunnable; friend class nsDOMWorkerScriptLoader; friend class nsDOMWorkerTimeout; + friend class nsDOMWorkerXHR; friend JSBool DOMWorkerOperationCallback(JSContext* aCx); @@ -167,7 +169,7 @@ private: inline nsDOMWorkerTimeout* FirstTimeout(); inline nsDOMWorkerTimeout* NextTimeout(nsDOMWorkerTimeout* aTimeout); - void AddTimeout(nsDOMWorkerTimeout* aTimeout); + PRBool AddTimeout(nsDOMWorkerTimeout* aTimeout); void RemoveTimeout(nsDOMWorkerTimeout* aTimeout); void ClearTimeouts(); void CancelTimeout(PRUint32 aId); @@ -176,6 +178,10 @@ private: void CancelScriptLoaders(); + PRBool AddXHR(nsDOMWorkerXHR* aXHR); + void RemoveXHR(nsDOMWorkerXHR* aXHR); + void CancelXHRs(); + PRLock* Lock() { return mLock; } @@ -195,6 +201,7 @@ private: PRCList mTimeouts; nsTArray mScriptLoaders; + nsTArray mXHRs; }; #endif /* __NSDOMWORKERTHREAD_H__ */ diff --git a/dom/src/threads/nsDOMWorkerTimeout.cpp b/dom/src/threads/nsDOMWorkerTimeout.cpp index c0466b32290..d04f561ae79 100644 --- a/dom/src/threads/nsDOMWorkerTimeout.cpp +++ b/dom/src/threads/nsDOMWorkerTimeout.cpp @@ -329,19 +329,26 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv, } mIsInterval = aIsInterval; - mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsIEventTarget* target = static_cast(nsDOMThreadService::get()); - rv = mTimer->SetTarget(target); + rv = timer->SetTarget(target); NS_ENSURE_SUCCESS(rv, rv); - rv = mTimer->InitWithCallback(this, interval, type); + rv = timer->InitWithCallback(this, interval, type); NS_ENSURE_SUCCESS(rv, rv); - mWorker->AddTimeout(this); + mTimer.swap(timer); + + if (!mWorker->AddTimeout(this)) { + // Must have been canceled. + mTimer->Cancel(); + mTimer = nsnull; + return NS_ERROR_ABORT; + } return NS_OK; } diff --git a/dom/src/threads/nsDOMWorkerXHR.cpp b/dom/src/threads/nsDOMWorkerXHR.cpp new file mode 100644 index 00000000000..c8bafc145e4 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerXHR.cpp @@ -0,0 +1,915 @@ +/* -*- 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): + * 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 "nsDOMWorkerXHR.h" + +// Interfaces +#include "nsIDocument.h" +#include "nsIDOMEvent.h" +#include "nsIThread.h" +#include "nsIXPConnect.h" + +// Other includes +#include "nsAutoLock.h" +#include "nsAXPCNativeCallContext.h" +#include "nsComponentManagerUtils.h" +#include "nsContentUtils.h" +#include "nsIClassInfoImpl.h" +#include "nsJSUtils.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +// DOMWorker includes +#include "nsDOMThreadService.h" +#include "nsDOMWorkerPool.h" +#include "nsDOMWorkerXHRProxy.h" + +// The list of event types that we support. This list and the defines based on +// it determine the sizes of the listener arrays in nsDOMWorkerXHRProxy. Make +// sure that any event types shared by both the XHR and Upload objects are +// together at the beginning of the list. Any changes made to this list may +// affect sMaxUploadEventTypes, so make sure that it is adjusted accordingly or +// things will break! +const char* const nsDOMWorkerXHREventTarget::sListenerTypes[] = { + // nsIXMLHttpRequestEventTarget listeners. + "abort", /* LISTENER_TYPE_ABORT */ + "error", /* LISTENER_TYPE_ERROR */ + "load", /* LISTENER_TYPE_LOAD */ + "loadstart", /* LISTENER_TYPE_LOADSTART */ + "progress", /* LISTENER_TYPE_PROGRESS */ + + // nsIXMLHttpRequest listeners. + "readystatechange" /* LISTENER_TYPE_READYSTATECHANGE */ +}; + +// Convenience defines for event *indexes* in the sListenerTypes array. +#define LISTENER_TYPE_ABORT 0 +#define LISTENER_TYPE_ERROR 1 +#define LISTENER_TYPE_LOAD 2 +#define LISTENER_TYPE_LOADSTART 3 +#define LISTENER_TYPE_PROGRESS 4 +#define LISTENER_TYPE_READYSTATECHANGE 5 + +// This should always be set to the length of sListenerTypes. +const PRUint32 nsDOMWorkerXHREventTarget::sMaxXHREventTypes = + NS_ARRAY_LENGTH(nsDOMWorkerXHREventTarget::sListenerTypes); + +// This should be set to the index of the first event type that is *not* +// supported by the Upload object. +const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes = + LISTENER_TYPE_READYSTATECHANGE; + +// Enforce the invariant that the upload object supports no more event types +// than the xhr object. +PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >= + nsDOMWorkerXHREventTarget::sMaxUploadEventTypes); + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget, + nsIDOMEventTarget, + nsIXMLHttpRequestEventTarget) + +PRUint32 +nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString) +{ + for (PRUint32 index = 0; index < sMaxXHREventTypes; index++) { + if (aString.EqualsASCII(sListenerTypes[index])) { + return index; + } + } + return PR_UINT32_MAX; +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort) +{ + NS_ENSURE_ARG_POINTER(aOnabort); + + nsCOMPtr listener = GetOnXListener(LISTENER_TYPE_ABORT); + listener.forget(aOnabort); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort) +{ + return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE); +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror) +{ + NS_ENSURE_ARG_POINTER(aOnerror); + + nsCOMPtr listener = GetOnXListener(LISTENER_TYPE_ERROR); + listener.forget(aOnerror); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror) +{ + return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE); +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload) +{ + NS_ENSURE_ARG_POINTER(aOnload); + + nsCOMPtr listener = GetOnXListener(LISTENER_TYPE_LOAD); + listener.forget(aOnload); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload) +{ + return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE); +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart) +{ + NS_ENSURE_ARG_POINTER(aOnloadstart); + + nsCOMPtr listener = + GetOnXListener(LISTENER_TYPE_LOADSTART); + listener.forget(aOnloadstart); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart) +{ + return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE); +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress) +{ + NS_ENSURE_ARG_POINTER(aOnprogress); + + nsCOMPtr listener = + GetOnXListener(LISTENER_TYPE_PROGRESS); + listener.forget(aOnprogress); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress) +{ + return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE); +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + PRBool aUseCapture) +{ + NS_ENSURE_ARG_POINTER(aListener); + + PRUint32 type = GetListenerTypeFromString(aType); + if (type > sMaxXHREventTypes) { + // Silently ignore junk events. + return NS_OK; + } + + return SetEventListener(type, aListener, PR_FALSE); +} + +NS_IMETHODIMP +nsDOMWorkerXHREventTarget::RemoveEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + PRBool aUseCapture) +{ + NS_ENSURE_ARG_POINTER(aListener); + + PRUint32 type = GetListenerTypeFromString(aType); + if (type > sMaxXHREventTypes) { + // Silently ignore junk events. + return NS_OK; + } + + return UnsetEventListener(type, aListener); +} + +/* ec702b78-c30f-439f-9a9b-a5dae17ee0fc */ +#define NS_IPRIVATEWORKERXHREVENT_IID \ +{ \ + 0xec702b78, \ + 0xc30f, \ + 0x439f, \ + { 0x9a, 0x9b, 0xa5, 0xda, 0xe1, 0x7e, 0xe0, 0xfc } \ +} + +class nsIPrivateWorkerXHREvent : public nsIDOMEvent +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRIVATEWORKERXHREVENT_IID) + virtual PRBool PreventDefaultCalled() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateWorkerXHREvent, + NS_IPRIVATEWORKERXHREVENT_IID) + +#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; +} + +nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR) +: mWorkerXHR(aWorkerXHR) +{ + NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!"); +} + +NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget, + nsIXMLHttpRequestUpload, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget, + nsIXMLHttpRequestEventTarget, + nsIXMLHttpRequestUpload) + +NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload) + +nsresult +nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType, + nsIDOMEventListener* aListener, + PRBool aOnXListener) +{ + if (mWorkerXHR->mCanceled) { + return NS_ERROR_ABORT; + } + + return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener, + PR_TRUE); +} + +nsresult +nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType, + nsIDOMEventListener* aListener) +{ + 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; + } + + return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE); +} + +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) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aWorker, "Must have a worker!"); +} + +nsDOMWorkerXHR::~nsDOMWorkerXHR() +{ + if (!mCanceled) { + mWorker->RemoveXHR(this); + } +} + +NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget, + nsIXMLHttpRequest, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget, + nsIXMLHttpRequestEventTarget, + nsIXMLHttpRequest) + +NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR) + +nsresult +nsDOMWorkerXHR::Init() +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (!mWorker->AddXHR(this)) { + // Must have been canceled. + return NS_ERROR_ABORT; + } + mCanceled = PR_FALSE; + + nsRefPtr proxy = new nsDOMWorkerXHRProxy(this); + NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = proxy->Init(); + NS_ENSURE_SUCCESS(rv, rv); + + proxy.swap(mXHRProxy); + return NS_OK; +} + +void +nsDOMWorkerXHR::Cancel() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // Just in case mUpload holds the only ref to this object we make sure to stay + // alive through this call. + nsRefPtr kungFuDeathGrip(this); + + { + // This lock is here to prevent a race between Cancel and GetUpload, not to + // protect mCanceled. + nsAutoLock lock(mWorker->Lock()); + + mCanceled = PR_TRUE; + mUpload = nsnull; + } + + if (mXHRProxy) { + mXHRProxy->Destroy(); + } + + mWorker->RemoveXHR(this); + mWorker = nsnull; +} + +nsresult +nsDOMWorkerXHR::SetEventListener(PRUint32 aType, + nsIDOMEventListener* aListener, + PRBool aOnXListener) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + return mXHRProxy->AddEventListener(aType, aListener, aOnXListener, PR_FALSE); +} + +nsresult +nsDOMWorkerXHR::UnsetEventListener(PRUint32 aType, + nsIDOMEventListener* aListener) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + 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); +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetResponseXML(nsIDOMDocument** aResponseXML) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetResponseText(nsAString& aResponseText) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->GetResponseText(aResponseText); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetStatus(PRUint32* aStatus) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(aStatus); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->GetStatus(aStatus); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetStatusText(nsACString& aStatusText) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->GetStatusText(aStatusText); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::Abort() +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->Abort(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetAllResponseHeaders(char** _retval) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ENSURE_ARG_POINTER(_retval); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->GetAllResponseHeaders(_retval); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetResponseHeader(const nsACString& aHeader, + nsACString& _retval) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->GetResponseHeader(aHeader, _retval); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::OpenRequest(const nsACString& aMethod, + const nsACString& aUrl, + PRBool aAsync, + const nsAString& aUser, + const nsAString& aPassword) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->OpenRequest(aMethod, aUrl, aAsync, aUser, aPassword); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::Open(const nsACString& aMethod, + const nsACString& aUrl) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + PRBool async = PR_TRUE; + nsAutoString user, password; + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED); + + nsAXPCNativeCallContext* cc; + nsresult rv = xpc->GetCurrentNativeCallContext(&cc); + + do { + if (NS_FAILED(rv) || !cc) { + break; + } + + PRUint32 argc; + rv = cc->GetArgc(&argc); + NS_ENSURE_SUCCESS(rv, rv); + + if (argc < 3) { + break; + } + + jsval* argv; + rv = cc->GetArgvPtr(&argv); + NS_ENSURE_SUCCESS(rv, rv); + + JSContext* cx; + rv = cc->GetJSContext(&cx); + NS_ENSURE_SUCCESS(rv, rv); + + JSAutoRequest ar(cx); + + JSBool asyncBool; + JS_ValueToBoolean(cx, argv[2], &asyncBool); + async = (PRBool)asyncBool; + + // XXX Remove me once we support sync XHR + NS_ENSURE_TRUE(async, NS_ERROR_INVALID_ARG); + + if (argc < 4) { + break; + } + + JSString* argStr; + if (!JSVAL_IS_NULL(argv[3]) && !JSVAL_IS_VOID(argv[3])) { + argStr = JS_ValueToString(cx, argv[3]); + if (argStr) { + user.Assign(nsDependentJSString(argStr)); + } + } + + if (argc < 5) { + break; + } + + if (!JSVAL_IS_NULL(argv[4]) && !JSVAL_IS_VOID(argv[4])) { + argStr = JS_ValueToString(cx, argv[4]); + if (argStr) { + password.Assign(nsDependentJSString(argStr)); + } + } + } while (PR_FALSE); + + return OpenRequest(aMethod, aUrl, async, user, password); +} + +NS_IMETHODIMP +nsDOMWorkerXHR::Send(nsIVariant* aBody) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->Send(aBody); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::SendAsBinary(const nsAString& aBody) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->SendAsBinary(aBody); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::SetRequestHeader(const nsACString& aHeader, + const nsACString& aValue) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->SetRequestHeader(aHeader, aValue); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetReadyState(PRInt32* aReadyState) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + NS_ENSURE_ARG_POINTER(aReadyState); + + nsresult rv = mXHRProxy->GetReadyState(aReadyState); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::OverrideMimeType(const nsACString& aMimetype) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->OverrideMimeType(aMimetype); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetMultipart(PRBool* aMultipart) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + NS_ENSURE_ARG_POINTER(aMultipart); + + nsresult rv = mXHRProxy->GetMultipart(aMultipart); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::SetMultipart(PRBool aMultipart) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = mXHRProxy->SetMultipart(aMultipart); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetMozBackgroundRequest(PRBool* aMozBackgroundRequest) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + NS_ENSURE_ARG_POINTER(aMozBackgroundRequest); + + *aMozBackgroundRequest = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::SetMozBackgroundRequest(PRBool aMozBackgroundRequest) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (aMozBackgroundRequest) { + return NS_ERROR_NOT_AVAILABLE; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::Init(nsIPrincipal* aPrincipal, + nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwnerWindow) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsRefPtr worker = mWorker; + if (!worker) { + return NS_ERROR_ABORT; + } + + nsAutoLock lock(worker->Lock()); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + NS_ENSURE_ARG_POINTER(aUpload); + + if (!mUpload) { + mUpload = new nsDOMWorkerXHRUpload(this); + NS_ENSURE_TRUE(mUpload, NS_ERROR_OUT_OF_MEMORY); + } + + NS_ADDREF(*aUpload = mUpload); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + NS_ENSURE_ARG_POINTER(aOnreadystatechange); + + nsCOMPtr listener = + mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE); + + listener.forget(aOnreadystatechange); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE, + aOnreadystatechange, PR_TRUE, PR_FALSE); +} diff --git a/dom/src/threads/nsDOMWorkerXHR.h b/dom/src/threads/nsDOMWorkerXHR.h new file mode 100644 index 00000000000..68d4e9486c3 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerXHR.h @@ -0,0 +1,167 @@ +/* -*- 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): + * 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 __NSDOMWORKERXHR_H__ +#define __NSDOMWORKERXHR_H__ + +// Bases +#include "nsIXMLHttpRequest.h" +#include "nsIClassInfo.h" + +// Interfaces + +// Other includes +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" +#include "prlock.h" + +// DOMWorker includes +#include "nsDOMWorkerThread.h" + +class nsDOMWorkerXHRProxy; + +class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTTARGET + NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET + + static const char* const sListenerTypes[]; + static const PRUint32 sMaxXHREventTypes; + static const PRUint32 sMaxUploadEventTypes; + + static PRUint32 GetListenerTypeFromString(const nsAString& aString); + + virtual nsresult SetEventListener(PRUint32 aType, + nsIDOMEventListener* aListener, + PRBool aOnXListener) = 0; + + virtual nsresult UnsetEventListener(PRUint32 aType, + nsIDOMEventListener* aListener) = 0; + + virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent) = 0; + + virtual already_AddRefed + GetOnXListener(PRUint32 aType) = 0; + +protected: + virtual ~nsDOMWorkerXHREventTarget() { } +}; + +class nsDOMWorkerXHR; + +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 nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget, + public nsIXMLHttpRequest, + public nsIClassInfo +{ + friend class nsDOMWorkerXHRProxy; + friend class nsDOMWorkerXHRUpload; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIXMLHTTPREQUEST + NS_DECL_NSICLASSINFO + + nsDOMWorkerXHR(nsDOMWorkerThread* aWorker); + + nsresult Init(); + + void Cancel(); + + 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 ~nsDOMWorkerXHR(); + + PRLock* Lock() { + return mWorker->Lock(); + } + + nsRefPtr mWorker; + nsRefPtr mXHRProxy; + nsRefPtr mUpload; + + volatile PRBool mCanceled; +}; + +#endif /* __NSDOMWORKERXHR_H__ */ diff --git a/dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h b/dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h new file mode 100644 index 00000000000..89907fed484 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h @@ -0,0 +1,248 @@ +/* -*- 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): + * 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 __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__ +#define __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__ + +#define MAKE_PROXIED_FUNCTION0(_name) \ + class _name : public nsRunnable \ + { \ + public: \ + _name (nsDOMWorkerXHRProxy* aXHR) \ + : mXHR(aXHR) \ + { \ + NS_ASSERTION(aXHR, "Null pointer!"); \ + } \ + \ + NS_IMETHOD Run() \ + { \ + nsCOMPtr xhr = mXHR->GetXMLHttpRequest(); \ + if (xhr) { \ + return xhr-> _name (); \ + } \ + return NS_OK; \ + } \ + private: \ + nsRefPtr mXHR; \ + } + +#define MAKE_PROXIED_FUNCTION1(_name, _arg1) \ + class _name : public nsRunnable \ + { \ + public: \ + _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1) \ + : mXHR(aXHR), mArg1(aArg1) \ + { \ + NS_ASSERTION(aXHR, "Null pointer!"); \ + } \ + \ + NS_IMETHOD Run() \ + { \ + nsCOMPtr xhr = mXHR->GetXMLHttpRequest(); \ + if (xhr) { \ + return xhr-> _name (mArg1); \ + } \ + return NS_OK; \ + } \ + private: \ + nsRefPtr mXHR; \ + _arg1 mArg1; \ + } + +#define MAKE_PROXIED_FUNCTION2(_name, _arg1, _arg2) \ + class _name : public nsRunnable \ + { \ + public: \ + _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2) \ + : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2) \ + { \ + NS_ASSERTION(aXHR, "Null pointer!"); \ + } \ + \ + NS_IMETHOD Run() \ + { \ + nsCOMPtr xhr = mXHR->GetXMLHttpRequest(); \ + if (xhr) { \ + return xhr-> _name (mArg1, mArg2); \ + } \ + return NS_OK; \ + } \ + private: \ + nsRefPtr mXHR; \ + _arg1 mArg1; \ + _arg2 mArg2; \ + } + +#define MAKE_PROXIED_FUNCTION3(_name, _arg1, _arg2, _arg3) \ + class _name : public nsRunnable \ + { \ + public: \ + _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3) \ + : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3) \ + { \ + NS_ASSERTION(aXHR, "Null pointer!"); \ + } \ + \ + NS_IMETHOD Run() \ + { \ + nsCOMPtr xhr = mXHR->GetXMLHttpRequest(); \ + if (xhr) { \ + return xhr-> _name (mArg1, mArg2, mArg3); \ + } \ + return NS_OK; \ + } \ + private: \ + nsRefPtr mXHR; \ + _arg1 mArg1; \ + _arg2 mArg2; \ + _arg3 mArg3; \ + } + +#define MAKE_PROXIED_FUNCTION4(_name, _arg1, _arg2, _arg3, _arg4) \ + class _name : public nsRunnable \ + { \ + public: \ + _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3, \ + _arg4 aArg4) \ + : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3), mArg4(aArg4) \ + { \ + NS_ASSERTION(aXHR, "Null pointer!"); \ + } \ + \ + NS_IMETHOD Run() \ + { \ + nsCOMPtr xhr = mXHR->GetXMLHttpRequest(); \ + if (xhr) { \ + return xhr-> _name (mArg1, mArg2, mArg3, mArg4); \ + } \ + return NS_OK; \ + } \ + private: \ + nsRefPtr mXHR; \ + _arg1 mArg1; \ + _arg2 mArg2; \ + _arg3 mArg3; \ + _arg4 mArg4; \ + } + +#define MAKE_PROXIED_FUNCTION5(_name, _arg1, _arg2, _arg3, _arg4, _arg5) \ + class _name : public nsRunnable \ + { \ + public: \ + _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3, \ + _arg4 aArg4, _arg5 aArg5) \ + : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3), mArg4(aArg4), \ + mArg5(aArg5) \ + { \ + NS_ASSERTION(aXHR, "Null pointer!"); \ + } \ + \ + NS_IMETHOD Run() \ + { \ + nsCOMPtr xhr = mXHR->GetXMLHttpRequest(); \ + if (xhr) { \ + return xhr-> _name (mArg1, mArg2, mArg3, mArg4, mArg5); \ + } \ + return NS_OK; \ + } \ + private: \ + nsRefPtr mXHR; \ + _arg1 mArg1; \ + _arg2 mArg2; \ + _arg3 mArg3; \ + _arg4 mArg4; \ + _arg5 mArg5; \ + } + +#define RUN_PROXIED_FUNCTION(_name, _args) \ + PR_BEGIN_MACRO \ + if (mCanceled) { \ + return NS_ERROR_ABORT; \ + } \ + \ + nsCOMPtr method = new :: _name _args; \ + NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY); \ + \ + nsRefPtr runnable = \ + new nsResultReturningRunnable(mMainThread, method, mWorkerXHR->mWorker); \ + NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); \ + \ + nsresult _rv = runnable->Dispatch(); \ + if (NS_FAILED(_rv)) { \ + return _rv; \ + } \ + PR_END_MACRO + +namespace nsDOMWorkerProxiedXHRFunctions +{ + class Abort : public nsRunnable + { + public: + Abort (nsDOMWorkerXHRProxy* aXHR) + : mXHR(aXHR) + { + NS_ASSERTION(aXHR, "Null pointer!"); + } + + NS_IMETHOD Run() { + return mXHR->Abort(); + } + private: + nsRefPtr mXHR; + }; + + MAKE_PROXIED_FUNCTION1(GetAllResponseHeaders, char**); + + MAKE_PROXIED_FUNCTION2(GetResponseHeader, const nsACString&, nsACString&); + + MAKE_PROXIED_FUNCTION5(OpenRequest, const nsACString&, const nsACString&, + PRBool, const nsAString&, const nsAString&); + + MAKE_PROXIED_FUNCTION1(Send, nsIVariant*); + + MAKE_PROXIED_FUNCTION1(SendAsBinary, const nsAString&); + + MAKE_PROXIED_FUNCTION2(SetRequestHeader, const nsACString&, + const nsACString&); + + MAKE_PROXIED_FUNCTION1(OverrideMimeType, const nsACString&); + + MAKE_PROXIED_FUNCTION1(SetMultipart, PRBool); +} + +#endif /* __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__ */ diff --git a/dom/src/threads/nsDOMWorkerXHRProxy.cpp b/dom/src/threads/nsDOMWorkerXHRProxy.cpp new file mode 100644 index 00000000000..5837f45f5d5 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp @@ -0,0 +1,1043 @@ +/* -*- 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): + * 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 "nsDOMWorkerXHRProxy.h" + +// Interfaces +#include "nsIThread.h" +#include "nsIVariant.h" +#include "nsIXMLHttpRequest.h" + +// Other includes +#include "nsAutoLock.h" +#include "nsComponentManagerUtils.h" +#include "nsIClassInfoImpl.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" +#include "nsXMLHttpRequest.h" +#include "prinrval.h" +#include "prthread.h" + +// DOMWorker includes +#include "nsDOMWorkerPool.h" +#include "nsDOMWorkerThread.h" +#include "nsDOMWorkerXHRProxiedFunctions.h" + +#define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes +#define MAX_UPLOAD_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxUploadEventTypes + +using namespace nsDOMWorkerProxiedXHRFunctions; + +class nsResultReturningRunnable : public nsRunnable +{ +public: + nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable, + nsDOMWorkerThread* aWorker) + : mTarget(aTarget), mRunnable(aRunnable), mWorker(aWorker), + mResult(NS_OK), mDone(PR_FALSE) { } + + nsresult Dispatch() { + if (!mWorker) { + // Must have been canceled, bail out. + return NS_ERROR_ABORT; + } + + nsIThread* currentThread = NS_GetCurrentThread(); + NS_ASSERTION(currentThread, "This should never be null!"); + + nsresult rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + // Complicated logic: Spin events while we haven't been canceled (if a + // cancel boolean was supplied) and while we're not done. + while (!mWorker->IsCanceled() && !mDone) { + // Process a single event or yield if no event is pending. + if (!NS_ProcessNextEvent(currentThread, PR_FALSE)) { + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + } + + if (mWorker->IsCanceled()) { + mResult = NS_ERROR_ABORT; + } + + return mResult; + } + + NS_IMETHOD Run() { +#ifdef DEBUG + PRBool rightThread = PR_FALSE; + mTarget->IsOnCurrentThread(&rightThread); + NS_ASSERTION(rightThread, "Run called on the wrong thread!"); +#endif + + mResult = mWorker->IsCanceled() ? NS_ERROR_ABORT : mRunnable->Run(); + mDone = PR_TRUE; + + return mResult; + } + +private: + nsCOMPtr mTarget; + nsCOMPtr mRunnable; + nsRefPtr mWorker; + nsresult mResult; + volatile PRBool mDone; +}; + +class nsDOMWorkerXHREvent : public nsRunnable, + public nsIDOMEvent, + public nsIClassInfo +{ + friend class nsDOMWorkerXHRProxy; + friend class nsDOMWorkerXHREventTargetProxy; + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + NS_DECL_NSIDOMEVENT + NS_DECL_NSICLASSINFO + + nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy); + + nsresult Init(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; + PRPackedBool mBubbles; + PRPackedBool mCancelable; + PRPackedBool mUploadEvent; +}; + +nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy) +: mXHRProxy(aXHRProxy), + mType(PR_UINT32_MAX), + mEventPhase(0), + mTimeStamp(0), + mStatus(NS_OK), + mReadyState(0), + mBubbles(PR_FALSE), + mCancelable(PR_FALSE), + mUploadEvent(PR_FALSE) +{ + NS_ASSERTION(aXHRProxy, "Can't be null!"); + + nsIDOMEventTarget* target = + static_cast(aXHRProxy->mWorkerXHR); + mTarget = do_QueryInterface(target); + NS_ASSERTION(mTarget, "Must support nsIDOMEventTarget!"); +} + +NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHREvent, nsRunnable, + nsIDOMEvent, + nsIClassInfo) + +NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerXHREvent, nsIDOMEvent) + +NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerXHREvent) + +nsresult +nsDOMWorkerXHREvent::Init(nsIDOMEvent* aEvent) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aEvent, "Don't pass null here!"); + + nsresult rv = aEvent->GetType(mTypeString); + NS_ENSURE_SUCCESS(rv, rv); + + mType = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(mTypeString); + if (mType >= MAX_XHR_LISTENER_TYPE) { + NS_ERROR("Shouldn't get this type of event!"); + return NS_ERROR_INVALID_ARG; + } + + nsCOMPtr target; + rv = aEvent->GetTarget(getter_AddRefs(target)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr upload(do_QueryInterface(target)); + mUploadEvent = !!upload; + + 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() +{ + 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; +} + +class nsDOMWorkerXHRWrappedListener : public nsIDOMEventListener +{ +public: + NS_DECL_ISUPPORTS + + nsDOMWorkerXHRWrappedListener(nsIDOMEventListener* aInner) + : mInner(aInner) { + NS_ASSERTION(aInner, "Null pointer!"); + } + + NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { + return mInner->HandleEvent(aEvent); + } + + nsIDOMEventListener* Inner() { + return mInner; + } + +private: + nsCOMPtr mInner; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerXHRWrappedListener, + nsIDOMEventListener) + +nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR) +: mWorkerXHR(aWorkerXHR), + mXHR(nsnull), + mConcreteXHR(nsnull), + mUpload(nsnull), + mOwnedByXHR(PR_FALSE), + mMultipart(PR_FALSE), + mCanceled(PR_FALSE) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(MAX_XHR_LISTENER_TYPE >= MAX_UPLOAD_LISTENER_TYPE, + "Upload should support a subset of XHR's event types!"); +} + +nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy() +{ + if (mOwnedByXHR) { + mWorkerXHR->Release(); + } + else if (mUpload) { + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + NS_ASSERTION(mainThread, "This isn't supposed to fail!"); + + // This will release immediately if we're on the main thread. + NS_ProxyRelease(mainThread, mUpload); + mUpload = nsnull; + } +} + +NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRProxy, nsRunnable, + nsIDOMEventListener, + nsIRequestObserver) + +nsresult +nsDOMWorkerXHRProxy::Init() +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + NS_ENSURE_FALSE(mXHR, NS_ERROR_ALREADY_INITIALIZED); + + mXHRListeners.SetLength(MAX_XHR_LISTENER_TYPE); + mXHROnXListeners.SetLength(MAX_XHR_LISTENER_TYPE); + + mUploadListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE); + mUploadOnXListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE); + + mMainThread = do_GetMainThread(); + NS_ENSURE_TRUE(mMainThread, NS_ERROR_UNEXPECTED); + + nsRefPtr runnable = + new nsResultReturningRunnable(mMainThread, this, mWorkerXHR->mWorker); + NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = runnable->Dispatch(); + if (NS_FAILED(rv)) { + // Only warn if we didn't get canceled. + NS_WARN_IF_FALSE(rv == NS_ERROR_ABORT, "Dispatch failed!"); + return rv; + } + + return NS_OK; +} + +nsIXMLHttpRequest* +nsDOMWorkerXHRProxy::GetXMLHttpRequest() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mCanceled ? nsnull : mXHR; +} + +nsresult +nsDOMWorkerXHRProxy::Destroy() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mCanceled = PR_TRUE; + + ClearEventListeners(); + mLastXHREvent = nsnull; + + if (mUpload) { + DestroyInternal(); + } + + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::InitInternal() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!"); + + nsDOMWorkerThread* worker = mWorkerXHR->mWorker; + nsRefPtr pool = worker->Pool(); + + if (worker->IsCanceled()) { + return NS_ERROR_ABORT; + } + + nsIPrincipal* nodePrincipal = pool->ParentDocument()->NodePrincipal(); + nsIScriptContext* scriptContext = pool->ScriptContext(); + NS_ASSERTION(nodePrincipal && scriptContext, "Shouldn't be null!"); + + nsRefPtr xhrConcrete = new nsXMLHttpRequest(); + NS_ENSURE_TRUE(xhrConcrete, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = xhrConcrete->Init(nodePrincipal, scriptContext, nsnull); + NS_ENSURE_SUCCESS(rv, rv); + + // Call QI manually here to avoid keeping up with the cast madness of + // nsXMLHttpRequest. + nsCOMPtr xhr = + do_QueryInterface(static_cast(xhrConcrete)); + NS_ENSURE_TRUE(xhr, NS_ERROR_NO_INTERFACE); + + nsCOMPtr upload; + rv = xhr->GetUpload(getter_AddRefs(upload)); + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr nullEvent = new nsDOMWorkerXHREvent(this); + NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY); + + rv = nullEvent->Init(xhr); + NS_ENSURE_SUCCESS(rv, rv); + + nullEvent->EventHandled(); + mLastXHREvent.swap(nullEvent); + + // We now own upload and it owns xhr. + upload.swap(mUpload); + + // Weak. + mXHR = xhr; + + xhrConcrete->SetRequestObserver(this); + + // Weak. + mConcreteXHR = xhrConcrete; + + return NS_OK; +} + +void +nsDOMWorkerXHRProxy::DestroyInternal() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsRefPtr kungFuDeathGrip; + + if (mOwnedByXHR) { + kungFuDeathGrip = this; + mXHR->Abort(); + } + + // mUpload could be null if Init fails. + if (mUpload) { + mConcreteXHR->SetRequestObserver(nsnull); + mUpload->Release(); + mUpload = nsnull; + mXHR = nsnull; + } +} + +void +nsDOMWorkerXHRProxy::FlipOwnership() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // Flip! + mOwnedByXHR = !mOwnedByXHR; + + nsCOMPtr xhrTarget(do_QueryInterface(mXHR)); + NS_ASSERTION(xhrTarget, "This shouldn't fail!"); + + nsCOMPtr uploadTarget(do_QueryInterface(mUpload)); + NS_ASSERTION(uploadTarget, "This shouldn't fail!"); + + // If mWorkerXHR has no outstanding refs from JS then we are about to die. + // Hold an extra ref here to make sure that we live through this call. + nsRefPtr kungFuDeathGrip(this); + + AddRemoveFunction addRemoveEventListener = mOwnedByXHR ? + &nsIDOMEventTarget::AddEventListener : + &nsIDOMEventTarget::RemoveEventListener; + + nsAutoString eventName; + PRUint32 index = 0; + + for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) { + eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]); + (xhrTarget->*addRemoveEventListener)(eventName, this, PR_FALSE); + (uploadTarget->*addRemoveEventListener)(eventName, this, PR_FALSE); + } + + for (; index < MAX_XHR_LISTENER_TYPE; index++) { + eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]); + (xhrTarget->*addRemoveEventListener)(eventName, this, PR_FALSE); + } + + if (mOwnedByXHR) { + mWorkerXHR->AddRef(); + mUpload->Release(); + } + else { + mUpload->AddRef(); + mWorkerXHR->Release(); + } +} + +nsresult +nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType, + nsIDOMEventListener* aListener, + PRBool aOnXListener, + PRBool aUploadListener) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) || + aType >= MAX_XHR_LISTENER_TYPE) { + // Silently fail on junk events. + return NS_OK; + } + + ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] : + mXHRListeners[aType]; + WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] : + mXHROnXListeners[aType]; + + nsAutoLock lock(mWorkerXHR->Lock()); + +#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); + + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::RemoveEventListener(PRUint32 aType, + nsIDOMEventListener* aListener, + PRBool aUploadListener) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aListener, "Null pointer!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) || + aType >= MAX_XHR_LISTENER_TYPE) { + // Silently fail on junk events. + return NS_OK; + } + + ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] : + mXHRListeners[aType]; + + nsAutoLock lock(mWorkerXHR->Lock()); + + listeners.RemoveElement(aListener); + + return NS_OK; +} + +already_AddRefed +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) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aEvent, "Should not be null!"); + + mLastXHREvent = aEvent; + + nsresult rv = HandleEventInternal(aEvent->mType, aEvent, aUploadEvent); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +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; + } + + rv = HandleEventInternal(type, aEvent, aUploadEvent); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType, + nsIDOMEvent* aEvent, + PRBool aUploadListener) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aEvent, "Should not be null!"); + +#ifdef DEBUG + if (aUploadListener) { + NS_ASSERTION(aType < MAX_UPLOAD_LISTENER_TYPE, "Bad type!"); + } + else { + NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!"); + } +#endif + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] : + mXHRListeners[aType]; + + 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. +} + +// nsIDOMEventListener +NS_IMETHODIMP +nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + NS_ENSURE_ARG_POINTER(aEvent); + + nsRefPtr newEvent = new nsDOMWorkerXHREvent(this); + NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = newEvent->Init(aEvent); + NS_ENSURE_SUCCESS(rv, rv); + + rv = nsDOMThreadService::get()->Dispatch(mWorkerXHR->mWorker, newEvent); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::Abort() +{ + if (NS_IsMainThread()) { + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsCOMPtr xhr = mXHR; + if (mOwnedByXHR) { + FlipOwnership(); + } + + return xhr->Abort(); + } + + RUN_PROXIED_FUNCTION(Abort, (this)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetAllResponseHeaders(char** _retval) +{ + RUN_PROXIED_FUNCTION(GetAllResponseHeaders, (this, _retval)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetResponseHeader(const nsACString& aHeader, + nsACString& _retval) +{ + RUN_PROXIED_FUNCTION(GetResponseHeader, (this, aHeader, _retval)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::OpenRequest(const nsACString& aMethod, + const nsACString& aUrl, + PRBool aAsync, + const nsAString& aUser, + const nsAString& aPassword) +{ + RUN_PROXIED_FUNCTION(OpenRequest, (this, aMethod, aUrl, aAsync, aUser, + aPassword)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::Send(nsIVariant* aBody) +{ + RUN_PROXIED_FUNCTION(Send, (this, aBody)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::SendAsBinary(const nsAString& aBody) +{ + RUN_PROXIED_FUNCTION(SendAsBinary, (this, aBody)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::SetRequestHeader(const nsACString& aHeader, + const nsACString& aValue) +{ + RUN_PROXIED_FUNCTION(SetRequestHeader, (this, aHeader, aValue)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::OverrideMimeType(const nsACString& aMimetype) +{ + RUN_PROXIED_FUNCTION(OverrideMimeType, (this, aMimetype)); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetMultipart(PRBool* aMultipart) +{ + if (mCanceled) { + return NS_ERROR_ABORT; + } + + *aMultipart = mMultipart; + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::SetMultipart(PRBool aMultipart) +{ + RUN_PROXIED_FUNCTION(SetMultipart, (this, aMultipart)); + mMultipart = aMultipart; + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetResponseText(nsAString& _retval) +{ + if (mCanceled) { + return NS_ERROR_ABORT; + } + + _retval.Assign(mLastXHREvent->mResponseText); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetStatusText(nsACString& _retval) +{ + if (mCanceled) { + return NS_ERROR_ABORT; + } + + _retval.Assign(mLastXHREvent->mStatusText); + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetStatus(nsresult* _retval) +{ + NS_ASSERTION(_retval, "Null pointer!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + *_retval = mLastXHREvent->mStatus; + return NS_OK; +} + +nsresult +nsDOMWorkerXHRProxy::GetReadyState(PRInt32* _retval) +{ + NS_ASSERTION(_retval, "Null pointer!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + *_retval = mLastXHREvent->mReadyState; + return NS_OK; +} + +// nsIRunnable +NS_IMETHODIMP +nsDOMWorkerXHRProxy::Run() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mUpload, "Run twice?!"); + + if (mCanceled) { + return NS_ERROR_ABORT; + } + + nsresult rv = InitInternal(); + if (NS_FAILED(rv)) { + NS_WARNING("InitInternal failed!"); + DestroyInternal(); + return rv; + } + + return NS_OK; +} + +// nsIRequestObserver +NS_IMETHODIMP +nsDOMWorkerXHRProxy::OnStartRequest(nsIRequest* /* aRequest */, + nsISupports* /* aContext */) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_OK; + } + + NS_ASSERTION(!mOwnedByXHR, "Inconsistent state!"); + + FlipOwnership(); + + return NS_OK; +} + +// nsIRequestObserver +NS_IMETHODIMP +nsDOMWorkerXHRProxy::OnStopRequest(nsIRequest* /* aRequest */, + nsISupports* /* aContext */, + nsresult /* aStatus */) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (mCanceled) { + return NS_OK; + } + + NS_ASSERTION(mOwnedByXHR, "Inconsistent state!"); + + FlipOwnership(); + + return NS_OK; +} diff --git a/dom/src/threads/nsDOMWorkerXHRProxy.h b/dom/src/threads/nsDOMWorkerXHRProxy.h new file mode 100644 index 00000000000..b5673cfe5b1 --- /dev/null +++ b/dom/src/threads/nsDOMWorkerXHRProxy.h @@ -0,0 +1,173 @@ +/* -*- 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): + * 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 __NSDOMWORKERXHRPROXY_H__ +#define __NSDOMWORKERXHRPROXY_H__ + +// Bases +#include "nsThreadUtils.h" +#include "nsIDOMEventListener.h" +#include "nsIRequestObserver.h" + +// Other includes +#include "nsCOMPtr.h" +#include "nsStringGlue.h" +#include "nsTArray.h" + +// DOMWorker includes +#include "nsDOMWorkerXHR.h" + +class nsIJSXMLHttpRequest; +class nsIThread; +class nsIVariant; +class nsIXMLHttpRequest; +class nsDOMWorkerXHREvent; +class nsDOMWorkerXHRWrappedListener; +class nsXMLHttpRequest; + +class nsDOMWorkerXHRProxy : public nsRunnable, + public nsIDOMEventListener, + public nsIRequestObserver +{ + + friend class nsDOMWorkerXHREvent; + friend class nsDOMWorkerXHR; + friend class nsDOMWorkerXHRUpload; + + typedef nsCOMPtr Listener; + typedef nsTArray ListenerArray; + + typedef nsRefPtr WrappedListener; + + typedef nsresult (NS_STDCALL nsIDOMEventTarget::*AddRemoveFunction) + (const nsAString&, nsIDOMEventListener*, PRBool); + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDOMEVENTLISTENER + NS_DECL_NSIRUNNABLE + NS_DECL_NSIREQUESTOBSERVER + + nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR); + virtual ~nsDOMWorkerXHRProxy(); + + nsresult Init(); + + nsIXMLHttpRequest* GetXMLHttpRequest(); + + nsresult Abort(); + +protected: + nsresult InitInternal(); + void DestroyInternal(); + + nsresult Destroy(); + + void FlipOwnership(); + + nsresult AddEventListener(PRUint32 aType, + nsIDOMEventListener* aListener, + PRBool aOnXListener, + PRBool aUploadListener); + + 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 HandleEventInternal(PRUint32 aType, + nsIDOMEvent* aEvent, + PRBool aUploadEvent); + + void ClearEventListeners(); + + // Methods of nsIXMLHttpRequest that we implement + nsresult GetAllResponseHeaders(char** _retval); + nsresult GetResponseHeader(const nsACString& aHeader, + nsACString& _retval); + nsresult OpenRequest(const nsACString& aMethod, + const nsACString& aUrl, + PRBool aAsync, + const nsAString& aUser, + const nsAString& aPassword); + nsresult Send(nsIVariant* aBody); + nsresult SendAsBinary(const nsAString& aBody); + nsresult GetResponseText(nsAString& _retval); + nsresult GetStatusText(nsACString& _retval); + nsresult GetStatus(nsresult* _retval); + nsresult GetReadyState(PRInt32* _retval); + nsresult SetRequestHeader(const nsACString& aHeader, + const nsACString& aValue); + nsresult OverrideMimeType(const nsACString& aMimetype); + nsresult GetMultipart(PRBool* aMultipart); + nsresult SetMultipart(PRBool aMultipart); + + // May be weak or strong, check mOwnedByXHR. + nsDOMWorkerXHR* mWorkerXHR; + + // Always weak! + nsIXMLHttpRequest* mXHR; + nsXMLHttpRequest* mConcreteXHR; + + // May be weak or strong, check mOwnedByXHR. + nsIXMLHttpRequestUpload* mUpload; + + nsCOMPtr mMainThread; + + nsRefPtr mLastXHREvent; + + nsTArray mXHRListeners; + nsTArray mXHROnXListeners; + + nsTArray mUploadListeners; + nsTArray mUploadOnXListeners; + + // Whether or not this object is owned by the real XHR object. + PRPackedBool mOwnedByXHR; + + PRPackedBool mMultipart; + PRPackedBool mCanceled; +}; + +#endif /* __NSDOMWORKERXHRPROXY_H__ */ diff --git a/dom/src/threads/test/Makefile.in b/dom/src/threads/test/Makefile.in index 2e162c1036f..eca731da43e 100644 --- a/dom/src/threads/test/Makefile.in +++ b/dom/src/threads/test/Makefile.in @@ -57,6 +57,8 @@ _TEST_FILES = \ test_threadErrors.html \ test_threadTimeouts.html \ test_longThread.html \ + test_xhr.html \ + testXHR.txt \ $(NULL) libs:: $(_TEST_FILES) diff --git a/dom/src/threads/test/testXHR.txt b/dom/src/threads/test/testXHR.txt new file mode 100644 index 00000000000..2beab22c660 --- /dev/null +++ b/dom/src/threads/test/testXHR.txt @@ -0,0 +1 @@ +A noisy noise annoys an oyster. \ No newline at end of file diff --git a/dom/src/threads/test/test_xhr.html b/dom/src/threads/test/test_xhr.html new file mode 100644 index 00000000000..3d480d31b85 --- /dev/null +++ b/dom/src/threads/test/test_xhr.html @@ -0,0 +1,114 @@ + + + + + Test for DOM Worker Threads XHR (Bug 450452 ) + + + + + +DOM Worker Threads XHR (Bug 450452) +

+ +
+
+
+ + + diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 3279ebe27b5..896f25c2dc8 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -3647,8 +3647,8 @@ public: * @param cx The JSContext, this can be null, we don't do anything then */ AutoScriptEvaluate(JSContext * cx) - : mJSContext(cx), mState(0), mEvaluated(PR_FALSE), - mContextHasThread(0) {} + : mJSContext(cx), mState(0), mErrorReporterSet(PR_FALSE), + mEvaluated(PR_FALSE), mContextHasThread(0) {} /** * Does the pre script evaluation and sets the error reporter if given @@ -3665,7 +3665,7 @@ public: private: JSContext* mJSContext; JSExceptionState* mState; - JSErrorReporter mOldErrorReporter; + PRBool mErrorReporterSet; PRBool mEvaluated; jsword mContextHasThread; diff --git a/js/src/xpconnect/src/xpcwrappedjsclass.cpp b/js/src/xpconnect/src/xpcwrappedjsclass.cpp index 043d5ac8175..89fe91c180f 100644 --- a/js/src/xpconnect/src/xpcwrappedjsclass.cpp +++ b/js/src/xpconnect/src/xpcwrappedjsclass.cpp @@ -55,7 +55,11 @@ void AutoScriptEvaluate::StartEvaluating(JSErrorReporter errorReporter) if(!mJSContext) return; mEvaluated = PR_TRUE; - mOldErrorReporter = JS_SetErrorReporter(mJSContext, errorReporter); + if(!mJSContext->errorReporter) + { + JS_SetErrorReporter(mJSContext, errorReporter); + mErrorReporterSet = PR_TRUE; + } mContextHasThread = JS_GetContextThread(mJSContext); if (mContextHasThread) JS_BeginRequest(mJSContext); @@ -105,7 +109,9 @@ AutoScriptEvaluate::~AutoScriptEvaluate() if(scriptNotify) scriptNotify->ScriptExecuted(); } - JS_SetErrorReporter(mJSContext, mOldErrorReporter); + + if(mErrorReporterSet) + JS_SetErrorReporter(mJSContext, NULL); } // It turns out that some errors may be not worth reporting. So, this @@ -906,6 +912,15 @@ nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type, } } +class AutoClearPendingException +{ +public: + AutoClearPendingException(JSContext *cx) : mCx(cx) { } + ~AutoClearPendingException() { JS_ClearPendingException(mCx); } +private: + JSContext* mCx; +}; + nsresult nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, const char * aPropertyName, @@ -926,8 +941,10 @@ nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, nsresult pending_result = xpcc->GetPendingResult(); jsval js_exception; + JSBool is_js_exception = JS_GetPendingException(cx, &js_exception); + /* JS might throw an expection whether the reporter was called or not */ - if(JS_GetPendingException(cx, &js_exception)) + if(is_js_exception) { if(!xpc_exception) XPCConvert::JSValToXPCException(ccx, js_exception, anInterfaceName, @@ -939,9 +956,10 @@ nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, { ccx.GetThreadData()->SetException(nsnull); // XXX necessary? } - JS_ClearPendingException(cx); } + AutoClearPendingException acpe(cx); + if(xpc_exception) { nsresult e_result; @@ -992,6 +1010,14 @@ nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, } } + // Try to use the error reporter set on the context to handle this + // error if it came from a JS exception. + if(reportable && is_js_exception && + cx->errorReporter != xpcWrappedJSErrorReporter) + { + reportable = !JS_ReportPendingException(cx); + } + if(reportable) { #ifdef DEBUG