Back out changeset b83d3c8ac166 (bug 460811) to try to fix bustage

This commit is contained in:
Robert O'Callahan 2008-11-05 12:47:52 +13:00
Родитель a161a6debb
Коммит 46de18dc67
33 изменённых файлов: 2018 добавлений и 1126 удалений

Просмотреть файл

@ -39,7 +39,9 @@
#include "domstubs.idl"
[scriptable, uuid(777bd8a1-38c1-4b12-ba8f-ff6c2eb8c56b)]
interface nsIDOMWorkerPool;
[scriptable, uuid(c206f746-52e2-47dd-8ccc-ce76ccda6c6d)]
interface nsIDOMNavigator : nsISupports
{
readonly attribute DOMString appCodeName;
@ -63,6 +65,8 @@ 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 = nsIDOMWorkers.idl
XPIDLSRCS = nsIDOMThreads.idl
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -458,8 +458,6 @@ enum nsDOMClassInfoID {
eDOMClassInfo_MathMLElement_id,
#endif
eDOMClassInfo_Worker_id,
// This one better be the last one in this list
eDOMClassInfoIDCount
};

Просмотреть файл

@ -457,9 +457,6 @@
#include "nsIDOMGeoPosition.h"
#include "nsIDOMGeoPositionError.h"
// Workers
#include "nsDOMWorker.h"
#include "nsDOMFile.h"
#include "nsIDOMFileException.h"
@ -1302,9 +1299,6 @@ 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();|
@ -1327,20 +1321,6 @@ 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;
@ -3572,12 +3552,6 @@ 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);
@ -4992,17 +4966,6 @@ 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,
@ -5013,16 +4976,7 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
const char *contractid =
FindConstructorContractID(name_struct->mDOMClassInfoID);
if (contractid) {
native = do_CreateInstance(contractid, &rv);
}
else {
nsDOMConstructorFunc func =
FindConstructorFunc(name_struct->mDOMClassInfoID);
if (func) {
rv = func(getter_AddRefs(native));
}
}
native = do_CreateInstance(contractid, &rv);
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
native = do_CreateInstance(name_struct->mCID, &rv);
} else if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
@ -5240,8 +5194,7 @@ private:
{
return
(aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
(FindConstructorContractID(aNameStruct->mDOMClassInfoID) ||
FindConstructorFunc(aNameStruct->mDOMClassInfoID))) ||
FindConstructorContractID(aNameStruct->mDOMClassInfoID)) ||
(aNameStruct->mType == nsGlobalNameStruct::eTypeExternalClassInfo &&
aNameStruct->mData->mConstructorCID) ||
aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructor ||

Просмотреть файл

@ -61,7 +61,6 @@ struct nsDOMClassInfoData;
typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc)
(nsDOMClassInfoData* aData);
typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject);
struct nsDOMClassInfoData
{

Просмотреть файл

@ -65,7 +65,6 @@
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
#endif
#include "nsThreadUtils.h"
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@ -396,10 +395,6 @@ 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,6 +113,7 @@
#include "nsIDOMPkcs11.h"
#include "nsIDOMOfflineResourceList.h"
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMThreads.h"
#include "nsDOMString.h"
#include "nsIEmbeddingSiteWindow2.h"
#include "nsThreadUtils.h"
@ -225,10 +226,6 @@ 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
@ -658,21 +655,9 @@ 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.
gRefCnt++;
#if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
if (gRefCnt == 0) {
static const char* prefName = "browser.dom.window.dump.enabled";
nsContentUtils::AddBoolPrefVarCache(prefName, &gDOMWindowDumpEnabled);
gDOMWindowDumpEnabled = nsContentUtils::GetBoolPref(prefName);
}
#endif
if (!gEntropyCollector) {
if (gRefCnt++ == 0 || !gEntropyCollector) {
CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
}
#ifdef DEBUG
printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt,
static_cast<void*>(static_cast<nsIScriptGlobalObject*>(this)),
@ -1682,11 +1667,6 @@ 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
@ -3848,25 +3828,24 @@ 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 (!DOMWindowDumpEnabled()) {
return NS_OK;
#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;
}
}
#endif
char *cstr = ToNewUTF8String(aStr);
@ -9464,3 +9443,18 @@ 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,7 +434,6 @@ public:
CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
nsScriptObjectHolder& aHandler);
static PRBool DOMWindowDumpEnabled();
protected:
// Object Management

Просмотреть файл

@ -2373,11 +2373,6 @@ 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,13 +51,11 @@ FORCE_STATIC_LIB = 1
REQUIRES = \
caps \
content \
docshell \
gfx \
js \
layout \
locale \
necko \
plugin \
pref \
string \
thebes \
@ -68,12 +66,11 @@ REQUIRES = \
CPPSRCS = \
nsDOMThreadService.cpp \
nsDOMWorker.cpp \
nsDOMWorkerEvents.cpp \
nsDOMWorkerMessageHandler.cpp \
nsDOMWorkerBase.cpp \
nsDOMWorkerPool.cpp \
nsDOMWorkerScriptLoader.cpp \
nsDOMWorkerSecurityManager.cpp \
nsDOMWorkerThread.cpp \
nsDOMWorkerTimeout.cpp \
nsDOMWorkerXHR.cpp \
nsDOMWorkerXHRProxy.cpp \
@ -90,3 +87,5 @@ DIRS += test
endif
include $(topsrcdir)/config/rules.mk
#CXXFLAGS += $(WARNINGS_AS_ERRORS)

Просмотреть файл

@ -41,6 +41,7 @@
// Interfaces
#include "nsIComponentManager.h"
#include "nsIConsoleService.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIEventTarget.h"
@ -54,7 +55,6 @@
#include "nsISupportsPriority.h"
#include "nsIThreadPool.h"
#include "nsIXPConnect.h"
#include "nsPIDOMWindow.h"
// Other includes
#include "nsAutoLock.h"
@ -71,10 +71,6 @@
#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"
@ -109,6 +105,14 @@ 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;
@ -151,31 +155,103 @@ private:
};
/**
* This class is used as to post an error to the worker's outer handler.
* This class is used as to post an error to the main thread. It logs the error
* to the console and calls the pool's onError callback.
*/
class nsReportErrorRunnable : public nsIRunnable
class nsReportErrorRunnable : public nsRunnable
{
public:
NS_DECL_ISUPPORTS
nsReportErrorRunnable(nsDOMWorker* aWorker, nsIWorkerMessageEvent* aEvent)
: mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mEvent(aEvent) { }
nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker)
: mError(aError), mWorker(aWorker) { }
NS_IMETHOD Run() {
if (mWorker->IsCanceled()) {
return NS_OK;
nsresult rv;
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
consoleService->LogMessage(mError);
}
return mWorker->DispatchEvent(mEvent, nsnull);
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:
nsRefPtr<nsDOMWorker> mWorker;
nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
nsCOMPtr<nsIWorkerMessageEvent> mEvent;
// 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;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
/**
* Need this to expose an nsIScriptError to content JS (implement nsIClassInfo
* with DOM_OBJECT flag set.
*/
class nsDOMWorkerScriptError : public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICLASSINFO
nsDOMWorkerScriptError(nsIScriptError* aError)
: mScriptError(this, aError) { }
protected:
// 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->)
InnerScriptError(nsDOMWorkerScriptError* aParent, nsIScriptError* aError)
: mParent(aParent), mError(aError) { }
protected:
nsDOMWorkerScriptError* mParent;
nsCOMPtr<nsIScriptError> mError;
};
InnerScriptError mScriptError;
};
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerScriptError)
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerScriptError)
// More hoops to jump through for the identical IDL methods
NS_INTERFACE_MAP_BEGIN(nsDOMWorkerScriptError)
if (aIID.Equals(NS_GET_IID(nsIScriptError)) ||
aIID.Equals(NS_GET_IID(nsIConsoleMessage))) {
foundInterface = static_cast<nsIConsoleMessage*>(&mScriptError);
}
else
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerScriptError, nsIScriptError,
nsIConsoleMessage)
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerScriptError)
/**
* Used to post an expired timeout to the correct worker.
@ -207,7 +283,7 @@ class nsDOMWorkerRunnable : public nsRunnable
friend class nsDOMThreadService;
public:
nsDOMWorkerRunnable(nsDOMWorker* aWorker)
nsDOMWorkerRunnable(nsDOMWorkerThread* aWorker)
: mWorker(aWorker) { }
virtual ~nsDOMWorkerRunnable() {
@ -215,6 +291,16 @@ 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) {
@ -228,9 +314,6 @@ 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!");
@ -238,8 +321,6 @@ 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
@ -254,7 +335,6 @@ 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);
@ -302,7 +382,7 @@ protected:
}
// Set at construction
nsRefPtr<nsDOMWorker> mWorker;
nsRefPtr<nsDOMWorkerThread> mWorker;
// Protected by mMonitor
nsDeque mRunnables;
@ -315,7 +395,7 @@ protected:
JSBool
DOMWorkerOperationCallback(JSContext* aCx)
{
nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
// Want a strong ref here to make sure that the monitor we wait on won't go
// away.
@ -402,7 +482,7 @@ DOMWorkerErrorReporter(JSContext* aCx,
{
NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
if (worker->IsCanceled()) {
// We don't want to report errors from canceled workers. It's very likely
@ -431,30 +511,21 @@ DOMWorkerErrorReporter(JSContext* aCx,
column, aReport->flags, "DOM Worker javascript");
NS_ENSURE_SUCCESS(rv,);
nsCString finalMessage;
rv = errorObject->ToString(finalMessage);
NS_ENSURE_SUCCESS(rv,);
nsRefPtr<nsDOMWorkerScriptError> domError =
new nsDOMWorkerScriptError(errorObject);
NS_ENSURE_TRUE(domError,);
nsRefPtr<nsDOMWorkerMessageEvent> event(new nsDOMWorkerMessageEvent());
NS_ENSURE_TRUE(event,);
nsCOMPtr<nsIScriptError> scriptError(do_QueryInterface(domError));
NS_ENSURE_TRUE(scriptError,);
rv = event->InitMessageEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE,
NS_ConvertUTF8toUTF16(finalMessage),
EmptyString(), nsnull);
NS_ENSURE_SUCCESS(rv,);
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
NS_ENSURE_TRUE(mainThread,);
event->SetTarget(worker);
nsCOMPtr<nsIRunnable> runnable(new nsReportErrorRunnable(worker, event));
nsCOMPtr<nsIRunnable> runnable =
new nsReportErrorRunnable(scriptError, worker);
NS_ENSURE_TRUE(runnable,);
nsRefPtr<nsDOMWorker> parent = worker->GetParent();
// If this worker has a parent then we need to send the message through the
// thread service to be run on the parent's thread. Otherwise it is a
// top-level worker and we send the message to the main thread.
rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable)
: NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv,);
}
@ -487,9 +558,10 @@ nsDOMThreadService::~nsDOMThreadService()
}
}
NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget,
nsIObserver,
nsIThreadPoolListener)
nsIThreadPoolListener,
nsIDOMThreadService)
nsresult
nsDOMThreadService::Init()
@ -525,9 +597,6 @@ 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);
@ -553,7 +622,7 @@ nsDOMThreadService::Init()
}
/* static */
already_AddRefed<nsDOMThreadService>
already_AddRefed<nsIDOMThreadService>
nsDOMThreadService::GetOrInitService()
{
if (!gDOMThreadService) {
@ -566,7 +635,7 @@ nsDOMThreadService::GetOrInitService()
service.swap(gDOMThreadService);
}
nsRefPtr<nsDOMThreadService> service(gDOMThreadService);
nsCOMPtr<nsIDOMThreadService> service(gDOMThreadService);
return service.forget();
}
@ -577,24 +646,6 @@ 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()
@ -636,15 +687,10 @@ 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(nsDOMWorker* aWorker,
nsDOMThreadService::Dispatch(nsDOMWorkerThread* aWorker,
nsIRunnable* aRunnable)
{
NS_ASSERTION(aWorker, "Null pointer!");
@ -709,7 +755,7 @@ nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
// No need to be in the monitor here because we should already be in it.
#ifdef DEBUG
nsRefPtr<nsDOMWorker>& debugWorker = aRunnable->mWorker;
nsRefPtr<nsDOMWorkerThread>& debugWorker = aRunnable->mWorker;
nsRefPtr<nsDOMWorkerRunnable> runnable;
NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) &&
@ -720,6 +766,19 @@ 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()
@ -751,64 +810,42 @@ nsDOMThreadService::CreateJSContext()
return cx.forget();
}
already_AddRefed<nsDOMWorkerPool>
nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
PRBool aRemove)
{
NS_ASSERTION(aGlobalObject, "Null pointer!");
nsAutoMonitor mon(mMonitor);
nsRefPtr<nsDOMWorkerPool> pool;
mPools.Get(aGlobalObject, getter_AddRefs(pool));
if (aRemove) {
mPools.Remove(aGlobalObject);
}
return pool.forget();
}
#define LOOP_OVER_POOLS(_func, _args) \
PR_BEGIN_MACRO \
PRUint32 poolCount = mPools.Length(); \
for (PRUint32 i = 0; i < poolCount; i++) { \
mPools[i]-> _func _args ; \
} \
PR_END_MACRO
void
nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(aGlobalObject, "Null pointer!");
nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
if (pool) {
pool->Cancel();
}
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(aGlobalObject, "Null pointer!");
nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
if (pool) {
pool->Suspend();
}
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(aGlobalObject, "Null pointer!");
nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
if (pool) {
pool->Resume();
}
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool)
{
NS_ASSERTION(aPool, "Null pointer!");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsAutoMonitor mon(mMonitor);
mPools.Remove(aPool->ScriptGlobalObject());
NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!");
mPools.RemoveElement(aPool);
}
void
@ -826,8 +863,6 @@ nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
{
NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
nsAutoMonitor mon(mMonitor);
PRUint32 currentThreadCount;
nsresult rv = mThreadPool->GetThreadLimit(&currentThreadCount);
NS_ENSURE_SUCCESS(rv, rv);
@ -877,6 +912,7 @@ 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;
@ -975,69 +1011,31 @@ nsDOMThreadService::OnThreadShuttingDown()
return NS_OK;
}
nsresult
nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
nsIScriptGlobalObject* aGlobalObject)
/**
* See nsIDOMThreadService
*/
NS_IMETHODIMP
nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
{
NS_ASSERTION(aWorker, "Null pointer!");
NS_ASSERTION(aGlobalObject, "Null pointer!");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
domWindow->GetCurrentInnerWindow() :
domWindow.get();
NS_ENSURE_STATE(innerWindow);
nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller();
NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
nsCOMPtr<nsIDocument> callingDocument(do_QueryInterface(domDocument));
NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE);
aGlobalObject = newGlobal;
}
nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool(callingDocument));
NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
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);
nsresult rv = pool->Init();
NS_ENSURE_SUCCESS(rv, rv);
aWorker->SetPool(pool);
NS_ASSERTION(!mPools.Contains(pool), "Um?!");
mPools.AppendElement(pool);
NS_ADDREF(*_retval = pool);
return NS_OK;
}

Просмотреть файл

@ -44,6 +44,7 @@
#include "nsIEventTarget.h"
#include "nsIObserver.h"
#include "nsIThreadPool.h"
#include "nsIDOMThreads.h"
// Other includes
#include "jsapi.h"
@ -58,9 +59,9 @@
extern PRLogModuleInfo* gDOMThreadsLog;
#endif
class nsDOMWorker;
class nsDOMWorkerPool;
class nsDOMWorkerRunnable;
class nsDOMWorkerThread;
class nsDOMWorkerTimeout;
class nsIJSRuntimeService;
class nsIScriptGlobalObject;
@ -70,9 +71,9 @@ class nsIXPCSecurityManager;
class nsDOMThreadService : public nsIEventTarget,
public nsIObserver,
public nsIThreadPoolListener
public nsIThreadPoolListener,
public nsIDOMThreadService
{
friend class nsDOMWorker;
friend class nsDOMWorkerPool;
friend class nsDOMWorkerRunnable;
friend class nsDOMWorkerThread;
@ -81,25 +82,20 @@ 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<nsDOMThreadService> GetOrInitService();
static already_AddRefed<nsIDOMThreadService> GetOrInitService();
// Simple getter for this service. This does not create the service if it
// hasn't been created already, and it never AddRef's!
static nsDOMThreadService* get();
static JSContext* GetCurrentContext();
// Easy access to the services we care about.
static nsIJSRuntimeService* JSRuntimeService();
static nsIThreadJSContextStack* ThreadJSContextStack();
@ -120,29 +116,24 @@ private:
static void Shutdown();
nsresult Dispatch(nsDOMWorker* aWorker,
nsresult Dispatch(nsDOMWorkerThread* aWorker,
nsIRunnable* aRunnable);
void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
void WaitForCanceledWorker(nsDOMWorkerThread* aWorker);
static JSContext* CreateJSContext();
already_AddRefed<nsDOMWorkerPool>
GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
PRBool aRemove);
void NoteEmptyPool(nsDOMWorkerPool* aPool);
void NoteDyingPool(nsDOMWorkerPool* aPool);
void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
nsresult RegisterWorker(nsDOMWorker* aWorker,
nsIScriptGlobalObject* aGlobalObject);
// Our internal thread pool.
nsCOMPtr<nsIThreadPool> mThreadPool;
// Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
nsRefPtrHashtable<nsISupportsHashKey, nsDOMWorkerPool> mPools;
// Weak references, only ever touched on the main thread!
nsTPtrArray<nsDOMWorkerPool> mPools;
// mMonitor protects all access to mWorkersInProgress and
// mCreationsInProgress.

Просмотреть файл

@ -58,37 +58,69 @@
// DOMWorker includes
#include "nsDOMThreadService.h"
#include "nsDOMWorker.h"
#include "nsDOMWorkerThread.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
nsDOMWorkerPool::nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
nsIDocument* aDocument)
: mParentGlobal(aGlobalObject),
mParentDocument(aDocument),
mMonitor(nsnull),
mCanceled(PR_FALSE),
mSuspended(PR_FALSE)
#define LOOP_OVER_WORKERS(_func, _args) \
PR_BEGIN_MACRO \
PRUint32 workerCount = mWorkers.Length(); \
for (PRUint32 i = 0; i < workerCount; i++) { \
mWorkers[i]-> _func _args ; \
} \
PR_END_MACRO
nsDOMWorkerPool::nsDOMWorkerPool(nsIDocument* aDocument)
: mParentGlobal(nsnull),
mParentDocument(aDocument)
{
NS_ASSERTION(aGlobalObject, "Must have a global object!");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aDocument, "Must have a document!");
}
nsDOMWorkerPool::~nsDOMWorkerPool()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_WORKERS(Cancel, ());
nsDOMThreadService::get()->NoteDyingPool(this);
if (mMonitor) {
nsAutoMonitor::DestroyMonitor(mMonitor);
}
}
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPool)
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPool)
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool,
nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool)
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool)
nsresult
nsDOMWorkerPool::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsIScriptGlobalObject* globalObject =
mParentDocument->GetScriptGlobalObject();
NS_ENSURE_STATE(globalObject);
nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(globalObject));
NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
domWindow->GetCurrentInnerWindow() :
domWindow.get();
NS_ENSURE_STATE(innerWindow);
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(innerWindow));
NS_ENSURE_TRUE(globalSupports, NS_ERROR_NO_INTERFACE);
// We don't want a strong ref, this guy owns us.
mParentGlobal = globalSupports.get();
mMonitor = nsAutoMonitor::NewMonitor("nsDOMWorkerPool::mMonitor");
NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
@ -96,147 +128,208 @@ nsDOMWorkerPool::Init()
}
nsresult
nsDOMWorkerPool::NoteWorker(nsDOMWorker* aWorker)
nsDOMWorkerPool::HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSource)
{
NS_ASSERTION(aWorker, "Null pointer!");
PRBool suspendWorker;
{
nsAutoMonitor mon(mMonitor);
if (mCanceled) {
return NS_ERROR_ABORT;
}
nsDOMWorker** newWorker = mWorkers.AppendElement(aWorker);
NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
suspendWorker = mSuspended;
nsCOMPtr<nsIDOMWorkerMessageListener> messageListener =
nsDOMWorkerBase::GetMessageListener();
if (!messageListener) {
LOG(("Message received on a worker with no listener!"));
return NS_OK;
}
if (suspendWorker) {
aWorker->Suspend();
}
nsCOMPtr<nsISupports> source;
aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
NS_ASSERTION(source, "Impossible!");
messageListener->OnMessage(aMessage, source);
return NS_OK;
}
nsresult
nsDOMWorkerPool::DispatchMessage(nsIRunnable* aRunnable)
{
// Can be called from many different threads!
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
nsresult rv = mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
nsDOMWorkerPool::NoteDyingWorker(nsDOMWorker* aWorker)
{
NS_ASSERTION(aWorker, "Null pointer!");
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::GetWorkers(nsTArray<nsDOMWorker*>& aArray)
{
aArray.Clear();
nsAutoMonitor mon(mMonitor);
#ifdef DEBUG
nsDOMWorker** newWorkers =
#endif
aArray.AppendElements(mWorkers);
NS_WARN_IF_FALSE(newWorkers, "Out of memory!");
}
void
nsDOMWorkerPool::Cancel()
nsDOMWorkerPool::HandleError(nsIScriptError* aError,
nsDOMWorkerThread* aSource)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mCanceled, "Canceled more than once!");
{
nsAutoMonitor mon(mMonitor);
if (mErrorListener) {
mErrorListener->OnError(aError,
NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource));
}
}
mCanceled = PR_TRUE;
void
nsDOMWorkerPool::NoteDyingWorker(nsDOMWorkerThread* aWorker)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsAutoTArray<nsDOMWorker*, 10> workers;
GetWorkers(workers);
NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
mWorkers.RemoveElement(aWorker);
}
PRUint32 count = workers.Length();
if (count) {
for (PRUint32 index = 0; index < count; index++) {
workers[index]->Cancel();
}
void
nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
NS_ASSERTION(globalSupports, "Null pointer?!");
if (globalSupports == mParentGlobal) {
LOOP_OVER_WORKERS(Cancel, ());
mWorkers.Clear();
if (IsSuspended()) {
nsAutoMonitor mon(mMonitor);
mon.NotifyAll();
}
}
mParentGlobal = nsnull;
mParentDocument = nsnull;
}
void
nsDOMWorkerPool::Suspend()
nsDOMWorkerPool::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsAutoTArray<nsDOMWorker*, 10> workers;
{
nsAutoMonitor mon(mMonitor);
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
NS_ASSERTION(globalSupports, "Null pointer?!");
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();
if (globalSupports == mParentGlobal) {
LOOP_OVER_WORKERS(Suspend, ());
Suspend();
}
}
void
nsDOMWorkerPool::Resume()
nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsAutoTArray<nsDOMWorker*, 10> workers;
{
nsAutoMonitor mon(mMonitor);
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
NS_ASSERTION(globalSupports, "Null pointer?!");
NS_ASSERTION(mSuspended, "Not suspended!");
mSuspended = PR_FALSE;
if (globalSupports == mParentGlobal) {
LOOP_OVER_WORKERS(Resume, ());
Resume();
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 mParentGlobal->GetContext();
return mParentDocument->GetScriptGlobalObject()->GetContext();
}
NS_IMETHODIMP
nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
{
nsresult rv = PostMessageInternal(aMessage);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::SetMessageListener(nsIDOMWorkerMessageListener* aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsDOMWorkerBase::SetMessageListener(aListener);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::GetMessageListener(nsIDOMWorkerMessageListener** aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMWorkerMessageListener> listener = nsDOMWorkerBase::GetMessageListener();
listener.forget(aListener);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::SetErrorListener(nsIDOMWorkerErrorListener* aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mErrorListener = aListener;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::GetErrorListener(nsIDOMWorkerErrorListener** aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_IF_ADDREF(*aListener = mErrorListener);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::CreateWorker(const nsAString& aFullScript,
nsIDOMWorkerThread** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG(!aFullScript.IsEmpty());
NS_ENSURE_ARG_POINTER(_retval);
nsRefPtr<nsDOMWorkerThread> worker =
new nsDOMWorkerThread(this, aFullScript, PR_FALSE);
NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = worker->Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
mWorkers.AppendElement(worker);
NS_ADDREF(*_retval = worker);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::CreateWorkerFromURL(const nsAString& aScriptURL,
nsIDOMWorkerThread** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG(!aScriptURL.IsEmpty());
NS_ENSURE_ARG_POINTER(_retval);
nsRefPtr<nsDOMWorkerThread> worker =
new nsDOMWorkerThread(this, aScriptURL, PR_TRUE);
NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = worker->Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
mWorkers.AppendElement(worker);
NS_ADDREF(*_retval = worker);
return NS_OK;
}

Просмотреть файл

@ -40,73 +40,94 @@
#ifndef __NSDOMWORKERPOOL_H__
#define __NSDOMWORKERPOOL_H__
// Bases
#include "nsDOMWorkerBase.h"
#include "nsIClassInfo.h"
#include "nsIDOMThreads.h"
// Other includes
#include "jsapi.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "nsTPtrArray.h"
#include "prmon.h"
class nsDOMWorker;
class nsDOMWorkerThread;
class nsIDocument;
class nsIScriptContext;
class nsIScriptError;
class nsIScriptGlobalObject;
class nsDOMWorkerPool
/**
* The pool is almost always touched only on the main thread.
*/
class nsDOMWorkerPool : public nsDOMWorkerBase,
public nsIDOMWorkerPool,
public nsIClassInfo
{
friend class nsDOMThreadService;
friend class nsDOMWorkerFunctions;
friend class nsDOMWorkerPoolWeakRef;
friend class nsDOMWorkerScriptLoader;
friend class nsDOMWorkerStreamObserver;
friend class nsDOMWorkerThread;
friend class nsReportErrorRunnable;
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
public:
nsDOMWorkerPool(nsIScriptGlobalObject* aGlobalObject,
nsIDocument* aDocument);
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMWORKERPOOL
NS_DECL_NSICLASSINFO
NS_IMETHOD_(nsrefcnt) AddRef();
NS_IMETHOD_(nsrefcnt) Release();
nsDOMWorkerPool(nsIDocument* aDocument);
// For nsDOMWorkerBase
virtual nsDOMWorkerPool* Pool() {
return this;
}
nsIDocument* ParentDocument();
nsIScriptContext* ScriptContext();
nsIScriptGlobalObject* ScriptGlobalObject() {
return mParentGlobal;
}
nsIDocument* ParentDocument() {
return mParentDocument;
}
private:
virtual ~nsDOMWorkerPool();
nsresult Init();
void Cancel();
void Suspend();
void Resume();
// For nsDOMWorkerBase
virtual nsresult HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSourceThread);
nsresult NoteWorker(nsDOMWorker* aWorker);
void NoteDyingWorker(nsDOMWorker* aWorker);
// 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);
PRMonitor* Monitor() {
return mMonitor;
}
private:
virtual ~nsDOMWorkerPool();
// Weak reference to the window that created and owns this pool.
nsISupports* mParentGlobal;
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 reference to the document that created this pool.
nsIDocument* mParentDocument;
// Weak array of workers. The idea is that workers can be garbage collected
// independently of the owning pool and other workers.
nsTArray<nsDOMWorker*> mWorkers;
nsTPtrArray<nsDOMWorkerThread> mWorkers;
// An error handler function, may be null.
nsCOMPtr<nsIDOMWorkerErrorListener> mErrorListener;
// Monitor for suspending and resuming workers.
PRMonitor* mMonitor;
PRPackedBool mCanceled;
PRPackedBool mSuspended;
};
#endif /* __NSDOMWORKERPOOL_H__ */

Просмотреть файл

@ -65,31 +65,78 @@
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader(nsDOMWorker* aWorker)
: nsDOMWorkerFeature(aWorker),
nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader()
: mWorker(nsnull),
mTarget(nsnull),
mCx(NULL),
mScriptCount(0),
mCanceled(PR_FALSE)
mCanceled(PR_FALSE),
mTrackedByWorker(PR_FALSE)
{
// Created on worker thread.
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aWorker, "Null worker!");
}
NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerScriptLoader, nsDOMWorkerFeature,
nsIRunnable,
nsDOMWorkerScriptLoader::~nsDOMWorkerScriptLoader()
{
// Can't touch mWorker's lock
if (!mCanceled) {
// Destroyed on worker thread, unless canceled (and then who knows!).
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mTrackedByWorker) {
jsrefcount suspendDepth = 0;
if (mCx) {
suspendDepth = JS_SuspendRequest(mCx);
}
nsAutoLock lock(mWorker->Lock());
#ifdef DEBUG
PRBool removed =
#endif
mWorker->mScriptLoaders.RemoveElement(this);
NS_ASSERTION(removed, "Something is wrong here!");
if (mCx) {
JS_ResumeRequest(mCx, suspendDepth);
}
}
}
}
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerScriptLoader, nsRunnable,
nsIStreamLoaderObserver)
nsresult
nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
nsDOMWorkerScriptLoader::LoadScripts(nsDOMWorkerThread* aWorker,
JSContext* aCx,
const nsTArray<nsString>& aURLs)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aWorker, "Null worker!");
NS_ASSERTION(aCx, "Null context!");
NS_ASSERTION(!mWorker, "Not designed to be used more than once!");
mWorker = aWorker;
mCx = aCx;
mTarget = NS_GetCurrentThread();
NS_ASSERTION(mTarget, "This should never be null!");
{
JSAutoSuspendRequest asr(aCx);
nsAutoLock lock(mWorker->Lock());
if (mWorker->IsCanceled()) {
return NS_ERROR_ABORT;
}
mTrackedByWorker = nsnull != mWorker->mScriptLoaders.AppendElement(this);
NS_ASSERTION(mTrackedByWorker, "Failed to add loader to worker's array!");
}
if (mCanceled) {
return NS_ERROR_ABORT;
}
@ -126,18 +173,30 @@ nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
// network or compiling.
AutoSuspendWorkerEvents aswe(this);
nsresult rv = DoRunLoop(aCx);
nsresult rv = DoRunLoop();
{
JSAutoSuspendRequest asr(aCx);
nsAutoLock lock(mWorker->Lock());
#ifdef DEBUG
PRBool removed =
#endif
mWorker->mScriptLoaders.RemoveElement(this);
NS_ASSERTION(removed, "Something is wrong here!");
mTrackedByWorker = PR_FALSE;
}
if (NS_FAILED(rv)) {
return rv;
}
// Verify that all scripts downloaded and compiled.
rv = VerifyScripts(aCx);
rv = VerifyScripts();
if (NS_FAILED(rv)) {
return rv;
}
rv = ExecuteScripts(aCx);
rv = ExecuteScripts();
if (NS_FAILED(rv)) {
return rv;
}
@ -146,17 +205,18 @@ nsDOMWorkerScriptLoader::LoadScripts(JSContext* aCx,
}
nsresult
nsDOMWorkerScriptLoader::LoadScript(JSContext* aCx,
nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker,
JSContext* aCx,
const nsString& aURL)
{
nsAutoTArray<nsString, 1> url;
url.AppendElement(aURL);
return LoadScripts(aCx, url);
return LoadScripts(aWorker, aCx, url);
}
nsresult
nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
nsDOMWorkerScriptLoader::DoRunLoop()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
@ -174,7 +234,7 @@ nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
PRBool changed = NS_SUCCEEDED(threadService->ChangeThreadPoolMaxThreads(1));
while (!(done || mCanceled)) {
JSAutoSuspendRequest asr(aCx);
JSAutoSuspendRequest asr(mCx);
NS_ProcessNextEvent(mTarget);
}
@ -187,10 +247,8 @@ nsDOMWorkerScriptLoader::DoRunLoop(JSContext* aCx)
}
nsresult
nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
nsDOMWorkerScriptLoader::VerifyScripts()
{
NS_ASSERTION(aCx, "Shouldn't be null!");
nsresult rv = NS_OK;
for (PRUint32 index = 0; index < mScriptCount; index++) {
@ -216,24 +274,10 @@ nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
// Ok, this is the script that caused us to fail.
JSAutoRequest ar(aCx);
// Only throw an error if there is no other pending exception.
if (!JS_IsExceptionPending(aCx)) {
const char* message;
switch (loadInfo.result) {
case NS_ERROR_MALFORMED_URI:
message = "Malformed script URI: %s";
break;
case NS_ERROR_FILE_NOT_FOUND:
message = "Script file not found: %s";
break;
default:
message = "Failed to load script: %s (nsresult = 0x%x)";
break;
}
// Only throw an error there is no other pending exception.
if (!JS_IsExceptionPending(mCx)) {
NS_ConvertUTF16toUTF8 url(loadInfo.url);
JS_ReportError(aCx, message, url.get(), loadInfo.result);
JS_ReportError(mCx, "Failed to compile script: %s", url.get());
}
break;
}
@ -242,34 +286,30 @@ nsDOMWorkerScriptLoader::VerifyScripts(JSContext* aCx)
}
nsresult
nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
nsDOMWorkerScriptLoader::ExecuteScripts()
{
NS_ASSERTION(aCx, "Shouldn't be null!");
// Now execute all the scripts.
for (PRUint32 index = 0; index < mScriptCount; index++) {
ScriptLoadInfo& loadInfo = mLoadInfos[index];
JSAutoRequest ar(aCx);
JSScript* script =
static_cast<JSScript*>(JS_GetPrivate(aCx, loadInfo.scriptObj));
static_cast<JSScript*>(JS_GetPrivate(mCx, loadInfo.scriptObj));
NS_ASSERTION(script, "This shouldn't ever be null!");
JSObject* global = mWorker->mGlobal ?
mWorker->mGlobal :
JS_GetGlobalObject(aCx);
JS_GetGlobalObject(mCx);
NS_ENSURE_STATE(global);
// Because we may have nested calls to this function we don't want the
// execution to automatically report errors. We let them propagate instead.
uint32 oldOpts =
JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
jsval val;
PRBool success = JS_ExecuteScript(aCx, global, script, &val);
PRBool success = JS_ExecuteScript(mCx, global, script, &val);
JS_SetOptions(aCx, oldOpts);
JS_SetOptions(mCx, oldOpts);
if (!success) {
return NS_ERROR_FAILURE;
@ -408,11 +448,8 @@ nsDOMWorkerScriptLoader::RunInternal()
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Things we need to make all this work...
nsCOMPtr<nsIDocument> parentDoc = mWorker->Pool()->ParentDocument();
if (!parentDoc) {
// Must have been canceled.
return NS_ERROR_ABORT;
}
nsIDocument* parentDoc = mWorker->Pool()->ParentDocument();
NS_ASSERTION(parentDoc, "Null parent document?!");
// All of these can potentially be null, but that should be ok. We'll either
// succeed without them or fail below.
@ -435,7 +472,9 @@ nsDOMWorkerScriptLoader::RunInternal()
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
rv = secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri, 0);
rv =
secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri,
nsIScriptSecurityManager::ALLOW_CHROME);
if (NS_FAILED(rv)) {
return rv;
}
@ -574,7 +613,8 @@ nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader,
}
nsRefPtr<ScriptCompiler> compiler =
new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj);
new ScriptCompiler(this, mCx, loadInfo.scriptText, filename,
loadInfo.scriptObj);
NS_ASSERTION(compiler, "Out of memory!");
if (!compiler) {
return rv = NS_ERROR_OUT_OF_MEMORY;
@ -623,14 +663,14 @@ void
nsDOMWorkerScriptLoader::SuspendWorkerEvents()
{
NS_ASSERTION(mWorker, "No worker yet!");
mWorker->SuspendFeatures();
mWorker->SuspendTimeouts();
}
void
nsDOMWorkerScriptLoader::ResumeWorkerEvents()
{
NS_ASSERTION(mWorker, "No worker yet!");
mWorker->ResumeFeatures();
mWorker->ResumeTimeouts();
}
nsDOMWorkerScriptLoader::
@ -659,9 +699,6 @@ ScriptLoaderRunnable::~ScriptLoaderRunnable()
}
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerScriptLoader::ScriptLoaderRunnable,
nsIRunnable)
void
nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke()
{
@ -670,14 +707,17 @@ 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!");
}
@ -695,34 +735,31 @@ nsDOMWorkerScriptLoader::ScriptCompiler::Run()
NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!");
NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!");
JSContext* cx = nsDOMThreadService::GetCurrentContext();
NS_ENSURE_STATE(cx);
JSAutoRequest ar(mCx);
JSAutoRequest ar(cx);
JSObject* global = JS_GetGlobalObject(cx);
JSObject* global = JS_GetGlobalObject(mCx);
NS_ENSURE_STATE(global);
// Because we may have nested calls to this function we don't want the
// execution to automatically report errors. We let them propagate instead.
uint32 oldOpts =
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
JS_SetOptions(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
JSScript* script =
JS_CompileUCScriptForPrincipals(cx, global, principal,
JS_CompileUCScriptForPrincipals(mCx, global, principal,
reinterpret_cast<const jschar*>
(mScriptText.BeginReading()),
mScriptText.Length(), mFilename.get(), 1);
JS_SetOptions(cx, oldOpts);
JS_SetOptions(mCx, oldOpts);
if (!script) {
return NS_ERROR_FAILURE;
}
mScriptObj = JS_NewScriptObject(cx, script);
mScriptObj = JS_NewScriptObject(mCx, script);
NS_ENSURE_STATE(mScriptObj);
return NS_OK;

Просмотреть файл

@ -40,7 +40,7 @@
#define __NSDOMWORKERSCRIPTLOADER_H__
// Bases
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
#include "nsIStreamLoader.h"
// Interfaces
@ -56,9 +56,8 @@
#include "nsTArray.h"
#include "prlock.h"
#include "nsDOMWorker.h"
class nsIThread;
// DOMWorker includes
#include "nsDOMWorkerThread.h"
/**
* This class takes a list of script URLs, downloads the scripts, compiles the
@ -87,11 +86,12 @@ class nsIThread;
* Currently if *anything* after 2 fails then we cancel any pending loads and
* bail out entirely.
*/
class nsDOMWorkerScriptLoader : public nsDOMWorkerFeature,
public nsIRunnable,
class nsDOMWorkerScriptLoader : public nsRunnable,
public nsIStreamLoaderObserver
{
friend class AutoSuspendWorkerEvents;
friend class nsDOMWorkerFunctions;
friend class nsDOMWorkerThread;
friend class ScriptLoaderRunnable;
public:
@ -99,23 +99,24 @@ public:
NS_DECL_NSIRUNNABLE
NS_DECL_NSISTREAMLOADEROBSERVER
nsDOMWorkerScriptLoader(nsDOMWorker* aWorker);
nsDOMWorkerScriptLoader();
nsresult LoadScripts(JSContext* aCx,
nsresult LoadScripts(nsDOMWorkerThread* aWorker,
JSContext* aCx,
const nsTArray<nsString>& aURLs);
nsresult LoadScript(JSContext* aCx,
const nsString& aURL);
nsresult LoadScript(nsDOMWorkerThread* aWorker,
JSContext* aCx,
const nsString& aURL);
virtual void Cancel();
void Cancel();
private:
~nsDOMWorkerScriptLoader() { }
~nsDOMWorkerScriptLoader();
nsresult DoRunLoop(JSContext* aCx);
nsresult VerifyScripts(JSContext* aCx);
nsresult ExecuteScripts(JSContext* aCx);
nsresult DoRunLoop();
nsresult VerifyScripts();
nsresult ExecuteScripts();
nsresult RunInternal();
@ -134,11 +135,8 @@ private:
return mWorker->Lock();
}
class ScriptLoaderRunnable : public nsIRunnable
class ScriptLoaderRunnable : public nsRunnable
{
public:
NS_DECL_ISUPPORTS
protected:
// Meant to be inherited.
ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader);
@ -160,11 +158,13 @@ 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,17 +205,20 @@ 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__ */

Просмотреть файл

@ -47,9 +47,7 @@
// Other includes
#include "nsContentUtils.h"
#include "nsJSUtils.h"
#include "nsThreadUtils.h"
#include "pratom.h"
#include "prtime.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
@ -75,15 +73,15 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
nsresult* aRv)
: mCallback(nsnull),
mCallbackArgs(nsnull),
mCallbackArgsLength(0),
mRuntime(NULL)
mCallbackArgsLength(0)
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
JSRuntime* rt;
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(*aRv,);
PRBool success = JS_AddNamedRootRT(mRuntime, &mCallback,
PRBool success = JS_AddNamedRootRT(rt, &mCallback,
"nsDOMWorkerTimeout Callback Object");
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
@ -104,7 +102,7 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) {
mCallbackArgs[i] = aArgv[i + 2];
success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[i],
success = JS_AddNamedRootRT(rt, &mCallbackArgs[i],
"nsDOMWorkerTimeout Callback Arg");
if (NS_UNLIKELY(!success)) {
// Set this to i so that the destructor only unroots the right number of
@ -119,7 +117,7 @@ nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
// Take care of the last arg.
mCallbackArgs[mCallbackArgsLength - 1] = 0;
success = JS_AddNamedRootRT(mRuntime, &mCallbackArgs[mCallbackArgsLength - 1],
success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1],
"nsDOMWorkerTimeout Callback Final Arg");
if (NS_UNLIKELY(!success)) {
// Decrement this so that the destructor only unroots the right number of
@ -139,10 +137,17 @@ nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
if (mCallback) {
for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
JS_RemoveRootRT(mRuntime, &mCallbackArgs[i]);
JSRuntime* rt;
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
if (NS_SUCCEEDED(rv)) {
for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
JS_RemoveRootRT(rt, &mCallbackArgs[i]);
}
JS_RemoveRootRT(rt, &mCallback);
}
JS_RemoveRootRT(mRuntime, &mCallback);
}
delete [] mCallbackArgs;
@ -173,8 +178,7 @@ nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
JSContext* aCx,
nsresult* aRv)
: mExpression(nsnull),
mLineNumber(0),
mRuntime(NULL)
mLineNumber(0)
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
@ -182,10 +186,11 @@ nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
*aRv = expr ? NS_OK : NS_ERROR_FAILURE;
NS_ENSURE_SUCCESS(*aRv,);
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&mRuntime);
JSRuntime* rt;
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(*aRv,);
PRBool success = JS_AddNamedRootRT(mRuntime, &mExpression,
PRBool success = JS_AddNamedRootRT(rt, &mExpression,
"nsDOMWorkerTimeout Expression");
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
@ -207,7 +212,14 @@ nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
if (mExpression) {
JS_RemoveRootRT(mRuntime, &mExpression);
JSRuntime* rt;
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
if (NS_SUCCEEDED(rv)) {
JS_RemoveRootRT(rt, &mExpression);
}
}
}
@ -219,22 +231,37 @@ nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
return NS_ERROR_NOT_IMPLEMENTED;
}
nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorker* aWorker,
nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
PRUint32 aId)
: nsDOMWorkerFeature(aWorker, aId),
: mWorker(aWorker),
mInterval(0),
mSuspendSpinlock(0),
mSuspendInterval(0),
mIsInterval(PR_FALSE),
mId(aId),
mSuspendSpinlock(0),
mIsSuspended(PR_FALSE),
mSuspendedBeforeStart(PR_FALSE),
mStarted(PR_FALSE)
mSuspendInterval(0)
#ifdef DEBUG
, mFiredOrCanceled(PR_FALSE)
#endif
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
NS_ASSERTION(mWorker, "Need a worker here!");
}
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerTimeout, nsDOMWorkerFeature,
nsITimerCallback)
nsDOMWorkerTimeout::~nsDOMWorkerTimeout()
{
MOZ_COUNT_DTOR(nsDOMWorkerTimeout);
// If we have a timer then we assume we added ourselves to the thread's list.
if (mTimer) {
NS_ASSERTION(mFiredOrCanceled || mWorker->IsCanceled(),
"Timeout should have fired or been canceled!");
mWorker->RemoveTimeout(this);
}
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeout, nsITimerCallback)
nsresult
nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
@ -267,8 +294,6 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
mInterval = interval;
mIsInterval = aIsInterval;
mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC;
nsresult rv;
@ -290,11 +315,20 @@ 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);
@ -304,30 +338,18 @@ nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
rv = timer->SetTarget(target);
NS_ENSURE_SUCCESS(rv, rv);
mTimer.swap(timer);
return NS_OK;
}
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);
rv = timer->InitWithCallback(this, interval, type);
NS_ENSURE_SUCCESS(rv, rv);
mStarted = PR_TRUE;
mTimer.swap(timer);
if (!mWorker->AddTimeout(this)) {
// Must have been canceled.
mTimer->Cancel();
mTimer = nsnull;
return NS_ERROR_ABORT;
}
return NS_OK;
}
@ -338,6 +360,10 @@ 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);
@ -365,6 +391,10 @@ 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);
@ -380,29 +410,21 @@ nsDOMWorkerTimeout::Cancel()
}
void
nsDOMWorkerTimeout::Suspend()
nsDOMWorkerTimeout::Suspend(PRTime aNow)
{
#ifdef DEBUG
if (mStarted) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
#endif
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
AutoSpinlock lock(this);
NS_ASSERTION(!IsSuspendedNoLock(), "Bad state!");
mIsSuspended = PR_TRUE;
mSuspendedRef = this;
if (!mStarted) {
mSuspendedBeforeStart = PR_TRUE;
return;
if (!mIsSuspended) {
mIsSuspended = PR_TRUE;
mSuspendedRef = this;
}
mTimer->Cancel();
mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - PR_Now())) /
mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
(PRTime)PR_USEC_PER_MSEC;
LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)",
@ -411,14 +433,9 @@ nsDOMWorkerTimeout::Suspend()
}
void
nsDOMWorkerTimeout::Resume()
nsDOMWorkerTimeout::Resume(PRTime aNow)
{
#ifdef DEBUG
if (!mSuspendedBeforeStart) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
#endif
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u",
@ -428,14 +445,7 @@ nsDOMWorkerTimeout::Resume()
NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!");
if (mSuspendedBeforeStart) {
NS_ASSERTION(!mSuspendInterval, "Bad state!");
mSuspendedBeforeStart = PR_FALSE;
mSuspendInterval = mInterval;
mStarted = PR_TRUE;
}
mTargetTime = PR_Now() + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
#ifdef DEBUG
nsresult rv =
@ -489,9 +499,10 @@ 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;
@ -500,6 +511,9 @@ nsDOMWorkerTimeout::Notify(nsITimer* aTimer)
nsITimer::TYPE_REPEATING_SLACK);
NS_ENSURE_SUCCESS(rv, rv);
}
mIsSuspended = PR_FALSE;
mSuspendedRef = nsnull;
}
}

Просмотреть файл

@ -47,61 +47,60 @@
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "prclist.h"
// DOMWorker includes
#include "nsDOMWorker.h"
#include "nsDOMWorkerThread.h"
/**
* The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created
* by an nsDOMWorker (or one of its JS context functions) and immediately takes
* a strong reference to the worker that created it. It does this so that the
* worker can't be collected while a timeout is outstanding. However, the worker
* needs a weak reference to the timeout so that it can be canceled if the
* worker is canceled (in the event that the page falls out of the fastback
* by an nsDOMWorkerThread (or one of its JS context functions) and immediately
* takes a strong reference to the worker that created it. It does this so that
* the worker can't be collected while a timeout is outstanding. However, the
* worker needs a weak reference to the timeout so that it can be canceled if
* the worker is canceled (in the event that the page falls out of the fastback
* cache or the application is exiting, for instance). The only thing that holds
* the timeout alive is it's mTimer via the nsITimerCallback interface. If the
* timer is single-shot and has run already or if the timer is canceled then
* this object should die.
*/
class nsDOMWorkerTimeout : public nsDOMWorkerFeature,
class nsDOMWorkerTimeout : public PRCList,
public nsITimerCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
nsDOMWorkerTimeout(nsDOMWorker* aWorker,
PRUint32 aId);
nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId);
~nsDOMWorkerTimeout();
nsresult Init(JSContext* aCx,
PRUint32 aArgc,
jsval* aArgv,
nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
PRBool aIsInterval);
nsresult Start();
nsresult Run();
virtual void Cancel();
virtual void Suspend();
virtual void Resume();
void Cancel();
void Suspend(PRTime aNow);
void Resume(PRTime aNow);
PRIntervalTime GetInterval() {
return mInterval;
}
nsDOMWorker* GetWorker() {
nsDOMWorkerThread* GetWorker() {
return mWorker;
}
PRUint32 GetId() {
return mId;
}
PRBool IsSuspended() {
AutoSpinlock lock(this);
return IsSuspendedNoLock();
}
private:
~nsDOMWorkerTimeout() { }
void AcquireSpinlock();
void ReleaseSpinlock();
@ -138,9 +137,7 @@ 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);
@ -148,15 +145,12 @@ 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,
@ -165,26 +159,31 @@ 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;
PRPackedBool mIsInterval;
PRPackedBool mIsSuspended;
PRPackedBool mSuspendedBeforeStart;
PRPackedBool mStarted;
#ifdef DEBUG
PRBool mFiredOrCanceled;
#endif
};
#endif /* __NSDOMWORKERTIMEOUT_H__ */

Просмотреть файл

@ -56,7 +56,6 @@
// DOMWorker includes
#include "nsDOMThreadService.h"
#include "nsDOMWorkerEvents.h"
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerXHRProxy.h"
@ -92,9 +91,9 @@ const PRUint32 nsDOMWorkerXHREventTarget::sMaxUploadEventTypes =
PR_STATIC_ASSERT(nsDOMWorkerXHREventTarget::sMaxXHREventTypes >=
nsDOMWorkerXHREventTarget::sMaxUploadEventTypes);
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHREventTarget,
nsDOMWorkerMessageHandler,
nsIXMLHttpRequestEventTarget)
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerXHREventTarget,
nsIDOMEventTarget,
nsIXMLHttpRequestEventTarget)
PRUint32
nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString)
@ -110,13 +109,9 @@ nsDOMWorkerXHREventTarget::GetListenerTypeFromString(const nsAString& aString)
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnabort);
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ABORT);
listener.forget(aOnabort);
return NS_OK;
@ -125,24 +120,15 @@ nsDOMWorkerXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ABORT]);
return SetOnXListener(type, aOnabort);
return SetEventListener(LISTENER_TYPE_ABORT, aOnabort, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
{
NS_ENSURE_ARG_POINTER(aOnerror);
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_ERROR);
listener.forget(aOnerror);
return NS_OK;
@ -151,24 +137,15 @@ nsDOMWorkerXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_ERROR]);
return SetOnXListener(type, aOnerror);
return SetEventListener(LISTENER_TYPE_ERROR, aOnerror, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnload);
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(LISTENER_TYPE_LOAD);
listener.forget(aOnload);
return NS_OK;
@ -177,24 +154,16 @@ nsDOMWorkerXHREventTarget::GetOnload(nsIDOMEventListener** aOnload)
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnload(nsIDOMEventListener* aOnload)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOAD]);
return SetOnXListener(type, aOnload);
return SetEventListener(LISTENER_TYPE_LOAD, aOnload, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnloadstart);
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
nsCOMPtr<nsIDOMEventListener> listener =
GetOnXListener(LISTENER_TYPE_LOADSTART);
listener.forget(aOnloadstart);
return NS_OK;
@ -203,24 +172,16 @@ nsDOMWorkerXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_LOADSTART]);
return SetOnXListener(type, aOnloadstart);
return SetEventListener(LISTENER_TYPE_LOADSTART, aOnloadstart, PR_TRUE);
}
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aOnprogress);
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
nsCOMPtr<nsIDOMEventListener> listener =
GetOnXListener(LISTENER_TYPE_PROGRESS);
listener.forget(aOnprogress);
return NS_OK;
@ -229,183 +190,230 @@ nsDOMWorkerXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
return SetEventListener(LISTENER_TYPE_PROGRESS, aOnprogress, PR_TRUE);
}
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_PROGRESS]);
NS_IMETHODIMP
nsDOMWorkerXHREventTarget::AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
PRBool aUseCapture)
{
NS_ENSURE_ARG_POINTER(aListener);
return SetOnXListener(type, aOnprogress);
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;
}
nsDOMWorkerXHRUpload::nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR)
: mWorkerXHR(aWorkerXHR)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aWorkerXHR, "Null pointer!");
NS_ASSERTION(aWorkerXHR, "Must have a worker XHR!");
}
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
nsIXMLHttpRequestUpload)
NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHRUpload, nsDOMWorkerXHREventTarget,
nsIXMLHttpRequestUpload,
nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHRUpload, nsIDOMEventTarget,
nsIXMLHttpRequestEventTarget,
nsIXMLHttpRequestUpload)
NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHRUpload)
NS_IMETHODIMP
nsDOMWorkerXHRUpload::AddEventListener(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;
}
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);
}
NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHRUpload)
nsresult
nsDOMWorkerXHRUpload::SetOnXListener(const nsAString& aType,
nsIDOMEventListener* aListener)
nsDOMWorkerXHRUpload::SetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aOnXListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mWorkerXHR->mCanceled) {
return NS_ERROR_ABORT;
}
PRUint32 type = GetListenerTypeFromString(aType);
if (type > sMaxUploadEventTypes) {
// Silently ignore junk events.
return NS_OK;
}
return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
return mWorkerXHR->mXHRProxy->AddEventListener(aType, aListener, aOnXListener,
PR_TRUE);
}
nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorker* aWorker)
: nsDOMWorkerFeature(aWorker),
mWrappedNative(nsnull),
mCanceled(PR_FALSE)
nsresult
nsDOMWorkerXHRUpload::UnsetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener)
{
if (mWorkerXHR->mCanceled) {
return NS_ERROR_ABORT;
}
return mWorkerXHR->mXHRProxy->RemoveEventListener(aType, aListener, PR_TRUE);
}
nsresult
nsDOMWorkerXHRUpload::HandleWorkerEvent(nsIDOMEvent* aEvent)
{
if (mWorkerXHR->mCanceled) {
return NS_ERROR_ABORT;
}
return mWorkerXHR->mXHRProxy->HandleWorkerEvent(aEvent, PR_TRUE);
}
already_AddRefed<nsIDOMEventListener>
nsDOMWorkerXHRUpload::GetOnXListener(PRUint32 aType)
{
if (mWorkerXHR->mCanceled) {
return nsnull;
}
return mWorkerXHR->mXHRProxy->GetOnXListener(aType, PR_TRUE);
}
nsDOMWorkerXHR::nsDOMWorkerXHR(nsDOMWorkerThread* aWorker)
: mWorker(aWorker),
mCanceled(PR_TRUE)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aWorker, "Must have a worker!");
}
// Tricky! We use the AddRef/Release method of nsDOMWorkerFeature (to make sure
// we properly remove ourselves from the worker array) but inherit the QI of
// nsDOMWorkerXHREventTarget.
NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHR, nsDOMWorkerFeature)
nsDOMWorkerXHR::~nsDOMWorkerXHR()
{
if (!mCanceled) {
mWorker->RemoveXHR(this);
}
}
NS_IMPL_QUERY_INTERFACE_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
nsIXMLHttpRequest,
nsIXPCScriptable)
NS_IMPL_ISUPPORTS_INHERITED2(nsDOMWorkerXHR, nsDOMWorkerXHREventTarget,
nsIXMLHttpRequest,
nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorkerXHR, nsIDOMEventTarget,
nsIXMLHttpRequestEventTarget,
nsIXMLHttpRequest)
NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerXHR)
#define XPC_MAP_CLASSNAME nsDOMWorkerXHR
#define XPC_MAP_QUOTED_CLASSNAME "XMLHttpRequest"
#define XPC_MAP_WANT_POSTCREATE
#define XPC_MAP_WANT_TRACE
#define XPC_MAP_WANT_FINALIZE
#define XPC_MAP_FLAGS \
nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \
nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY | \
nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
#include "xpc_map_end.h"
NS_IMETHODIMP
nsDOMWorkerXHR::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
JSTracer* aTracer,
JSObject* /*aObj */)
{
if (!mCanceled) {
nsDOMWorkerMessageHandler::Trace(aTracer);
if (mUpload) {
mUpload->Trace(aTracer);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
JSContext* /* aCx */,
JSObject* /* aObj */)
{
nsDOMWorkerMessageHandler::ClearAllListeners();
if (mUpload) {
mUpload->ClearAllListeners();
}
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::PostCreate(nsIXPConnectWrappedNative* aWrapper,
JSContext* /* aCx */,
JSObject* /* aObj */)
{
mWrappedNative = aWrapper;
return NS_OK;
}
NS_IMPL_THREADSAFE_CI(nsDOMWorkerXHR)
nsresult
nsDOMWorkerXHR::Init()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (!mWorker->AddXHR(this)) {
// Must have been canceled.
return NS_ERROR_ABORT;
}
mCanceled = PR_FALSE;
nsRefPtr<nsDOMWorkerXHRProxy> proxy = new nsDOMWorkerXHRProxy(this);
NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
@ -438,12 +446,14 @@ nsDOMWorkerXHR::Cancel()
mXHRProxy->Destroy();
}
mWorker->RemoveXHR(this);
mWorker = nsnull;
}
nsresult
nsDOMWorkerXHR::SetOnXListener(const nsAString& aType,
nsIDOMEventListener* aListener)
nsDOMWorkerXHR::SetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aOnXListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
@ -451,20 +461,49 @@ nsDOMWorkerXHR::SetOnXListener(const nsAString& aType,
return NS_ERROR_ABORT;
}
PRUint32 type = GetListenerTypeFromString(aType);
if (type > sMaxXHREventTypes) {
// Silently ignore junk events.
return NS_OK;
return mXHRProxy->AddEventListener(aType, aListener, aOnXListener, PR_FALSE);
}
nsresult
nsDOMWorkerXHR::UnsetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mCanceled) {
return NS_ERROR_ABORT;
}
return nsDOMWorkerXHREventTarget::SetOnXListener(aType, aListener);
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);
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetChannel(nsIChannel** aChannel)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG_POINTER(aChannel);
*aChannel = nsnull;
return NS_OK;
@ -473,8 +512,6 @@ 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;
@ -816,7 +853,7 @@ nsDOMWorkerXHR::GetUpload(nsIXMLHttpRequestUpload** aUpload)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsRefPtr<nsDOMWorker> worker = mWorker;
nsRefPtr<nsDOMWorkerThread> worker = mWorker;
if (!worker) {
return NS_ERROR_ABORT;
}
@ -841,24 +878,32 @@ 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);
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
nsCOMPtr<nsIDOMEventListener> listener =
mXHRProxy->GetOnXListener(LISTENER_TYPE_READYSTATECHANGE, PR_FALSE);
nsCOMPtr<nsIDOMEventListener> listener = GetOnXListener(type);
listener.forget(aOnreadystatechange);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHR::SetOnreadystatechange(nsIDOMEventListener* aOnreadystatechange)
{
nsAutoString type;
type.AssignASCII(sListenerTypes[LISTENER_TYPE_READYSTATECHANGE]);
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
return SetOnXListener(type, aOnreadystatechange);
if (mCanceled) {
return NS_ERROR_ABORT;
}
return mXHRProxy->AddEventListener(LISTENER_TYPE_READYSTATECHANGE,
aOnreadystatechange, PR_TRUE, PR_FALSE);
}
NS_IMETHODIMP

Просмотреть файл

@ -40,9 +40,10 @@
#define __NSDOMWORKERXHR_H__
// Bases
#include "nsIClassInfo.h"
#include "nsIXMLHttpRequest.h"
#include "nsIXPCScriptable.h"
#include "nsIClassInfo.h"
// Interfaces
// Other includes
#include "nsAutoPtr.h"
@ -51,9 +52,7 @@
#include "prlock.h"
// DOMWorker includes
#include "nsDOMWorker.h"
#include "nsDOMWorkerMacros.h"
#include "nsDOMWorkerXHRProxy.h"
#include "nsDOMWorkerThread.h"
// Convenience defines for event *indexes* in the sListenerTypes array.
#define LISTENER_TYPE_ABORT 0
@ -63,14 +62,15 @@
#define LISTENER_TYPE_PROGRESS 4
#define LISTENER_TYPE_READYSTATECHANGE 5
class nsIXPConnectWrappedNative;
class nsDOMWorkerXHR;
class nsDOMWorkerXHREvent;
class nsDOMWorkerXHRProxy;
class nsDOMWorkerXHREventTarget : public nsDOMWorkerMessageHandler,
public nsIXMLHttpRequestEventTarget
class nsDOMWorkerXHREventTarget : public nsIXMLHttpRequestEventTarget
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTTARGET
NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET
static const char* const sListenerTypes[];
@ -79,16 +79,58 @@ 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;
class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
public nsIXMLHttpRequestUpload,
public nsIClassInfo
{
friend class nsDOMWorkerXHR;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_NSIDOMEVENTTARGET(nsDOMWorkerXHREventTarget::)
NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
NS_DECL_NSIXMLHTTPREQUESTUPLOAD
NS_DECL_NSICLASSINFO
nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
virtual nsresult SetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aOnXListener);
virtual nsresult UnsetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener);
virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
virtual already_AddRefed<nsIDOMEventListener>
GetOnXListener(PRUint32 aType);
protected:
virtual ~nsDOMWorkerXHRUpload() { }
nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
};
class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
public nsDOMWorkerFeature,
public nsIXMLHttpRequest,
public nsIXPCScriptable
public nsIClassInfo
{
friend class nsDOMWorkerXHREvent;
friend class nsDOMWorkerXHRLastProgressOrLoadEvent;
@ -98,60 +140,38 @@ class nsDOMWorkerXHR : public nsDOMWorkerXHREventTarget,
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIXMLHTTPREQUEST
NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
NS_DECL_NSICLASSINFO_GETINTERFACES
NS_DECL_NSIXPCSCRIPTABLE
NS_DECL_NSICLASSINFO
nsDOMWorkerXHR(nsDOMWorker* aWorker);
nsDOMWorkerXHR(nsDOMWorkerThread* aWorker);
nsresult Init();
virtual void Cancel();
void Cancel();
virtual nsresult SetOnXListener(const nsAString& aType,
nsIDOMEventListener* aListener);
virtual nsresult SetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aOnXListener);
private:
virtual ~nsDOMWorkerXHR() { }
virtual nsresult UnsetEventListener(PRUint32 aType,
nsIDOMEventListener* aListener);
virtual nsresult HandleWorkerEvent(nsIDOMEvent* aEvent);
virtual already_AddRefed<nsIDOMEventListener>
GetOnXListener(PRUint32 aType);
protected:
virtual ~nsDOMWorkerXHR();
PRLock* Lock() {
return mWorker->Lock();
}
nsIXPConnectWrappedNative* GetWrappedNative() {
return mWrappedNative;
}
nsRefPtr<nsDOMWorkerThread> mWorker;
nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
nsRefPtr<nsDOMWorkerXHRUpload> mUpload;
nsIXPConnectWrappedNative* mWrappedNative;
volatile PRBool mCanceled;
};
class nsDOMWorkerXHRUpload : public nsDOMWorkerXHREventTarget,
public nsIXMLHttpRequestUpload
{
friend class nsDOMWorkerXHR;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMEVENTTARGET
NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsDOMWorkerXHREventTarget::)
NS_DECL_NSIXMLHTTPREQUESTUPLOAD
NS_FORWARD_NSICLASSINFO_NOGETINTERFACES(nsDOMWorkerXHREventTarget::)
NS_DECL_NSICLASSINFO_GETINTERFACES
nsDOMWorkerXHRUpload(nsDOMWorkerXHR* aWorkerXHR);
virtual nsresult SetOnXListener(const nsAString& aType,
nsIDOMEventListener* aListener);
protected:
virtual ~nsDOMWorkerXHRUpload() { }
nsRefPtr<nsDOMWorkerXHR> mWorkerXHR;
};
#endif /* __NSDOMWORKERXHR_H__ */

Просмотреть файл

@ -59,12 +59,8 @@
#include "prthread.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
#include "nsDOMWorker.h"
#include "nsDOMWorkerEvents.h"
#include "nsDOMWorkerMacros.h"
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerXHR.h"
#include "nsDOMWorkerThread.h"
#include "nsDOMWorkerXHRProxiedFunctions.h"
#define MAX_XHR_LISTENER_TYPE nsDOMWorkerXHREventTarget::sMaxXHREventTypes
@ -113,7 +109,7 @@ public:
NS_DECL_ISUPPORTS
nsResultReturningRunnable(nsIEventTarget* aTarget, nsIRunnable* aRunnable,
nsDOMWorker* aWorker)
nsDOMWorkerThread* aWorker)
: mTarget(aTarget), mRunnable(aRunnable), mWorker(aWorker),
mResult(NS_OK), mDone(PR_FALSE) { }
@ -161,13 +157,329 @@ public:
private:
nsCOMPtr<nsIEventTarget> mTarget;
nsCOMPtr<nsIRunnable> mRunnable;
nsRefPtr<nsDOMWorker> mWorker;
nsRefPtr<nsDOMWorkerThread> mWorker;
nsresult mResult;
volatile PRBool mDone;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsResultReturningRunnable, nsIRunnable)
class nsDOMWorkerXHREvent : public nsIRunnable,
public nsIDOMProgressEvent,
public nsIClassInfo
{
friend class nsDOMWorkerXHRProxy;
friend class nsDOMWorkerXHREventTargetProxy;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIDOMEVENT
NS_DECL_NSIDOMPROGRESSEVENT
NS_DECL_NSICLASSINFO
nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy);
nsresult Init(PRUint32 aType,
const nsAString& aTypeString,
nsIDOMEvent* aEvent);
nsresult Init(nsIXMLHttpRequest* aXHR);
void EventHandled();
protected:
nsRefPtr<nsDOMWorkerXHRProxy> mXHRProxy;
nsCOMPtr<nsIDOMEventTarget> mTarget;
nsString mTypeString;
PRUint32 mType;
PRUint16 mEventPhase;
DOMTimeStamp mTimeStamp;
nsString mResponseText;
nsCString mStatusText;
nsresult mStatus;
PRInt32 mReadyState;
PRUint64 mLoaded;
PRUint64 mTotal;
PRInt32 mChannelID;
PRPackedBool mBubbles;
PRPackedBool mCancelable;
PRPackedBool mUploadEvent;
PRPackedBool mProgressEvent;
PRPackedBool mLengthComputable;
};
nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy)
: mXHRProxy(aXHRProxy),
mType(PR_UINT32_MAX),
mEventPhase(0),
mTimeStamp(0),
mStatus(NS_OK),
mReadyState(0),
mLoaded(0),
mTotal(0),
mChannelID(-1),
mBubbles(PR_FALSE),
mCancelable(PR_FALSE),
mUploadEvent(PR_FALSE),
mProgressEvent(PR_FALSE),
mLengthComputable(PR_FALSE)
{
NS_ASSERTION(aXHRProxy, "Can't be null!");
}
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerXHREvent)
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerXHREvent)
NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEvent)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent)
NS_INTERFACE_MAP_END
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerXHREvent, nsIDOMEvent)
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetInterfaces(PRUint32* aCount,
nsIID*** aArray)
{
PRUint32 count = *aCount = mProgressEvent ? 2 : 1;
*aArray = (nsIID**)nsMemory::Alloc(sizeof(nsIID*) * count);
if (mProgressEvent) {
(*aArray)[--count] =
(nsIID*)nsMemory::Clone(&NS_GET_IID(nsIDOMProgressEvent), sizeof(nsIID));
}
(*aArray)[--count] =
(nsIID *)nsMemory::Clone(&NS_GET_IID(nsIDOMEvent), sizeof(nsIID));
NS_ASSERTION(!count, "Bad math!");
return NS_OK;
}
NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerXHREvent)
nsresult
nsDOMWorkerXHREvent::Init(PRUint32 aType,
const nsAString& aTypeString,
nsIDOMEvent* aEvent)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aEvent, "Don't pass null here!");
nsresult rv;
mType = aType;
mTypeString.Assign(aTypeString);
mChannelID = mXHRProxy->ChannelID();
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(aEvent));
if (progressEvent) {
mProgressEvent = PR_TRUE;
PRBool lengthComputable;
rv = progressEvent->GetLengthComputable(&lengthComputable);
NS_ENSURE_SUCCESS(rv, rv);
mLengthComputable = lengthComputable ? PR_TRUE : PR_FALSE;
rv = progressEvent->GetLoaded(&mLoaded);
NS_ENSURE_SUCCESS(rv, rv);
rv = progressEvent->GetTotal(&mTotal);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIDOMEventTarget> mainThreadTarget;
rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXMLHttpRequestUpload> upload(do_QueryInterface(mainThreadTarget));
if (upload) {
mUploadEvent = PR_TRUE;
mTarget =
static_cast<nsDOMWorkerXHREventTarget*>(mXHRProxy->mWorkerXHR->mUpload);
}
else {
mUploadEvent = PR_FALSE;
mTarget = mXHRProxy->mWorkerXHR;
}
PRBool boolVal;
rv = aEvent->GetBubbles(&boolVal);
NS_ENSURE_SUCCESS(rv, rv);
mBubbles = boolVal ? PR_TRUE : PR_FALSE;
rv = aEvent->GetCancelable(&boolVal);
NS_ENSURE_SUCCESS(rv, rv);
mCancelable = boolVal ? PR_TRUE : PR_FALSE;
rv = aEvent->GetTimeStamp(&mTimeStamp);
NS_ENSURE_SUCCESS(rv, rv);
rv = Init(mXHRProxy->mXHR);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMWorkerXHREvent::Init(nsIXMLHttpRequest* aXHR)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aXHR, "Don't pass null here!");
nsresult rv = aXHR->GetResponseText(mResponseText);
NS_ENSURE_SUCCESS(rv, rv);
rv = aXHR->GetStatusText(mStatusText);
NS_ENSURE_SUCCESS(rv, rv);
rv = aXHR->GetStatus(&mStatus);
NS_ENSURE_SUCCESS(rv, rv);
rv = aXHR->GetReadyState(&mReadyState);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
nsDOMWorkerXHREvent::EventHandled()
{
// Prevent reference cycles by releasing these here.
mXHRProxy = nsnull;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::Run()
{
nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent);
EventHandled();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetType(nsAString& aType)
{
aType.Assign(mTypeString);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetTarget(nsIDOMEventTarget** aTarget)
{
NS_ENSURE_ARG_POINTER(aTarget);
NS_ADDREF(*aTarget = mTarget);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget)
{
NS_ENSURE_ARG_POINTER(aCurrentTarget);
NS_ADDREF(*aCurrentTarget = mTarget);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetEventPhase(PRUint16* aEventPhase)
{
NS_ENSURE_ARG_POINTER(aEventPhase);
*aEventPhase = mEventPhase;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetBubbles(PRBool* aBubbles)
{
NS_ENSURE_ARG_POINTER(aBubbles);
*aBubbles = mBubbles;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetCancelable(PRBool* aCancelable)
{
NS_ENSURE_ARG_POINTER(aCancelable);
*aCancelable = mCancelable;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetTimeStamp(DOMTimeStamp* aTimeStamp)
{
NS_ENSURE_ARG_POINTER(aTimeStamp);
*aTimeStamp = mTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::StopPropagation()
{
NS_WARNING("StopPropagation doesn't do anything here!");
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::PreventDefault()
{
NS_WARNING("PreventDefault doesn't do anything yet!");
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::InitEvent(const nsAString& aEventTypeArg,
PRBool aCanBubbleArg,
PRBool aCancelableArg)
{
NS_WARNING("InitEvent doesn't do anything here!");
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetLengthComputable(PRBool* aLengthComputable)
{
NS_ENSURE_ARG_POINTER(aLengthComputable);
*aLengthComputable = mLengthComputable;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetLoaded(PRUint64* aLoaded)
{
NS_ENSURE_ARG_POINTER(aLoaded);
*aLoaded = mLoaded;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::GetTotal(PRUint64* aTotal)
{
NS_ENSURE_ARG_POINTER(aTotal);
*aTotal = mTotal;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerXHREvent::InitProgressEvent(const nsAString_internal& aTypeArg,
PRBool aCanBubbleArg,
PRBool aCancelableArg,
PRBool aLengthComputableArg,
PRUint64 aLoadedArg,
PRUint64 aTotalArg)
{
NS_WARNING("InitProgressEvent doesn't do anything here!");
return NS_OK;
}
class nsDOMWorkerXHRLastProgressOrLoadEvent : public nsIRunnable
{
public:
@ -184,9 +496,6 @@ public:
if (!mProxy->mCanceled) {
nsAutoLock lock(mProxy->mWorkerXHR->Lock());
mProxy->mLastProgressOrLoadEvent.swap(lastProgressOrLoadEvent);
if (mProxy->mCanceled) {
return NS_ERROR_ABORT;
}
}
if (lastProgressOrLoadEvent) {
@ -320,7 +629,7 @@ nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR)
nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy()
{
if (mOwnedByXHR) {
mWorkerXHRWN = nsnull;
mWorkerXHR->Release();
}
else if (mXHR) {
nsCOMPtr<nsIThread> mainThread;
@ -343,6 +652,18 @@ 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);
@ -372,20 +693,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();
}
NS_ASSERTION(!(mLastProgressOrLoadEvent && mLastXHREvent), "Going to leak!");
mLastXHREvent = nsnull;
return NS_OK;
}
@ -396,7 +717,7 @@ nsDOMWorkerXHRProxy::InitInternal()
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mXHR, "InitInternal shouldn't be called twice!");
nsDOMWorker* worker = mWorkerXHR->mWorker;
nsDOMWorkerThread* worker = mWorkerXHR->mWorker;
nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
if (worker->IsCanceled()) {
@ -426,12 +747,12 @@ nsDOMWorkerXHRProxy::InitInternal()
nsRefPtr<nsDOMWorkerXHREvent> nullEvent = new nsDOMWorkerXHREvent(this);
NS_ENSURE_TRUE(nullEvent, NS_ERROR_OUT_OF_MEMORY);
rv = nullEvent->SnapshotXHRState(xhr);
rv = nullEvent->Init(xhr);
NS_ENSURE_SUCCESS(rv, rv);
nullEvent->EventHandled();
mLastXHREvent.swap(nullEvent);
mLastXHREvent->EventHandled();
xhrConcrete->SetRequestObserver(this);
// We now own mXHR and it owns upload.
@ -476,6 +797,7 @@ 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) {
@ -494,10 +816,9 @@ nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
NS_ASSERTION(xhrTarget, "This shouldn't fail!");
EventListenerFunction addRemoveEventListener =
aAdd ?
&nsIDOMEventTarget::AddEventListener :
&nsIDOMEventTarget::RemoveEventListener;
EventListenerFunction function = aAdd ?
&nsIDOMEventTarget::AddEventListener :
&nsIDOMEventTarget::RemoveEventListener;
nsAutoString eventName;
PRUint32 index = 0;
@ -508,14 +829,14 @@ nsDOMWorkerXHRProxy::AddRemoveXHRListeners(PRBool aAdd)
for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
(xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
(uploadTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
(xhrTarget.get()->*function)(eventName, this, PR_FALSE);
(uploadTarget.get()->*function)(eventName, this, PR_FALSE);
}
}
for (; index < MAX_XHR_LISTENER_TYPE; index++) {
eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
(xhrTarget.get()->*addRemoveEventListener)(eventName, this, PR_FALSE);
(xhrTarget.get()->*function)(eventName, this, PR_FALSE);
}
}
@ -532,46 +853,146 @@ nsDOMWorkerXHRProxy::FlipOwnership()
nsRefPtr<nsDOMWorkerXHRProxy> kungFuDeathGrip(this);
if (mOwnedByXHR) {
mWorkerXHRWN = mWorkerXHR->GetWrappedNative();
NS_ASSERTION(mWorkerXHRWN, "Null pointer!");
mWorkerXHR->AddRef();
mXHR->Release();
}
else {
mXHR->AddRef();
mWorkerXHRWN = nsnull;
mWorkerXHR->Release();
}
}
nsresult
nsDOMWorkerXHRProxy::UploadEventListenerAdded()
nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aOnXListener,
PRBool aUploadListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
// If this is the first time we're setting an upload listener then we have to
// hit the main thread to attach the upload listeners. Otherwise there's
// nothing to do here.
if (mWantUploadListeners) {
if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
aType >= MAX_XHR_LISTENER_TYPE) {
// Silently fail on junk events.
return NS_OK;
}
nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
mXHRListeners[aType];
WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
mXHROnXListeners[aType];
nsRefPtr<nsResultReturningRunnable> runnable =
new nsResultReturningRunnable(mMainThread, attachRunnable,
mWorkerXHR->mWorker);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
{
nsAutoLock lock(mWorkerXHR->Lock());
nsresult rv = runnable->Dispatch();
if (NS_FAILED(rv)) {
return rv;
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!");
}
NS_ASSERTION(mWantUploadListeners, "Should have set this!");
return NS_OK;
}
nsresult
nsDOMWorkerXHRProxy::RemoveEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aUploadListener)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aListener, "Null pointer!");
if (mCanceled) {
return NS_ERROR_ABORT;
}
if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
aType >= MAX_XHR_LISTENER_TYPE) {
// Silently fail on junk events.
return NS_OK;
}
ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
mXHRListeners[aType];
nsAutoLock lock(mWorkerXHR->Lock());
listeners.RemoveElement(aListener);
return NS_OK;
}
already_AddRefed<nsIDOMEventListener>
nsDOMWorkerXHRProxy::GetOnXListener(PRUint32 aType,
PRBool aUploadListener)
{
if (mCanceled) {
return nsnull;
}
if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
aType >= MAX_XHR_LISTENER_TYPE) {
// Silently fail on junk events.
return nsnull;
}
WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
mXHROnXListeners[aType];
nsAutoLock lock(mWorkerXHR->Lock());
nsCOMPtr<nsIDOMEventListener> listener = onXListener->Inner();
return listener.forget();
}
nsresult
nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
PRBool aUploadEvent)
@ -579,19 +1000,38 @@ nsDOMWorkerXHRProxy::HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aEvent, "Should not be null!");
{
nsAutoLock lock(mWorkerXHR->Lock());
if (mCanceled ||
(aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
return NS_OK;
}
mLastXHREvent = aEvent;
if (mCanceled ||
(aEvent->mChannelID != -1 && aEvent->mChannelID != mChannelID)) {
return NS_OK;
}
nsIDOMEvent* event = static_cast<nsDOMWorkerEvent*>(aEvent);
return HandleEventInternal(aEvent->mXHREventType, event, aUploadEvent);
mLastXHREvent = aEvent;
return HandleEventInternal(aEvent->mType, aEvent, aUploadEvent);
}
nsresult
nsDOMWorkerXHRProxy::HandleWorkerEvent(nsIDOMEvent* aEvent,
PRBool aUploadEvent)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aEvent, "Should not be null!");
nsString typeString;
nsresult rv = aEvent->GetType(typeString);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 maxType = aUploadEvent ? MAX_UPLOAD_LISTENER_TYPE :
MAX_XHR_LISTENER_TYPE;
PRUint32 type =
nsDOMWorkerXHREventTarget::GetListenerTypeFromString(typeString);
if (type >= maxType) {
// Silently fail on junk events.
return NS_OK;
}
return HandleEventInternal(type, aEvent, aUploadEvent);
}
nsresult
@ -605,7 +1045,6 @@ 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!");
@ -616,23 +1055,82 @@ nsDOMWorkerXHRProxy::HandleEventInternal(PRUint32 aType,
return NS_ERROR_ABORT;
}
nsIDOMEventTarget* target = aUploadListener ?
static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR->mUpload) :
static_cast<nsDOMWorkerMessageHandler*>(mWorkerXHR);
ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
mXHRListeners[aType];
return target->DispatchEvent(aEvent, nsnull);
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.
}
PRBool
nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType,
nsDOMWorkerXHRProxy::HasListenersForType(PRUint32 aType,
nsIDOMEvent* aEvent)
{
#ifdef DEBUG
PRUint32 type = nsDOMWorkerXHREventTarget::GetListenerTypeFromString(aType);
NS_ASSERTION(type < MAX_XHR_LISTENER_TYPE, "Bad type!");
#endif
NS_ASSERTION(aType < MAX_XHR_LISTENER_TYPE, "Bad type!");
if (mWorkerXHR->HasListeners(aType)) {
if (mXHRListeners[aType].Length()) {
return PR_TRUE;
}
@ -648,7 +1146,7 @@ nsDOMWorkerXHRProxy::HasListenersForType(const nsAString& aType,
checkUploadListeners = PR_TRUE;
}
if (checkUploadListeners && mWorkerXHR->mUpload->HasListeners(aType)) {
if (checkUploadListeners && mUploadListeners[aType].Length()) {
return PR_TRUE;
}
@ -673,29 +1171,20 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent)
return NS_OK;
}
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);
}
// 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);
}
// Always bail out if we're canceled.
if (mCanceled) {
return NS_ERROR_ABORT;
}
PRBool ignoreEvent = !HasListenersForType(typeString, aEvent);
if (ignoreEvent && mSyncRequest) {
// Only ignore the event if it isn't final.
ignoreEvent = type != LISTENER_TYPE_ABORT &&
type != LISTENER_TYPE_ERROR &&
type != LISTENER_TYPE_LOAD;
}
if (ignoreEvent) {
if (!HasListenersForType(type, aEvent)) {
return NS_OK;
}
@ -713,11 +1202,6 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoLock lock(mWorkerXHR->Lock());
if (mCanceled) {
return NS_ERROR_ABORT;
}
mLastProgressOrLoadEvent.swap(newEvent);
if (newEvent) {

Просмотреть файл

@ -45,18 +45,17 @@
#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;
@ -73,6 +72,11 @@ 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);
@ -114,15 +118,28 @@ protected:
void AddRemoveXHRListeners(PRBool aAdd);
void FlipOwnership();
nsresult UploadEventListenerAdded();
nsresult AddEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aOnXListener,
PRBool aUploadListener);
nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent,
PRBool aUploadEvent);
nsresult RemoveEventListener(PRUint32 aType,
nsIDOMEventListener* aListener,
PRBool aUploadListener);
already_AddRefed<nsIDOMEventListener> GetOnXListener(PRUint32 aType,
PRBool aUploadListener);
nsresult HandleWorkerEvent(nsDOMWorkerXHREvent* aEvent, PRBool aUploadEvent);
nsresult HandleWorkerEvent(nsIDOMEvent* aEvent, PRBool aUploadEvent);
nsresult HandleEventInternal(PRUint32 aType,
nsIDOMEvent* aEvent,
PRBool aUploadEvent);
void ClearEventListeners();
// Methods of nsIXMLHttpRequest that we implement
nsresult GetAllResponseHeaders(char** _retval);
nsresult GetResponseHeader(const nsACString& aHeader,
@ -145,12 +162,10 @@ protected:
// aEvent is used to see if we should check upload listeners as well. If left
// unset we always check upload listeners.
PRBool HasListenersForType(const nsAString& aType,
nsIDOMEvent* aEvent = nsnull);
PRBool HasListenersForType(PRUint32 aType, nsIDOMEvent* aEvent = nsnull);
// May be weak or strong, check mOwnedByXHR.
nsDOMWorkerXHR* mWorkerXHR;
nsCOMPtr<nsIXPConnectWrappedNative> mWorkerXHRWN;
// May be weak or strong, check mOwnedByXHR.
nsIXMLHttpRequest* mXHR;
@ -164,6 +179,12 @@ protected:
nsRefPtr<nsDOMWorkerXHREvent> mLastXHREvent;
nsRefPtr<nsDOMWorkerXHREvent> mLastProgressOrLoadEvent;
nsTArray<ListenerArray> mXHRListeners;
nsTArray<WrappedListener> mXHROnXListeners;
nsTArray<ListenerArray> mUploadListeners;
nsTArray<WrappedListener> mUploadOnXListeners;
SyncEventQueue* mSyncEventQueue;
PRInt32 mChannelID;

Просмотреть файл

@ -47,32 +47,20 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_importScripts.html \
importScripts_worker.js \
importScripts_worker_imported1.js \
importScripts_worker_imported2.js \
importScripts_worker_imported3.js \
importScripts_worker_imported4.js \
test_longThread.html \
longThread_worker.js \
test_recursion.html \
recursion_worker.js \
test_regExpStatics.html \
regExpStatics_worker.js \
test_importScripts.html \
test_simpleThread.html \
simpleThread_worker.js \
test_threadErrors.html \
threadErrors_worker1.js \
threadErrors_worker2.js \
threadErrors_worker3.js \
threadErrors_worker4.js \
test_threadTimeouts.html \
threadTimeouts_worker.js \
test_longThread.html \
test_recursion.html \
test_regExpStatics.html \
test_xhr.html \
xhr_worker.js \
testXHR.txt \
test_fibonacci.html \
fibonacci_worker.js \
$(NULL)
libs:: $(_TEST_FILES)

Просмотреть файл

@ -1,17 +1,17 @@
onmessage = function(event) {
switch (event.data) {
function messageListener(message, source) {
switch (message) {
case 'start':
importScripts("importScripts_worker_imported2.js");
loadScripts("importScripts_worker_imported2.js");
importedScriptFunction2();
tryBadScripts();
postMessage('started');
source.postMessage('started');
break;
case 'stop':
tryBadScripts();
postMessage('stopped');
postMessageToPool('stopped');
break;
default:
throw new Error("Bad message: " + event.data);
throw new Error("Bad message: " + message);
break;
}
}
@ -19,7 +19,7 @@ onmessage = function(event) {
// This caused security exceptions in the past, make sure it doesn't!
var constructor = {}.constructor;
importScripts("importScripts_worker_imported1.js");
loadScripts("importScripts_worker_imported1.js");
// Try to call a function defined in the imported script.
importedScriptFunction();
@ -40,7 +40,7 @@ function tryBadScripts() {
var caughtException = false;
var url = badScripts[i];
try {
importScripts(url);
loadScripts(url);
}
catch (e) {
caughtException = true;

Просмотреть файл

@ -18,12 +18,11 @@ Tests of DOM Worker Threads (Bug 437152)
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("importScripts_worker.js");
worker.onmessage = function(event) {
switch (event.data) {
var pool = navigator.newWorkerPool();
pool.messageListener = function(message, source) {
switch (message) {
case "started":
worker.postMessage("stop");
source.postMessage("stop");
break;
case "stopped":
SimpleTest.finish();
@ -34,15 +33,15 @@ Tests of DOM Worker Threads (Bug 437152)
}
};
worker.onerror = function(event) {
ok(false, "Worker had an error:" + event.data);
pool.errorListener = function(error, source) {
ok(false, "Worker had an error:" + error);
SimpleTest.finish();
}
var worker = pool.createWorkerFromURL("importScripts_worker.js");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>

Просмотреть файл

@ -18,36 +18,63 @@ Tests of DOM Worker Threads (Bug 437152)
<pre id="test">
<script class="testbody" type="text/javascript">
const numThreads = 5;
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
//prefs.setIntPref("javascript.options.gczeal", 2);
var workerScript =
"function messageListener(message, source) { " +
" switch (message) { " +
" case 'start': " +
" /* do a ton of stuff! */ " +
" for (var i = 0; i < 10000000; i++) { } " +
" dump('done!\\n'); " +
" /* pass message to source */ " +
" source.postMessage('done'); " +
" break; " +
" default: " +
" throw 'Bad message: ' + message; " +
" } " +
"} " +
"";
var pool = navigator.newWorkerPool();
ok(pool, "Couldn't get worker pool");
const numThreads = 10;
var doneThreads = 0;
function onmessage(event) {
switch (event.data) {
pool.messageListener = function(message, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
switch (message) {
case "done":
if (++doneThreads == numThreads) {
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
break;
default:
ok(false, "Unexpected message");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
}
};
function onerror(event) {
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
for (var i = 0; i < numThreads; i++) {
var worker = new Worker("longThread_worker.js");
worker.onmessage = onmessage;
worker.onerror = onerror;
var worker = pool.createWorker(workerScript);
worker.postMessage("start");
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>

Просмотреть файл

@ -17,16 +17,31 @@ Tests of DOM Worker Threads
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("recursion_worker.js");
function workerScript() {
function recurse() {
recurse();
}
worker.onerror = function(event) {
dump(event.data + "\n");
is(event.data,
'[JavaScript Error: "too much recursion" {file: "http://localhost:8888' +
'/tests/dom/src/threads/tests/recursion_worker.js" line: 2}]');
this.messageListener = function(message, source) {
switch (message) {
case "start":
recurse();
break;
default:
}
}
}
var pool = navigator.newWorkerPool();
pool.errorListener = function(error, source) {
is(error.message,
'[JavaScript Error: "too much recursion" {file: "DOMWorker inline ' +
'script" line: 4}]');
SimpleTest.finish();
}
var worker = pool.createWorker("(" + workerScript + ")();");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -19,22 +19,50 @@ Tests of DOM Worker Threads RegExp statics
const WORKER_COUNT = 25;
var doneWorkers = 0;
function onmessage(event) {
if (++doneWorkers == WORKER_COUNT) {
SimpleTest.finish();
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");
}
}
}
function onerror(event) {
ok(false, "Worker had an error: " + event.data);
var pool = navigator.newWorkerPool();
var doneWorkers = 0;
pool.messageListener = function(message, source) {
if (++doneWorkers == WORKER_COUNT) {
SimpleTest.finish();
}
};
pool.errorListener = function(error, source) {
ok(false, "Worker had an error: " + error);
SimpleTest.finish();
};
for (var i = 0; i < WORKER_COUNT; i++) {
var worker = new Worker("regExpStatics_worker.js");
worker.onmessage = onmessage;
worker.onerror = onerror;
var worker = pool.createWorker("(" + workerScript + ")();");
worker.postMessage("start");
}

Просмотреть файл

@ -18,46 +18,78 @@ Tests of DOM Worker Threads (Bug 437152)
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("simpleThread_worker.js");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
worker.addEventListener("message",function(event) {
is(event.target, worker);
switch (event.data) {
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) {
case "no-op":
break;
case "started":
is(gotError, true);
worker.postMessage("no-op");
worker.postMessage("stop");
// pass message to self
pool.postMessage("no-op");
// pass message to source
source.postMessage("stop");
break;
case "stopped":
// pass message to worker
worker.postMessage("no-op");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
break;
default:
ok(false, "Unexpected message:" + event.data);
ok(false, "Unexpected message");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
}, false);
var gotError = false;
worker.onerror = function(event) {
is(event.target, worker);
is(event.data,
'[JavaScript Error: "uncaught exception: Bad message: asdf"]');
gotError = true;
worker.onerror = function(otherEvent) {
ok(false, "Worker had an error:" + otherEvent.data);
SimpleTest.finish();
}
};
worker.postMessage("asdf");
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
};
var worker = pool.createWorker(workerScript);
ok(worker, "Couldn't make worker");
pool.postMessage("no-op");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>

Просмотреть файл

@ -18,40 +18,79 @@ Tests of DOM Worker Threads (Bug 437152)
<pre id="test">
<script class="testbody" type="text/javascript">
const expectedErrorCount = 4;
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
function messageListener(event) {
ok(false, "Unexpected message: " + event.data);
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");
SimpleTest.finish();
};
var actualErrorCount = 0;
var failedWorkers = [];
function errorListener(event) {
if (failedWorkers.indexOf(event.target) != -1) {
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (failedWorkers.indexOf(source) != -1) {
dump("Already seen worker: " + source + "\n");
ok(false, "Seen an extra error from this worker");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
return;
}
failedWorkers.push(event.target);
failedWorkers.push(source);
actualErrorCount++;
if (actualErrorCount == expectedErrorCount) {
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
for (var i = 1; i <= expectedErrorCount; i++) {
var worker = new Worker("threadErrors_worker" + i + ".js");
worker.onmessage = messageListener;
worker.onerror = errorListener;
for (var i = 0; i < expectedErrorCount; i++) {
var worker = pool.createWorker(badWorkerScripts[i]);
worker.postMessage("Hi");
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>

Просмотреть файл

@ -18,36 +18,86 @@ Tests of DOM Worker Threads (Bug 437152)
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("threadTimeouts_worker.js");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
worker.onmessage = function(event) {
is(event.target, worker);
switch (event.data) {
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) {
case "timeoutFinished":
event.target.postMessage("startInterval");
source.postMessage("startInterval");
break;
case "intervalFinished":
event.target.postMessage("cancelInterval");
source.postMessage("cancelInterval");
break;
case "intervalCanceled":
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
break;
default:
ok(false, "Unexpected message");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
worker.onerror = function(event) {
is(event.target, worker);
ok(false, "Worker had an error: " + event.data);
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
};
var worker = pool.createWorker(workerScript);
ok(worker, "Couldn't make worker");
worker.postMessage("startTimeout");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>

Просмотреть файл

@ -18,11 +18,66 @@ Tests of DOM Worker Threads XHR(Bug 450452 )
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("xhr_worker.js");
function workerScript() {
var xhr = new XMLHttpRequest();
worker.onmessage = function(event) {
is(event.target, worker);
var args = eval(event.data);
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);
switch (args.type) {
case "progress": {
ok(parseInt(args.current) <= parseInt(args.total));
@ -42,12 +97,12 @@ Tests of DOM Worker Threads XHR(Bug 450452 )
}
};
worker.onerror = function(event) {
is(event.target, worker);
ok(false, "Worker had an error:" + event.data);
pool.errorListener = function(error, source) {
ok(false, "Worker had an error:" + error);
SimpleTest.finish();
}
var worker = pool.createWorker("(" + workerScript + ")();");
worker.postMessage("testXHR.txt");
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -744,9 +744,6 @@ 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))