mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 460811 - 'Bring workers up to latest spec'.r+sr=jst.
This commit is contained in:
parent
25f0edcfdb
commit
c031080e02
@ -39,9 +39,7 @@
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
interface nsIDOMWorkerPool;
|
||||
|
||||
[scriptable, uuid(c206f746-52e2-47dd-8ccc-ce76ccda6c6d)]
|
||||
[scriptable, uuid(777bd8a1-38c1-4b12-ba8f-ff6c2eb8c56b)]
|
||||
interface nsIDOMNavigator : nsISupports
|
||||
{
|
||||
readonly attribute DOMString appCodeName;
|
||||
@ -65,8 +63,6 @@ interface nsIDOMNavigator : nsISupports
|
||||
boolean javaEnabled();
|
||||
boolean taintEnabled();
|
||||
|
||||
nsIDOMWorkerPool newWorkerPool();
|
||||
|
||||
// XXX This one's tough, would nsISupports preference(in DOMString
|
||||
// pref /*, ... */); work?
|
||||
|
||||
|
@ -47,6 +47,6 @@ MODULE = dom
|
||||
XPIDL_MODULE = dom_threads
|
||||
GRE_MODULE = 1
|
||||
|
||||
XPIDLSRCS = nsIDOMThreads.idl
|
||||
XPIDLSRCS = nsIDOMWorkers.idl
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -1,149 +0,0 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is worker threads.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
|
||||
* Ben Turner <bent.mozilla@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIScriptError;
|
||||
|
||||
[scriptable, function, uuid(e50ca05d-1381-4abb-a021-02eb720cfc38)]
|
||||
interface nsIDOMWorkerMessageListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* An nsIDOMWorkerThread receives the onMessage callback when another
|
||||
* worker posts a message to it.
|
||||
*
|
||||
* @param aMessage (in DOMString)
|
||||
* The message sent from another worker.
|
||||
* @param aSource (in nsISupports)
|
||||
* The worker that sent the message. Useful for a quick response.
|
||||
*/
|
||||
void onMessage(in DOMString aMessage,
|
||||
in nsISupports aSource);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(9df8422e-25dd-43f4-b9b9-709f9e074647)]
|
||||
interface nsIDOMWorkerErrorListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* An nsIDOMWorkerPool receives the onError callback when one of its child
|
||||
* workers has a parse error or an unhandled exception.
|
||||
*
|
||||
* @param aMessage (in nsIScriptError)
|
||||
* Details about the error that occurred. See nsIScriptError.
|
||||
* @param aSource (in nsISupports)
|
||||
* The worker that sent the message. Depending on the specific error in
|
||||
* question it may not be possible to use this object (in the case of a
|
||||
* parse error, for instance, aSource will be unusable).
|
||||
*/
|
||||
void onError(in nsIScriptError aError,
|
||||
in nsISupports aSource);
|
||||
};
|
||||
|
||||
[scriptable, uuid(6f19f3ff-2aaa-4504-9b71-dca3c191efed)]
|
||||
interface nsIDOMWorkerThread : nsISupports
|
||||
{
|
||||
/**
|
||||
* Sends a message to the worker.
|
||||
*
|
||||
* @param aMessage (in DOMString)
|
||||
* The message to send.
|
||||
*/
|
||||
void postMessage(in DOMString aMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(45312e93-8a3e-4493-9bd9-272a6c23a16c)]
|
||||
interface nsIDOMWorkerPool : nsISupports
|
||||
{
|
||||
/**
|
||||
* Sends a message to the pool.
|
||||
*
|
||||
* @param aMessage (in DOMString)
|
||||
* The message to send..
|
||||
*/
|
||||
void postMessage(in DOMString aMessage);
|
||||
|
||||
/**
|
||||
* The nsIDOMWorkerMessageListener which handles messages for this worker.
|
||||
*
|
||||
* Developers should set this attribute to a proper object before another
|
||||
* worker begins sending messages to ensure that all messages are received.
|
||||
*/
|
||||
attribute nsIDOMWorkerMessageListener messageListener;
|
||||
|
||||
/**
|
||||
* The nsIDOMWorkerErrorListener which handles errors in child threads.
|
||||
*
|
||||
* Developers should set this attribute to a proper object before calling
|
||||
* createWorker in order to catch parse errors as well as runtime exceptions.
|
||||
*/
|
||||
attribute nsIDOMWorkerErrorListener errorListener;
|
||||
|
||||
/**
|
||||
* Create a new worker object by evaluating the given script.
|
||||
*
|
||||
* @param aSourceScript (in DOMString)
|
||||
* The script to compile. See below for details on the scope in which
|
||||
* the script will run.
|
||||
*/
|
||||
nsIDOMWorkerThread createWorker(in DOMString aSourceScript);
|
||||
|
||||
/**
|
||||
* Create a new worker object by evaluating the given script.
|
||||
*
|
||||
* @param aSourceURL (in AString)
|
||||
* The script url to load and compile. See below for details on the
|
||||
* scope in which the script will run.
|
||||
*/
|
||||
nsIDOMWorkerThread createWorkerFromURL(in AString aSourceURL);
|
||||
};
|
||||
|
||||
[scriptable, uuid(0f2a52ea-afc9-49e6-86dd-2d0cb65b5dd5)]
|
||||
interface nsIDOMThreadService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Creates a new DOM worker pool.
|
||||
*/
|
||||
nsIDOMWorkerPool createPool();
|
||||
};
|
||||
|
||||
[scriptable, uuid(fcf387be-a7e3-4283-8bc5-06bfe13c5e8c)]
|
||||
interface nsIDOMWorkerThreadContext : nsISupports
|
||||
{
|
||||
readonly attribute nsIDOMWorkerThread thisThread;
|
||||
};
|
@ -458,6 +458,8 @@ enum nsDOMClassInfoID {
|
||||
eDOMClassInfo_MathMLElement_id,
|
||||
#endif
|
||||
|
||||
eDOMClassInfo_Worker_id,
|
||||
|
||||
// This one better be the last one in this list
|
||||
eDOMClassInfoIDCount
|
||||
};
|
||||
|
@ -457,6 +457,9 @@
|
||||
#include "nsIDOMGeoPosition.h"
|
||||
#include "nsIDOMGeoPositionError.h"
|
||||
|
||||
// Workers
|
||||
#include "nsDOMWorker.h"
|
||||
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsIDOMFileException.h"
|
||||
|
||||
@ -1299,6 +1302,9 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(MathMLElement, Element, nsElementSH,
|
||||
ELEMENT_SCRIPTABLE_FLAGS)
|
||||
#endif
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(Worker, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
};
|
||||
|
||||
// Objects that shuld be constructable through |new Name();|
|
||||
@ -1321,6 +1327,20 @@ static const nsContractIDMapData kConstructorMap[] =
|
||||
"@mozilla.org/document-transformer;1?type=xslt")
|
||||
};
|
||||
|
||||
struct nsConstructorFuncMapData
|
||||
{
|
||||
PRInt32 mDOMClassInfoID;
|
||||
nsDOMConstructorFunc mConstructorFunc;
|
||||
};
|
||||
|
||||
#define NS_DEFINE_CONSTRUCTOR_FUNC_DATA(_class, _func) \
|
||||
{ eDOMClassInfo_##_class##_id, _func },
|
||||
|
||||
static const nsConstructorFuncMapData kConstructorFuncMap[] =
|
||||
{
|
||||
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Worker, nsDOMWorker::NewWorker)
|
||||
};
|
||||
|
||||
nsIXPConnect *nsDOMClassInfo::sXPConnect = nsnull;
|
||||
nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nsnull;
|
||||
PRBool nsDOMClassInfo::sIsInitialized = PR_FALSE;
|
||||
@ -3552,6 +3572,12 @@ nsDOMClassInfo::Init()
|
||||
DOM_CLASSINFO_MAP_END
|
||||
#endif
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(Worker, nsIWorker)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIWorker)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIAbstractWorker)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
{
|
||||
PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
|
||||
@ -4966,6 +4992,17 @@ FindConstructorContractID(PRInt32 aDOMClassInfoID)
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
static nsDOMConstructorFunc
|
||||
FindConstructorFunc(PRInt32 aDOMClassInfoID)
|
||||
{
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kConstructorFuncMap); ++i) {
|
||||
if (kConstructorFuncMap[i].mDOMClassInfoID == aDOMClassInfoID) {
|
||||
return kConstructorFuncMap[i].mConstructorFunc;
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
const nsGlobalNameStruct *name_struct, JSContext *cx,
|
||||
@ -4976,7 +5013,16 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
|
||||
const char *contractid =
|
||||
FindConstructorContractID(name_struct->mDOMClassInfoID);
|
||||
native = do_CreateInstance(contractid, &rv);
|
||||
if (contractid) {
|
||||
native = do_CreateInstance(contractid, &rv);
|
||||
}
|
||||
else {
|
||||
nsDOMConstructorFunc func =
|
||||
FindConstructorFunc(name_struct->mDOMClassInfoID);
|
||||
if (func) {
|
||||
rv = func(getter_AddRefs(native));
|
||||
}
|
||||
}
|
||||
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
|
||||
native = do_CreateInstance(name_struct->mCID, &rv);
|
||||
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
|
||||
@ -5194,7 +5240,8 @@ private:
|
||||
{
|
||||
return
|
||||
(aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
|
||||
FindConstructorContractID(aNameStruct->mDOMClassInfoID)) ||
|
||||
(FindConstructorContractID(aNameStruct->mDOMClassInfoID) ||
|
||||
FindConstructorFunc(aNameStruct->mDOMClassInfoID))) ||
|
||||
(aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo &&
|
||||
aNameStruct->mData->mConstructorCID) ||
|
||||
aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructor ||
|
||||
|
@ -61,6 +61,7 @@ struct nsDOMClassInfoData;
|
||||
typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc)
|
||||
(nsDOMClassInfoData* aData);
|
||||
|
||||
typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject);
|
||||
|
||||
struct nsDOMClassInfoData
|
||||
{
|
||||
|
@ -65,6 +65,7 @@
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsXULPrototypeCache.h"
|
||||
#endif
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
|
||||
|
||||
@ -395,6 +396,10 @@ nsDOMExceptionProvider::GetException(nsresult result,
|
||||
nsIException *aDefaultException,
|
||||
nsIException **_retval)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
switch (NS_ERROR_GET_MODULE(result))
|
||||
{
|
||||
case NS_ERROR_MODULE_DOM_RANGE:
|
||||
|
@ -113,7 +113,6 @@
|
||||
#include "nsIDOMPkcs11.h"
|
||||
#include "nsIDOMOfflineResourceList.h"
|
||||
#include "nsIDOMGeoGeolocation.h"
|
||||
#include "nsIDOMThreads.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsIEmbeddingSiteWindow2.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -226,6 +225,10 @@ static PRUint32 gSerialCounter = 0;
|
||||
PRInt32 gTimeoutCnt = 0;
|
||||
#endif
|
||||
|
||||
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
static PRBool gDOMWindowDumpEnabled = PR_FALSE;
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_bryner) || defined(DEBUG_chb)
|
||||
#define DEBUG_PAGE_CACHE
|
||||
#endif
|
||||
@ -655,9 +658,21 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
||||
// We could have failed the first time through trying
|
||||
// to create the entropy collector, so we should
|
||||
// try to get one until we succeed.
|
||||
if (gRefCnt++ == 0 || !gEntropyCollector) {
|
||||
|
||||
gRefCnt++;
|
||||
|
||||
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
if (gRefCnt == 1) {
|
||||
static const char* prefName = "browser.dom.window.dump.enabled";
|
||||
nsContentUtils::AddBoolPrefVarCache(prefName, &gDOMWindowDumpEnabled);
|
||||
gDOMWindowDumpEnabled = nsContentUtils::GetBoolPref(prefName);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!gEntropyCollector) {
|
||||
CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt,
|
||||
static_cast<void*>(static_cast<nsIScriptGlobalObject*>(this)),
|
||||
@ -1667,6 +1682,11 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
|
||||
PRBool isChrome = PR_FALSE;
|
||||
|
||||
nsCxPusher cxPusher;
|
||||
if (!cxPusher.Push(cx)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
// Make sure to clear scope on the outer window *before* we
|
||||
@ -3823,24 +3843,25 @@ nsGlobalWindow::GetFullScreen(PRBool* aFullScreen)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsGlobalWindow::DOMWindowDumpEnabled()
|
||||
{
|
||||
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
// In optimized builds we check a pref that controls if we should
|
||||
// enable output from dump() or not, in debug builds it's always
|
||||
// enabled.
|
||||
return gDOMWindowDumpEnabled;
|
||||
#else
|
||||
return PR_TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::Dump(const nsAString& aStr)
|
||||
{
|
||||
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
{
|
||||
// In optimized builds we check a pref that controls if we should
|
||||
// enable output from dump() or not, in debug builds it's always
|
||||
// enabled.
|
||||
|
||||
// if pref doesn't exist, disable dump output.
|
||||
PRBool enable_dump =
|
||||
nsContentUtils::GetBoolPref("browser.dom.window.dump.enabled");
|
||||
|
||||
if (!enable_dump) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (!DOMWindowDumpEnabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *cstr = ToNewUTF8String(aStr);
|
||||
|
||||
@ -9438,18 +9459,3 @@ NS_IMETHODIMP nsNavigator::GetGeolocation(nsIDOMGeoGeolocation **_retval)
|
||||
NS_IF_ADDREF(*_retval = mGeolocation);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavigator::NewWorkerPool(nsIDOMWorkerPool** _retval)
|
||||
{
|
||||
nsCOMPtr<nsIDOMThreadService> threadService =
|
||||
nsDOMThreadService::GetOrInitService();
|
||||
NS_ENSURE_TRUE(threadService, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsCOMPtr<nsIDOMWorkerPool> newPool;
|
||||
nsresult rv = threadService->CreatePool(getter_AddRefs(newPool));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
newPool.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -434,6 +434,7 @@ public:
|
||||
CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
|
||||
nsScriptObjectHolder& aHandler);
|
||||
|
||||
static PRBool DOMWindowDumpEnabled();
|
||||
|
||||
protected:
|
||||
// Object Management
|
||||
|
@ -2373,6 +2373,11 @@ nsJSContext::InitContext(nsIScriptGlobalObject *aGlobalObject)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCxPusher cxPusher;
|
||||
if (!cxPusher.Push(mContext)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsIXPConnect *xpc = nsContentUtils::XPConnect();
|
||||
|
||||
JSObject *global = ::JS_GetGlobalObject(mContext);
|
||||
|
@ -51,11 +51,13 @@ FORCE_STATIC_LIB = 1
|
||||
REQUIRES = \
|
||||
caps \
|
||||
content \
|
||||
docshell \
|
||||
gfx \
|
||||
js \
|
||||
layout \
|
||||
locale \
|
||||
necko \
|
||||
plugin \
|
||||
pref \
|
||||
string \
|
||||
thebes \
|
||||
@ -66,11 +68,12 @@ REQUIRES = \
|
||||
|
||||
CPPSRCS = \
|
||||
nsDOMThreadService.cpp \
|
||||
nsDOMWorkerBase.cpp \
|
||||
nsDOMWorker.cpp \
|
||||
nsDOMWorkerEvents.cpp \
|
||||
nsDOMWorkerMessageHandler.cpp \
|
||||
nsDOMWorkerPool.cpp \
|
||||
nsDOMWorkerScriptLoader.cpp \
|
||||
nsDOMWorkerSecurityManager.cpp \
|
||||
nsDOMWorkerThread.cpp \
|
||||
nsDOMWorkerTimeout.cpp \
|
||||
nsDOMWorkerXHR.cpp \
|
||||
nsDOMWorkerXHRProxy.cpp \
|
||||
@ -87,5 +90,3 @@ DIRS += test
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
#CXXFLAGS += $(WARNINGS_AS_ERRORS)
|
||||
|
@ -147,7 +147,11 @@ public:
|
||||
* Pretend to be a JSObject*. Assert if not held.
|
||||
*/
|
||||
JSObject* operator=(JSObject* aOther) {
|
||||
NS_ASSERTION(mHeld, "Not rooted!");
|
||||
#ifdef DEBUG
|
||||
if (aOther) {
|
||||
NS_ASSERTION(mHeld, "Not rooted!");
|
||||
}
|
||||
#endif
|
||||
return mObj = aOther;
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@
|
||||
|
||||
// Interfaces
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIEventTarget.h"
|
||||
@ -55,6 +54,7 @@
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
// Other includes
|
||||
#include "nsAutoLock.h"
|
||||
@ -71,6 +71,10 @@
|
||||
#include "prthread.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorker.h"
|
||||
#include "nsDOMWorkerEvents.h"
|
||||
#include "nsDOMWorkerMacros.h"
|
||||
#include "nsDOMWorkerMessageHandler.h"
|
||||
#include "nsDOMWorkerPool.h"
|
||||
#include "nsDOMWorkerSecurityManager.h"
|
||||
#include "nsDOMWorkerTimeout.h"
|
||||
@ -105,14 +109,6 @@ PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >= THREADPOOL_MAX_THREADS);
|
||||
// A "bad" value for the NSPR TLS functions.
|
||||
#define BAD_TLS_INDEX (PRUintn)-1
|
||||
|
||||
// Don't know why nsISupports.idl defines this out...
|
||||
#define NS_FORWARD_NSISUPPORTS(_to) \
|
||||
NS_IMETHOD QueryInterface(const nsIID& uuid, void** result) { \
|
||||
return _to QueryInterface(uuid, result); \
|
||||
} \
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void) { return _to AddRef(); } \
|
||||
NS_IMETHOD_(nsrefcnt) Release(void) { return _to Release(); }
|
||||
|
||||
// Easy access for static functions. No reference here.
|
||||
static nsDOMThreadService* gDOMThreadService = nsnull;
|
||||
|
||||
@ -155,103 +151,31 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is used as to post an error to the main thread. It logs the error
|
||||
* to the console and calls the pool's onError callback.
|
||||
* This class is used as to post an error to the worker's outer handler.
|
||||
*/
|
||||
class nsReportErrorRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker)
|
||||
: mError(aError), mWorker(aWorker) { }
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIConsoleService> consoleService =
|
||||
do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
consoleService->LogMessage(mError);
|
||||
}
|
||||
|
||||
if (!mWorker->IsCanceled()) {
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoString message;
|
||||
mError->GetErrorMessage(message);
|
||||
#endif
|
||||
nsRefPtr<nsDOMWorkerPool> pool = mWorker->Pool();
|
||||
|
||||
LOG(("Posting error '%s' to pool [0x%p]",
|
||||
NS_LossyConvertUTF16toASCII(message).get(),
|
||||
static_cast<void*>(pool.get())));
|
||||
|
||||
pool->HandleError(mError, mWorker);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
// XXX Maybe this should be an nsIException...
|
||||
nsCOMPtr<nsIScriptError> mError;
|
||||
|
||||
// Have to carry a strong ref since this is used as a parameter to the
|
||||
// onError callback.
|
||||
nsRefPtr<nsDOMWorkerThread> mWorker;
|
||||
};
|
||||
|
||||
/**
|
||||
* Need this to expose an nsIScriptError to content JS (implement nsIClassInfo
|
||||
* with DOM_OBJECT flag set.
|
||||
*/
|
||||
class nsDOMWorkerScriptError : public nsIClassInfo
|
||||
class nsReportErrorRunnable : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerScriptError(nsIScriptError* aError)
|
||||
: mScriptError(this, aError) { }
|
||||
nsReportErrorRunnable(nsDOMWorker* aWorker, nsIWorkerMessageEvent* aEvent)
|
||||
: mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mEvent(aEvent) { }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD Run() {
|
||||
if (mWorker->IsCanceled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Lame, nsIScriptError and nsIClassInfo both have 'readonly attribute
|
||||
// unsigned long flags' so we have to use an inner class to do this the
|
||||
// right way...
|
||||
class InnerScriptError : public nsIScriptError
|
||||
{
|
||||
public:
|
||||
NS_FORWARD_NSISUPPORTS(mParent->)
|
||||
NS_FORWARD_NSISCRIPTERROR(mError->)
|
||||
NS_FORWARD_NSICONSOLEMESSAGE(mError->)
|
||||
return mWorker->DispatchEvent(mEvent, nsnull);
|
||||
}
|
||||
|
||||
InnerScriptError(nsDOMWorkerScriptError* aParent, nsIScriptError* aError)
|
||||
: mParent(aParent), mError(aError) { }
|
||||
|
||||
protected:
|
||||
nsDOMWorkerScriptError* mParent;
|
||||
nsCOMPtr<nsIScriptError> mError;
|
||||
};
|
||||
|
||||
InnerScriptError mScriptError;
|
||||
private:
|
||||
nsRefPtr<nsDOMWorker> mWorker;
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
|
||||
nsCOMPtr<nsIWorkerMessageEvent> mEvent;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerScriptError)
|
||||
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerScriptError)
|
||||
|
||||
// More hoops to jump through for the identical IDL methods
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMWorkerScriptError)
|
||||
if (aIID.Equals(NS_GET_IID(nsIScriptError)) ||
|
||||
aIID.Equals(NS_GET_IID(nsIConsoleMessage))) {
|
||||
foundInterface = static_cast<nsIConsoleMessage*>(&mScriptError);
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerScriptError, nsIScriptError,
|
||||
nsIConsoleMessage)
|
||||
|
||||
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerScriptError)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
|
||||
|
||||
/**
|
||||
* Used to post an expired timeout to the correct worker.
|
||||
@ -283,7 +207,7 @@ class nsDOMWorkerRunnable : public nsRunnable
|
||||
friend class nsDOMThreadService;
|
||||
|
||||
public:
|
||||
nsDOMWorkerRunnable(nsDOMWorkerThread* aWorker)
|
||||
nsDOMWorkerRunnable(nsDOMWorker* aWorker)
|
||||
: mWorker(aWorker) { }
|
||||
|
||||
virtual ~nsDOMWorkerRunnable() {
|
||||
@ -291,16 +215,6 @@ public:
|
||||
while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
|
||||
// Loop until all the runnables are dead.
|
||||
}
|
||||
|
||||
// Only release mWorker on the main thread!
|
||||
nsDOMWorkerThread* worker = nsnull;
|
||||
mWorker.swap(worker);
|
||||
|
||||
nsISupports* supports = NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, worker);
|
||||
NS_ASSERTION(supports, "This should never be null!");
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
|
||||
NS_ProxyRelease(mainThread, supports);
|
||||
}
|
||||
|
||||
void PutRunnable(nsIRunnable* aRunnable) {
|
||||
@ -314,6 +228,9 @@ public:
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
NS_ASSERTION(!NS_IsMainThread(),
|
||||
"This should *never* run on the main thread!");
|
||||
|
||||
// This must have been set up by the thread service
|
||||
NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
|
||||
|
||||
@ -321,6 +238,8 @@ public:
|
||||
JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
|
||||
NS_ASSERTION(cx, "nsDOMThreadService didn't give us a context!");
|
||||
|
||||
NS_ASSERTION(!JS_GetGlobalObject(cx), "Shouldn't have a global!");
|
||||
|
||||
JS_SetContextPrivate(cx, mWorker);
|
||||
|
||||
// Tell the worker which context it will be using
|
||||
@ -335,6 +254,7 @@ public:
|
||||
else {
|
||||
// This is usually due to a parse error in the worker script...
|
||||
JS_SetGlobalObject(cx, NULL);
|
||||
JS_SetContextPrivate(cx, NULL);
|
||||
|
||||
nsAutoMonitor mon(gDOMThreadService->mMonitor);
|
||||
gDOMThreadService->WorkerComplete(this);
|
||||
@ -382,7 +302,7 @@ protected:
|
||||
}
|
||||
|
||||
// Set at construction
|
||||
nsRefPtr<nsDOMWorkerThread> mWorker;
|
||||
nsRefPtr<nsDOMWorker> mWorker;
|
||||
|
||||
// Protected by mMonitor
|
||||
nsDeque mRunnables;
|
||||
@ -395,7 +315,7 @@ protected:
|
||||
JSBool
|
||||
DOMWorkerOperationCallback(JSContext* aCx)
|
||||
{
|
||||
nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
|
||||
nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
|
||||
|
||||
// Want a strong ref here to make sure that the monitor we wait on won't go
|
||||
// away.
|
||||
@ -482,7 +402,7 @@ DOMWorkerErrorReporter(JSContext* aCx,
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
|
||||
|
||||
nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
|
||||
nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
// We don't want to report errors from canceled workers. It's very likely
|
||||
@ -511,21 +431,30 @@ DOMWorkerErrorReporter(JSContext* aCx,
|
||||
column, aReport->flags, "DOM Worker javascript");
|
||||
NS_ENSURE_SUCCESS(rv,);
|
||||
|
||||
nsRefPtr<nsDOMWorkerScriptError> domError =
|
||||
new nsDOMWorkerScriptError(errorObject);
|
||||
NS_ENSURE_TRUE(domError,);
|
||||
nsCString finalMessage;
|
||||
rv = errorObject->ToString(finalMessage);
|
||||
NS_ENSURE_SUCCESS(rv,);
|
||||
|
||||
nsCOMPtr<nsIScriptError> scriptError(do_QueryInterface(domError));
|
||||
NS_ENSURE_TRUE(scriptError,);
|
||||
nsRefPtr<nsDOMWorkerMessageEvent> event(new nsDOMWorkerMessageEvent());
|
||||
NS_ENSURE_TRUE(event,);
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
|
||||
NS_ENSURE_TRUE(mainThread,);
|
||||
rv = event->InitMessageEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE,
|
||||
NS_ConvertUTF8toUTF16(finalMessage),
|
||||
EmptyString(), nsnull);
|
||||
NS_ENSURE_SUCCESS(rv,);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new nsReportErrorRunnable(scriptError, worker);
|
||||
event->SetTarget(worker);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable(new nsReportErrorRunnable(worker, event));
|
||||
NS_ENSURE_TRUE(runnable,);
|
||||
|
||||
rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
nsRefPtr<nsDOMWorker> parent = worker->GetParent();
|
||||
|
||||
// If this worker has a parent then we need to send the message through the
|
||||
// thread service to be run on the parent's thread. Otherwise it is a
|
||||
// top-level worker and we send the message to the main thread.
|
||||
rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable)
|
||||
: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv,);
|
||||
}
|
||||
|
||||
@ -558,10 +487,9 @@ nsDOMThreadService::~nsDOMThreadService()
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget,
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
|
||||
nsIObserver,
|
||||
nsIThreadPoolListener,
|
||||
nsIDOMThreadService)
|
||||
nsIThreadPoolListener)
|
||||
|
||||
nsresult
|
||||
nsDOMThreadService::Init()
|
||||
@ -597,6 +525,9 @@ nsDOMThreadService::Init()
|
||||
PRBool success = mWorkersInProgress.Init();
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
success = mPools.Init();
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsCOMPtr<nsIJSRuntimeService>
|
||||
runtimeSvc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
|
||||
NS_ENSURE_TRUE(runtimeSvc, NS_ERROR_FAILURE);
|
||||
@ -622,7 +553,7 @@ nsDOMThreadService::Init()
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<nsIDOMThreadService>
|
||||
already_AddRefed<nsDOMThreadService>
|
||||
nsDOMThreadService::GetOrInitService()
|
||||
{
|
||||
if (!gDOMThreadService) {
|
||||
@ -635,7 +566,7 @@ nsDOMThreadService::GetOrInitService()
|
||||
service.swap(gDOMThreadService);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMThreadService> service(gDOMThreadService);
|
||||
nsRefPtr<nsDOMThreadService> service(gDOMThreadService);
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
@ -646,6 +577,24 @@ nsDOMThreadService::get()
|
||||
return gDOMThreadService;
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSContext*
|
||||
nsDOMThreadService::GetCurrentContext()
|
||||
{
|
||||
JSContext* cx;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsresult rv = ThreadJSContextStack()->GetSafeJSContext(&cx);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
}
|
||||
else {
|
||||
NS_ENSURE_TRUE(gJSContextIndex, nsnull);
|
||||
cx = static_cast<JSContext*>(PR_GetThreadPrivate(gJSContextIndex));
|
||||
}
|
||||
|
||||
return cx;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsDOMThreadService::Shutdown()
|
||||
@ -687,10 +636,15 @@ nsDOMThreadService::Cleanup()
|
||||
// These must be released after the thread pool is shut down.
|
||||
NS_IF_RELEASE(gJSRuntimeService);
|
||||
NS_IF_RELEASE(gWorkerSecurityManager);
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
NS_ASSERTION(!mPools.Count(), "Live workers left!");
|
||||
|
||||
mPools.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMThreadService::Dispatch(nsDOMWorkerThread* aWorker,
|
||||
nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
|
||||
nsIRunnable* aRunnable)
|
||||
{
|
||||
NS_ASSERTION(aWorker, "Null pointer!");
|
||||
@ -755,7 +709,7 @@ nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
|
||||
// No need to be in the monitor here because we should already be in it.
|
||||
|
||||
#ifdef DEBUG
|
||||
nsRefPtr<nsDOMWorkerThread>& debugWorker = aRunnable->mWorker;
|
||||
nsRefPtr<nsDOMWorker>& debugWorker = aRunnable->mWorker;
|
||||
|
||||
nsRefPtr<nsDOMWorkerRunnable> runnable;
|
||||
NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) &&
|
||||
@ -766,19 +720,6 @@ nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
|
||||
mWorkersInProgress.Remove(aRunnable->mWorker);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMThreadService::WaitForCanceledWorker(nsDOMWorkerThread* aWorker)
|
||||
{
|
||||
NS_ASSERTION(aWorker->IsCanceled(),
|
||||
"Waiting on a worker that isn't canceled!");
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
while (mWorkersInProgress.Get(aWorker, nsnull)) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSContext*
|
||||
nsDOMThreadService::CreateJSContext()
|
||||
@ -810,42 +751,64 @@ nsDOMThreadService::CreateJSContext()
|
||||
return cx.forget();
|
||||
}
|
||||
|
||||
#define LOOP_OVER_POOLS(_func, _args) \
|
||||
PR_BEGIN_MACRO \
|
||||
PRUint32 poolCount = mPools.Length(); \
|
||||
for (PRUint32 i = 0; i < poolCount; i++) { \
|
||||
mPools[i]-> _func _args ; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
already_AddRefed<nsDOMWorkerPool>
|
||||
nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
|
||||
PRBool aRemove)
|
||||
{
|
||||
NS_ASSERTION(aGlobalObject, "Null pointer!");
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool;
|
||||
mPools.Get(aGlobalObject, getter_AddRefs(pool));
|
||||
|
||||
if (aRemove) {
|
||||
mPools.Remove(aGlobalObject);
|
||||
}
|
||||
|
||||
return pool.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject));
|
||||
NS_ASSERTION(aGlobalObject, "Null pointer!");
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
|
||||
if (pool) {
|
||||
pool->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject));
|
||||
NS_ASSERTION(aGlobalObject, "Null pointer!");
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
|
||||
if (pool) {
|
||||
pool->Suspend();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject));
|
||||
NS_ASSERTION(aGlobalObject, "Null pointer!");
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
|
||||
if (pool) {
|
||||
pool->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool)
|
||||
nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aPool, "Null pointer!");
|
||||
|
||||
NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!");
|
||||
mPools.RemoveElement(aPool);
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mPools.Remove(aPool->ScriptGlobalObject());
|
||||
}
|
||||
|
||||
void
|
||||
@ -863,6 +826,8 @@ nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
|
||||
{
|
||||
NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
PRUint32 currentThreadCount;
|
||||
nsresult rv = mThreadPool->GetThreadLimit(¤tThreadCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -912,7 +877,6 @@ nsDOMThreadService::Dispatch(nsIRunnable* aEvent,
|
||||
|
||||
// This should only ever be called by the timer code! We run the event right
|
||||
// now, but all that does is queue the real event for the proper worker.
|
||||
|
||||
aEvent->Run();
|
||||
|
||||
return NS_OK;
|
||||
@ -1011,31 +975,69 @@ nsDOMThreadService::OnThreadShuttingDown()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* See nsIDOMThreadService
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
|
||||
nsresult
|
||||
nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
|
||||
nsIScriptGlobalObject* aGlobalObject)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aWorker, "Null pointer!");
|
||||
NS_ASSERTION(aGlobalObject, "Null pointer!");
|
||||
|
||||
NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
|
||||
NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller();
|
||||
NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED);
|
||||
nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
|
||||
domWindow->GetCurrentInnerWindow() :
|
||||
domWindow.get();
|
||||
NS_ENSURE_STATE(innerWindow);
|
||||
|
||||
nsCOMPtr<nsIDocument> callingDocument(do_QueryInterface(domDocument));
|
||||
NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE);
|
||||
nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
|
||||
NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool(callingDocument));
|
||||
NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
|
||||
aGlobalObject = newGlobal;
|
||||
}
|
||||
|
||||
nsresult rv = pool->Init();
|
||||
nsRefPtr<nsDOMWorkerPool> pool;
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
if (!mThreadPool) {
|
||||
// Shutting down!
|
||||
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
|
||||
}
|
||||
|
||||
mPools.Get(aGlobalObject, getter_AddRefs(pool));
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (!pool) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
|
||||
NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
|
||||
NS_ENSURE_STATE(domDocument);
|
||||
|
||||
nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
|
||||
NS_ENSURE_STATE(document);
|
||||
|
||||
pool = new nsDOMWorkerPool(aGlobalObject, document);
|
||||
NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = pool->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
PRBool success = mPools.Put(aGlobalObject, pool);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
rv = pool->NoteWorker(aWorker);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(!mPools.Contains(pool), "Um?!");
|
||||
mPools.AppendElement(pool);
|
||||
|
||||
NS_ADDREF(*_retval = pool);
|
||||
aWorker->SetPool(pool);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsIDOMThreads.h"
|
||||
|
||||
// Other includes
|
||||
#include "jsapi.h"
|
||||
@ -59,9 +58,9 @@
|
||||
extern PRLogModuleInfo* gDOMThreadsLog;
|
||||
#endif
|
||||
|
||||
class nsDOMWorker;
|
||||
class nsDOMWorkerPool;
|
||||
class nsDOMWorkerRunnable;
|
||||
class nsDOMWorkerThread;
|
||||
class nsDOMWorkerTimeout;
|
||||
class nsIJSRuntimeService;
|
||||
class nsIScriptGlobalObject;
|
||||
@ -71,9 +70,9 @@ class nsIXPCSecurityManager;
|
||||
|
||||
class nsDOMThreadService : public nsIEventTarget,
|
||||
public nsIObserver,
|
||||
public nsIThreadPoolListener,
|
||||
public nsIDOMThreadService
|
||||
public nsIThreadPoolListener
|
||||
{
|
||||
friend class nsDOMWorker;
|
||||
friend class nsDOMWorkerPool;
|
||||
friend class nsDOMWorkerRunnable;
|
||||
friend class nsDOMWorkerThread;
|
||||
@ -82,20 +81,25 @@ class nsDOMThreadService : public nsIEventTarget,
|
||||
friend class nsDOMWorkerXHRProxy;
|
||||
friend class nsLayoutStatics;
|
||||
|
||||
friend void DOMWorkerErrorReporter(JSContext* aCx,
|
||||
const char* aMessage,
|
||||
JSErrorReport* aReport);
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
NS_DECL_NSIDOMTHREADSERVICE
|
||||
|
||||
// Any DOM consumers that need access to this service should use this method.
|
||||
static already_AddRefed<nsIDOMThreadService> GetOrInitService();
|
||||
static already_AddRefed<nsDOMThreadService> GetOrInitService();
|
||||
|
||||
// Simple getter for this service. This does not create the service if it
|
||||
// hasn't been created already, and it never AddRef's!
|
||||
static nsDOMThreadService* get();
|
||||
|
||||
static JSContext* GetCurrentContext();
|
||||
|
||||
// Easy access to the services we care about.
|
||||
static nsIJSRuntimeService* JSRuntimeService();
|
||||
static nsIThreadJSContextStack* ThreadJSContextStack();
|
||||
@ -116,24 +120,29 @@ private:
|
||||
|
||||
static void Shutdown();
|
||||
|
||||
nsresult Dispatch(nsDOMWorkerThread* aWorker,
|
||||
nsresult Dispatch(nsDOMWorker* aWorker,
|
||||
nsIRunnable* aRunnable);
|
||||
|
||||
void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
|
||||
|
||||
void WaitForCanceledWorker(nsDOMWorkerThread* aWorker);
|
||||
|
||||
static JSContext* CreateJSContext();
|
||||
|
||||
void NoteDyingPool(nsDOMWorkerPool* aPool);
|
||||
already_AddRefed<nsDOMWorkerPool>
|
||||
GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
|
||||
PRBool aRemove);
|
||||
|
||||
void NoteEmptyPool(nsDOMWorkerPool* aPool);
|
||||
|
||||
void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
|
||||
|
||||
nsresult RegisterWorker(nsDOMWorker* aWorker,
|
||||
nsIScriptGlobalObject* aGlobalObject);
|
||||
|
||||
// Our internal thread pool.
|
||||
nsCOMPtr<nsIThreadPool> mThreadPool;
|
||||
|
||||
// Weak references, only ever touched on the main thread!
|
||||
nsTPtrArray<nsDOMWorkerPool> mPools;
|
||||
// Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
|
||||
nsRefPtrHashtable<nsISupportsHashKey, nsDOMWorkerPool> mPools;
|
||||
|
||||
// mMonitor protects all access to mWorkersInProgress and
|
||||
// mCreationsInProgress.
|
||||
|
@ -58,69 +58,37 @@
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorkerThread.h"
|
||||
#include "nsDOMWorker.h"
|
||||
|
||||
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
|
||||
|
||||
#define LOOP_OVER_WORKERS(_func, _args) \
|
||||
PR_BEGIN_MACRO \
|
||||
PRUint32 workerCount = mWorkers.Length(); \
|
||||
for (PRUint32 i = 0; i < workerCount; i++) { \
|
||||
mWorkers[i]-> _func _args ; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
nsDOMWorkerPool::nsDOMWorkerPool(nsIDocument* aDocument)
|
||||
: mParentGlobal(nsnull),
|
||||
mParentDocument(aDocument)
|
||||
nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
|
||||
nsIDocument* aDocument)
|
||||
: mParentGlobal(aGlobalObject),
|
||||
mParentDocument(aDocument),
|
||||
mMonitor(nsnull),
|
||||
mCanceled(PR_FALSE),
|
||||
mSuspended(PR_FALSE)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aGlobalObject, "Must have a global object!");
|
||||
NS_ASSERTION(aDocument, "Must have a document!");
|
||||
}
|
||||
|
||||
nsDOMWorkerPool::~nsDOMWorkerPool()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
LOOP_OVER_WORKERS(Cancel, ());
|
||||
|
||||
nsDOMThreadService::get()->NoteDyingPool(this);
|
||||
|
||||
if (mMonitor) {
|
||||
nsAutoMonitor::DestroyMonitor(mMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool,
|
||||
nsIClassInfo)
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool)
|
||||
|
||||
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool)
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPool)
|
||||
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerPool::Init()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsIScriptGlobalObject* globalObject =
|
||||
mParentDocument->GetScriptGlobalObject();
|
||||
NS_ENSURE_STATE(globalObject);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(globalObject));
|
||||
NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
|
||||
domWindow->GetCurrentInnerWindow() :
|
||||
domWindow.get();
|
||||
NS_ENSURE_STATE(innerWindow);
|
||||
|
||||
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(innerWindow));
|
||||
NS_ENSURE_TRUE(globalSupports, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
// We don't want a strong ref, this guy owns us.
|
||||
mParentGlobal = globalSupports.get();
|
||||
|
||||
mMonitor = nsAutoMonitor::NewMonitor("nsDOMWorkerPool::mMonitor");
|
||||
NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
@ -128,208 +96,147 @@ nsDOMWorkerPool::Init()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerPool::HandleMessage(const nsAString& aMessage,
|
||||
nsDOMWorkerBase* aSource)
|
||||
nsDOMWorkerPool::NoteWorker(nsDOMWorker* aWorker)
|
||||
{
|
||||
nsCOMPtr<nsIDOMWorkerMessageListener> messageListener =
|
||||
nsDOMWorkerBase::GetMessageListener();
|
||||
if (!messageListener) {
|
||||
LOG(("Message received on a worker with no listener!"));
|
||||
return NS_OK;
|
||||
NS_ASSERTION(aWorker, "Null pointer!");
|
||||
|
||||
PRBool suspendWorker;
|
||||
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
nsDOMWorker** newWorker = mWorkers.AppendElement(aWorker);
|
||||
NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
suspendWorker = mSuspended;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> source;
|
||||
aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
|
||||
NS_ASSERTION(source, "Impossible!");
|
||||
|
||||
messageListener->OnMessage(aMessage, source);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerPool::DispatchMessage(nsIRunnable* aRunnable)
|
||||
{
|
||||
// Can be called from many different threads!
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
|
||||
NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (suspendWorker) {
|
||||
aWorker->Suspend();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerPool::HandleError(nsIScriptError* aError,
|
||||
nsDOMWorkerThread* aSource)
|
||||
nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aWorker, "Null pointer!");
|
||||
|
||||
if (mErrorListener) {
|
||||
mErrorListener->OnError(aError,
|
||||
NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource));
|
||||
PRBool removeFromThreadService = PR_FALSE;
|
||||
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
|
||||
mWorkers.RemoveElement(aWorker);
|
||||
|
||||
if (!mCanceled && !mWorkers.Length()) {
|
||||
removeFromThreadService = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeFromThreadService) {
|
||||
nsRefPtr<nsDOMWorkerPool> kungFuDeathGrip(this);
|
||||
nsDOMThreadService::get()->NoteEmptyPool(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerPool::NoteDyingWorker(nsDOMWorkerThread* aWorker)
|
||||
nsDOMWorkerPool::GetWorkers(nsTArray<nsDOMWorker*>& aArray)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
aArray.Clear();
|
||||
|
||||
NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
|
||||
mWorkers.RemoveElement(aWorker);
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
#ifdef DEBUG
|
||||
nsDOMWorker** newWorkers =
|
||||
#endif
|
||||
aArray.AppendElements(mWorkers);
|
||||
NS_WARN_IF_FALSE(newWorkers, "Out of memory!");
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
||||
nsDOMWorkerPool::Cancel()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(!mCanceled, "Canceled more than once!");
|
||||
|
||||
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
|
||||
NS_ASSERTION(globalSupports, "Null pointer?!");
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
if (globalSupports == mParentGlobal) {
|
||||
LOOP_OVER_WORKERS(Cancel, ());
|
||||
mWorkers.Clear();
|
||||
if (IsSuspended()) {
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mCanceled = PR_TRUE;
|
||||
|
||||
nsAutoTArray<nsDOMWorker*, 10> workers;
|
||||
GetWorkers(workers);
|
||||
|
||||
PRUint32 count = workers.Length();
|
||||
if (count) {
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
workers[index]->Cancel();
|
||||
}
|
||||
mon.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
mParentGlobal = nsnull;
|
||||
mParentDocument = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerPool::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
||||
nsDOMWorkerPool::Suspend()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
|
||||
NS_ASSERTION(globalSupports, "Null pointer?!");
|
||||
nsAutoTArray<nsDOMWorker*, 10> workers;
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
if (globalSupports == mParentGlobal) {
|
||||
LOOP_OVER_WORKERS(Suspend, ());
|
||||
Suspend();
|
||||
NS_ASSERTION(!mSuspended, "Suspended more than once!");
|
||||
mSuspended = PR_TRUE;
|
||||
|
||||
GetWorkers(workers);
|
||||
}
|
||||
|
||||
PRUint32 count = workers.Length();
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
workers[index]->Suspend();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
||||
nsDOMWorkerPool::Resume()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
|
||||
NS_ASSERTION(globalSupports, "Null pointer?!");
|
||||
nsAutoTArray<nsDOMWorker*, 10> workers;
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
if (globalSupports == mParentGlobal) {
|
||||
LOOP_OVER_WORKERS(Resume, ());
|
||||
Resume();
|
||||
NS_ASSERTION(mSuspended, "Not suspended!");
|
||||
mSuspended = PR_FALSE;
|
||||
|
||||
GetWorkers(workers);
|
||||
}
|
||||
|
||||
PRUint32 count = workers.Length();
|
||||
if (count) {
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
workers[index]->Resume();
|
||||
}
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
mon.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
nsDOMWorkerPool::ParentDocument()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"Don't touch the non-threadsafe document off the main thread!");
|
||||
return mParentDocument;
|
||||
}
|
||||
|
||||
nsIScriptContext*
|
||||
nsDOMWorkerPool::ScriptContext()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"Don't touch the non-threadsafe script context off the main "
|
||||
"thread!");
|
||||
return mParentDocument->GetScriptGlobalObject()->GetContext();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
|
||||
{
|
||||
nsresult rv = PostMessageInternal(aMessage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::SetMessageListener(nsIDOMWorkerMessageListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
nsDOMWorkerBase::SetMessageListener(aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::GetMessageListener(nsIDOMWorkerMessageListener** aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
nsCOMPtr<nsIDOMWorkerMessageListener> listener = nsDOMWorkerBase::GetMessageListener();
|
||||
listener.forget(aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::SetErrorListener(nsIDOMWorkerErrorListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mErrorListener = aListener;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::GetErrorListener(nsIDOMWorkerErrorListener** aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_IF_ADDREF(*aListener = mErrorListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::CreateWorker(const nsAString& aFullScript,
|
||||
nsIDOMWorkerThread** _retval)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ENSURE_ARG(!aFullScript.IsEmpty());
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
|
||||
nsRefPtr<nsDOMWorkerThread> worker =
|
||||
new nsDOMWorkerThread(this, aFullScript, PR_FALSE);
|
||||
NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = worker->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
|
||||
mWorkers.AppendElement(worker);
|
||||
|
||||
NS_ADDREF(*_retval = worker);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::CreateWorkerFromURL(const nsAString& aScriptURL,
|
||||
nsIDOMWorkerThread** _retval)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ENSURE_ARG(!aScriptURL.IsEmpty());
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
|
||||
nsRefPtr<nsDOMWorkerThread> worker =
|
||||
new nsDOMWorkerThread(this, aScriptURL, PR_TRUE);
|
||||
NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = worker->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
|
||||
mWorkers.AppendElement(worker);
|
||||
|
||||
NS_ADDREF(*_retval = worker);
|
||||
return NS_OK;
|
||||
return mParentGlobal->GetContext();
|
||||
}
|
||||
|
@ -40,94 +40,73 @@
|
||||
#ifndef __NSDOMWORKERPOOL_H__
|
||||
#define __NSDOMWORKERPOOL_H__
|
||||
|
||||
// Bases
|
||||
#include "nsDOMWorkerBase.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsIDOMThreads.h"
|
||||
|
||||
// Other includes
|
||||
#include "jsapi.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTPtrArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "prmon.h"
|
||||
|
||||
class nsDOMWorkerThread;
|
||||
class nsDOMWorker;
|
||||
class nsIDocument;
|
||||
class nsIScriptContext;
|
||||
class nsIScriptError;
|
||||
class nsIScriptGlobalObject;
|
||||
|
||||
/**
|
||||
* The pool is almost always touched only on the main thread.
|
||||
*/
|
||||
class nsDOMWorkerPool : public nsDOMWorkerBase,
|
||||
public nsIDOMWorkerPool,
|
||||
public nsIClassInfo
|
||||
class nsDOMWorkerPool
|
||||
{
|
||||
friend class nsDOMThreadService;
|
||||
friend class nsDOMWorkerFunctions;
|
||||
friend class nsDOMWorkerPoolWeakRef;
|
||||
friend class nsDOMWorkerScriptLoader;
|
||||
friend class nsDOMWorkerStreamObserver;
|
||||
friend class nsDOMWorkerThread;
|
||||
friend class nsReportErrorRunnable;
|
||||
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMWORKERPOOL
|
||||
NS_DECL_NSICLASSINFO
|
||||
nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
|
||||
nsIDocument* aDocument);
|
||||
|
||||
nsDOMWorkerPool(nsIDocument* aDocument);
|
||||
NS_IMETHOD_(nsrefcnt) AddRef();
|
||||
NS_IMETHOD_(nsrefcnt) Release();
|
||||
|
||||
// For nsDOMWorkerBase
|
||||
virtual nsDOMWorkerPool* Pool() {
|
||||
return this;
|
||||
}
|
||||
|
||||
nsIDocument* ParentDocument();
|
||||
nsIScriptContext* ScriptContext();
|
||||
|
||||
private:
|
||||
virtual ~nsDOMWorkerPool();
|
||||
nsIScriptGlobalObject* ScriptGlobalObject() {
|
||||
return mParentGlobal;
|
||||
}
|
||||
|
||||
nsIDocument* ParentDocument() {
|
||||
return mParentDocument;
|
||||
}
|
||||
|
||||
nsresult Init();
|
||||
|
||||
// For nsDOMWorkerBase
|
||||
virtual nsresult HandleMessage(const nsAString& aMessage,
|
||||
nsDOMWorkerBase* aSourceThread);
|
||||
void Cancel();
|
||||
void Suspend();
|
||||
void Resume();
|
||||
|
||||
// For nsDOMWorkerBase
|
||||
virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
|
||||
|
||||
void HandleError(nsIScriptError* aError,
|
||||
nsDOMWorkerThread* aSource);
|
||||
|
||||
void NoteDyingWorker(nsDOMWorkerThread* aWorker);
|
||||
|
||||
void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
|
||||
void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
|
||||
void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
|
||||
nsresult NoteWorker(nsDOMWorker* aWorker);
|
||||
void NoteDyingWorker(nsDOMWorker* aWorker);
|
||||
|
||||
PRMonitor* Monitor() {
|
||||
return mMonitor;
|
||||
}
|
||||
|
||||
// Weak reference to the window that created and owns this pool.
|
||||
nsISupports* mParentGlobal;
|
||||
private:
|
||||
virtual ~nsDOMWorkerPool();
|
||||
|
||||
// Weak reference to the document that created this pool.
|
||||
nsIDocument* mParentDocument;
|
||||
void GetWorkers(nsTArray<nsDOMWorker*>& aArray);
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
|
||||
// Reference to the window that created and owns this pool.
|
||||
nsCOMPtr<nsIScriptGlobalObject> mParentGlobal;
|
||||
|
||||
// Reference to the document that created this pool.
|
||||
nsCOMPtr<nsIDocument> mParentDocument;
|
||||
|
||||
// Weak array of workers. The idea is that workers can be garbage collected
|
||||
// independently of the owning pool and other workers.
|
||||
nsTPtrArray<nsDOMWorkerThread> mWorkers;
|
||||
|
||||
// An error handler function, may be null.
|
||||
nsCOMPtr<nsIDOMWorkerErrorListener> mErrorListener;
|
||||
nsTArray<nsDOMWorker*> mWorkers;
|
||||
|
||||
// Monitor for suspending and resuming workers.
|
||||
PRMonitor* mMonitor;
|
||||
|
||||
PRPackedBool mCanceled;
|
||||
PRPackedBool mSuspended;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERPOOL_H__ */
|
||||
|
@ -65,78 +65,31 @@
|
||||
|
||||
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
|
||||
|
||||
nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader()
|
||||
: mWorker(nsnull),
|
||||
nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker)
|
||||
: nsDOMWorkerFeature(aWorker),
|
||||
mTarget(nsnull),
|
||||
mCx(NULL),
|
||||
mScriptCount(0),
|
||||
mCanceled(PR_FALSE),
|
||||
mTrackedByWorker(PR_FALSE)
|
||||
mCanceled(PR_FALSE)
|
||||
{
|
||||
// Created on worker thread.
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aWorker, "Null worker!");
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::~nsDOMWorkerScriptLoader()
|
||||
{
|
||||
// Can't touch mWorker's lock
|
||||
if (!mCanceled) {
|
||||
// Destroyed on worker thread, unless canceled (and then who knows!).
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mTrackedByWorker) {
|
||||
jsrefcount suspendDepth = 0;
|
||||
if (mCx) {
|
||||
suspendDepth = JS_SuspendRequest(mCx);
|
||||
}
|
||||
|
||||
nsAutoLock lock(mWorker->Lock());
|
||||
#ifdef DEBUG
|
||||
PRBool removed =
|
||||
#endif
|
||||
mWorker->mScriptLoaders.RemoveElement(this);
|
||||
NS_ASSERTION(removed, "Something is wrong here!");
|
||||
|
||||
if (mCx) {
|
||||
JS_ResumeRequest(mCx, suspendDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerScriptLoader, nsRunnable,
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
|
||||
nsIRunnable,
|
||||
nsIStreamLoaderObserver)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
|
||||
const nsTArray<nsString>& aURLs)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aWorker, "Null worker!");
|
||||
NS_ASSERTION(aCx, "Null context!");
|
||||
|
||||
NS_ASSERTION(!mWorker, "Not designed to be used more than once!");
|
||||
|
||||
mWorker = aWorker;
|
||||
mCx = aCx;
|
||||
|
||||
mTarget = NS_GetCurrentThread();
|
||||
NS_ASSERTION(mTarget, "This should never be null!");
|
||||
|
||||
{
|
||||
JSAutoSuspendRequest asr(aCx);
|
||||
|
||||
nsAutoLock lock(mWorker->Lock());
|
||||
|
||||
if (mWorker->IsCanceled()) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
mTrackedByWorker = nsnull != mWorker->mScriptLoaders.AppendElement(this);
|
||||
NS_ASSERTION(mTrackedByWorker, "Failed to add loader to worker's array!");
|
||||
}
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
@ -173,30 +126,18 @@ nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
|
||||
// network or compiling.
|
||||
AutoSuspendWorkerEvents aswe(this);
|
||||
|
||||
nsresult rv = DoRunLoop();
|
||||
|
||||
{
|
||||
JSAutoSuspendRequest asr(aCx);
|
||||
nsAutoLock lock(mWorker->Lock());
|
||||
#ifdef DEBUG
|
||||
PRBool removed =
|
||||
#endif
|
||||
mWorker->mScriptLoaders.RemoveElement(this);
|
||||
NS_ASSERTION(removed, "Something is wrong here!");
|
||||
mTrackedByWorker = PR_FALSE;
|
||||
}
|
||||
|
||||
nsresult rv = DoRunLoop(aCx);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Verify that all scripts downloaded and compiled.
|
||||
rv = VerifyScripts();
|
||||
rv = VerifyScripts(aCx);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ExecuteScripts();
|
||||
rv = ExecuteScripts(aCx);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -205,18 +146,17 @@ nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx,
|
||||
const nsString& aURL)
|
||||
{
|
||||
nsAutoTArray<nsString, 1> url;
|
||||
url.AppendElement(aURL);
|
||||
|
||||
return LoadScripts(aWorker, aCx, url);
|
||||
return LoadScripts(aCx, url);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::DoRunLoop()
|
||||
nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
@ -234,7 +174,7 @@ nsDOMWorkerScriptLoader::DoRunLoop()
|
||||
PRBool changed = NS_SUCCEEDED(threadService->ChangeThreadPoolMaxThreads(1));
|
||||
|
||||
while (!(done || mCanceled)) {
|
||||
JSAutoSuspendRequest asr(mCx);
|
||||
JSAutoSuspendRequest asr(aCx);
|
||||
NS_ProcessNextEvent(mTarget);
|
||||
}
|
||||
|
||||
@ -247,8 +187,10 @@ nsDOMWorkerScriptLoader::DoRunLoop()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::VerifyScripts()
|
||||
nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Shouldn't be null!");
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
@ -274,10 +216,24 @@ nsDOMWorkerScriptLoader::VerifyScripts()
|
||||
|
||||
// Ok, this is the script that caused us to fail.
|
||||
|
||||
// Only throw an error there is no other pending exception.
|
||||
if (!JS_IsExceptionPending(mCx)) {
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
// Only throw an error if there is no other pending exception.
|
||||
if (!JS_IsExceptionPending(aCx)) {
|
||||
const char* message;
|
||||
switch (loadInfo.result) {
|
||||
case NS_ERROR_MALFORMED_URI:
|
||||
message = "Malformed script URI: %s";
|
||||
break;
|
||||
case NS_ERROR_FILE_NOT_FOUND:
|
||||
message = "Script file not found: %s";
|
||||
break;
|
||||
default:
|
||||
message = "Failed to load script: %s (nsresult = 0x%x)";
|
||||
break;
|
||||
}
|
||||
NS_ConvertUTF16toUTF8 url(loadInfo.url);
|
||||
JS_ReportError(mCx, "Failed to compile script: %s", url.get());
|
||||
JS_ReportError(aCx, message, url.get(), loadInfo.result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -286,30 +242,34 @@ nsDOMWorkerScriptLoader::VerifyScripts()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::ExecuteScripts()
|
||||
nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Shouldn't be null!");
|
||||
|
||||
// Now execute all the scripts.
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JSScript* script =
|
||||
static_cast<JSScript*>(JS_GetPrivate(mCx, loadInfo.scriptObj));
|
||||
static_cast<JSScript*>(JS_GetPrivate(aCx, loadInfo.scriptObj));
|
||||
NS_ASSERTION(script, "This shouldn't ever be null!");
|
||||
|
||||
JSObject* global = mWorker->mGlobal ?
|
||||
mWorker->mGlobal :
|
||||
JS_GetGlobalObject(mCx);
|
||||
JS_GetGlobalObject(aCx);
|
||||
NS_ENSURE_STATE(global);
|
||||
|
||||
// Because we may have nested calls to this function we don't want the
|
||||
// execution to automatically report errors. We let them propagate instead.
|
||||
uint32 oldOpts =
|
||||
JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
|
||||
jsval val;
|
||||
PRBool success = JS_ExecuteScript(mCx, global, script, &val);
|
||||
PRBool success = JS_ExecuteScript(aCx, global, script, &val);
|
||||
|
||||
JS_SetOptions(mCx, oldOpts);
|
||||
JS_SetOptions(aCx, oldOpts);
|
||||
|
||||
if (!success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -448,8 +408,11 @@ nsDOMWorkerScriptLoader::RunInternal()
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// Things we need to make all this work...
|
||||
nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
|
||||
NS_ASSERTION(parentDoc, "Null parent document?!");
|
||||
nsCOMPtr<nsIDocument> parentDoc = mWorker->Pool()->ParentDocument();
|
||||
if (!parentDoc) {
|
||||
// Must have been canceled.
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
// All of these can potentially be null, but that should be ok. We'll either
|
||||
// succeed without them or fail below.
|
||||
@ -472,9 +435,7 @@ nsDOMWorkerScriptLoader::RunInternal()
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
|
||||
|
||||
rv =
|
||||
secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri,
|
||||
nsIScriptSecurityManager::ALLOW_CHROME);
|
||||
rv = secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@ -613,8 +574,7 @@ nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader,
|
||||
}
|
||||
|
||||
nsRefPtr<ScriptCompiler> compiler =
|
||||
new ScriptCompiler(this, mCx, loadInfo.scriptText, filename,
|
||||
loadInfo.scriptObj);
|
||||
new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj);
|
||||
NS_ASSERTION(compiler, "Out of memory!");
|
||||
if (!compiler) {
|
||||
return rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -663,14 +623,14 @@ void
|
||||
nsDOMWorkerScriptLoader::SuspendWorkerEvents()
|
||||
{
|
||||
NS_ASSERTION(mWorker, "No worker yet!");
|
||||
mWorker->SuspendTimeouts();
|
||||
mWorker->SuspendFeatures();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::ResumeWorkerEvents()
|
||||
{
|
||||
NS_ASSERTION(mWorker, "No worker yet!");
|
||||
mWorker->ResumeTimeouts();
|
||||
mWorker->ResumeFeatures();
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
@ -699,6 +659,9 @@ ScriptLoaderRunnable::~ScriptLoaderRunnable()
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerScriptLoader::ScriptLoaderRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke()
|
||||
{
|
||||
@ -707,17 +670,14 @@ nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke()
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
ScriptCompiler::ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
|
||||
JSContext* aCx,
|
||||
const nsString& aScriptText,
|
||||
const nsCString& aFilename,
|
||||
nsAutoJSObjectHolder& aScriptObj)
|
||||
: ScriptLoaderRunnable(aLoader),
|
||||
mCx(aCx),
|
||||
mScriptText(aScriptText),
|
||||
mFilename(aFilename),
|
||||
mScriptObj(aScriptObj)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Null context!");
|
||||
NS_ASSERTION(!aScriptText.IsEmpty(), "No script to compile!");
|
||||
NS_ASSERTION(aScriptObj.IsHeld(), "Should be held!");
|
||||
}
|
||||
@ -735,31 +695,34 @@ nsDOMWorkerScriptLoader::ScriptCompiler::Run()
|
||||
NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!");
|
||||
NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!");
|
||||
|
||||
JSAutoRequest ar(mCx);
|
||||
JSContext* cx = nsDOMThreadService::GetCurrentContext();
|
||||
NS_ENSURE_STATE(cx);
|
||||
|
||||
JSObject* global = JS_GetGlobalObject(mCx);
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JSObject* global = JS_GetGlobalObject(cx);
|
||||
NS_ENSURE_STATE(global);
|
||||
|
||||
// Because we may have nested calls to this function we don't want the
|
||||
// execution to automatically report errors. We let them propagate instead.
|
||||
uint32 oldOpts =
|
||||
JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
|
||||
JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
|
||||
|
||||
JSScript* script =
|
||||
JS_CompileUCScriptForPrincipals(mCx, global, principal,
|
||||
JS_CompileUCScriptForPrincipals(cx, global, principal,
|
||||
reinterpret_cast<const jschar*>
|
||||
(mScriptText.BeginReading()),
|
||||
mScriptText.Length(), mFilename.get(), 1);
|
||||
|
||||
JS_SetOptions(mCx, oldOpts);
|
||||
JS_SetOptions(cx, oldOpts);
|
||||
|
||||
if (!script) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mScriptObj = JS_NewScriptObject(mCx, script);
|
||||
mScriptObj = JS_NewScriptObject(cx, script);
|
||||
NS_ENSURE_STATE(mScriptObj);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -40,7 +40,7 @@
|
||||
#define __NSDOMWORKERSCRIPTLOADER_H__
|
||||
|
||||
// Bases
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
|
||||
// Interfaces
|
||||
@ -56,8 +56,9 @@
|
||||
#include "nsTArray.h"
|
||||
#include "prlock.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerThread.h"
|
||||
#include "nsDOMWorker.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
/**
|
||||
* This class takes a list of script URLs, downloads the scripts, compiles the
|
||||
@ -86,12 +87,11 @@
|
||||
* Currently if *anything* after 2 fails then we cancel any pending loads and
|
||||
* bail out entirely.
|
||||
*/
|
||||
class nsDOMWorkerScriptLoader : public nsRunnable,
|
||||
class nsDOMWorkerScriptLoader : public nsDOMWorkerFeature,
|
||||
public nsIRunnable,
|
||||
public nsIStreamLoaderObserver
|
||||
{
|
||||
friend class AutoSuspendWorkerEvents;
|
||||
friend class nsDOMWorkerFunctions;
|
||||
friend class nsDOMWorkerThread;
|
||||
friend class ScriptLoaderRunnable;
|
||||
|
||||
public:
|
||||
@ -99,24 +99,23 @@ public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
|
||||
nsDOMWorkerScriptLoader();
|
||||
nsDOMWorkerScriptLoader(nsDOMWorker* aWorker);
|
||||
|
||||
nsresult LoadScripts(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
nsresult LoadScripts(JSContext* aCx,
|
||||
const nsTArray<nsString>& aURLs);
|
||||
|
||||
nsresult LoadScript(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
const nsString& aURL);
|
||||
nsresult LoadScript(JSContext* aCx,
|
||||
const nsString& aURL);
|
||||
|
||||
void Cancel();
|
||||
virtual void Cancel();
|
||||
|
||||
private:
|
||||
~nsDOMWorkerScriptLoader();
|
||||
~nsDOMWorkerScriptLoader() { }
|
||||
|
||||
nsresult DoRunLoop();
|
||||
nsresult VerifyScripts();
|
||||
nsresult ExecuteScripts();
|
||||
|
||||
nsresult DoRunLoop(JSContext* aCx);
|
||||
nsresult VerifyScripts(JSContext* aCx);
|
||||
nsresult ExecuteScripts(JSContext* aCx);
|
||||
|
||||
nsresult RunInternal();
|
||||
|
||||
@ -135,8 +134,11 @@ private:
|
||||
return mWorker->Lock();
|
||||
}
|
||||
|
||||
class ScriptLoaderRunnable : public nsRunnable
|
||||
class ScriptLoaderRunnable : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
protected:
|
||||
// Meant to be inherited.
|
||||
ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader);
|
||||
@ -158,13 +160,11 @@ private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
|
||||
JSContext* aCx,
|
||||
const nsString& aScriptText,
|
||||
const nsCString& aFilename,
|
||||
nsAutoJSObjectHolder& aScriptObj);
|
||||
|
||||
private:
|
||||
JSContext* mCx;
|
||||
nsString mScriptText;
|
||||
nsCString mFilename;
|
||||
nsAutoJSObjectHolder& mScriptObj;
|
||||
@ -205,20 +205,17 @@ private:
|
||||
nsAutoJSObjectHolder scriptObj;
|
||||
};
|
||||
|
||||
nsDOMWorkerThread* mWorker;
|
||||
nsIThread* mTarget;
|
||||
JSContext* mCx;
|
||||
|
||||
nsRefPtr<ScriptLoaderDone> mDoneRunnable;
|
||||
|
||||
PRUint32 mScriptCount;
|
||||
nsTArray<ScriptLoadInfo> mLoadInfos;
|
||||
|
||||
PRPackedBool mCanceled;
|
||||
PRPackedBool mTrackedByWorker;
|
||||
|
||||
// Protected by mWorker's lock!
|
||||
nsTArray<ScriptLoaderRunnable*> mPendingRunnables;
|
||||
|
||||
PRPackedBool mCanceled;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERSCRIPTLOADER_H__ */
|
||||
|
@ -1,984 +0,0 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is worker threads.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
|
||||
* Ben Turner <bent.mozilla@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsDOMWorkerThread.h"
|
||||
|
||||
// Interfaces
|
||||
#include "nsIDOMClassInfo.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
||||
// Other includes
|
||||
#ifdef MOZ_SHARK
|
||||
#include "jsdbgapi.h"
|
||||
#endif
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerPool.h"
|
||||
#include "nsDOMWorkerScriptLoader.h"
|
||||
#include "nsDOMWorkerSecurityManager.h"
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorkerTimeout.h"
|
||||
#include "nsDOMWorkerXHR.h"
|
||||
|
||||
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
|
||||
|
||||
// XXX Could make these functions of nsDOMWorkerThread instead.
|
||||
class nsDOMWorkerFunctions
|
||||
{
|
||||
public:
|
||||
// Same as window.dump().
|
||||
static JSBool Dump(JSContext* aCx, JSObject* aObj, uintN aArgc, jsval* aArgv,
|
||||
jsval* aRval);
|
||||
|
||||
// Debug-only version of window.dump(), like the JS component loader has.
|
||||
static JSBool DebugDump(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
// Same as nsIDOMWorkerThread::PostMessage
|
||||
static JSBool PostMessage(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
// Same as window.setTimeout().
|
||||
static JSBool SetTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval) {
|
||||
return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_FALSE);
|
||||
}
|
||||
|
||||
// Same as window.setInterval().
|
||||
static JSBool SetInterval(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval) {
|
||||
return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_TRUE);
|
||||
}
|
||||
|
||||
// Used for both clearTimeout() and clearInterval().
|
||||
static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
static JSBool NewXMLHttpRequest(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
private:
|
||||
// Internal helper for SetTimeout and SetInterval.
|
||||
static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval, PRBool aIsInterval);
|
||||
};
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::Dump(JSContext* aCx,
|
||||
JSObject* /* aObj */,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* /* aRval */)
|
||||
{
|
||||
// XXX Expose this to the JS console? Only if that DOM pref is set?
|
||||
|
||||
JSString* str;
|
||||
if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
|
||||
nsDependentJSString string(str);
|
||||
fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::DebugDump(JSContext* aCx,
|
||||
JSObject* aObj,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* aRval)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
return nsDOMWorkerFunctions::Dump(aCx, aObj, aArgc, aArgv, aRval);
|
||||
#else
|
||||
return JS_TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::PostMessage(JSContext* aCx,
|
||||
JSObject* /* aObj */,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* /* aRval */)
|
||||
{
|
||||
nsDOMWorkerThread* worker =
|
||||
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
|
||||
NS_ASSERTION(pool, "Shouldn't ever be null!");
|
||||
|
||||
nsresult rv;
|
||||
|
||||
JSString* str;
|
||||
if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
|
||||
rv = pool->PostMessageInternal(nsDependentJSString(str), worker);
|
||||
}
|
||||
else {
|
||||
rv = pool->PostMessageInternal(EmptyString(), worker);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to post message!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
|
||||
JSObject* /* aObj */,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* aRval,
|
||||
PRBool aIsInterval)
|
||||
{
|
||||
nsDOMWorkerThread* worker =
|
||||
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
PRUint32 id = ++worker->mNextTimeoutId;
|
||||
|
||||
nsAutoPtr<nsDOMWorkerTimeout>
|
||||
timeout(new nsDOMWorkerTimeout(worker, id));
|
||||
if (!timeout) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsresult rv = timeout->Init(aCx, aArgc, aArgv, aIsInterval);
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to initialize timeout!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
timeout.forget();
|
||||
|
||||
*aRval = INT_TO_JSVAL(id);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
|
||||
JSObject* /* aObj */,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* /* aRval */)
|
||||
{
|
||||
nsDOMWorkerThread* worker =
|
||||
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
// A canceled worker should have already killed all timeouts.
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
if (!aArgc) {
|
||||
JS_ReportError(aCx, "Function requires at least 1 parameter");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
uint32 id;
|
||||
if (!JS_ValueToECMAUint32(aCx, aArgv[0], &id)) {
|
||||
JS_ReportError(aCx, "First argument must be a timeout id");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
worker->CancelTimeout(PRUint32(id));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
|
||||
JSObject* /* aObj */,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* /* aRval */)
|
||||
{
|
||||
nsDOMWorkerThread* worker =
|
||||
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (!aArgc) {
|
||||
JS_ReportError(aCx, "Function must have at least one argument!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsAutoTArray<nsString, 5> urls;
|
||||
|
||||
if (!urls.SetCapacity((PRUint32)aArgc)) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
for (uintN index = 0; index < aArgc; index++) {
|
||||
jsval val = aArgv[index];
|
||||
|
||||
if (!JSVAL_IS_STRING(val)) {
|
||||
JS_ReportError(aCx, "Argument %d must be a string", index);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSString* str = JS_ValueToString(aCx, val);
|
||||
if (!str) {
|
||||
JS_ReportError(aCx, "Couldn't convert argument %d to a string", index);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsString* newURL = urls.AppendElement();
|
||||
NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!");
|
||||
|
||||
newURL->Assign(nsDependentJSString(str));
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMWorkerScriptLoader> loader = new nsDOMWorkerScriptLoader();
|
||||
if (!loader) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsresult rv = loader->LoadScripts(worker, aCx, urls);
|
||||
if (NS_FAILED(rv)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
|
||||
JSObject* aObj,
|
||||
uintN aArgc,
|
||||
jsval* /* aArgv */,
|
||||
jsval* aRval)
|
||||
{
|
||||
nsDOMWorkerThread* worker =
|
||||
static_cast<nsDOMWorkerThread*>(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<nsDOMWorkerXHR> 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<nsISupports> xhrSupports;
|
||||
xhr->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(xhrSupports));
|
||||
NS_ASSERTION(xhrSupports, "Impossible!");
|
||||
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> xhrWrapped;
|
||||
rv = xpc->WrapNative(aCx, aObj, xhrSupports, NS_GET_IID(nsIXMLHttpRequest),
|
||||
getter_AddRefs(xhrWrapped));
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to wrap XHR!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject* xhrJSObj;
|
||||
rv = xhrWrapped->GetJSObject(&xhrJSObj);
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to get JSObject!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
*aRval = OBJECT_TO_JSVAL(xhrJSObj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSFunctionSpec gDOMWorkerFunctions[] = {
|
||||
{ "dump", nsDOMWorkerFunctions::Dump, 1, 0, 0 },
|
||||
{ "debug", nsDOMWorkerFunctions::DebugDump, 1, 0, 0 },
|
||||
{ "postMessageToPool", nsDOMWorkerFunctions::PostMessage, 1, 0, 0 },
|
||||
{ "setTimeout", nsDOMWorkerFunctions::SetTimeout, 1, 0, 0 },
|
||||
{ "clearTimeout", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 },
|
||||
{ "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0, 0 },
|
||||
{ "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 },
|
||||
{ "loadScripts", nsDOMWorkerFunctions::LoadScripts, 1, 0, 0 },
|
||||
{ "XMLHttpRequest", nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0, 0 },
|
||||
#ifdef MOZ_SHARK
|
||||
{ "startShark", js_StartShark, 0, 0, 0 },
|
||||
{ "stopShark", js_StopShark, 0, 0, 0 },
|
||||
{ "connectShark", js_ConnectShark, 0, 0, 0 },
|
||||
{ "disconnectShark", js_DisconnectShark, 0, 0, 0 },
|
||||
#endif
|
||||
{ nsnull, nsnull, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* An nsISupports that holds a weak ref to the worker. The worker owns the
|
||||
* thread context so we don't have to worry about nulling this out ever.
|
||||
*/
|
||||
class nsDOMWorkerThreadWeakRef : public nsIDOMWorkerThread,
|
||||
public nsIClassInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_FORWARD_NSIDOMWORKERTHREAD(mWorker->)
|
||||
NS_FORWARD_NSICLASSINFO(mWorker->)
|
||||
|
||||
nsDOMWorkerThreadWeakRef(nsDOMWorkerThread* aWorker)
|
||||
: mWorker(aWorker) { }
|
||||
|
||||
protected:
|
||||
nsDOMWorkerThread* mWorker;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadWeakRef, nsIDOMWorkerThread,
|
||||
nsIClassInfo)
|
||||
|
||||
/**
|
||||
* The 'threadContext' object for a worker's JS global object.
|
||||
*/
|
||||
class nsDOMWorkerThreadContext : public nsIDOMWorkerThreadContext,
|
||||
public nsIClassInfo
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMWORKERTHREADCONTEXT
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerThreadContext(nsDOMWorkerThread* aWorker)
|
||||
: mWorker(aWorker) { }
|
||||
|
||||
protected:
|
||||
nsDOMWorkerThread* mWorker;
|
||||
nsCOMPtr<nsIDOMWorkerThread> mWeakRef;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadContext,
|
||||
nsIDOMWorkerThreadContext,
|
||||
nsIClassInfo)
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThreadContext,
|
||||
nsIDOMWorkerThreadContext)
|
||||
|
||||
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThreadContext)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerThreadContext::GetThisThread(nsIDOMWorkerThread** aThisThread)
|
||||
{
|
||||
if (!mWeakRef) {
|
||||
mWeakRef = new nsDOMWorkerThreadWeakRef(mWorker);
|
||||
NS_ENSURE_TRUE(mWeakRef, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
NS_ADDREF(*aThisThread = mWeakRef);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsDOMWorkerThread::nsDOMWorkerThread(nsDOMWorkerPool* aPool,
|
||||
const nsAString& aSource,
|
||||
PRBool aSourceIsURL)
|
||||
: mPool(aPool),
|
||||
mCompiled(PR_FALSE),
|
||||
mCallbackCount(0),
|
||||
mNextTimeoutId(0),
|
||||
mLock(nsnull)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (aSourceIsURL) {
|
||||
mSourceURL.Assign(aSource);
|
||||
NS_ASSERTION(!mSourceURL.IsEmpty(), "Empty source url!");
|
||||
}
|
||||
else {
|
||||
mSource.Assign(aSource);
|
||||
NS_ASSERTION(!mSource.IsEmpty(), "Empty source string!");
|
||||
}
|
||||
|
||||
PR_INIT_CLIST(&mTimeouts);
|
||||
}
|
||||
|
||||
nsDOMWorkerThread::~nsDOMWorkerThread()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (!IsCanceled()) {
|
||||
nsRefPtr<nsDOMWorkerPool> pool = Pool();
|
||||
pool->NoteDyingWorker(this);
|
||||
}
|
||||
|
||||
ClearTimeouts();
|
||||
|
||||
if (mLock) {
|
||||
nsAutoLock::DestroyLock(mLock);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThread, nsIDOMWorkerThread,
|
||||
nsIClassInfo)
|
||||
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThread, nsIDOMWorkerThread)
|
||||
|
||||
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThread)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerThread::Init()
|
||||
{
|
||||
mLock = nsAutoLock::NewLock("nsDOMWorkerThread::mLock");
|
||||
NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_ASSERTION(!mGlobal, "Already got a global?!");
|
||||
|
||||
JSRuntime* rt;
|
||||
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool success = mGlobal.Hold(rt);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
|
||||
|
||||
// This is pretty cool - all we have to do to get our script executed is to
|
||||
// pass a no-op runnable to the thread service and it will make sure we have
|
||||
// a context and global object.
|
||||
nsCOMPtr<nsIRunnable> runnable(new nsRunnable());
|
||||
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = nsDOMThreadService::get()->Dispatch(this, runnable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// From nsDOMWorkerBase
|
||||
nsresult
|
||||
nsDOMWorkerThread::HandleMessage(const nsAString& aMessage,
|
||||
nsDOMWorkerBase* aSource)
|
||||
{
|
||||
nsCOMPtr<nsIDOMWorkerMessageListener> messageListener = GetMessageListener();
|
||||
if (!messageListener) {
|
||||
LOG(("Message received on a worker with no listener!"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We have to call this manually because XPConnect will replace our error
|
||||
// reporter with its own and we won't properly notify the pool of any
|
||||
// unhandled exceptions...
|
||||
|
||||
JSContext* cx;
|
||||
nsresult rv =
|
||||
nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
||||
// Get a JS string for the message.
|
||||
JSString* message = JS_NewUCStringCopyN(cx, (jschar*)aMessage.BeginReading(),
|
||||
aMessage.Length());
|
||||
NS_ENSURE_TRUE(message, NS_ERROR_FAILURE);
|
||||
|
||||
// Root it
|
||||
jsval messageVal = STRING_TO_JSVAL(message);
|
||||
nsAutoGCRoot rootedMessage(&messageVal, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
|
||||
nsCOMPtr<nsISupports> source;
|
||||
aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
|
||||
NS_ASSERTION(source, "Impossible!");
|
||||
|
||||
// Wrap the source thread.
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrappedThread;
|
||||
rv = xpc->WrapNative(cx, mGlobal, source, NS_GET_IID(nsISupports),
|
||||
getter_AddRefs(wrappedThread));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSObject* sourceThread;
|
||||
rv = wrappedThread->GetJSObject(&sourceThread);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set up our arguments.
|
||||
jsval argv[2] = {
|
||||
STRING_TO_JSVAL(message),
|
||||
OBJECT_TO_JSVAL(sourceThread)
|
||||
};
|
||||
|
||||
// Get the listener object out of our wrapped listener.
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrappedListener =
|
||||
do_QueryInterface(messageListener);
|
||||
NS_ENSURE_TRUE(wrappedListener, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
JSObject* listener;
|
||||
rv = wrappedListener->GetJSObject(&listener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// And call it.
|
||||
jsval rval;
|
||||
PRBool success = JS_CallFunctionValue(cx, mGlobal, OBJECT_TO_JSVAL(listener),
|
||||
2, argv, &rval);
|
||||
if (!success && JS_IsExceptionPending(cx)) {
|
||||
// Make sure any pending exceptions are converted to errors for the pool.
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// From nsDOMWorkerBase
|
||||
nsresult
|
||||
nsDOMWorkerThread::DispatchMessage(nsIRunnable* aRunnable)
|
||||
{
|
||||
nsresult rv = nsDOMThreadService::get()->Dispatch(this, aRunnable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::Cancel()
|
||||
{
|
||||
nsDOMWorkerBase::Cancel();
|
||||
|
||||
// Do this before waiting on the thread service below!
|
||||
CancelScriptLoaders();
|
||||
CancelXHRs();
|
||||
|
||||
// If we're suspended there's a good chance that we're already paused waiting
|
||||
// on the pool's monitor. Waiting on the thread service's lock will deadlock.
|
||||
if (!IsSuspended()) {
|
||||
nsDOMThreadService::get()->WaitForCanceledWorker(this);
|
||||
}
|
||||
|
||||
ClearTimeouts();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::Suspend()
|
||||
{
|
||||
nsDOMWorkerBase::Suspend();
|
||||
SuspendTimeouts();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::Resume()
|
||||
{
|
||||
nsDOMWorkerBase::Resume();
|
||||
ResumeTimeouts();
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMWorkerThread::SetGlobalForContext(JSContext* aCx)
|
||||
{
|
||||
PRBool success = CompileGlobalObject(aCx);
|
||||
if (!success) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
JS_SetGlobalObject(aCx, mGlobal);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMWorkerThread::CompileGlobalObject(JSContext* aCx)
|
||||
{
|
||||
if (mGlobal) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
if (mCompiled) {
|
||||
// Don't try to recompile a bad script.
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
mCompiled = PR_TRUE;
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JSObject* global = JS_NewObject(aCx, nsnull, nsnull, nsnull);
|
||||
NS_ENSURE_TRUE(global, PR_FALSE);
|
||||
|
||||
NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!");
|
||||
|
||||
// This call will root global.
|
||||
PRBool success = JS_InitStandardClasses(aCx, global);
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
// Set up worker thread functions
|
||||
success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions);
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
nsRefPtr<nsDOMWorkerThreadContext>
|
||||
context(new nsDOMWorkerThreadContext(this));
|
||||
NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
nsresult rv = xpc->InitClasses(aCx, global);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
// XXX Fix this!
|
||||
success = JS_DeleteProperty(aCx, global, "Components");
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> contextWrapper;
|
||||
rv = xpc->WrapNative(aCx, global,
|
||||
NS_ISUPPORTS_CAST(nsIDOMWorkerThreadContext*, context),
|
||||
NS_GET_IID(nsIDOMWorkerThreadContext),
|
||||
getter_AddRefs(contextWrapper));
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
JSObject* contextObj;
|
||||
rv = contextWrapper->GetJSObject(&contextObj);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
// Set up a name for our worker object
|
||||
success = JS_DefineProperty(aCx, global, "threadContext",
|
||||
OBJECT_TO_JSVAL(contextObj), nsnull, nsnull,
|
||||
JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
jsval val;
|
||||
|
||||
// From here on out we have to remember to null mGlobal if something fails!
|
||||
mGlobal = global;
|
||||
|
||||
if (mSource.IsEmpty()) {
|
||||
NS_ASSERTION(!mSourceURL.IsEmpty(), "Must have a url here!");
|
||||
|
||||
nsRefPtr<nsDOMWorkerScriptLoader> loader = new nsDOMWorkerScriptLoader();
|
||||
NS_ASSERTION(loader, "Out of memory!");
|
||||
if (!loader) {
|
||||
mGlobal = NULL;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
rv = loader->LoadScript(this, aCx, mSourceURL);
|
||||
JS_ReportPendingException(aCx);
|
||||
if (NS_FAILED(rv)) {
|
||||
mGlobal = NULL;
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(!mSource.IsEmpty(), "No source text!");
|
||||
|
||||
JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
|
||||
|
||||
// Evaluate and execute the script
|
||||
success = JS_EvaluateUCScriptForPrincipals(aCx, global, principal,
|
||||
reinterpret_cast<const jschar*>
|
||||
(mSource.get()),
|
||||
mSource.Length(),
|
||||
"DOMWorker inline script", 1,
|
||||
&val);
|
||||
if (!success) {
|
||||
mGlobal = NULL;
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// See if the message listener function was defined.
|
||||
nsCOMPtr<nsIDOMWorkerMessageListener> listener;
|
||||
if (JS_LookupProperty(aCx, global, "messageListener", &val) &&
|
||||
JSVAL_IS_OBJECT(val) &&
|
||||
NS_SUCCEEDED(xpc->WrapJS(aCx, JSVAL_TO_OBJECT(val),
|
||||
NS_GET_IID(nsIDOMWorkerMessageListener),
|
||||
getter_AddRefs(listener)))) {
|
||||
SetMessageListener(listener);
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
nsDOMWorkerTimeout*
|
||||
nsDOMWorkerThread::FirstTimeout()
|
||||
{
|
||||
// Only called within the lock!
|
||||
PRCList* first = PR_LIST_HEAD(&mTimeouts);
|
||||
return first == &mTimeouts ?
|
||||
nsnull :
|
||||
static_cast<nsDOMWorkerTimeout*>(first);
|
||||
}
|
||||
|
||||
nsDOMWorkerTimeout*
|
||||
nsDOMWorkerThread::NextTimeout(nsDOMWorkerTimeout* aTimeout)
|
||||
{
|
||||
// Only called within the lock!
|
||||
nsDOMWorkerTimeout* next =
|
||||
static_cast<nsDOMWorkerTimeout*>(PR_NEXT_LINK(aTimeout));
|
||||
return next == &mTimeouts ? nsnull : next;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout)
|
||||
{
|
||||
// This should only ever be called on the worker thread... but there's no way
|
||||
// to really assert that since we're using a thread pool.
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aTimeout, "Null pointer!");
|
||||
|
||||
PRIntervalTime newInterval = aTimeout->GetInterval();
|
||||
|
||||
if (IsSuspended()) {
|
||||
aTimeout->Suspend(PR_Now());
|
||||
}
|
||||
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
if (IsCanceled()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// XXX Currently stored in the order that they should execute (like the window
|
||||
// timeouts are) but we don't flush all expired timeouts the same way that
|
||||
// the window does... Either we should or this is unnecessary.
|
||||
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
|
||||
timeout;
|
||||
timeout = NextTimeout(timeout)) {
|
||||
if (timeout->GetInterval() > newInterval) {
|
||||
PR_INSERT_BEFORE(aTimeout, timeout);
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
PR_APPEND_LINK(aTimeout, &mTimeouts);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::RemoveTimeout(nsDOMWorkerTimeout* aTimeout)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
PR_REMOVE_LINK(aTimeout);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::ClearTimeouts()
|
||||
{
|
||||
nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
|
||||
timeout;
|
||||
timeout = NextTimeout(timeout)) {
|
||||
timeouts.AppendElement(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
PRUint32 count = timeouts.Length();
|
||||
for (PRUint32 i = 0; i < count; i++) {
|
||||
timeouts[i]->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::CancelTimeout(PRUint32 aId)
|
||||
{
|
||||
nsRefPtr<nsDOMWorkerTimeout> foundTimeout;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
|
||||
timeout;
|
||||
timeout = NextTimeout(timeout)) {
|
||||
if (timeout->GetId() == aId) {
|
||||
foundTimeout = timeout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundTimeout) {
|
||||
foundTimeout->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::SuspendTimeouts()
|
||||
{
|
||||
nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
|
||||
timeout;
|
||||
timeout = NextTimeout(timeout)) {
|
||||
timeouts.AppendElement(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
PRTime now = PR_Now();
|
||||
|
||||
PRUint32 count = timeouts.Length();
|
||||
for (PRUint32 i = 0; i < count; i++) {
|
||||
timeouts[i]->Suspend(now);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::ResumeTimeouts()
|
||||
{
|
||||
nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
|
||||
timeout;
|
||||
timeout = NextTimeout(timeout)) {
|
||||
NS_ASSERTION(timeout->IsSuspended(), "Should be suspended!");
|
||||
timeouts.AppendElement(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
PRTime now = PR_Now();
|
||||
|
||||
PRUint32 count = timeouts.Length();
|
||||
for (PRUint32 i = 0; i < count; i++) {
|
||||
timeouts[i]->Resume(now);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::CancelScriptLoaders()
|
||||
{
|
||||
nsAutoTArray<nsDOMWorkerScriptLoader*, 20> loaders;
|
||||
|
||||
// Must call cancel on the loaders outside the lock!
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
loaders.AppendElements(mScriptLoaders);
|
||||
|
||||
// Don't clear mScriptLoaders, they'll remove themselves as they get
|
||||
// destroyed.
|
||||
}
|
||||
|
||||
PRUint32 loaderCount = loaders.Length();
|
||||
for (PRUint32 index = 0; index < loaderCount; index++) {
|
||||
loaders[index]->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMWorkerThread::AddXHR(nsDOMWorkerXHR* aXHR)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
if (IsCanceled()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
PRBool contains = mXHRs.Contains(aXHR);
|
||||
NS_ASSERTION(!contains, "Adding an XHR twice!");
|
||||
#endif
|
||||
|
||||
nsDOMWorkerXHR** newElement = mXHRs.AppendElement(aXHR);
|
||||
NS_ENSURE_TRUE(newElement, PR_FALSE);
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::RemoveXHR(nsDOMWorkerXHR* aXHR)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
#ifdef DEBUG
|
||||
PRBool removed =
|
||||
#endif
|
||||
mXHRs.RemoveElement(aXHR);
|
||||
NS_WARN_IF_FALSE(removed, "Removed an XHR that was never added?!");
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::CancelXHRs()
|
||||
{
|
||||
nsAutoTArray<nsDOMWorkerXHR*, 20> xhrs;
|
||||
|
||||
// Must call Cancel outside the lock!
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
xhrs.AppendElements(mXHRs);
|
||||
}
|
||||
|
||||
PRUint32 xhrCount = xhrs.Length();
|
||||
for (PRUint32 index = 0; index < xhrCount; index++) {
|
||||
xhrs[index]->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerThread::PostMessage(const nsAString& aMessage)
|
||||
{
|
||||
nsresult rv = PostMessageInternal(aMessage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is worker threads.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
|
||||
* Ben Turner <bent.mozilla@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __NSDOMWORKERTHREAD_H__
|
||||
#define __NSDOMWORKERTHREAD_H__
|
||||
|
||||
// Bases
|
||||
#include "nsDOMWorkerBase.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsIDOMThreads.h"
|
||||
|
||||
// Other includes
|
||||
#include "jsapi.h"
|
||||
#include "nsAutoJSObjectHolder.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prclist.h"
|
||||
#include "prlock.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMThreadService.h"
|
||||
|
||||
// Macro to generate nsIClassInfo methods for these threadsafe DOM classes
|
||||
#define NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(_class) \
|
||||
NS_IMETHODIMP \
|
||||
_class::GetInterfaces(PRUint32* _count, nsIID*** _array) \
|
||||
{ \
|
||||
return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \
|
||||
} \
|
||||
|
||||
#define NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class) \
|
||||
NS_IMETHODIMP \
|
||||
_class::GetHelperForLanguage(PRUint32 _language, nsISupports** _retval) \
|
||||
{ \
|
||||
*_retval = nsnull; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
\
|
||||
NS_IMETHODIMP \
|
||||
_class::GetContractID(char** _contractID) \
|
||||
{ \
|
||||
*_contractID = nsnull; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
\
|
||||
NS_IMETHODIMP \
|
||||
_class::GetClassDescription(char** _classDescription) \
|
||||
{ \
|
||||
*_classDescription = nsnull; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
\
|
||||
NS_IMETHODIMP \
|
||||
_class::GetClassID(nsCID** _classID) \
|
||||
{ \
|
||||
*_classID = nsnull; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
\
|
||||
NS_IMETHODIMP \
|
||||
_class::GetImplementationLanguage(PRUint32* _language) \
|
||||
{ \
|
||||
*_language = nsIProgrammingLanguage::CPLUSPLUS; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
\
|
||||
NS_IMETHODIMP \
|
||||
_class::GetFlags(PRUint32* _flags) \
|
||||
{ \
|
||||
*_flags = nsIClassInfo::THREADSAFE | nsIClassInfo::DOM_OBJECT; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
\
|
||||
NS_IMETHODIMP \
|
||||
_class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \
|
||||
{ \
|
||||
return NS_ERROR_NOT_AVAILABLE; \
|
||||
}
|
||||
|
||||
#define NS_IMPL_THREADSAFE_DOM_CI(_class) \
|
||||
NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(_class) \
|
||||
NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(_class)
|
||||
|
||||
class nsDOMWorkerPool;
|
||||
class nsDOMWorkerScriptLoader;
|
||||
class nsDOMWorkerTimeout;
|
||||
class nsDOMWorkerXHR;
|
||||
|
||||
class nsDOMWorkerThread : public nsDOMWorkerBase,
|
||||
public nsIDOMWorkerThread,
|
||||
public nsIClassInfo
|
||||
{
|
||||
friend class nsDOMCreateJSContextRunnable;
|
||||
friend class nsDOMWorkerFunctions;
|
||||
friend class nsDOMWorkerPool;
|
||||
friend class nsDOMWorkerRunnable;
|
||||
friend class nsDOMWorkerScriptLoader;
|
||||
friend class nsDOMWorkerTimeout;
|
||||
friend class nsDOMWorkerXHR;
|
||||
|
||||
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMWORKERTHREAD
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerThread(nsDOMWorkerPool* aPool,
|
||||
const nsAString& aSource,
|
||||
PRBool aSourceIsURL);
|
||||
|
||||
virtual nsDOMWorkerPool* Pool() {
|
||||
NS_ASSERTION(!IsCanceled(), "Don't touch Pool after we've been canceled!");
|
||||
return mPool;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsDOMWorkerThread();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
// For nsDOMWorkerBase
|
||||
virtual nsresult HandleMessage(const nsAString& aMessage,
|
||||
nsDOMWorkerBase* aSourceThread);
|
||||
|
||||
// For nsDOMWorkerBase
|
||||
virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
|
||||
|
||||
virtual void Cancel();
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
|
||||
PRBool SetGlobalForContext(JSContext* aCx);
|
||||
PRBool CompileGlobalObject(JSContext* aCx);
|
||||
|
||||
inline nsDOMWorkerTimeout* FirstTimeout();
|
||||
inline nsDOMWorkerTimeout* NextTimeout(nsDOMWorkerTimeout* aTimeout);
|
||||
|
||||
PRBool AddTimeout(nsDOMWorkerTimeout* aTimeout);
|
||||
void RemoveTimeout(nsDOMWorkerTimeout* aTimeout);
|
||||
void ClearTimeouts();
|
||||
void CancelTimeout(PRUint32 aId);
|
||||
void SuspendTimeouts();
|
||||
void ResumeTimeouts();
|
||||
|
||||
void CancelScriptLoaders();
|
||||
|
||||
PRBool AddXHR(nsDOMWorkerXHR* aXHR);
|
||||
void RemoveXHR(nsDOMWorkerXHR* aXHR);
|
||||
void CancelXHRs();
|
||||
|
||||
PRLock* Lock() {
|
||||
return mLock;
|
||||
}
|
||||
|
||||
nsDOMWorkerPool* mPool;
|
||||
nsString mSource;
|
||||
nsString mSourceURL;
|
||||
|
||||
nsAutoJSObjectHolder mGlobal;
|
||||
PRBool mCompiled;
|
||||
|
||||
PRUint32 mCallbackCount;
|
||||
|
||||
PRUint32 mNextTimeoutId;
|
||||
|
||||
PRLock* mLock;
|
||||
PRCList mTimeouts;
|
||||
|
||||
nsTArray<nsDOMWorkerScriptLoader*> mScriptLoaders;
|
||||
nsTArray<nsDOMWorkerXHR*> mXHRs;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERTHREAD_H__ */
|
@ -47,7 +47,9 @@
|
||||
// Other includes
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "pratom.h"
|
||||
#include "prtime.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMThreadService.h"
|
||||
@ -73,15 +75,15 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
|
||||
nsresult* aRv)
|
||||
: mCallback(nsnull),
|
||||
mCallbackArgs(nsnull),
|
||||
mCallbackArgsLength(0)
|
||||
mCallbackArgsLength(0),
|
||||
mRuntime(NULL)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
|
||||
|
||||
JSRuntime* rt;
|
||||
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
|
||||
NS_ENSURE_SUCCESS(*aRv,);
|
||||
|
||||
PRBool success = JS_AddNamedRootRT(rt, &mCallback,
|
||||
PRBool success = JS_AddNamedRootRT(mRuntime, &mCallback,
|
||||
"nsDOMWorkerTimeout Callback Object");
|
||||
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
|
||||
|
||||
@ -102,7 +104,7 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
|
||||
|
||||
for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) {
|
||||
mCallbackArgs[i] = aArgv[i + 2];
|
||||
success = JS_AddNamedRootRT(rt, &mCallbackArgs[i],
|
||||
success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[i],
|
||||
"nsDOMWorkerTimeout Callback Arg");
|
||||
if (NS_UNLIKELY(!success)) {
|
||||
// Set this to i so that the destructor only unroots the right number of
|
||||
@ -117,7 +119,7 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
|
||||
|
||||
// Take care of the last arg.
|
||||
mCallbackArgs[mCallbackArgsLength - 1] = 0;
|
||||
success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1],
|
||||
success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[mCallbackArgsLength - 1],
|
||||
"nsDOMWorkerTimeout Callback Final Arg");
|
||||
if (NS_UNLIKELY(!success)) {
|
||||
// Decrement this so that the destructor only unroots the right number of
|
||||
@ -137,17 +139,10 @@ nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
|
||||
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
|
||||
|
||||
if (mCallback) {
|
||||
JSRuntime* rt;
|
||||
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
|
||||
JS_RemoveRootRT(rt, &mCallbackArgs[i]);
|
||||
}
|
||||
JS_RemoveRootRT(rt, &mCallback);
|
||||
for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
|
||||
JS_RemoveRootRT(mRuntime, &mCallbackArgs[i]);
|
||||
}
|
||||
JS_RemoveRootRT(mRuntime, &mCallback);
|
||||
}
|
||||
|
||||
delete [] mCallbackArgs;
|
||||
@ -178,7 +173,8 @@ nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
|
||||
JSContext* aCx,
|
||||
nsresult* aRv)
|
||||
: mExpression(nsnull),
|
||||
mLineNumber(0)
|
||||
mLineNumber(0),
|
||||
mRuntime(NULL)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
|
||||
|
||||
@ -186,11 +182,10 @@ nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
|
||||
*aRv = expr ? NS_OK : NS_ERROR_FAILURE;
|
||||
NS_ENSURE_SUCCESS(*aRv,);
|
||||
|
||||
JSRuntime* rt;
|
||||
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
|
||||
NS_ENSURE_SUCCESS(*aRv,);
|
||||
|
||||
PRBool success = JS_AddNamedRootRT(rt, &mExpression,
|
||||
PRBool success = JS_AddNamedRootRT(mRuntime, &mExpression,
|
||||
"nsDOMWorkerTimeout Expression");
|
||||
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
|
||||
|
||||
@ -212,14 +207,7 @@ nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
|
||||
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
|
||||
|
||||
if (mExpression) {
|
||||
JSRuntime* rt;
|
||||
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
JS_RemoveRootRT(rt, &mExpression);
|
||||
}
|
||||
JS_RemoveRootRT(mRuntime, &mExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,37 +219,22 @@ nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
|
||||
nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorker* aWorker,
|
||||
PRUint32 aId)
|
||||
: mWorker(aWorker),
|
||||
: nsDOMWorkerFeature(aWorker, aId),
|
||||
mInterval(0),
|
||||
mIsInterval(PR_FALSE),
|
||||
mId(aId),
|
||||
mSuspendSpinlock(0),
|
||||
mSuspendInterval(0),
|
||||
mIsInterval(PR_FALSE),
|
||||
mIsSuspended(PR_FALSE),
|
||||
mSuspendInterval(0)
|
||||
#ifdef DEBUG
|
||||
, mFiredOrCanceled(PR_FALSE)
|
||||
#endif
|
||||
mSuspendedBeforeStart(PR_FALSE),
|
||||
mStarted(PR_FALSE)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
|
||||
NS_ASSERTION(mWorker, "Need a worker here!");
|
||||
}
|
||||
|
||||
nsDOMWorkerTimeout::~nsDOMWorkerTimeout()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDOMWorkerTimeout);
|
||||
|
||||
// If we have a timer then we assume we added ourselves to the thread's list.
|
||||
if (mTimer) {
|
||||
NS_ASSERTION(mFiredOrCanceled || mWorker->IsCanceled(),
|
||||
"Timeout should have fired or been canceled!");
|
||||
|
||||
mWorker->RemoveTimeout(this);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeout, nsITimerCallback)
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerTimeout, nsDOMWorkerFeature,
|
||||
nsITimerCallback)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
|
||||
@ -294,6 +267,8 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
|
||||
|
||||
mInterval = interval;
|
||||
|
||||
mIsInterval = aIsInterval;
|
||||
|
||||
mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC;
|
||||
|
||||
nsresult rv;
|
||||
@ -315,20 +290,11 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
|
||||
default:
|
||||
JS_ReportError(aCx, "useless %s call (missing quotes around argument?)",
|
||||
aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
|
||||
|
||||
|
||||
// Return an error that nsGlobalWindow can recognize and turn into NS_OK.
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
PRInt32 type;
|
||||
if (aIsInterval) {
|
||||
type = nsITimer::TYPE_REPEATING_SLACK;
|
||||
}
|
||||
else {
|
||||
type = nsITimer::TYPE_ONE_SHOT;
|
||||
}
|
||||
mIsInterval = aIsInterval;
|
||||
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -338,18 +304,30 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
|
||||
rv = timer->SetTarget(target);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = timer->InitWithCallback(this, interval, type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mTimer.swap(timer);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mWorker->AddTimeout(this)) {
|
||||
// Must have been canceled.
|
||||
mTimer->Cancel();
|
||||
mTimer = nsnull;
|
||||
return NS_ERROR_ABORT;
|
||||
nsresult
|
||||
nsDOMWorkerTimeout::Start()
|
||||
{
|
||||
if (IsSuspended()) {
|
||||
NS_ASSERTION(mSuspendedBeforeStart, "Bad state!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRInt32 type;
|
||||
if (mIsInterval) {
|
||||
type = nsITimer::TYPE_REPEATING_SLACK;
|
||||
}
|
||||
else {
|
||||
type = nsITimer::TYPE_ONE_SHOT;
|
||||
}
|
||||
|
||||
nsresult rv = mTimer->InitWithCallback(this, mInterval, type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mStarted = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -360,10 +338,6 @@ nsDOMWorkerTimeout::Run()
|
||||
LOG(("Worker [0x%p] running timeout [0x%p] with id %u",
|
||||
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
|
||||
|
||||
#ifdef DEBUG
|
||||
mFiredOrCanceled = PR_TRUE;
|
||||
#endif
|
||||
|
||||
JSContext* cx;
|
||||
nsresult rv =
|
||||
nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
|
||||
@ -391,10 +365,6 @@ nsDOMWorkerTimeout::Cancel()
|
||||
LOG(("Worker [0x%p] canceling timeout [0x%p] with id %u",
|
||||
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
|
||||
|
||||
#ifdef DEBUG
|
||||
mFiredOrCanceled = PR_TRUE;
|
||||
#endif
|
||||
|
||||
{
|
||||
AutoSpinlock lock(this);
|
||||
|
||||
@ -410,21 +380,29 @@ nsDOMWorkerTimeout::Cancel()
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerTimeout::Suspend(PRTime aNow)
|
||||
nsDOMWorkerTimeout::Suspend()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
|
||||
#ifdef DEBUG
|
||||
if (mStarted) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
#endif
|
||||
|
||||
AutoSpinlock lock(this);
|
||||
|
||||
if (!mIsSuspended) {
|
||||
mIsSuspended = PR_TRUE;
|
||||
mSuspendedRef = this;
|
||||
NS_ASSERTION(!IsSuspendedNoLock(), "Bad state!");
|
||||
|
||||
mIsSuspended = PR_TRUE;
|
||||
mSuspendedRef = this;
|
||||
|
||||
if (!mStarted) {
|
||||
mSuspendedBeforeStart = PR_TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
mTimer->Cancel();
|
||||
|
||||
mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
|
||||
mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - PR_Now())) /
|
||||
(PRTime)PR_USEC_PER_MSEC;
|
||||
|
||||
LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)",
|
||||
@ -433,9 +411,14 @@ nsDOMWorkerTimeout::Suspend(PRTime aNow)
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerTimeout::Resume(PRTime aNow)
|
||||
nsDOMWorkerTimeout::Resume()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
#ifdef DEBUG
|
||||
if (!mSuspendedBeforeStart) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
|
||||
|
||||
LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u",
|
||||
@ -445,7 +428,14 @@ nsDOMWorkerTimeout::Resume(PRTime aNow)
|
||||
|
||||
NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!");
|
||||
|
||||
mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
|
||||
if (mSuspendedBeforeStart) {
|
||||
NS_ASSERTION(!mSuspendInterval, "Bad state!");
|
||||
mSuspendedBeforeStart = PR_FALSE;
|
||||
mSuspendInterval = mInterval;
|
||||
mStarted = PR_TRUE;
|
||||
}
|
||||
|
||||
mTargetTime = PR_Now() + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
@ -499,10 +489,9 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTimer)
|
||||
if (type == nsITimer::TYPE_ONE_SHOT) {
|
||||
AutoSpinlock lock(this);
|
||||
if (mIsSuspended) {
|
||||
mIsSuspended = PR_FALSE;
|
||||
mSuspendedRef = nsnull;
|
||||
if (mIsInterval) {
|
||||
//LOG(("Timeout [0x%p] resuming normal interval (%u) with id %u",
|
||||
//static_cast<void*>(this), mInterval, mId));
|
||||
|
||||
// This is the first fire since we resumed. Set our interval back to the
|
||||
// real interval.
|
||||
mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
|
||||
@ -511,9 +500,6 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTimer)
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mIsSuspended = PR_FALSE;
|
||||
mSuspendedRef = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,60 +47,61 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "prclist.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerThread.h"
|
||||
#include "nsDOMWorker.h"
|
||||
|
||||
/**
|
||||
* The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created
|
||||
* by an nsDOMWorkerThread (or one of its JS context functions) and immediately
|
||||
* takes a strong reference to the worker that created it. It does this so that
|
||||
* the worker can't be collected while a timeout is outstanding. However, the
|
||||
* worker needs a weak reference to the timeout so that it can be canceled if
|
||||
* the worker is canceled (in the event that the page falls out of the fastback
|
||||
* by an nsDOMWorker (or one of its JS context functions) and immediately takes
|
||||
* a strong reference to the worker that created it. It does this so that the
|
||||
* worker can't be collected while a timeout is outstanding. However, the worker
|
||||
* needs a weak reference to the timeout so that it can be canceled if the
|
||||
* worker is canceled (in the event that the page falls out of the fastback
|
||||
* cache or the application is exiting, for instance). The only thing that holds
|
||||
* the timeout alive is it's mTimer via the nsITimerCallback interface. If the
|
||||
* timer is single-shot and has run already or if the timer is canceled then
|
||||
* this object should die.
|
||||
*/
|
||||
class nsDOMWorkerTimeout : public PRCList,
|
||||
class nsDOMWorkerTimeout : public nsDOMWorkerFeature,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId);
|
||||
~nsDOMWorkerTimeout();
|
||||
nsDOMWorkerTimeout(nsDOMWorker* aWorker,
|
||||
PRUint32 aId);
|
||||
|
||||
nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
|
||||
nsresult Init(JSContext* aCx,
|
||||
PRUint32 aArgc,
|
||||
jsval* aArgv,
|
||||
PRBool aIsInterval);
|
||||
|
||||
nsresult Start();
|
||||
|
||||
nsresult Run();
|
||||
|
||||
void Cancel();
|
||||
void Suspend(PRTime aNow);
|
||||
void Resume(PRTime aNow);
|
||||
virtual void Cancel();
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
|
||||
PRIntervalTime GetInterval() {
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
nsDOMWorkerThread* GetWorker() {
|
||||
nsDOMWorker* GetWorker() {
|
||||
return mWorker;
|
||||
}
|
||||
|
||||
PRUint32 GetId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
PRBool IsSuspended() {
|
||||
AutoSpinlock lock(this);
|
||||
return IsSuspendedNoLock();
|
||||
}
|
||||
|
||||
private:
|
||||
~nsDOMWorkerTimeout() { }
|
||||
|
||||
void AcquireSpinlock();
|
||||
void ReleaseSpinlock();
|
||||
|
||||
@ -137,7 +138,9 @@ private:
|
||||
class FunctionCallback : public CallbackBase
|
||||
{
|
||||
public:
|
||||
FunctionCallback(PRUint32 aArgc, jsval* aArgv, nsresult* aRv);
|
||||
FunctionCallback(PRUint32 aArgc,
|
||||
jsval* aArgv,
|
||||
nsresult* aRv);
|
||||
virtual ~FunctionCallback();
|
||||
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
|
||||
JSContext* aCx);
|
||||
@ -145,12 +148,15 @@ private:
|
||||
jsval mCallback;
|
||||
jsval* mCallbackArgs;
|
||||
PRUint32 mCallbackArgsLength;
|
||||
JSRuntime* mRuntime;
|
||||
};
|
||||
|
||||
|
||||
class ExpressionCallback : public CallbackBase
|
||||
{
|
||||
public:
|
||||
ExpressionCallback(PRUint32 aArgc, jsval* aArgv, JSContext* aCx,
|
||||
ExpressionCallback(PRUint32 aArgc,
|
||||
jsval* aArgv,
|
||||
JSContext* aCx,
|
||||
nsresult* aRv);
|
||||
virtual ~ExpressionCallback();
|
||||
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
|
||||
@ -159,31 +165,26 @@ private:
|
||||
JSString* mExpression;
|
||||
nsString mFileName;
|
||||
PRUint32 mLineNumber;
|
||||
JSRuntime* mRuntime;
|
||||
};
|
||||
|
||||
// Hold the worker alive!
|
||||
nsRefPtr<nsDOMWorkerThread> mWorker;
|
||||
|
||||
// Hold this object alive!
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
PRUint32 mInterval;
|
||||
PRBool mIsInterval;
|
||||
|
||||
PRTime mTargetTime;
|
||||
|
||||
nsAutoPtr<CallbackBase> mCallback;
|
||||
|
||||
PRUint32 mId;
|
||||
|
||||
PRInt32 mSuspendSpinlock;
|
||||
PRBool mIsSuspended;
|
||||
PRUint32 mSuspendInterval;
|
||||
nsRefPtr<nsDOMWorkerTimeout> mSuspendedRef;
|
||||
|
||||
#ifdef DEBUG
|
||||
PRBool mFiredOrCanceled;
|
||||
#endif
|
||||
PRPackedBool mIsInterval;
|
||||
PRPackedBool mIsSuspended;
|
||||
PRPackedBool mSuspendedBeforeStart;
|
||||
PRPackedBool mStarted;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERTIMEOUT_H__ */
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorkerEvents.h"
|
||||
#include "nsDOMWorkerPool.h"
|
||||
#include "nsDOMWorkerXHRProxy.h"
|
||||
|
||||
@ -91,9 +92,9 @@ const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes =
|
||||
PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >=
|
||||
nsDOMWorkerXHREventTarget::sMaxUploadEventTypes);
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget,
|
||||
nsIDOMEventTarget,
|
||||
nsIXMLHttpRequestEventTarget)
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHREventTarget,
|
||||
nsDOMWorkerMessageHandler,
|
||||
nsIXMLHttpRequestEventTarget)
|
||||
|
||||
PRUint32
|
||||
nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString)
|
||||
@ -109,9 +110,13 @@ nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aOnabort);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ABORT);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
|
||||
listener.forget(aOnabort);
|
||||
|
||||
return NS_OK;
|
||||
@ -120,15 +125,24 @@ nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
|
||||
{
|
||||
return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE);
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
|
||||
|
||||
return SetOnXListener(type, aOnabort);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnerror);
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ERROR);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
|
||||
listener.forget(aOnerror);
|
||||
|
||||
return NS_OK;
|
||||
@ -137,15 +151,24 @@ nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
|
||||
{
|
||||
return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE);
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
|
||||
|
||||
return SetOnXListener(type, aOnerror);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aOnload);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_LOAD);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
|
||||
listener.forget(aOnload);
|
||||
|
||||
return NS_OK;
|
||||
@ -154,16 +177,24 @@ nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload)
|
||||
{
|
||||
return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE);
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
|
||||
|
||||
return SetOnXListener(type, aOnload);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aOnloadstart);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener =
|
||||
GetOnXListener(LISTENER_TYPE_LOADSTART);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
|
||||
listener.forget(aOnloadstart);
|
||||
|
||||
return NS_OK;
|
||||
@ -172,16 +203,24 @@ nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
|
||||
{
|
||||
return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE);
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
|
||||
|
||||
return SetOnXListener(type, aOnloadstart);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aOnprogress);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener =
|
||||
GetOnXListener(LISTENER_TYPE_PROGRESS);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
|
||||
listener.forget(aOnprogress);
|
||||
|
||||
return NS_OK;
|
||||
@ -190,230 +229,183 @@ nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
|
||||
{
|
||||
return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE);
|
||||
}
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aUseCapture)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aListener);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
|
||||
|
||||
PRUint32 type = GetListenerTypeFromString(aType);
|
||||
if (type > sMaxXHREventTypes) {
|
||||
// Silently ignore junk events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return SetEventListener(type, aListener, PR_FALSE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::RemoveEventListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aUseCapture)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aListener);
|
||||
|
||||
PRUint32 type = GetListenerTypeFromString(aType);
|
||||
if (type > sMaxXHREventTypes) {
|
||||
// Silently ignore junk events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return UnsetEventListener(type, aListener);
|
||||
}
|
||||
|
||||
/* ec702b78-c30f-439f-9a9b-a5dae17ee0fc */
|
||||
#define NS_IPRIVATEWORKERXHREVENT_IID \
|
||||
{ \
|
||||
0xec702b78, \
|
||||
0xc30f, \
|
||||
0x439f, \
|
||||
{ 0x9a, 0x9b, 0xa5, 0xda, 0xe1, 0x7e, 0xe0, 0xfc } \
|
||||
}
|
||||
|
||||
class nsIPrivateWorkerXHREvent : public nsIDOMEvent
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRIVATEWORKERXHREVENT_IID)
|
||||
virtual PRBool PreventDefaultCalled() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateWorkerXHREvent,
|
||||
NS_IPRIVATEWORKERXHREVENT_IID)
|
||||
|
||||
#define NS_FORWARD_NSIDOMEVENT_SPECIAL \
|
||||
NS_IMETHOD GetType(nsAString& aType) \
|
||||
{ return mEvent->GetType(aType); } \
|
||||
NS_IMETHOD GetTarget(nsIDOMEventTarget** aTarget) \
|
||||
{ return mEvent->GetTarget(aTarget); } \
|
||||
NS_IMETHOD GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) \
|
||||
{ return mEvent->GetCurrentTarget(aCurrentTarget); } \
|
||||
NS_IMETHOD GetEventPhase(PRUint16* aEventPhase) \
|
||||
{ return mEvent->GetEventPhase(aEventPhase); } \
|
||||
NS_IMETHOD GetBubbles(PRBool* aBubbles) \
|
||||
{ return mEvent->GetBubbles(aBubbles); } \
|
||||
NS_IMETHOD GetCancelable(PRBool* aCancelable) \
|
||||
{ return mEvent->GetCancelable(aCancelable); } \
|
||||
NS_IMETHOD GetTimeStamp(DOMTimeStamp* aTimeStamp) \
|
||||
{ return mEvent->GetTimeStamp(aTimeStamp); } \
|
||||
NS_IMETHOD StopPropagation() \
|
||||
{ return mEvent->StopPropagation(); }
|
||||
|
||||
class nsDOMWorkerXHREventWrapper : public nsIPrivateWorkerXHREvent
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_FORWARD_NSIDOMEVENT_SPECIAL
|
||||
|
||||
nsDOMWorkerXHREventWrapper(nsIDOMEvent* aEvent)
|
||||
: mEvent(aEvent), mPreventDefaultCalled(PR_FALSE) {
|
||||
NS_ASSERTION(aEvent, "Null pointer!");
|
||||
}
|
||||
|
||||
NS_IMETHOD PreventDefault() {
|
||||
mPreventDefaultCalled = PR_TRUE;
|
||||
return mEvent->PreventDefault();
|
||||
}
|
||||
|
||||
NS_IMETHOD InitEvent(const nsAString& aEventType, PRBool aCanBubble,
|
||||
PRBool aCancelable) {
|
||||
mPreventDefaultCalled = PR_FALSE;
|
||||
return mEvent->InitEvent(aEventType, aCanBubble, aCancelable);
|
||||
}
|
||||
|
||||
// nsIPrivateWorkerXHREvent
|
||||
virtual PRBool PreventDefaultCalled() {
|
||||
return mPreventDefaultCalled;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMEvent> mEvent;
|
||||
PRBool mPreventDefaultCalled;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventWrapper,
|
||||
nsIDOMEvent,
|
||||
nsIPrivateWorkerXHREvent)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREventTarget::DispatchEvent(nsIDOMEvent* aEvent,
|
||||
PRBool* _retval)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEvent);
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
|
||||
nsCOMPtr<nsIPrivateWorkerXHREvent> wrapper(do_QueryInterface(aEvent));
|
||||
if (!wrapper) {
|
||||
wrapper = new nsDOMWorkerXHREventWrapper(aEvent);
|
||||
NS_ENSURE_TRUE(wrapper, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
nsresult rv = HandleWorkerEvent(wrapper);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*_retval = wrapper->PreventDefaultCalled();
|
||||
return NS_OK;
|
||||
return SetOnXListener(type, aOnprogress);
|
||||
}
|
||||
|
||||
nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR)
|
||||
: mWorkerXHR(aWorkerXHR)
|
||||
{
|
||||
NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!");
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aWorkerXHR, "Null pointer!");
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
|
||||
nsIXMLHttpRequestUpload,
|
||||
nsIClassInfo)
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
|
||||
nsIXMLHttpRequestUpload)
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget,
|
||||
nsIXMLHttpRequestEventTarget,
|
||||
nsIXMLHttpRequestUpload)
|
||||
|
||||
NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload)
|
||||
NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHRUpload)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType,
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHRUpload::AddEventListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener)
|
||||
PRBool aUseCapture)
|
||||
{
|
||||
if (mWorkerXHR->mCanceled) {
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aListener);
|
||||
|
||||
if (mWorkerXHR->mWorker->IsCanceled()) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener,
|
||||
PR_TRUE);
|
||||
nsresult rv = nsDOMWorkerXHREventTarget::AddEventListener(aType, aListener,
|
||||
aUseCapture);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mWorkerXHR->mXHRProxy->UploadEventListenerAdded();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("UploadEventListenerAdded failed!");
|
||||
RemoveEventListener(aType, aListener, aUseCapture);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHRUpload::RemoveEventListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aUseCapture)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aListener);
|
||||
|
||||
if (mWorkerXHR->mWorker->IsCanceled()) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return nsDOMWorkerXHREventTarget::RemoveEventListener(aType, aListener,
|
||||
aUseCapture);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHRUpload::DispatchEvent(nsIDOMEvent* aEvent,
|
||||
PRBool* _retval)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ENSURE_ARG_POINTER(aEvent);
|
||||
|
||||
if (mWorkerXHR->mWorker->IsCanceled()) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return nsDOMWorkerXHREventTarget::DispatchEvent(aEvent, _retval);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener)
|
||||
nsDOMWorkerXHRUpload::SetOnXListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mWorkerXHR->mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
if (mWorkerXHR->mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
PRUint32 type = GetListenerTypeFromString(aType);
|
||||
if (type > sMaxUploadEventTypes) {
|
||||
// Silently ignore junk events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE);
|
||||
return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMEventListener>
|
||||
nsDOMWorkerXHRUpload::GetOnXListener(PRUint32 aType)
|
||||
{
|
||||
if (mWorkerXHR->mCanceled) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return mWorkerXHR->mXHRProxy->GetOnXListener(aType, PR_TRUE);
|
||||
}
|
||||
|
||||
nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorkerThread* aWorker)
|
||||
: mWorker(aWorker),
|
||||
mCanceled(PR_TRUE)
|
||||
nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorker* aWorker)
|
||||
: nsDOMWorkerFeature(aWorker),
|
||||
mWrappedNative(nsnull),
|
||||
mCanceled(PR_FALSE)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aWorker, "Must have a worker!");
|
||||
}
|
||||
|
||||
nsDOMWorkerXHR::~nsDOMWorkerXHR()
|
||||
{
|
||||
if (!mCanceled) {
|
||||
mWorker->RemoveXHR(this);
|
||||
}
|
||||
}
|
||||
// Tricky! We use the AddRef/Release method of nsDOMWorkerFeature (to make sure
|
||||
// we properly remove ourselves from the worker array) but inherit the QI of
|
||||
// nsDOMWorkerXHREventTarget.
|
||||
NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
|
||||
NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
|
||||
nsIXMLHttpRequest,
|
||||
nsIClassInfo)
|
||||
NS_IMPL_QUERY_INTERFACE_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
|
||||
nsIXMLHttpRequest,
|
||||
nsIXPCScriptable)
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget,
|
||||
nsIXMLHttpRequestEventTarget,
|
||||
nsIXMLHttpRequest)
|
||||
|
||||
NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR)
|
||||
NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHR)
|
||||
|
||||
#define XPC_MAP_CLASSNAME nsDOMWorkerXHR
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "XMLHttpRequest"
|
||||
#define XPC_MAP_WANT_POSTCREATE
|
||||
#define XPC_MAP_WANT_TRACE
|
||||
#define XPC_MAP_WANT_FINALIZE
|
||||
|
||||
#define XPC_MAP_FLAGS \
|
||||
nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \
|
||||
nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY | \
|
||||
nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
|
||||
|
||||
#include "xpc_map_end.h"
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
|
||||
JSTracer* aTracer,
|
||||
JSObject* /*aObj */)
|
||||
{
|
||||
if (!mCanceled) {
|
||||
nsDOMWorkerMessageHandler::Trace(aTracer);
|
||||
if (mUpload) {
|
||||
mUpload->Trace(aTracer);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
|
||||
JSContext* /* aCx */,
|
||||
JSObject* /* aObj */)
|
||||
{
|
||||
nsDOMWorkerMessageHandler::ClearAllListeners();
|
||||
if (mUpload) {
|
||||
mUpload->ClearAllListeners();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::PostCreate(nsIXPConnectWrappedNative* aWrapper,
|
||||
JSContext* /* aCx */,
|
||||
JSObject* /* aObj */)
|
||||
{
|
||||
mWrappedNative = aWrapper;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHR::Init()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (!mWorker->AddXHR(this)) {
|
||||
// Must have been canceled.
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
mCanceled = PR_FALSE;
|
||||
|
||||
nsRefPtr<nsDOMWorkerXHRProxy> proxy = new nsDOMWorkerXHRProxy(this);
|
||||
NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
@ -446,14 +438,12 @@ nsDOMWorkerXHR::Cancel()
|
||||
mXHRProxy->Destroy();
|
||||
}
|
||||
|
||||
mWorker->RemoveXHR(this);
|
||||
mWorker = nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHR::SetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener)
|
||||
nsDOMWorkerXHR::SetOnXListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
@ -461,49 +451,20 @@ nsDOMWorkerXHR::SetEventListener(PRUint32 aType,
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return mXHRProxy->AddEventListener(aType, aListener, aOnXListener, PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHR::UnsetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
PRUint32 type = GetListenerTypeFromString(aType);
|
||||
if (type > sMaxXHREventTypes) {
|
||||
// Silently ignore junk events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mXHRProxy->RemoveEventListener(aType, aListener, PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHR::HandleWorkerEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return mXHRProxy->HandleWorkerEvent(aEvent, PR_FALSE);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMEventListener>
|
||||
nsDOMWorkerXHR::GetOnXListener(PRUint32 aType)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mCanceled) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return mXHRProxy->GetOnXListener(aType, PR_FALSE);
|
||||
return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
*aChannel = nsnull;
|
||||
return NS_OK;
|
||||
@ -512,6 +473,8 @@ nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::GetResponseXML(nsIDOMDocument** aResponseXML)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aResponseXML);
|
||||
*aResponseXML = nsnull;
|
||||
return NS_OK;
|
||||
@ -853,7 +816,7 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsRefPtr<nsDOMWorkerThread> worker = mWorker;
|
||||
nsRefPtr<nsDOMWorker> worker = mWorker;
|
||||
if (!worker) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
@ -878,32 +841,24 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::GetOnreadystatechange(nsIDOMEventListener** aOnreadystatechange)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aOnreadystatechange);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener =
|
||||
mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE);
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
|
||||
listener.forget(aOnreadystatechange);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
nsAutoString type;
|
||||
type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE,
|
||||
aOnreadystatechange, PR_TRUE, PR_FALSE);
|
||||
return SetOnXListener(type, aOnreadystatechange);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -40,10 +40,9 @@
|
||||
#define __NSDOMWORKERXHR_H__
|
||||
|
||||
// Bases
|
||||
#include "nsIXMLHttpRequest.h"
|
||||
#include "nsIClassInfo.h"
|
||||
|
||||
// Interfaces
|
||||
#include "nsIXMLHttpRequest.h"
|
||||
#include "nsIXPCScriptable.h"
|
||||
|
||||
// Other includes
|
||||
#include "nsAutoPtr.h"
|
||||
@ -52,7 +51,9 @@
|
||||
#include "prlock.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerThread.h"
|
||||
#include "nsDOMWorker.h"
|
||||
#include "nsDOMWorkerMacros.h"
|
||||
#include "nsDOMWorkerXHRProxy.h"
|
||||
|
||||
// Convenience defines for event *indexes* in the sListenerTypes array.
|
||||
#define LISTENER_TYPE_ABORT 0
|
||||
@ -62,15 +63,14 @@
|
||||
#define LISTENER_TYPE_PROGRESS 4
|
||||
#define LISTENER_TYPE_READYSTATECHANGE 5
|
||||
|
||||
class nsDOMWorkerXHR;
|
||||
class nsDOMWorkerXHREvent;
|
||||
class nsDOMWorkerXHRProxy;
|
||||
class nsIXPConnectWrappedNative;
|
||||
|
||||
class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget
|
||||
class nsDOMWorkerXHREventTarget : public nsDOMWorkerMessageHandler,
|
||||
public nsIXMLHttpRequestEventTarget
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTTARGET
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
|
||||
NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET
|
||||
|
||||
static const char* const sListenerTypes[];
|
||||
@ -79,58 +79,16 @@ public:
|
||||
|
||||
static PRUint32 GetListenerTypeFromString(const nsAString& aString);
|
||||
|
||||
virtual nsresult SetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener) = 0;
|
||||
|
||||
virtual nsresult UnsetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener) = 0;
|
||||
|
||||
virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent) = 0;
|
||||
|
||||
virtual already_AddRefed<nsIDOMEventListener>
|
||||
GetOnXListener(PRUint32 aType) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~nsDOMWorkerXHREventTarget() { }
|
||||
};
|
||||
|
||||
class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
|
||||
public nsIXMLHttpRequestUpload,
|
||||
public nsIClassInfo
|
||||
{
|
||||
friend class nsDOMWorkerXHR;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerXHREventTarget::)
|
||||
NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
|
||||
NS_DECL_NSIXMLHTTPREQUESTUPLOAD
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
|
||||
|
||||
virtual nsresult SetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener);
|
||||
|
||||
virtual nsresult UnsetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener);
|
||||
|
||||
virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
virtual already_AddRefed<nsIDOMEventListener>
|
||||
GetOnXListener(PRUint32 aType);
|
||||
|
||||
protected:
|
||||
virtual ~nsDOMWorkerXHRUpload() { }
|
||||
|
||||
nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
|
||||
};
|
||||
class nsDOMWorkerXHRUpload;
|
||||
|
||||
class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
|
||||
public nsDOMWorkerFeature,
|
||||
public nsIXMLHttpRequest,
|
||||
public nsIClassInfo
|
||||
public nsIXPCScriptable
|
||||
{
|
||||
friend class nsDOMWorkerXHREvent;
|
||||
friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
|
||||
@ -140,38 +98,60 @@ class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIXMLHTTPREQUEST
|
||||
NS_DECL_NSICLASSINFO
|
||||
NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
|
||||
NS_DECL_NSICLASSINFO_GETINTERFACES
|
||||
NS_DECL_NSIXPCSCRIPTABLE
|
||||
|
||||
nsDOMWorkerXHR(nsDOMWorkerThread* aWorker);
|
||||
nsDOMWorkerXHR(nsDOMWorker* aWorker);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
void Cancel();
|
||||
virtual void Cancel();
|
||||
|
||||
virtual nsresult SetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener);
|
||||
virtual nsresult SetOnXListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener);
|
||||
|
||||
virtual nsresult UnsetEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener);
|
||||
|
||||
virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
virtual already_AddRefed<nsIDOMEventListener>
|
||||
GetOnXListener(PRUint32 aType);
|
||||
|
||||
protected:
|
||||
virtual ~nsDOMWorkerXHR();
|
||||
private:
|
||||
virtual ~nsDOMWorkerXHR() { }
|
||||
|
||||
PRLock* Lock() {
|
||||
return mWorker->Lock();
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMWorkerThread> mWorker;
|
||||
nsIXPConnectWrappedNative* GetWrappedNative() {
|
||||
return mWrappedNative;
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
|
||||
nsRefPtr<nsDOMWorkerXHRUpload> mUpload;
|
||||
|
||||
nsIXPConnectWrappedNative* mWrappedNative;
|
||||
|
||||
volatile PRBool mCanceled;
|
||||
};
|
||||
|
||||
class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
|
||||
public nsIXMLHttpRequestUpload
|
||||
{
|
||||
friend class nsDOMWorkerXHR;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIDOMEVENTTARGET
|
||||
NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
|
||||
NS_DECL_NSIXMLHTTPREQUESTUPLOAD
|
||||
NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
|
||||
NS_DECL_NSICLASSINFO_GETINTERFACES
|
||||
|
||||
nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
|
||||
|
||||
virtual nsresult SetOnXListener(const nsAString& aType,
|
||||
nsIDOMEventListener* aListener);
|
||||
|
||||
protected:
|
||||
virtual ~nsDOMWorkerXHRUpload() { }
|
||||
|
||||
nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERXHR_H__ */
|
||||
|
@ -59,8 +59,12 @@
|
||||
#include "prthread.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorker.h"
|
||||
#include "nsDOMWorkerEvents.h"
|
||||
#include "nsDOMWorkerMacros.h"
|
||||
#include "nsDOMWorkerPool.h"
|
||||
#include "nsDOMWorkerThread.h"
|
||||
#include "nsDOMWorkerXHR.h"
|
||||
#include "nsDOMWorkerXHRProxiedFunctions.h"
|
||||
|
||||
#define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes
|
||||
@ -109,7 +113,7 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable,
|
||||
nsDOMWorkerThread* aWorker)
|
||||
nsDOMWorker* aWorker)
|
||||
: mTarget(aTarget), mRunnable(aRunnable), mWorker(aWorker),
|
||||
mResult(NS_OK), mDone(PR_FALSE) { }
|
||||
|
||||
@ -157,329 +161,13 @@ public:
|
||||
private:
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
nsRefPtr<nsDOMWorkerThread> mWorker;
|
||||
nsRefPtr<nsDOMWorker> mWorker;
|
||||
nsresult mResult;
|
||||
volatile PRBool mDone;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsResultReturningRunnable, nsIRunnable)
|
||||
|
||||
class nsDOMWorkerXHREvent : public nsIRunnable,
|
||||
public nsIDOMProgressEvent,
|
||||
public nsIClassInfo
|
||||
{
|
||||
friend class nsDOMWorkerXHRProxy;
|
||||
friend class nsDOMWorkerXHREventTargetProxy;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSIDOMEVENT
|
||||
NS_DECL_NSIDOMPROGRESSEVENT
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
|
||||
|
||||
nsresult Init(PRUint32 aType,
|
||||
const nsAString& aTypeString,
|
||||
nsIDOMEvent* aEvent);
|
||||
|
||||
nsresult Init(nsIXMLHttpRequest* aXHR);
|
||||
|
||||
void EventHandled();
|
||||
|
||||
protected:
|
||||
nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
|
||||
nsCOMPtr<nsIDOMEventTarget> mTarget;
|
||||
nsString mTypeString;
|
||||
PRUint32 mType;
|
||||
PRUint16 mEventPhase;
|
||||
DOMTimeStamp mTimeStamp;
|
||||
nsString mResponseText;
|
||||
nsCString mStatusText;
|
||||
nsresult mStatus;
|
||||
PRInt32 mReadyState;
|
||||
PRUint64 mLoaded;
|
||||
PRUint64 mTotal;
|
||||
PRInt32 mChannelID;
|
||||
PRPackedBool mBubbles;
|
||||
PRPackedBool mCancelable;
|
||||
PRPackedBool mUploadEvent;
|
||||
PRPackedBool mProgressEvent;
|
||||
PRPackedBool mLengthComputable;
|
||||
};
|
||||
|
||||
nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
|
||||
: mXHRProxy(aXHRProxy),
|
||||
mType(PR_UINT32_MAX),
|
||||
mEventPhase(0),
|
||||
mTimeStamp(0),
|
||||
mStatus(NS_OK),
|
||||
mReadyState(0),
|
||||
mLoaded(0),
|
||||
mTotal(0),
|
||||
mChannelID(-1),
|
||||
mBubbles(PR_FALSE),
|
||||
mCancelable(PR_FALSE),
|
||||
mUploadEvent(PR_FALSE),
|
||||
mProgressEvent(PR_FALSE),
|
||||
mLengthComputable(PR_FALSE)
|
||||
{
|
||||
NS_ASSERTION(aXHRProxy, "Can't be null!");
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerXHREvent)
|
||||
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerXHREvent)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEvent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerXHREvent, nsIDOMEvent)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetInterfaces(PRUint32* aCount,
|
||||
nsIID*** aArray)
|
||||
{
|
||||
PRUint32 count = *aCount = mProgressEvent ? 2 : 1;
|
||||
|
||||
*aArray = (nsIID**)nsMemory::Alloc(sizeof(nsIID*) * count);
|
||||
|
||||
if (mProgressEvent) {
|
||||
(*aArray)[--count] =
|
||||
(nsIID*)nsMemory::Clone(&NS_GET_IID(nsIDOMProgressEvent), sizeof(nsIID));
|
||||
}
|
||||
|
||||
(*aArray)[--count] =
|
||||
(nsIID *)nsMemory::Clone(&NS_GET_IID(nsIDOMEvent), sizeof(nsIID));
|
||||
|
||||
NS_ASSERTION(!count, "Bad math!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerXHREvent)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHREvent::Init(PRUint32 aType,
|
||||
const nsAString& aTypeString,
|
||||
nsIDOMEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aEvent, "Don't pass null here!");
|
||||
|
||||
nsresult rv;
|
||||
|
||||
mType = aType;
|
||||
mTypeString.Assign(aTypeString);
|
||||
|
||||
mChannelID = mXHRProxy->ChannelID();
|
||||
|
||||
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(aEvent));
|
||||
if (progressEvent) {
|
||||
mProgressEvent = PR_TRUE;
|
||||
|
||||
PRBool lengthComputable;
|
||||
rv = progressEvent->GetLengthComputable(&lengthComputable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mLengthComputable = lengthComputable ? PR_TRUE : PR_FALSE;
|
||||
|
||||
rv = progressEvent->GetLoaded(&mLoaded);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = progressEvent->GetTotal(&mTotal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> mainThreadTarget;
|
||||
rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(mainThreadTarget));
|
||||
if (upload) {
|
||||
mUploadEvent = PR_TRUE;
|
||||
mTarget =
|
||||
static_cast<nsDOMWorkerXHREventTarget*>(mXHRProxy->mWorkerXHR->mUpload);
|
||||
}
|
||||
else {
|
||||
mUploadEvent = PR_FALSE;
|
||||
mTarget = mXHRProxy->mWorkerXHR;
|
||||
}
|
||||
|
||||
PRBool boolVal;
|
||||
rv = aEvent->GetBubbles(&boolVal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mBubbles = boolVal ? PR_TRUE : PR_FALSE;
|
||||
|
||||
rv = aEvent->GetCancelable(&boolVal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mCancelable = boolVal ? PR_TRUE : PR_FALSE;
|
||||
|
||||
rv = aEvent->GetTimeStamp(&mTimeStamp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = Init(mXHRProxy->mXHR);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHREvent::Init(nsIXMLHttpRequest* aXHR)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aXHR, "Don't pass null here!");
|
||||
|
||||
nsresult rv = aXHR->GetResponseText(mResponseText);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aXHR->GetStatusText(mStatusText);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aXHR->GetStatus(&mStatus);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aXHR->GetReadyState(&mReadyState);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerXHREvent::EventHandled()
|
||||
{
|
||||
// Prevent reference cycles by releasing these here.
|
||||
mXHRProxy = nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::Run()
|
||||
{
|
||||
nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent);
|
||||
|
||||
EventHandled();
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetType(nsAString& aType)
|
||||
{
|
||||
aType.Assign(mTypeString);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetTarget(nsIDOMEventTarget** aTarget)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aTarget);
|
||||
NS_ADDREF(*aTarget = mTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCurrentTarget);
|
||||
NS_ADDREF(*aCurrentTarget = mTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetEventPhase(PRUint16* aEventPhase)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEventPhase);
|
||||
*aEventPhase = mEventPhase;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetBubbles(PRBool* aBubbles)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aBubbles);
|
||||
*aBubbles = mBubbles;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetCancelable(PRBool* aCancelable)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCancelable);
|
||||
*aCancelable = mCancelable;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetTimeStamp(DOMTimeStamp* aTimeStamp)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aTimeStamp);
|
||||
*aTimeStamp = mTimeStamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::StopPropagation()
|
||||
{
|
||||
NS_WARNING("StopPropagation doesn't do anything here!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::PreventDefault()
|
||||
{
|
||||
NS_WARNING("PreventDefault doesn't do anything yet!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::InitEvent(const nsAString& aEventTypeArg,
|
||||
PRBool aCanBubbleArg,
|
||||
PRBool aCancelableArg)
|
||||
{
|
||||
NS_WARNING("InitEvent doesn't do anything here!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetLengthComputable(PRBool* aLengthComputable)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLengthComputable);
|
||||
*aLengthComputable = mLengthComputable;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetLoaded(PRUint64* aLoaded)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLoaded);
|
||||
*aLoaded = mLoaded;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::GetTotal(PRUint64* aTotal)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aTotal);
|
||||
*aTotal = mTotal;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerXHREvent::InitProgressEvent(const nsAString_internal& aTypeArg,
|
||||
PRBool aCanBubbleArg,
|
||||
PRBool aCancelableArg,
|
||||
PRBool aLengthComputableArg,
|
||||
PRUint64 aLoadedArg,
|
||||
PRUint64 aTotalArg)
|
||||
{
|
||||
NS_WARNING("InitProgressEvent doesn't do anything here!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class nsDOMWorkerXHRLastProgressOrLoadEvent : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
@ -496,6 +184,9 @@ public:
|
||||
if (!mProxy->mCanceled) {
|
||||
nsAutoLock lock(mProxy->mWorkerXHR->Lock());
|
||||
mProxy->mLastProgressOrLoadEvent.swap(lastProgressOrLoadEvent);
|
||||
if (mProxy->mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastProgressOrLoadEvent) {
|
||||
@ -629,7 +320,7 @@ nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR)
|
||||
nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy()
|
||||
{
|
||||
if (mOwnedByXHR) {
|
||||
mWorkerXHR->Release();
|
||||
mWorkerXHRWN = nsnull;
|
||||
}
|
||||
else if (mXHR) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
@ -652,18 +343,6 @@ nsDOMWorkerXHRProxy::Init()
|
||||
|
||||
NS_ENSURE_FALSE(mXHR, NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
PRBool success = mXHRListeners.SetLength(MAX_XHR_LISTENER_TYPE);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
success = mXHROnXListeners.SetLength(MAX_XHR_LISTENER_TYPE);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
success = mUploadListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
success = mUploadOnXListeners.SetLength(MAX_UPLOAD_LISTENER_TYPE);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
mMainThread = do_GetMainThread();
|
||||
NS_ENSURE_TRUE(mMainThread, NS_ERROR_UNEXPECTED);
|
||||
|
||||
@ -693,20 +372,20 @@ nsDOMWorkerXHRProxy::Destroy()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
mCanceled = PR_TRUE;
|
||||
|
||||
ClearEventListeners();
|
||||
|
||||
{
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
mCanceled = PR_TRUE;
|
||||
|
||||
mLastProgressOrLoadEvent = nsnull;
|
||||
mLastXHREvent = nsnull;
|
||||
}
|
||||
|
||||
if (mXHR) {
|
||||
DestroyInternal();
|
||||
}
|
||||
|
||||
mLastXHREvent = nsnull;
|
||||
NS_ASSERTION(!(mLastProgressOrLoadEvent && mLastXHREvent), "Going to leak!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -717,7 +396,7 @@ nsDOMWorkerXHRProxy::InitInternal()
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!");
|
||||
|
||||
nsDOMWorkerThread* worker = mWorkerXHR->mWorker;
|
||||
nsDOMWorker* worker = mWorkerXHR->mWorker;
|
||||
nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
@ -747,11 +426,11 @@ nsDOMWorkerXHRProxy::InitInternal()
|
||||
nsRefPtr<nsDOMWorkerXHREvent> nullEvent = new nsDOMWorkerXHREvent(this);
|
||||
NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = nullEvent->Init(xhr);
|
||||
rv = nullEvent->SnapshotXHRState(xhr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mLastXHREvent.swap(nullEvent);
|
||||
|
||||
mLastXHREvent->EventHandled();
|
||||
nullEvent->EventHandled();
|
||||
mLastXHREvent.swap(nullEvent);
|
||||
|
||||
xhrConcrete->SetRequestObserver(this);
|
||||
|
||||
@ -797,7 +476,6 @@ nsDOMWorkerXHRProxy::DestroyInternal()
|
||||
|
||||
NS_ASSERTION(!mOwnedByXHR, "Should have flipped already!");
|
||||
NS_ASSERTION(!mSyncFinishedRunnable, "Should have fired this already!");
|
||||
NS_ASSERTION(!mLastProgressOrLoadEvent, "Should have killed this already!");
|
||||
|
||||
// mXHR could be null if Init fails.
|
||||
if (mXHR) {
|
||||
@ -816,9 +494,10 @@ nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
|
||||
nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
|
||||
NS_ASSERTION(xhrTarget, "This shouldn't fail!");
|
||||
|
||||
EventListenerFunction function = aAdd ?
|
||||
&nsIDOMEventTarget::AddEventListener :
|
||||
&nsIDOMEventTarget::RemoveEventListener;
|
||||
EventListenerFunction addRemoveEventListener =
|
||||
aAdd ?
|
||||
&nsIDOMEventTarget::AddEventListener :
|
||||
&nsIDOMEventTarget::RemoveEventListener;
|
||||
|
||||
nsAutoString eventName;
|
||||
PRUint32 index = 0;
|
||||
@ -829,14 +508,14 @@ nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
|
||||
|
||||
for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
|
||||
eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
|
||||
(xhrTarget.get()->*function)(eventName, this, PR_FALSE);
|
||||
(uploadTarget.get()->*function)(eventName, this, PR_FALSE);
|
||||
(xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
|
||||
(uploadTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
for (; index < MAX_XHR_LISTENER_TYPE; index++) {
|
||||
eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
|
||||
(xhrTarget.get()->*function)(eventName, this, PR_FALSE);
|
||||
(xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -853,146 +532,46 @@ nsDOMWorkerXHRProxy::FlipOwnership()
|
||||
nsRefPtr<nsDOMWorkerXHRProxy> kungFuDeathGrip(this);
|
||||
|
||||
if (mOwnedByXHR) {
|
||||
mWorkerXHR->AddRef();
|
||||
mWorkerXHRWN = mWorkerXHR->GetWrappedNative();
|
||||
NS_ASSERTION(mWorkerXHRWN, "Null pointer!");
|
||||
mXHR->Release();
|
||||
}
|
||||
else {
|
||||
mXHR->AddRef();
|
||||
mWorkerXHR->Release();
|
||||
mWorkerXHRWN = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener,
|
||||
PRBool aUploadListener)
|
||||
nsDOMWorkerXHRProxy::UploadEventListenerAdded()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
|
||||
aType >= MAX_XHR_LISTENER_TYPE) {
|
||||
// Silently fail on junk events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
|
||||
mXHRListeners[aType];
|
||||
WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
|
||||
mXHROnXListeners[aType];
|
||||
|
||||
{
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!aListener) {
|
||||
NS_ASSERTION(aOnXListener, "Shouldn't pass a null listener!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aOnXListener) {
|
||||
// Remove the old one from the array if it exists.
|
||||
if (onXListener) {
|
||||
#ifdef DEBUG
|
||||
PRBool removed =
|
||||
#endif
|
||||
listeners.RemoveElement(onXListener);
|
||||
NS_ASSERTION(removed, "Should still be in the array!");
|
||||
}
|
||||
|
||||
if (!aListener) {
|
||||
onXListener = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
onXListener = new nsDOMWorkerXHRWrappedListener(aListener);
|
||||
NS_ENSURE_TRUE(onXListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
aListener = onXListener;
|
||||
}
|
||||
|
||||
Listener* added = listeners.AppendElement(aListener);
|
||||
NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
// If this is the first time we're setting an upload listener then we have to
|
||||
// hit the main thread to attach the upload listeners.
|
||||
if (aUploadListener && aListener && !mWantUploadListeners) {
|
||||
nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
|
||||
new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
|
||||
NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsRefPtr<nsResultReturningRunnable> runnable =
|
||||
new nsResultReturningRunnable(mMainThread, attachRunnable,
|
||||
mWorkerXHR->mWorker);
|
||||
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = runnable->Dispatch();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mWantUploadListeners, "Should have set this!");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRProxy::RemoveEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aUploadListener)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aListener, "Null pointer!");
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
|
||||
aType >= MAX_XHR_LISTENER_TYPE) {
|
||||
// Silently fail on junk events.
|
||||
// hit the main thread to attach the upload listeners. Otherwise there's
|
||||
// nothing to do here.
|
||||
if (mWantUploadListeners) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
|
||||
mXHRListeners[aType];
|
||||
nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
|
||||
new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
|
||||
NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
nsRefPtr<nsResultReturningRunnable> runnable =
|
||||
new nsResultReturningRunnable(mMainThread, attachRunnable,
|
||||
mWorkerXHR->mWorker);
|
||||
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
listeners.RemoveElement(aListener);
|
||||
nsresult rv = runnable->Dispatch();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mWantUploadListeners, "Should have set this!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMEventListener>
|
||||
nsDOMWorkerXHRProxy::GetOnXListener(PRUint32 aType,
|
||||
PRBool aUploadListener)
|
||||
{
|
||||
if (mCanceled) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
|
||||
aType >= MAX_XHR_LISTENER_TYPE) {
|
||||
// Silently fail on junk events.
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
|
||||
mXHROnXListeners[aType];
|
||||
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = onXListener->Inner();
|
||||
return listener.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
|
||||
PRBool aUploadEvent)
|
||||
@ -1000,38 +579,19 @@ nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aEvent, "Should not be null!");
|
||||
|
||||
if (mCanceled ||
|
||||
(aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
|
||||
return NS_OK;
|
||||
{
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
if (mCanceled ||
|
||||
(aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mLastXHREvent = aEvent;
|
||||
}
|
||||
|
||||
mLastXHREvent = aEvent;
|
||||
|
||||
return HandleEventInternal(aEvent->mType, aEvent, aUploadEvent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerXHRProxy::HandleWorkerEvent(nsIDOMEvent* aEvent,
|
||||
PRBool aUploadEvent)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aEvent, "Should not be null!");
|
||||
|
||||
nsString typeString;
|
||||
nsresult rv = aEvent->GetType(typeString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 maxType = aUploadEvent ? MAX_UPLOAD_LISTENER_TYPE :
|
||||
MAX_XHR_LISTENER_TYPE;
|
||||
|
||||
PRUint32 type =
|
||||
nsDOMWorkerXHREventTarget::GetListenerTypeFromString(typeString);
|
||||
if (type >= maxType) {
|
||||
// Silently fail on junk events.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return HandleEventInternal(type, aEvent, aUploadEvent);
|
||||
nsIDOMEvent* event = static_cast<nsDOMWorkerEvent*>(aEvent);
|
||||
return HandleEventInternal(aEvent->mXHREventType, event, aUploadEvent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -1045,6 +605,7 @@ nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType,
|
||||
#ifdef DEBUG
|
||||
if (aUploadListener) {
|
||||
NS_ASSERTION(aType < MAX_UPLOAD_LISTENER_TYPE, "Bad type!");
|
||||
NS_ASSERTION(mWorkerXHR->mUpload, "No upload object!");
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
|
||||
@ -1055,82 +616,23 @@ nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType,
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
|
||||
mXHRListeners[aType];
|
||||
nsIDOMEventTarget* target = aUploadListener ?
|
||||
static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR->mUpload) :
|
||||
static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR);
|
||||
|
||||
nsAutoTArray<Listener, 10> listenerCopy;
|
||||
PRUint32 count;
|
||||
|
||||
{
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
count = listeners.Length();
|
||||
if (!count) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Listener* copied = listenerCopy.AppendElements(listeners);
|
||||
NS_ENSURE_TRUE(copied, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
for (PRUint32 index = 0; index < count; index++) {
|
||||
NS_ASSERTION(listenerCopy[index], "Null listener?!");
|
||||
listenerCopy[index]->HandleEvent(aEvent);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerXHRProxy::ClearEventListeners()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsTArray<ListenerArray> doomedArrays;
|
||||
PRBool ok = doomedArrays.SetLength(MAX_XHR_LISTENER_TYPE +
|
||||
MAX_UPLOAD_LISTENER_TYPE);
|
||||
NS_ENSURE_TRUE(ok,);
|
||||
|
||||
nsTArray<WrappedListener> doomedListeners;
|
||||
ok = doomedListeners.SetLength(MAX_XHR_LISTENER_TYPE +
|
||||
MAX_UPLOAD_LISTENER_TYPE);
|
||||
NS_ENSURE_TRUE(ok,);
|
||||
|
||||
{
|
||||
PRUint32 listenerIndex, doomedIndex;
|
||||
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
for (listenerIndex = 0, doomedIndex = 0;
|
||||
listenerIndex < MAX_UPLOAD_LISTENER_TYPE;
|
||||
listenerIndex++, doomedIndex++) {
|
||||
mUploadListeners[listenerIndex].
|
||||
SwapElements(doomedArrays[doomedIndex]);
|
||||
mUploadOnXListeners[listenerIndex].
|
||||
swap(doomedListeners[doomedIndex]);
|
||||
}
|
||||
|
||||
for (listenerIndex = 0;
|
||||
listenerIndex < MAX_XHR_LISTENER_TYPE;
|
||||
listenerIndex++, doomedIndex++) {
|
||||
mXHRListeners[listenerIndex].
|
||||
SwapElements(doomedArrays[doomedIndex]);
|
||||
mXHROnXListeners[listenerIndex].
|
||||
swap(doomedListeners[doomedIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructors for the nsTArrays actually kill the listeners outside of the
|
||||
// lock.
|
||||
return target->DispatchEvent(aEvent, nsnull);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType,
|
||||
nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType,
|
||||
nsIDOMEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
|
||||
#ifdef DEBUG
|
||||
PRUint32 type = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(aType);
|
||||
NS_ASSERTION(type < MAX_XHR_LISTENER_TYPE, "Bad type!");
|
||||
#endif
|
||||
|
||||
if (mXHRListeners[aType].Length()) {
|
||||
if (mWorkerXHR->HasListeners(aType)) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
@ -1146,7 +648,7 @@ nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType,
|
||||
checkUploadListeners = PR_TRUE;
|
||||
}
|
||||
|
||||
if (checkUploadListeners && mUploadListeners[aType].Length()) {
|
||||
if (checkUploadListeners && mWorkerXHR->mUpload->HasListeners(aType)) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
@ -1171,20 +673,29 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// When Abort is called on nsXMLHttpRequest (either from a proxied Abort call
|
||||
// or from DestroyInternal) the OnStopRequest call is not run synchronously.
|
||||
// Thankfully an abort event *is* fired synchronously so we can flip our
|
||||
// ownership around and fire the sync finished runnable if we're running in
|
||||
// sync mode.
|
||||
if (type == LISTENER_TYPE_ABORT && mCanceled) {
|
||||
OnStopRequest(nsnull, nsnull, NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
if (mCanceled) {
|
||||
// When Abort is called on nsXMLHttpRequest (either from a proxied Abort
|
||||
// call or from DestroyInternal) the OnStopRequest call is not run
|
||||
// synchronously. Thankfully an abort event *is* fired synchronously so we
|
||||
// can flip our ownership around and fire the sync finished runnable if
|
||||
// we're running in sync mode.
|
||||
if (type == LISTENER_TYPE_ABORT) {
|
||||
OnStopRequest(nsnull, nsnull, NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
// Always bail out if we're canceled.
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
if (!HasListenersForType(type, aEvent)) {
|
||||
PRBool ignoreEvent = !HasListenersForType(typeString, aEvent);
|
||||
if (ignoreEvent && mSyncRequest) {
|
||||
// Only ignore the event if it isn't final.
|
||||
ignoreEvent = type != LISTENER_TYPE_ABORT &&
|
||||
type != LISTENER_TYPE_ERROR &&
|
||||
type != LISTENER_TYPE_LOAD;
|
||||
}
|
||||
|
||||
if (ignoreEvent) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1202,6 +713,11 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent)
|
||||
|
||||
{
|
||||
nsAutoLock lock(mWorkerXHR->Lock());
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
mLastProgressOrLoadEvent.swap(newEvent);
|
||||
|
||||
if (newEvent) {
|
||||
|
@ -45,17 +45,18 @@
|
||||
#include "nsIRequestObserver.h"
|
||||
|
||||
// Other includes
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerXHR.h"
|
||||
|
||||
class nsIJSXMLHttpRequest;
|
||||
class nsIThread;
|
||||
class nsIVariant;
|
||||
class nsIXMLHttpRequest;
|
||||
class nsIXMLHttpRequestUpload;
|
||||
class nsIXPConnectWrappedNative;
|
||||
class nsDOMWorkerXHR;
|
||||
class nsDOMWorkerXHREvent;
|
||||
class nsDOMWorkerXHRFinishSyncXHRRunnable;
|
||||
class nsDOMWorkerXHRWrappedListener;
|
||||
@ -72,11 +73,6 @@ class nsDOMWorkerXHRProxy : public nsIRunnable,
|
||||
friend class nsDOMWorkerXHR;
|
||||
friend class nsDOMWorkerXHRUpload;
|
||||
|
||||
typedef nsCOMPtr<nsIDOMEventListener> Listener;
|
||||
typedef nsTArray<Listener> ListenerArray;
|
||||
|
||||
typedef nsRefPtr<nsDOMWorkerXHRWrappedListener> WrappedListener;
|
||||
|
||||
typedef nsresult (NS_STDCALL nsIDOMEventTarget::*EventListenerFunction)
|
||||
(const nsAString&, nsIDOMEventListener*, PRBool);
|
||||
|
||||
@ -118,28 +114,15 @@ protected:
|
||||
void AddRemoveXHRListeners(PRBool aAdd);
|
||||
void FlipOwnership();
|
||||
|
||||
nsresult AddEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aOnXListener,
|
||||
PRBool aUploadListener);
|
||||
nsresult UploadEventListenerAdded();
|
||||
|
||||
nsresult RemoveEventListener(PRUint32 aType,
|
||||
nsIDOMEventListener* aListener,
|
||||
PRBool aUploadListener);
|
||||
|
||||
already_AddRefed<nsIDOMEventListener> GetOnXListener(PRUint32 aType,
|
||||
PRBool aUploadListener);
|
||||
|
||||
nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, PRBool aUploadEvent);
|
||||
|
||||
nsresult HandleWorkerEvent(nsIDOMEvent* aEvent, PRBool aUploadEvent);
|
||||
nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
|
||||
PRBool aUploadEvent);
|
||||
|
||||
nsresult HandleEventInternal(PRUint32 aType,
|
||||
nsIDOMEvent* aEvent,
|
||||
PRBool aUploadEvent);
|
||||
|
||||
void ClearEventListeners();
|
||||
|
||||
// Methods of nsIXMLHttpRequest that we implement
|
||||
nsresult GetAllResponseHeaders(char** _retval);
|
||||
nsresult GetResponseHeader(const nsACString& aHeader,
|
||||
@ -162,10 +145,12 @@ protected:
|
||||
|
||||
// aEvent is used to see if we should check upload listeners as well. If left
|
||||
// unset we always check upload listeners.
|
||||
PRBool HasListenersForType(PRUint32 aType, nsIDOMEvent* aEvent = nsnull);
|
||||
PRBool HasListenersForType(const nsAString& aType,
|
||||
nsIDOMEvent* aEvent = nsnull);
|
||||
|
||||
// May be weak or strong, check mOwnedByXHR.
|
||||
nsDOMWorkerXHR* mWorkerXHR;
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> mWorkerXHRWN;
|
||||
|
||||
// May be weak or strong, check mOwnedByXHR.
|
||||
nsIXMLHttpRequest* mXHR;
|
||||
@ -179,12 +164,6 @@ protected:
|
||||
nsRefPtr<nsDOMWorkerXHREvent> mLastXHREvent;
|
||||
nsRefPtr<nsDOMWorkerXHREvent> mLastProgressOrLoadEvent;
|
||||
|
||||
nsTArray<ListenerArray> mXHRListeners;
|
||||
nsTArray<WrappedListener> mXHROnXListeners;
|
||||
|
||||
nsTArray<ListenerArray> mUploadListeners;
|
||||
nsTArray<WrappedListener> mUploadOnXListeners;
|
||||
|
||||
SyncEventQueue* mSyncEventQueue;
|
||||
|
||||
PRInt32 mChannelID;
|
||||
|
@ -47,20 +47,32 @@ include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = \
|
||||
test_importScripts.html \
|
||||
importScripts_worker.js \
|
||||
importScripts_worker_imported1.js \
|
||||
importScripts_worker_imported2.js \
|
||||
importScripts_worker_imported3.js \
|
||||
importScripts_worker_imported4.js \
|
||||
test_importScripts.html \
|
||||
test_simpleThread.html \
|
||||
test_threadErrors.html \
|
||||
test_threadTimeouts.html \
|
||||
test_longThread.html \
|
||||
longThread_worker.js \
|
||||
test_recursion.html \
|
||||
recursion_worker.js \
|
||||
test_regExpStatics.html \
|
||||
regExpStatics_worker.js \
|
||||
test_simpleThread.html \
|
||||
simpleThread_worker.js \
|
||||
test_threadErrors.html \
|
||||
threadErrors_worker1.js \
|
||||
threadErrors_worker2.js \
|
||||
threadErrors_worker3.js \
|
||||
threadErrors_worker4.js \
|
||||
test_threadTimeouts.html \
|
||||
threadTimeouts_worker.js \
|
||||
test_xhr.html \
|
||||
xhr_worker.js \
|
||||
testXHR.txt \
|
||||
test_fibonacci.html \
|
||||
fibonacci_worker.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
@ -1,17 +1,17 @@
|
||||
function messageListener(message, source) {
|
||||
switch (message) {
|
||||
onmessage = function(event) {
|
||||
switch (event.data) {
|
||||
case 'start':
|
||||
loadScripts("importScripts_worker_imported2.js");
|
||||
importScripts("importScripts_worker_imported2.js");
|
||||
importedScriptFunction2();
|
||||
tryBadScripts();
|
||||
source.postMessage('started');
|
||||
postMessage('started');
|
||||
break;
|
||||
case 'stop':
|
||||
tryBadScripts();
|
||||
postMessageToPool('stopped');
|
||||
postMessage('stopped');
|
||||
break;
|
||||
default:
|
||||
throw new Error("Bad message: " + message);
|
||||
throw new Error("Bad message: " + event.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ function messageListener(message, source) {
|
||||
// This caused security exceptions in the past, make sure it doesn't!
|
||||
var constructor = {}.constructor;
|
||||
|
||||
loadScripts("importScripts_worker_imported1.js");
|
||||
importScripts("importScripts_worker_imported1.js");
|
||||
|
||||
// Try to call a function defined in the imported script.
|
||||
importedScriptFunction();
|
||||
@ -40,7 +40,7 @@ function tryBadScripts() {
|
||||
var caughtException = false;
|
||||
var url = badScripts[i];
|
||||
try {
|
||||
loadScripts(url);
|
||||
importScripts(url);
|
||||
}
|
||||
catch (e) {
|
||||
caughtException = true;
|
||||
|
@ -18,11 +18,12 @@ Tests of DOM Worker Threads (Bug 437152)
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
pool.messageListener = function(message, source) {
|
||||
switch (message) {
|
||||
var worker = new Worker("importScripts_worker.js");
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
switch (event.data) {
|
||||
case "started":
|
||||
source.postMessage("stop");
|
||||
worker.postMessage("stop");
|
||||
break;
|
||||
case "stopped":
|
||||
SimpleTest.finish();
|
||||
@ -33,15 +34,15 @@ Tests of DOM Worker Threads (Bug 437152)
|
||||
}
|
||||
};
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
ok(false, "Worker had an error:" + error);
|
||||
worker.onerror = function(event) {
|
||||
ok(false, "Worker had an error:" + event.data);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var worker = pool.createWorkerFromURL("importScripts_worker.js");
|
||||
worker.postMessage("start");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -18,63 +18,36 @@ Tests of DOM Worker Threads (Bug 437152)
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
//prefs.setIntPref("javascript.options.gczeal", 2);
|
||||
|
||||
var workerScript =
|
||||
"function messageListener(message, source) { " +
|
||||
" switch (message) { " +
|
||||
" case 'start': " +
|
||||
" /* do a ton of stuff! */ " +
|
||||
" for (var i = 0; i < 10000000; i++) { } " +
|
||||
" dump('done!\\n'); " +
|
||||
" /* pass message to source */ " +
|
||||
" source.postMessage('done'); " +
|
||||
" break; " +
|
||||
" default: " +
|
||||
" throw 'Bad message: ' + message; " +
|
||||
" } " +
|
||||
"} " +
|
||||
"";
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
ok(pool, "Couldn't get worker pool");
|
||||
|
||||
const numThreads = 10;
|
||||
const numThreads = 5;
|
||||
var doneThreads = 0;
|
||||
|
||||
pool.messageListener = function(message, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
switch (message) {
|
||||
function onmessage(event) {
|
||||
switch (event.data) {
|
||||
case "done":
|
||||
if (++doneThreads == numThreads) {
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unexpected message");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
function onerror(event) {
|
||||
ok(false, "Worker had an error");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < numThreads; i++) {
|
||||
var worker = pool.createWorker(workerScript);
|
||||
var worker = new Worker("longThread_worker.js");
|
||||
worker.onmessage = onmessage;
|
||||
worker.onerror = onerror;
|
||||
worker.postMessage("start");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -17,31 +17,16 @@ Tests of DOM Worker Threads
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function workerScript() {
|
||||
function recurse() {
|
||||
recurse();
|
||||
}
|
||||
var worker = new Worker("recursion_worker.js");
|
||||
|
||||
this.messageListener = function(message, source) {
|
||||
switch (message) {
|
||||
case "start":
|
||||
recurse();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
is(error.message,
|
||||
'[JavaScript Error: "too much recursion" {file: "DOMWorker inline ' +
|
||||
'script" line: 4}]');
|
||||
worker.onerror = function(event) {
|
||||
dump(event.data + "\n");
|
||||
is(event.data,
|
||||
'[JavaScript Error: "too much recursion" {file: "http://localhost:8888' +
|
||||
'/tests/dom/src/threads/tests/recursion_worker.js" line: 2}]');
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var worker = pool.createWorker("(" + workerScript + ")();");
|
||||
worker.postMessage("start");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -19,50 +19,22 @@ Tests of DOM Worker Threads RegExp statics
|
||||
|
||||
const WORKER_COUNT = 25;
|
||||
|
||||
function workerScript() {
|
||||
var runCount = 0;
|
||||
var timeout;
|
||||
|
||||
this.messageListener = function(message, source) {
|
||||
run();
|
||||
timeout = setTimeout(run, 0);
|
||||
timeout = setTimeout(run, 5000);
|
||||
};
|
||||
|
||||
function run() {
|
||||
if (RegExp.$1) {
|
||||
throw "RegExp.$1 already set!";
|
||||
cancelTimeout(timeout);
|
||||
}
|
||||
|
||||
var match = /a(sd)f/("asdf");
|
||||
if (!RegExp.$1) {
|
||||
throw "RegExp.$1 didn't get set!";
|
||||
cancelTimeout(timeout);
|
||||
}
|
||||
|
||||
if (++runCount == 3) {
|
||||
postMessageToPool("done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
|
||||
var doneWorkers = 0;
|
||||
pool.messageListener = function(message, source) {
|
||||
function onmessage(event) {
|
||||
if (++doneWorkers == WORKER_COUNT) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
ok(false, "Worker had an error: " + error);
|
||||
function onerror(event) {
|
||||
ok(false, "Worker had an error: " + event.data);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
for (var i = 0; i < WORKER_COUNT; i++) {
|
||||
var worker = pool.createWorker("(" + workerScript + ")();");
|
||||
var worker = new Worker("regExpStatics_worker.js");
|
||||
worker.onmessage = onmessage;
|
||||
worker.onerror = onerror;
|
||||
worker.postMessage("start");
|
||||
}
|
||||
|
||||
|
@ -18,78 +18,46 @@ Tests of DOM Worker Threads (Bug 437152)
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var worker = new Worker("simpleThread_worker.js");
|
||||
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
//prefs.setIntPref("javascript.options.gczeal", 2);
|
||||
|
||||
var workerScript =
|
||||
"function messageListener(message, source) { " +
|
||||
" switch (message) { " +
|
||||
" case 'no-op': " +
|
||||
" break; " +
|
||||
" case 'start': " +
|
||||
" /* do a ton of stuff! */ " +
|
||||
" for (var i = 0; i < 1000; i++) { } " +
|
||||
" /* pass message to source */ " +
|
||||
" source.postMessage('started'); " +
|
||||
" break; " +
|
||||
" case 'stop': " +
|
||||
" /* do some more stuff! */ " +
|
||||
" for (var i = 0; i < 1000; i++) { } " +
|
||||
" /* pass message to self */ " +
|
||||
" threadContext.thisThread.postMessage('no-op'); " +
|
||||
" /* pass message to pool */ " +
|
||||
" postMessageToPool('stopped'); " +
|
||||
" break; " +
|
||||
" default: " +
|
||||
" throw 'Bad message: ' + message; " +
|
||||
" } " +
|
||||
"} " +
|
||||
"";
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
ok(pool, "Couldn't get worker pool");
|
||||
|
||||
pool.messageListener = function(message, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
switch (message) {
|
||||
worker.addEventListener("message",function(event) {
|
||||
is(event.target, worker);
|
||||
switch (event.data) {
|
||||
case "no-op":
|
||||
break;
|
||||
case "started":
|
||||
// pass message to self
|
||||
pool.postMessage("no-op");
|
||||
// pass message to source
|
||||
source.postMessage("stop");
|
||||
is(gotError, true);
|
||||
worker.postMessage("no-op");
|
||||
worker.postMessage("stop");
|
||||
break;
|
||||
case "stopped":
|
||||
// pass message to worker
|
||||
worker.postMessage("no-op");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unexpected message");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
ok(false, "Unexpected message:" + event.data);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}, false);
|
||||
|
||||
var gotError = false;
|
||||
worker.onerror = function(event) {
|
||||
is(event.target, worker);
|
||||
is(event.data,
|
||||
'[JavaScript Error: "uncaught exception: Bad message: asdf"]');
|
||||
gotError = true;
|
||||
|
||||
worker.onerror = function(otherEvent) {
|
||||
ok(false, "Worker had an error:" + otherEvent.data);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
ok(false, "Worker had an error");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
var worker = pool.createWorker(workerScript);
|
||||
ok(worker, "Couldn't make worker");
|
||||
|
||||
pool.postMessage("no-op");
|
||||
worker.postMessage("asdf");
|
||||
worker.postMessage("start");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -18,79 +18,40 @@ Tests of DOM Worker Threads (Bug 437152)
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
const expectedErrorCount = 4;
|
||||
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
//prefs.setIntPref("javascript.options.gczeal", 2);
|
||||
|
||||
var badWorkerScripts = [
|
||||
// Syntax error
|
||||
"function messageListener(message, source) { " +
|
||||
" for (var i = 0; i < 10) { } " +
|
||||
"} " +
|
||||
"",
|
||||
|
||||
// Bad function error
|
||||
"function messageListener(message, source) { " +
|
||||
" foopy(); " +
|
||||
"} " +
|
||||
"",
|
||||
|
||||
// Unhandled exception in body
|
||||
"function messageListener(message, source) { " +
|
||||
"} " +
|
||||
"throw new Error('Bah!'); " +
|
||||
"",
|
||||
|
||||
// Throwing message listener
|
||||
"function messageListener(message, source) { " +
|
||||
" throw 'Bad message: ' + message; " +
|
||||
"} " +
|
||||
""
|
||||
];
|
||||
|
||||
var expectedErrorCount = badWorkerScripts.length;
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
|
||||
pool.messageListener = function(message, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
ok(false, "Unexpected message");
|
||||
function messageListener(event) {
|
||||
ok(false, "Unexpected message: " + event.data);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
var actualErrorCount = 0;
|
||||
var failedWorkers = [];
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
if (failedWorkers.indexOf(source) != -1) {
|
||||
dump("Already seen worker: " + source + "\n");
|
||||
function errorListener(event) {
|
||||
if (failedWorkers.indexOf(event.target) != -1) {
|
||||
ok(false, "Seen an extra error from this worker");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
failedWorkers.push(source);
|
||||
failedWorkers.push(event.target);
|
||||
actualErrorCount++;
|
||||
|
||||
if (actualErrorCount == expectedErrorCount) {
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < expectedErrorCount; i++) {
|
||||
var worker = pool.createWorker(badWorkerScripts[i]);
|
||||
for (var i = 1; i <= expectedErrorCount; i++) {
|
||||
var worker = new Worker("threadErrors_worker" + i + ".js");
|
||||
worker.onmessage = messageListener;
|
||||
worker.onerror = errorListener;
|
||||
worker.postMessage("Hi");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -18,86 +18,36 @@ Tests of DOM Worker Threads (Bug 437152)
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var worker = new Worker("threadTimeouts_worker.js");
|
||||
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
//prefs.setIntPref("javascript.options.gczeal", 2);
|
||||
|
||||
var workerScript =
|
||||
"var gTimeoutId; " +
|
||||
"var gTimeoutCount = 0; " +
|
||||
"var gIntervalCount = 0; " +
|
||||
"" +
|
||||
"function timeoutFunc() { " +
|
||||
" if (++gTimeoutCount > 1) { " +
|
||||
" throw new Error('Timeout called more than once!'); " +
|
||||
" } " +
|
||||
" postMessageToPool('timeoutFinished'); " +
|
||||
"} " +
|
||||
"" +
|
||||
"function intervalFunc() { " +
|
||||
" if (++gIntervalCount == 2) { " +
|
||||
" postMessageToPool('intervalFinished'); " +
|
||||
" } " +
|
||||
"} " +
|
||||
"" +
|
||||
"function messageListener(message, source) { " +
|
||||
" switch (message) { " +
|
||||
" case 'startTimeout': " +
|
||||
" gTimeoutId = setTimeout(timeoutFunc, 5000); " +
|
||||
" clearTimeout(gTimeoutId); " +
|
||||
" gTimeoutId = setTimeout(timeoutFunc, 5000); " +
|
||||
" break; " +
|
||||
" case 'startInterval': " +
|
||||
" gTimeoutId = setInterval(intervalFunc, 5000); " +
|
||||
" break; " +
|
||||
" case 'cancelInterval': " +
|
||||
" clearInterval(gTimeoutId); " +
|
||||
" postMessageToPool('intervalCanceled'); " +
|
||||
" break; " +
|
||||
" default: " +
|
||||
" throw 'Bad message: ' + message; " +
|
||||
" } " +
|
||||
"} " +
|
||||
"";
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
ok(pool, "Couldn't get worker pool");
|
||||
|
||||
pool.messageListener = function(message, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
switch (message) {
|
||||
worker.onmessage = function(event) {
|
||||
is(event.target, worker);
|
||||
switch (event.data) {
|
||||
case "timeoutFinished":
|
||||
source.postMessage("startInterval");
|
||||
event.target.postMessage("startInterval");
|
||||
break;
|
||||
case "intervalFinished":
|
||||
source.postMessage("cancelInterval");
|
||||
event.target.postMessage("cancelInterval");
|
||||
break;
|
||||
case "intervalCanceled":
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unexpected message");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
ok(false, "Worker had an error");
|
||||
prefs.setIntPref("javascript.options.gczeal", 0);
|
||||
worker.onerror = function(event) {
|
||||
is(event.target, worker);
|
||||
ok(false, "Worker had an error: " + event.data);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
var worker = pool.createWorker(workerScript);
|
||||
ok(worker, "Couldn't make worker");
|
||||
|
||||
worker.postMessage("startTimeout");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -18,66 +18,11 @@ Tests of DOM Worker Threads XHR(Bug 450452 )
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function workerScript() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var worker = new Worker("xhr_worker.js");
|
||||
|
||||
function onload(event) {
|
||||
if (event.target.status != 200) {
|
||||
var message = { type: "error",
|
||||
error: event.target.status };
|
||||
postMessageToPool(message.toSource());
|
||||
}
|
||||
|
||||
var message = { type: "load",
|
||||
data: xhr.responseText };
|
||||
postMessageToPool(message.toSource());
|
||||
}
|
||||
|
||||
xhr.onload = onload;
|
||||
xhr.addEventListener("load", onload, false);
|
||||
xhr.removeEventListener("load", onload, false);
|
||||
if (!xhr.onload) {
|
||||
var message = { type: "error",
|
||||
error: "Lost message listener!" };
|
||||
postMessageToPool(message.toSource());
|
||||
}
|
||||
|
||||
xhr.addEventListener("error", function(event) {
|
||||
var message = { type: "error",
|
||||
error: event.target.status };
|
||||
postMessageToPool(message.toSource());
|
||||
}, false);
|
||||
|
||||
function onprogress(event) {
|
||||
var message = { type: "progress",
|
||||
current: event.loaded,
|
||||
total: event.total };
|
||||
postMessageToPool(message.toSource());
|
||||
}
|
||||
xhr.addEventListener("progress", onprogress, false);
|
||||
|
||||
xhr.addEventListener("foopety", function(event) {}, false);
|
||||
xhr.removeEventListener("doopety", function(event) {}, false);
|
||||
|
||||
var upload = xhr.upload;
|
||||
upload.onprogress = function(event) { };
|
||||
upload.addEventListener("readystatechange", function(event) { }, false);
|
||||
upload.removeEventListener("readystatechange", function(event) { }, false);
|
||||
upload.addEventListener("load", function(event) { }, false);
|
||||
upload.removeEventListener("readystatechange", function(event) { }, false);
|
||||
|
||||
this.messageListener = function(message, source) {
|
||||
if (xhr.readystate > 0) {
|
||||
throw "XHR already running!";
|
||||
}
|
||||
xhr.open("GET", message);
|
||||
xhr.send(null);
|
||||
}
|
||||
}
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
pool.messageListener = function(message, source) {
|
||||
var args = eval(message);
|
||||
worker.onmessage = function(event) {
|
||||
is(event.target, worker);
|
||||
var args = eval(event.data);
|
||||
switch (args.type) {
|
||||
case "progress": {
|
||||
ok(parseInt(args.current) <= parseInt(args.total));
|
||||
@ -97,12 +42,12 @@ Tests of DOM Worker Threads XHR(Bug 450452 )
|
||||
}
|
||||
};
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
ok(false, "Worker had an error:" + error);
|
||||
worker.onerror = function(event) {
|
||||
is(event.target, worker);
|
||||
ok(false, "Worker had an error:" + event.data);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var worker = pool.createWorker("(" + workerScript + ")();");
|
||||
worker.postMessage("testXHR.txt");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -744,6 +744,9 @@ void DEBUG_CheckForComponentsInScope(XPCCallContext& ccx, JSObject* obj,
|
||||
if(OKIfNotInitialized)
|
||||
return;
|
||||
|
||||
if(!(JS_GetOptions(ccx) & JSOPTION_PRIVATE_IS_NSISUPPORTS))
|
||||
return;
|
||||
|
||||
const char* name = ccx.GetRuntime()->GetStringName(XPCJSRuntime::IDX_COMPONENTS);
|
||||
jsval prop;
|
||||
if(JS_LookupProperty(ccx, obj, name, &prop) && !JSVAL_IS_PRIMITIVE(prop))
|
||||
|
Loading…
Reference in New Issue
Block a user