зеркало из https://github.com/mozilla/pjs.git
Bug 450449 - Backing out, bad merge.
This commit is contained in:
Родитель
fc98f85b3d
Коммит
58ce8fe4b7
|
@ -122,15 +122,6 @@ interface nsIDOMWorkerPool : nsISupports
|
|||
* the script will run.
|
||||
*/
|
||||
nsIDOMWorkerThread createWorker(in DOMString aSourceScript);
|
||||
|
||||
/**
|
||||
* Create a new worker object by evaluating the given script.
|
||||
*
|
||||
* @param aSourceURL (in AString)
|
||||
* The script url to load and compile. See below for details on the
|
||||
* scope in which the script will run.
|
||||
*/
|
||||
nsIDOMWorkerThread createWorkerFromURL(in AString aSourceURL);
|
||||
};
|
||||
|
||||
[scriptable, uuid(0f2a52ea-afc9-49e6-86dd-2d0cb65b5dd5)]
|
||||
|
|
|
@ -53,7 +53,6 @@ REQUIRES = \
|
|||
content \
|
||||
js \
|
||||
layout \
|
||||
necko \
|
||||
pref \
|
||||
string \
|
||||
widget \
|
||||
|
@ -65,7 +64,6 @@ CPPSRCS = \
|
|||
nsDOMThreadService.cpp \
|
||||
nsDOMWorkerBase.cpp \
|
||||
nsDOMWorkerPool.cpp \
|
||||
nsDOMWorkerScriptLoader.cpp \
|
||||
nsDOMWorkerSecurityManager.cpp \
|
||||
nsDOMWorkerThread.cpp \
|
||||
nsDOMWorkerTimeout.cpp \
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is worker threads.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __NSAUTOJSOBJECTHOLDER_H__
|
||||
#define __NSAUTOJSOBJECTHOLDER_H__
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
/**
|
||||
* Simple class that looks and acts like a JSObject* except that it unroots
|
||||
* itself automatically if Root() is ever called. Designed to be rooted on the
|
||||
* context or runtime (but not both!). Also automatically nulls its JSObject*
|
||||
* on Unroot and asserts that Root has been called prior to assigning an object.
|
||||
*/
|
||||
class nsAutoJSObjectHolder
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor, no holding.
|
||||
*/
|
||||
nsAutoJSObjectHolder()
|
||||
: mRt(NULL), mObj(NULL), mHeld(PR_FALSE) { }
|
||||
|
||||
/**
|
||||
* Hold by rooting on the context's runtime in the constructor, passing the
|
||||
* result out.
|
||||
*/
|
||||
nsAutoJSObjectHolder(JSContext* aCx, JSBool* aRv = NULL,
|
||||
JSObject* aObj = NULL)
|
||||
: mRt(NULL), mObj(aObj), mHeld(JS_FALSE) {
|
||||
JSBool rv = Hold(aCx);
|
||||
if (aRv) {
|
||||
*aRv = rv;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold by rooting on the runtime in the constructor, passing the result out.
|
||||
*/
|
||||
nsAutoJSObjectHolder(JSRuntime* aRt, JSBool* aRv = NULL,
|
||||
JSObject* aObj = NULL)
|
||||
: mRt(aRt), mObj(aObj), mHeld(JS_FALSE) {
|
||||
JSBool rv = Hold(aRt);
|
||||
if (aRv) {
|
||||
*aRv = rv;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always release on destruction.
|
||||
*/
|
||||
~nsAutoJSObjectHolder() {
|
||||
Release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold by rooting on the context's runtime.
|
||||
*/
|
||||
JSBool Hold(JSContext* aCx) {
|
||||
return Hold(JS_GetRuntime(aCx));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold by rooting on the runtime.
|
||||
*/
|
||||
JSBool Hold(JSRuntime* aRt) {
|
||||
if (!mHeld) {
|
||||
mHeld = JS_AddNamedRootRT(aRt, &mObj, "nsAutoRootedJSObject");
|
||||
if (mHeld) {
|
||||
mRt = aRt;
|
||||
}
|
||||
}
|
||||
return mHeld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually release.
|
||||
*/
|
||||
void Release() {
|
||||
NS_ASSERTION(!mHeld || mRt, "Bad!");
|
||||
if (mHeld) {
|
||||
mHeld = !JS_RemoveRootRT(mRt, &mObj);
|
||||
if (!mHeld) {
|
||||
mRt = NULL;
|
||||
}
|
||||
mObj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if Hold has been called.
|
||||
*/
|
||||
JSBool IsHeld() {
|
||||
return mHeld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretend to be a JSObject*.
|
||||
*/
|
||||
JSObject* get() const {
|
||||
return mObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretend to be a JSObject*.
|
||||
*/
|
||||
operator JSObject*() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretend to be a JSObject*. Assert if not held.
|
||||
*/
|
||||
JSObject* operator=(JSObject* aOther) {
|
||||
NS_ASSERTION(mHeld, "Not rooted!");
|
||||
return mObj = aOther;
|
||||
}
|
||||
|
||||
private:
|
||||
JSRuntime* mRt;
|
||||
JSObject* mObj;
|
||||
JSBool mHeld;
|
||||
};
|
||||
|
||||
#endif /* __NSAUTOJSOBJECTHOLDER_H__ */
|
|
@ -42,8 +42,6 @@
|
|||
// Interfaces
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
|
@ -90,14 +88,6 @@ PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= 1);
|
|||
|
||||
PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= THREADPOOL_IDLE_THREADS);
|
||||
|
||||
// As we suspend threads for various reasons (navigating away from the page,
|
||||
// loading scripts, etc.) we open another slot in the thread pool for another
|
||||
// worker to use. We can't do this forever so we set an absolute cap on the
|
||||
// number of threads we'll allow to prevent DOS attacks.
|
||||
#define THREADPOOL_THREAD_CAP 20
|
||||
|
||||
PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >= THREADPOOL_MAX_THREADS);
|
||||
|
||||
// The number of times our JS operation callback will be called before yielding
|
||||
// the thread
|
||||
#define CALLBACK_YIELD_THRESHOLD 100
|
||||
|
@ -396,7 +386,6 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
|||
nsRefPtr<nsDOMWorkerPool> pool;
|
||||
|
||||
PRBool wasSuspended = PR_FALSE;
|
||||
PRBool extraThreadAllowed = PR_FALSE;
|
||||
jsrefcount suspendDepth = 0;
|
||||
|
||||
while (1) {
|
||||
|
@ -406,9 +395,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
|||
static_cast<void*>(worker)));
|
||||
|
||||
if (wasSuspended) {
|
||||
if (extraThreadAllowed) {
|
||||
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
|
||||
}
|
||||
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
|
||||
JS_ResumeRequest(aCx, suspendDepth);
|
||||
}
|
||||
|
||||
|
@ -419,9 +406,7 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
|||
// Break out if we're not suspended.
|
||||
if (!worker->IsSuspended()) {
|
||||
if (wasSuspended) {
|
||||
if (extraThreadAllowed) {
|
||||
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
|
||||
}
|
||||
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
|
||||
JS_ResumeRequest(aCx, suspendDepth);
|
||||
}
|
||||
break;
|
||||
|
@ -442,10 +427,8 @@ DOMWorkerOperationCallback(JSContext* aCx)
|
|||
suspendDepth = JS_SuspendRequest(aCx);
|
||||
|
||||
// Since we're going to block this thread we should open up a new thread
|
||||
// in the thread pool for other workers. Must check the return value to
|
||||
// make sure we don't decrement when we failed.
|
||||
extraThreadAllowed =
|
||||
NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
|
||||
// in the thread pool for other workers.
|
||||
gDOMThreadService->ChangeThreadPoolMaxThreads(1);
|
||||
|
||||
// Only do all this setup once.
|
||||
wasSuspended = PR_TRUE;
|
||||
|
@ -782,14 +765,6 @@ nsDOMThreadService::CreateJSContext()
|
|||
JS_SetOperationCallback(cx, DOMWorkerOperationCallback,
|
||||
100 * JS_OPERATION_WEIGHT_BASE);
|
||||
|
||||
static JSSecurityCallbacks securityCallbacks = {
|
||||
nsDOMWorkerSecurityManager::JSCheckAccess,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
JS_SetContextSecurityCallbacks(cx, &securityCallbacks);
|
||||
|
||||
nsresult rv = nsContentUtils::XPConnect()->
|
||||
SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
@ -858,11 +833,6 @@ nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
|
|||
NS_ASSERTION(newThreadCount >= THREADPOOL_MAX_THREADS,
|
||||
"Can't go below initial thread count!");
|
||||
|
||||
if (newThreadCount > THREADPOOL_THREAD_CAP) {
|
||||
NS_WARNING("Thread pool cap reached!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = mThreadPool->SetThreadLimit((PRUint32)newThreadCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -1008,13 +978,7 @@ nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
|
|||
|
||||
NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
|
||||
|
||||
nsIDOMDocument* domDocument = nsContentUtils::GetDocumentFromCaller();
|
||||
NS_ENSURE_TRUE(domDocument, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsIDocument> callingDocument(do_QueryInterface(domDocument));
|
||||
NS_ENSURE_TRUE(callingDocument, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool(callingDocument));
|
||||
nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool());
|
||||
NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = pool->Init();
|
||||
|
|
|
@ -69,12 +69,10 @@
|
|||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
nsDOMWorkerPool::nsDOMWorkerPool(nsIDocument* aDocument)
|
||||
: mParentGlobal(nsnull),
|
||||
mParentDocument(aDocument)
|
||||
nsDOMWorkerPool::nsDOMWorkerPool()
|
||||
: mParentGlobal(nsnull)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(aDocument, "Must have a document!");
|
||||
}
|
||||
|
||||
nsDOMWorkerPool::~nsDOMWorkerPool()
|
||||
|
@ -236,14 +234,6 @@ nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
|
|||
}
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
nsDOMWorkerPool::GetParentDocument()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(),
|
||||
"Don't touch the non-threadsafe document off the main thread!");
|
||||
return mParentDocument;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
|
||||
{
|
||||
|
@ -286,39 +276,15 @@ nsDOMWorkerPool::GetErrorListener(nsIDOMWorkerErrorListener** aListener)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerPool::CreateWorker(const nsAString& aFullScript,
|
||||
nsDOMWorkerPool::CreateWorker(const nsAString& fullScript,
|
||||
nsIDOMWorkerThread** _retval)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ENSURE_ARG(!aFullScript.IsEmpty());
|
||||
NS_ENSURE_ARG(!fullScript.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);
|
||||
nsRefPtr<nsDOMWorkerThread> worker(new nsDOMWorkerThread(this, fullScript));
|
||||
NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = worker->Init();
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "prmon.h"
|
||||
|
||||
class nsDOMWorkerThread;
|
||||
class nsIDocument;
|
||||
class nsIScriptError;
|
||||
class nsIScriptGlobalObject;
|
||||
|
||||
|
@ -66,8 +65,6 @@ class nsDOMWorkerPool : public nsDOMWorkerBase,
|
|||
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);
|
||||
|
@ -77,7 +74,7 @@ public:
|
|||
NS_DECL_NSIDOMWORKERPOOL
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerPool(nsIDocument* aDocument);
|
||||
nsDOMWorkerPool();
|
||||
|
||||
// For nsDOMWorkerBase
|
||||
virtual nsDOMWorkerPool* Pool() {
|
||||
|
@ -109,14 +106,9 @@ private:
|
|||
return mMonitor;
|
||||
}
|
||||
|
||||
nsIDocument* GetParentDocument();
|
||||
|
||||
// Weak reference to the window that created and owns this pool.
|
||||
nsISupports* mParentGlobal;
|
||||
|
||||
// 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.
|
||||
nsTPtrArray<nsDOMWorkerThread> mWorkers;
|
||||
|
|
|
@ -1,790 +0,0 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is worker threads.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsDOMWorkerScriptLoader.h"
|
||||
|
||||
// Interfaces
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIRequest.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
|
||||
// Other includes
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsContentErrors.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsNetError.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsScriptLoader.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "pratom.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerPool.h"
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorkerTimeout.h"
|
||||
|
||||
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
|
||||
|
||||
nsDOMWorkerScriptLoader::nsDOMWorkerScriptLoader()
|
||||
: mWorker(nsnull),
|
||||
mTarget(nsnull),
|
||||
mCx(NULL),
|
||||
mScriptCount(0),
|
||||
mCanceled(PR_FALSE),
|
||||
mTrackedByWorker(PR_FALSE)
|
||||
{
|
||||
// Created on worker thread.
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
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;
|
||||
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(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());
|
||||
mTrackedByWorker = nsnull != mWorker->mScriptLoaders.AppendElement(this);
|
||||
NS_ASSERTION(mTrackedByWorker, "Failed to add loader to worker's array!");
|
||||
}
|
||||
|
||||
if (mCanceled) {
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
mScriptCount = aURLs.Length();
|
||||
if (!mScriptCount) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Do all the memory work for these arrays now rather than checking for
|
||||
// failures all along the way.
|
||||
PRBool success = mLoadInfos.SetCapacity(mScriptCount);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Need one runnable per script and then an extra for the finished
|
||||
// notification.
|
||||
success = mPendingRunnables.SetCapacity(mScriptCount + 1);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo* newInfo = mLoadInfos.AppendElement();
|
||||
NS_ASSERTION(newInfo, "Shouldn't fail if SetCapacity succeeded above!");
|
||||
|
||||
newInfo->url.Assign(aURLs[index]);
|
||||
if (newInfo->url.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
success = newInfo->scriptObj.Hold(aCx);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// Don't want timeouts, etc., from queuing up while we're waiting on the
|
||||
// network or compiling.
|
||||
AutoSuspendWorkerEvents aswe(this);
|
||||
|
||||
nsresult rv = DoRunLoop();
|
||||
|
||||
{
|
||||
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();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ExecuteScripts();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::LoadScript(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
const nsString& aURL)
|
||||
{
|
||||
nsAutoTArray<nsString, 1> url;
|
||||
url.AppendElement(aURL);
|
||||
|
||||
return LoadScripts(aWorker, aCx, url);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::DoRunLoop()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
volatile PRBool done = PR_FALSE;
|
||||
mDoneRunnable = new ScriptLoaderDone(this, &done);
|
||||
NS_ENSURE_TRUE(mDoneRunnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = NS_DispatchToMainThread(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!(done || mCanceled)) {
|
||||
// Since we're going to lock up this thread we might as well allow the
|
||||
// thread service to schedule another worker on a new thread.
|
||||
nsDOMThreadService* threadService = nsDOMThreadService::get();
|
||||
PRBool changed = NS_SUCCEEDED(threadService->ChangeThreadPoolMaxThreads(1));
|
||||
|
||||
while (!(done || mCanceled)) {
|
||||
JSAutoSuspendRequest asr(mCx);
|
||||
NS_ProcessNextEvent(mTarget);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
threadService->ChangeThreadPoolMaxThreads(-1);
|
||||
}
|
||||
}
|
||||
|
||||
return mCanceled ? NS_ERROR_ABORT : NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::VerifyScripts()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
NS_ASSERTION(loadInfo.done, "Inconsistent state!");
|
||||
|
||||
if (NS_SUCCEEDED(loadInfo.result) && loadInfo.scriptObj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!loadInfo.scriptObj, "Inconsistent state!");
|
||||
|
||||
// Flag failure before worrying about whether or not to report an error.
|
||||
rv = NS_FAILED(loadInfo.result) ? loadInfo.result : NS_ERROR_FAILURE;
|
||||
|
||||
// If loadInfo.result is a success code then the compiler probably reported
|
||||
// an error already. Also we don't really care about NS_BINDING_ABORTED
|
||||
// since that's the code we set when some other script had a problem and the
|
||||
// rest were canceled.
|
||||
if (NS_SUCCEEDED(loadInfo.result) || loadInfo.result == NS_BINDING_ABORTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok, this is the script that caused us to fail.
|
||||
|
||||
// Only throw an error there is no other pending exception.
|
||||
if (!JS_IsExceptionPending(mCx)) {
|
||||
NS_ConvertUTF16toUTF8 url(loadInfo.url);
|
||||
JS_ReportError(mCx, "Failed to compile script: %s", url.get());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::ExecuteScripts()
|
||||
{
|
||||
// Now execute all the scripts.
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
|
||||
JSScript* script =
|
||||
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(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(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
|
||||
jsval val;
|
||||
PRBool success = JS_ExecuteScript(mCx, global, script, &val);
|
||||
|
||||
JS_SetOptions(mCx, oldOpts);
|
||||
|
||||
if (!success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::Cancel()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ASSERTION(!mCanceled, "Cancel called more than once!");
|
||||
mCanceled = PR_TRUE;
|
||||
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
|
||||
nsIRequest* request =
|
||||
static_cast<nsIRequest*>(loadInfo.channel.get());
|
||||
if (request) {
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
request->Cancel(NS_BINDING_ABORTED);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to cancel channel!");
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoTArray<ScriptLoaderRunnable*, 10> runnables;
|
||||
{
|
||||
nsAutoLock lock(mWorker->Lock());
|
||||
runnables.AppendElements(mPendingRunnables);
|
||||
mPendingRunnables.Clear();
|
||||
}
|
||||
|
||||
PRUint32 runnableCount = runnables.Length();
|
||||
for (PRUint32 index = 0; index < runnableCount; index++) {
|
||||
runnables[index]->Revoke();
|
||||
}
|
||||
|
||||
// We're about to post a revoked event to the worker thread, which seems
|
||||
// silly, but we have to do this because the worker thread may be sleeping
|
||||
// waiting on its event queue.
|
||||
NotifyDone();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerScriptLoader::Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// We may have been canceled already.
|
||||
if (mCanceled) {
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
nsresult rv = RunInternal();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Ok, something failed beyond a normal cancel.
|
||||
|
||||
// If necko is holding a ref to us then we'll end up notifying in the
|
||||
// OnStreamComplete method, not here.
|
||||
PRBool needsNotify = PR_TRUE;
|
||||
|
||||
// Cancel any async channels that were already opened.
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
|
||||
nsIRequest* request = static_cast<nsIRequest*>(loadInfo.channel.get());
|
||||
if (request) {
|
||||
#ifdef DEBUG
|
||||
nsresult rvInner =
|
||||
#endif
|
||||
request->Cancel(NS_BINDING_ABORTED);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rvInner), "Failed to cancel channel!");
|
||||
|
||||
// Necko is holding a ref to us so make sure that the OnStreamComplete
|
||||
// code sends the done event.
|
||||
needsNotify = PR_FALSE;
|
||||
}
|
||||
else {
|
||||
// Make sure to set this so that the OnStreamComplete code will dispatch
|
||||
// the done event.
|
||||
loadInfo.done = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsNotify) {
|
||||
NotifyDone();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatus,
|
||||
PRUint32 aStringLen,
|
||||
const PRUint8* aString)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// We may have been canceled already.
|
||||
if (mCanceled) {
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
nsresult rv = OnStreamCompleteInternal(aLoader, aContext, aStatus, aStringLen,
|
||||
aString);
|
||||
|
||||
// Dispatch the done event if we've received all the data.
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
if (!mLoadInfos[index].done) {
|
||||
// Some async load is still outstanding, don't notify yet.
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == mScriptCount - 1) {
|
||||
// All loads complete, signal the thread.
|
||||
NotifyDone();
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::RunInternal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// Things we need to make all this work...
|
||||
nsIDocument* parentDoc = mWorker->Pool()->GetParentDocument();
|
||||
NS_ASSERTION(parentDoc, "Null parent document?!");
|
||||
|
||||
// All of these can potentially be null, but that should be ok. We'll either
|
||||
// succeed without them or fail below.
|
||||
nsIURI* parentBaseURI = parentDoc->GetBaseURI();
|
||||
nsCOMPtr<nsILoadGroup> loadGroup(parentDoc->GetDocumentLoadGroup());
|
||||
nsCOMPtr<nsIIOService> ios(do_GetIOService());
|
||||
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
nsresult& rv = loadInfo.result;
|
||||
|
||||
nsCOMPtr<nsIURI>& uri = loadInfo.finalURI;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
|
||||
loadInfo.url, parentDoc,
|
||||
parentBaseURI);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
|
||||
|
||||
rv =
|
||||
secMan->CheckLoadURIWithPrincipal(parentDoc->NodePrincipal(), uri,
|
||||
nsIScriptSecurityManager::ALLOW_CHROME);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
|
||||
uri,
|
||||
parentDoc->NodePrincipal(),
|
||||
parentDoc,
|
||||
NS_LITERAL_CSTRING("text/javascript"),
|
||||
nsnull,
|
||||
&shouldLoad,
|
||||
nsContentUtils::GetContentPolicy(),
|
||||
secMan);
|
||||
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
|
||||
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
|
||||
}
|
||||
|
||||
// We need to know which index we're on in OnStreamComplete so we know where
|
||||
// to put the result.
|
||||
nsCOMPtr<nsISupportsPRUint32> indexSupports =
|
||||
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = indexSupports->SetData(index);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We don't care about progress so just use the simple stream loader for
|
||||
// OnStreamComplete notification only.
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(loadInfo.channel), uri, ios, loadGroup);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = loadInfo.channel->AsyncOpen(loader, indexSupports);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Null this out so we don't try to cancel it later.
|
||||
loadInfo.channel = nsnull;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatus,
|
||||
PRUint32 aStringLen,
|
||||
const PRUint8* aString)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
|
||||
NS_ENSURE_TRUE(indexSupports, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
PRUint32 index = PR_UINT32_MAX;
|
||||
indexSupports->GetData(&index);
|
||||
|
||||
if (index >= mScriptCount) {
|
||||
NS_NOTREACHED("This really can't fail or we'll hang!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
|
||||
NS_ASSERTION(!loadInfo.done, "Got complete on the same load twice!");
|
||||
loadInfo.done = PR_TRUE;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Make sure we're seeing the channel that we expect.
|
||||
nsCOMPtr<nsIRequest> request;
|
||||
nsresult rvDebug = aLoader->GetRequest(getter_AddRefs(request));
|
||||
|
||||
// When we cancel sometimes we get null here. That should be ok, but only if
|
||||
// we're canceled.
|
||||
NS_ASSERTION(NS_SUCCEEDED(rvDebug) || mCanceled, "GetRequest failed!");
|
||||
|
||||
if (NS_SUCCEEDED(rvDebug)) {
|
||||
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
|
||||
NS_ASSERTION(channel, "QI failed!");
|
||||
|
||||
nsCOMPtr<nsISupports> thisChannel(do_QueryInterface(channel));
|
||||
NS_ASSERTION(thisChannel, "QI failed!");
|
||||
|
||||
nsCOMPtr<nsISupports> ourChannel(do_QueryInterface(loadInfo.channel));
|
||||
NS_ASSERTION(ourChannel, "QI failed!");
|
||||
|
||||
NS_ASSERTION(thisChannel == ourChannel, "Wrong channel!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use an alias to keep rv and loadInfo.result in sync.
|
||||
nsresult& rv = loadInfo.result = aStatus;
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!(aStringLen && aString)) {
|
||||
return rv = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsIDocument* parentDoc = mWorker->Pool()->GetParentDocument();
|
||||
NS_ASSERTION(parentDoc, "Null parent document?!");
|
||||
|
||||
// Use the regular nsScriptLoader for this grunt work! Should be just fine
|
||||
// because we're running on the main thread.
|
||||
rv = nsScriptLoader::ConvertToUTF16(loadInfo.channel, aString, aStringLen,
|
||||
EmptyString(), parentDoc,
|
||||
loadInfo.scriptText);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (loadInfo.scriptText.IsEmpty()) {
|
||||
return rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCString filename;
|
||||
loadInfo.finalURI->GetSpec(filename);
|
||||
|
||||
if (filename.IsEmpty()) {
|
||||
filename.Assign(NS_LossyConvertUTF16toASCII(loadInfo.url));
|
||||
}
|
||||
else {
|
||||
// This will help callers figure out what their script url resolved to in
|
||||
// case of errors.
|
||||
loadInfo.url.Assign(NS_ConvertUTF8toUTF16(filename));
|
||||
}
|
||||
|
||||
nsRefPtr<ScriptCompiler> compiler =
|
||||
new ScriptCompiler(this, mCx, loadInfo.scriptText, filename,
|
||||
loadInfo.scriptObj);
|
||||
NS_ASSERTION(compiler, "Out of memory!");
|
||||
if (!compiler) {
|
||||
return rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
rv = mTarget->Dispatch(compiler, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::NotifyDone()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (!mDoneRunnable) {
|
||||
// We've already completed, no need to cancel anything.
|
||||
return;
|
||||
}
|
||||
|
||||
for (PRUint32 index = 0; index < mScriptCount; index++) {
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[index];
|
||||
// Null both of these out because they aren't threadsafe and must be
|
||||
// destroyed on this thread.
|
||||
loadInfo.channel = nsnull;
|
||||
loadInfo.finalURI = nsnull;
|
||||
|
||||
if (mCanceled) {
|
||||
// Simulate a complete, yet failed, load.
|
||||
loadInfo.done = PR_TRUE;
|
||||
loadInfo.result = NS_BINDING_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
mTarget->Dispatch(mDoneRunnable, NS_DISPATCH_NORMAL);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't dispatch done event!");
|
||||
|
||||
mDoneRunnable = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::SuspendWorkerEvents()
|
||||
{
|
||||
NS_ASSERTION(mWorker, "No worker yet!");
|
||||
mWorker->SuspendTimeouts();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::ResumeWorkerEvents()
|
||||
{
|
||||
NS_ASSERTION(mWorker, "No worker yet!");
|
||||
mWorker->ResumeTimeouts();
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
ScriptLoaderRunnable::ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader)
|
||||
: mRevoked(PR_FALSE),
|
||||
mLoader(aLoader)
|
||||
{
|
||||
nsAutoLock lock(aLoader->Lock());
|
||||
#ifdef DEBUG
|
||||
nsDOMWorkerScriptLoader::ScriptLoaderRunnable** added =
|
||||
#endif
|
||||
aLoader->mPendingRunnables.AppendElement(this);
|
||||
NS_ASSERTION(added, "This shouldn't fail because we SetCapacity earlier!");
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
ScriptLoaderRunnable::~ScriptLoaderRunnable()
|
||||
{
|
||||
if (!mRevoked) {
|
||||
nsAutoLock lock(mLoader->Lock());
|
||||
#ifdef DEBUG
|
||||
PRBool removed =
|
||||
#endif
|
||||
mLoader->mPendingRunnables.RemoveElement(this);
|
||||
NS_ASSERTION(removed, "Someone has changed the array!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerScriptLoader::ScriptLoaderRunnable::Revoke()
|
||||
{
|
||||
mRevoked = PR_TRUE;
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
ScriptCompiler::ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
|
||||
JSContext* aCx,
|
||||
const nsString& aScriptText,
|
||||
const nsCString& aFilename,
|
||||
nsAutoJSObjectHolder& aScriptObj)
|
||||
: ScriptLoaderRunnable(aLoader),
|
||||
mCx(aCx),
|
||||
mScriptText(aScriptText),
|
||||
mFilename(aFilename),
|
||||
mScriptObj(aScriptObj)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Null context!");
|
||||
NS_ASSERTION(!aScriptText.IsEmpty(), "No script to compile!");
|
||||
NS_ASSERTION(aScriptObj.IsHeld(), "Should be held!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerScriptLoader::ScriptCompiler::Run()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mRevoked) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!mScriptObj, "Already have a script object?!");
|
||||
NS_ASSERTION(mScriptObj.IsHeld(), "Not held?!");
|
||||
NS_ASSERTION(!mScriptText.IsEmpty(), "Shouldn't have empty source here!");
|
||||
|
||||
JSAutoRequest ar(mCx);
|
||||
|
||||
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(mCx, JS_GetOptions(mCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
|
||||
|
||||
JSScript* script = JS_CompileUCScript(mCx, global, mScriptText.BeginReading(),
|
||||
mScriptText.Length(), mFilename.get(),
|
||||
1);
|
||||
JS_SetOptions(mCx, oldOpts);
|
||||
|
||||
if (!script) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mScriptObj = JS_NewScriptObject(mCx, script);
|
||||
NS_ENSURE_STATE(mScriptObj);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
ScriptLoaderDone::ScriptLoaderDone(nsDOMWorkerScriptLoader* aLoader,
|
||||
volatile PRBool* aDoneFlag)
|
||||
: ScriptLoaderRunnable(aLoader),
|
||||
mDoneFlag(aDoneFlag)
|
||||
{
|
||||
NS_ASSERTION(aDoneFlag && !*aDoneFlag, "Bad setup!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerScriptLoader::ScriptLoaderDone::Run()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (mRevoked) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*mDoneFlag = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
AutoSuspendWorkerEvents::AutoSuspendWorkerEvents(nsDOMWorkerScriptLoader* aLoader)
|
||||
: mLoader(aLoader)
|
||||
{
|
||||
NS_ASSERTION(aLoader, "Don't hand me null!");
|
||||
aLoader->SuspendWorkerEvents();
|
||||
}
|
||||
|
||||
nsDOMWorkerScriptLoader::
|
||||
AutoSuspendWorkerEvents::~AutoSuspendWorkerEvents()
|
||||
{
|
||||
mLoader->ResumeWorkerEvents();
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is worker threads.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __NSDOMWORKERSCRIPTLOADER_H__
|
||||
#define __NSDOMWORKERSCRIPTLOADER_H__
|
||||
|
||||
// Bases
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
|
||||
// Interfaces
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
// Other includes
|
||||
#include "jsapi.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsAutoJSObjectHolder.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
#include "prlock.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerThread.h"
|
||||
|
||||
/**
|
||||
* This class takes a list of script URLs, downloads the scripts, compiles the
|
||||
* scripts, and then finally executes them. Due to platform limitations all
|
||||
* network operations must happen on the main thread so this object sends events
|
||||
* back and forth from the worker thread to the main thread. The flow goes like
|
||||
* this:
|
||||
*
|
||||
* 1. (Worker thread) nsDOMWorkerScriptLoader created.
|
||||
* 2. (Worker thread) LoadScript(s) called. Some simple argument validation is
|
||||
* performed (currently limited to ensuring that all
|
||||
* arguments are strings). nsDOMWorkerScriptLoader is then
|
||||
* dispatched to the main thread.
|
||||
* 3. (Main thread) Arguments validated as URIs, security checks performed,
|
||||
* content policy consulted. Network loads begin.
|
||||
* 4. (Necko thread) Necko stuff!
|
||||
* 5. (Main thread) Completed downloads are packaged in a ScriptCompiler
|
||||
* runnable and sent to the worker thread.
|
||||
* 6. (Worker thread) ScriptCompiler runnables are processed (i.e. their
|
||||
* scripts are compiled) in the order in which the necko
|
||||
* downloads completed.
|
||||
* 7. (Worker thread) After all loads complete and all compilation succeeds
|
||||
* the scripts are executed in the order that the URLs were
|
||||
* given to LoadScript(s).
|
||||
*
|
||||
* Currently if *anything* after 2 fails then we cancel any pending loads and
|
||||
* bail out entirely.
|
||||
*/
|
||||
class nsDOMWorkerScriptLoader : public nsRunnable,
|
||||
public nsIStreamLoaderObserver
|
||||
{
|
||||
friend class AutoSuspendWorkerEvents;
|
||||
friend class nsDOMWorkerFunctions;
|
||||
friend class nsDOMWorkerThread;
|
||||
friend class ScriptLoaderRunnable;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
|
||||
nsDOMWorkerScriptLoader();
|
||||
|
||||
nsresult LoadScripts(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
const nsTArray<nsString>& aURLs);
|
||||
|
||||
nsresult LoadScript(nsDOMWorkerThread* aWorker,
|
||||
JSContext* aCx,
|
||||
const nsString& aURL);
|
||||
|
||||
void Cancel();
|
||||
|
||||
private:
|
||||
~nsDOMWorkerScriptLoader();
|
||||
|
||||
nsresult DoRunLoop();
|
||||
nsresult VerifyScripts();
|
||||
nsresult ExecuteScripts();
|
||||
|
||||
nsresult RunInternal();
|
||||
|
||||
nsresult OnStreamCompleteInternal(nsIStreamLoader* aLoader,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatus,
|
||||
PRUint32 aStringLen,
|
||||
const PRUint8* aString);
|
||||
|
||||
void NotifyDone();
|
||||
|
||||
void SuspendWorkerEvents();
|
||||
void ResumeWorkerEvents();
|
||||
|
||||
PRLock* Lock() {
|
||||
return mWorker->Lock();
|
||||
}
|
||||
|
||||
class ScriptLoaderRunnable : public nsRunnable
|
||||
{
|
||||
protected:
|
||||
// Meant to be inherited.
|
||||
ScriptLoaderRunnable(nsDOMWorkerScriptLoader* aLoader);
|
||||
virtual ~ScriptLoaderRunnable();
|
||||
|
||||
public:
|
||||
void Revoke();
|
||||
|
||||
protected:
|
||||
PRBool mRevoked;
|
||||
|
||||
private:
|
||||
nsDOMWorkerScriptLoader* mLoader;
|
||||
};
|
||||
|
||||
class ScriptCompiler : public ScriptLoaderRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
ScriptCompiler(nsDOMWorkerScriptLoader* aLoader,
|
||||
JSContext* aCx,
|
||||
const nsString& aScriptText,
|
||||
const nsCString& aFilename,
|
||||
nsAutoJSObjectHolder& aScriptObj);
|
||||
|
||||
private:
|
||||
JSContext* mCx;
|
||||
nsString mScriptText;
|
||||
nsCString mFilename;
|
||||
nsAutoJSObjectHolder& mScriptObj;
|
||||
};
|
||||
|
||||
class ScriptLoaderDone : public ScriptLoaderRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
ScriptLoaderDone(nsDOMWorkerScriptLoader* aLoader,
|
||||
volatile PRBool* aDoneFlag);
|
||||
|
||||
private:
|
||||
volatile PRBool* mDoneFlag;
|
||||
};
|
||||
|
||||
class AutoSuspendWorkerEvents
|
||||
{
|
||||
public:
|
||||
AutoSuspendWorkerEvents(nsDOMWorkerScriptLoader* aLoader);
|
||||
~AutoSuspendWorkerEvents();
|
||||
|
||||
private:
|
||||
nsDOMWorkerScriptLoader* mLoader;
|
||||
};
|
||||
|
||||
struct ScriptLoadInfo
|
||||
{
|
||||
ScriptLoadInfo() : done(PR_FALSE), result(NS_ERROR_NOT_INITIALIZED) { }
|
||||
|
||||
nsString url;
|
||||
nsString scriptText;
|
||||
PRBool done;
|
||||
nsresult result;
|
||||
nsCOMPtr<nsIURI> finalURI;
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsAutoJSObjectHolder scriptObj;
|
||||
};
|
||||
|
||||
nsDOMWorkerThread* mWorker;
|
||||
nsIThread* mTarget;
|
||||
JSContext* mCx;
|
||||
|
||||
nsRefPtr<ScriptLoaderDone> mDoneRunnable;
|
||||
|
||||
PRUint32 mScriptCount;
|
||||
nsTArray<ScriptLoadInfo> mLoadInfos;
|
||||
|
||||
PRPackedBool mCanceled;
|
||||
PRPackedBool mTrackedByWorker;
|
||||
|
||||
// Protected by mWorker's lock!
|
||||
nsTArray<ScriptLoaderRunnable*> mPendingRunnables;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERSCRIPTLOADER_H__ */
|
|
@ -88,13 +88,3 @@ nsDOMWorkerSecurityManager::CanAccess(PRUint32 aAction,
|
|||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerSecurityManager::JSCheckAccess(JSContext *cx,
|
||||
JSObject *obj,
|
||||
jsval id,
|
||||
JSAccessMode mode,
|
||||
jsval *vp)
|
||||
{
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
|
|
@ -40,17 +40,12 @@
|
|||
#define __NSDOMWORKERSECURITYMANAGER_H__
|
||||
|
||||
#include "nsIXPCSecurityManager.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
class nsDOMWorkerSecurityManager : public nsIXPCSecurityManager
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIXPCSECURITYMANAGER
|
||||
|
||||
static JSBool JSCheckAccess(JSContext *cx, JSObject *obj, jsval id,
|
||||
JSAccessMode mode, jsval *vp);
|
||||
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERSECURITYMANAGER_H__ */
|
||||
|
|
|
@ -54,11 +54,9 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMWorkerPool.h"
|
||||
#include "nsDOMWorkerScriptLoader.h"
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorkerTimeout.h"
|
||||
|
||||
|
@ -96,9 +94,6 @@ public:
|
|||
static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
static JSBool LoadScripts(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
jsval* aArgv, jsval* aRval);
|
||||
|
||||
private:
|
||||
// Internal helper for SetTimeout and SetInterval.
|
||||
static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
|
||||
|
@ -164,11 +159,7 @@ nsDOMWorkerFunctions::PostMessage(JSContext* aCx,
|
|||
else {
|
||||
rv = pool->PostMessageInternal(EmptyString(), worker);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to post message!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, JS_FALSE);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -193,16 +184,10 @@ nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
|
|||
|
||||
nsAutoPtr<nsDOMWorkerTimeout>
|
||||
timeout(new nsDOMWorkerTimeout(worker, id));
|
||||
if (!timeout) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
NS_ENSURE_TRUE(timeout, JS_FALSE);
|
||||
|
||||
nsresult rv = timeout->Init(aCx, aArgc, aArgv, aIsInterval);
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to initialize timeout!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, JS_FALSE);
|
||||
|
||||
timeout.forget();
|
||||
|
||||
|
@ -241,67 +226,6 @@ nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool JS_DLL_CALLBACK
|
||||
nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
|
||||
JSObject* /* aObj */,
|
||||
uintN aArgc,
|
||||
jsval* aArgv,
|
||||
jsval* /* aRval */)
|
||||
{
|
||||
nsDOMWorkerThread* worker =
|
||||
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (!aArgc) {
|
||||
JS_ReportError(aCx, "Function must have at least one argument!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsAutoTArray<nsString, 5> urls;
|
||||
|
||||
if (!urls.SetCapacity((PRUint32)aArgc)) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
for (uintN index = 0; index < aArgc; index++) {
|
||||
jsval val = aArgv[index];
|
||||
|
||||
if (!JSVAL_IS_STRING(val)) {
|
||||
JS_ReportError(aCx, "Argument %d must be a string", index);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSString* str = JS_ValueToString(aCx, val);
|
||||
if (!str) {
|
||||
JS_ReportError(aCx, "Couldn't convert argument %d to a string", index);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsString* newURL = urls.AppendElement();
|
||||
NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!");
|
||||
|
||||
newURL->Assign(nsDependentJSString(str));
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMWorkerScriptLoader> loader = new nsDOMWorkerScriptLoader();
|
||||
if (!loader) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsresult rv = loader->LoadScripts(worker, aCx, urls);
|
||||
if (NS_FAILED(rv)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSFunctionSpec gDOMWorkerFunctions[] = {
|
||||
{ "dump", nsDOMWorkerFunctions::Dump, 1, 0, 0 },
|
||||
{ "debug", nsDOMWorkerFunctions::DebugDump, 1, 0, 0 },
|
||||
|
@ -310,7 +234,6 @@ JSFunctionSpec gDOMWorkerFunctions[] = {
|
|||
{ "clearTimeout", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 },
|
||||
{ "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0, 0 },
|
||||
{ "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 },
|
||||
{ "loadScripts", nsDOMWorkerFunctions::LoadScripts, 1, 0, 0 },
|
||||
#ifdef MOZ_SHARK
|
||||
{ "startShark", js_StartShark, 0, 0, 0 },
|
||||
{ "stopShark", js_StopShark, 0, 0, 0 },
|
||||
|
@ -383,24 +306,17 @@ nsDOMWorkerThreadContext::GetThisThread(nsIDOMWorkerThread** aThisThread)
|
|||
}
|
||||
|
||||
nsDOMWorkerThread::nsDOMWorkerThread(nsDOMWorkerPool* aPool,
|
||||
const nsAString& aSource,
|
||||
PRBool aSourceIsURL)
|
||||
const nsAString& aSource)
|
||||
: mPool(aPool),
|
||||
mSource(aSource),
|
||||
mGlobal(nsnull),
|
||||
mCompiled(PR_FALSE),
|
||||
mCallbackCount(0),
|
||||
mNextTimeoutId(0),
|
||||
mLock(nsnull)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
if (aSourceIsURL) {
|
||||
mSourceURL.Assign(aSource);
|
||||
NS_ASSERTION(!mSourceURL.IsEmpty(), "Empty source url!");
|
||||
}
|
||||
else {
|
||||
mSource.Assign(aSource);
|
||||
NS_ASSERTION(!mSource.IsEmpty(), "Empty source string!");
|
||||
}
|
||||
NS_ASSERTION(!aSource.IsEmpty(), "Empty source string!");
|
||||
|
||||
PR_INIT_CLIST(&mTimeouts);
|
||||
}
|
||||
|
@ -416,6 +332,17 @@ nsDOMWorkerThread::~nsDOMWorkerThread()
|
|||
|
||||
ClearTimeouts();
|
||||
|
||||
// Only clean up if we created a global object
|
||||
if (mGlobal) {
|
||||
JSRuntime* rt;
|
||||
if (NS_SUCCEEDED(nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt))) {
|
||||
JS_RemoveRootRT(rt, &mGlobal);
|
||||
}
|
||||
else {
|
||||
NS_ERROR("This shouldn't fail!");
|
||||
}
|
||||
}
|
||||
|
||||
if (mLock) {
|
||||
nsAutoLock::DestroyLock(mLock);
|
||||
}
|
||||
|
@ -435,20 +362,13 @@ nsDOMWorkerThread::Init()
|
|||
|
||||
NS_ASSERTION(!mGlobal, "Already got a global?!");
|
||||
|
||||
JSRuntime* rt;
|
||||
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool success = mGlobal.Hold(rt);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
|
||||
|
||||
// This is pretty cool - all we have to do to get our script executed is to
|
||||
// pass a no-op runnable to the thread service and it will make sure we have
|
||||
// a context and global object.
|
||||
nsCOMPtr<nsIRunnable> runnable(new nsRunnable());
|
||||
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = nsDOMThreadService::get()->Dispatch(this, runnable);
|
||||
nsresult rv = nsDOMThreadService::get()->Dispatch(this, runnable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -525,11 +445,15 @@ nsDOMWorkerThread::HandleMessage(const nsAString& aMessage,
|
|||
jsval rval;
|
||||
PRBool success = JS_CallFunctionValue(cx, mGlobal, OBJECT_TO_JSVAL(listener),
|
||||
2, argv, &rval);
|
||||
if (!success && JS_IsExceptionPending(cx)) {
|
||||
if (!success) {
|
||||
// Make sure any pending exceptions are converted to errors for the pool.
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
|
||||
// We shouldn't leave any pending exceptions - our error reporter should
|
||||
// clear any exception it reports.
|
||||
NS_ASSERTION(!JS_IsExceptionPending(cx), "Huh?!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -548,9 +472,6 @@ nsDOMWorkerThread::Cancel()
|
|||
{
|
||||
nsDOMWorkerBase::Cancel();
|
||||
|
||||
// Do this before waiting on the thread service below!
|
||||
CancelScriptLoaders();
|
||||
|
||||
// If we're suspended there's a good chance that we're already paused waiting
|
||||
// on the pool's monitor. Waiting on the thread service's lock will deadlock.
|
||||
if (!IsSuspended()) {
|
||||
|
@ -578,9 +499,7 @@ PRBool
|
|||
nsDOMWorkerThread::SetGlobalForContext(JSContext* aCx)
|
||||
{
|
||||
PRBool success = CompileGlobalObject(aCx);
|
||||
if (!success) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
JS_SetGlobalObject(aCx, mGlobal);
|
||||
return PR_TRUE;
|
||||
|
@ -644,38 +563,32 @@ nsDOMWorkerThread::CompileGlobalObject(JSContext* aCx)
|
|||
JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
jsval val;
|
||||
JSScript* script = JS_CompileUCScript(aCx, global,
|
||||
reinterpret_cast<const jschar*>
|
||||
(mSource.BeginReading()),
|
||||
mSource.Length(), nsnull, 1);
|
||||
NS_ENSURE_TRUE(script, PR_FALSE);
|
||||
|
||||
JSRuntime* rt;
|
||||
rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
// From here on out we have to remember to null mGlobal if something fails!
|
||||
mGlobal = global;
|
||||
|
||||
if (mSource.IsEmpty()) {
|
||||
NS_ASSERTION(!mSourceURL.IsEmpty(), "Must have a url here!");
|
||||
|
||||
nsRefPtr<nsDOMWorkerScriptLoader> loader = new nsDOMWorkerScriptLoader();
|
||||
NS_ASSERTION(loader, "Out of memory!");
|
||||
if (!loader) {
|
||||
mGlobal = NULL;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
rv = loader->LoadScript(this, aCx, mSourceURL);
|
||||
JS_ReportPendingException(aCx);
|
||||
if (NS_FAILED(rv)) {
|
||||
mGlobal = NULL;
|
||||
return PR_FALSE;
|
||||
}
|
||||
success = JS_AddNamedRootRT(rt, &mGlobal, "nsDOMWorkerThread Global Object");
|
||||
if (!success) {
|
||||
NS_WARNING("Failed to root global object for worker thread!");
|
||||
mGlobal = nsnull;
|
||||
return PR_FALSE;
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(!mSource.IsEmpty(), "No source text!");
|
||||
|
||||
// Evaluate and execute the script
|
||||
success = JS_EvaluateUCScript(aCx, global, mSource.get(), mSource.Length(),
|
||||
"DOMWorker inline script", 1, &val);
|
||||
if (!success) {
|
||||
mGlobal = NULL;
|
||||
return PR_FALSE;
|
||||
}
|
||||
// Execute the script
|
||||
jsval val;
|
||||
success = JS_ExecuteScript(aCx, global, script, &val);
|
||||
if (!success) {
|
||||
NS_WARNING("Failed to evaluate script for worker thread!");
|
||||
JS_RemoveRootRT(rt, &mGlobal);
|
||||
mGlobal = nsnull;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// See if the message listener function was defined.
|
||||
|
@ -832,26 +745,6 @@ nsDOMWorkerThread::ResumeTimeouts()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMWorkerThread::CancelScriptLoaders()
|
||||
{
|
||||
nsAutoTArray<nsDOMWorkerScriptLoader*, 20> loaders;
|
||||
|
||||
// Must call cancel on the loaders outside the lock!
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
loaders.AppendElements(mScriptLoaders);
|
||||
|
||||
// Don't clear mScriptLoaders, they'll remove themselves as they get
|
||||
// destroyed.
|
||||
}
|
||||
|
||||
PRUint32 loaderCount = loaders.Length();
|
||||
for (PRUint32 index = 0; index < loaderCount; index++) {
|
||||
loaders[index]->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerThread::PostMessage(const nsAString& aMessage)
|
||||
{
|
||||
|
|
|
@ -47,10 +47,8 @@
|
|||
|
||||
// Other includes
|
||||
#include "jsapi.h"
|
||||
#include "nsAutoJSObjectHolder.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prclist.h"
|
||||
#include "prlock.h"
|
||||
|
@ -115,7 +113,6 @@ _class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \
|
|||
}
|
||||
|
||||
class nsDOMWorkerPool;
|
||||
class nsDOMWorkerScriptLoader;
|
||||
class nsDOMWorkerTimeout;
|
||||
|
||||
class nsDOMWorkerThread : public nsDOMWorkerBase,
|
||||
|
@ -126,7 +123,6 @@ class nsDOMWorkerThread : public nsDOMWorkerBase,
|
|||
friend class nsDOMWorkerFunctions;
|
||||
friend class nsDOMWorkerPool;
|
||||
friend class nsDOMWorkerRunnable;
|
||||
friend class nsDOMWorkerScriptLoader;
|
||||
friend class nsDOMWorkerTimeout;
|
||||
|
||||
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
|
||||
|
@ -137,8 +133,7 @@ public:
|
|||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsDOMWorkerThread(nsDOMWorkerPool* aPool,
|
||||
const nsAString& aSource,
|
||||
PRBool aSourceIsURL);
|
||||
const nsAString& aSource);
|
||||
|
||||
virtual nsDOMWorkerPool* Pool() {
|
||||
NS_ASSERTION(!IsCanceled(), "Don't touch Pool after we've been canceled!");
|
||||
|
@ -174,17 +169,10 @@ private:
|
|||
void SuspendTimeouts();
|
||||
void ResumeTimeouts();
|
||||
|
||||
void CancelScriptLoaders();
|
||||
|
||||
PRLock* Lock() {
|
||||
return mLock;
|
||||
}
|
||||
|
||||
nsDOMWorkerPool* mPool;
|
||||
nsString mSource;
|
||||
nsString mSourceURL;
|
||||
|
||||
nsAutoJSObjectHolder mGlobal;
|
||||
JSObject* mGlobal;
|
||||
PRBool mCompiled;
|
||||
|
||||
PRUint32 mCallbackCount;
|
||||
|
@ -193,8 +181,6 @@ private:
|
|||
|
||||
PRLock* mLock;
|
||||
PRCList mTimeouts;
|
||||
|
||||
nsTArray<nsDOMWorkerScriptLoader*> mScriptLoaders;
|
||||
};
|
||||
|
||||
#endif /* __NSDOMWORKERTHREAD_H__ */
|
||||
|
|
|
@ -47,12 +47,6 @@ include $(DEPTH)/config/autoconf.mk
|
|||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = \
|
||||
importScripts_worker.js \
|
||||
importScripts_worker_imported1.js \
|
||||
importScripts_worker_imported2.js \
|
||||
importScripts_worker_imported3.js \
|
||||
importScripts_worker_imported4.js \
|
||||
test_importScripts.html \
|
||||
test_simpleThread.html \
|
||||
test_threadErrors.html \
|
||||
test_threadTimeouts.html \
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
function messageListener(message, source) {
|
||||
switch (message) {
|
||||
case 'start':
|
||||
loadScripts("importScripts_worker_imported2.js");
|
||||
importedScriptFunction2();
|
||||
tryBadScripts();
|
||||
source.postMessage('started');
|
||||
break;
|
||||
case 'stop':
|
||||
tryBadScripts();
|
||||
postMessageToPool('stopped');
|
||||
break;
|
||||
default:
|
||||
throw new Error("Bad message: " + message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This caused security exceptions in the past, make sure it doesn't!
|
||||
var constructor = {}.constructor;
|
||||
|
||||
loadScripts("importScripts_worker_imported1.js");
|
||||
|
||||
// Try to call a function defined in the imported script.
|
||||
importedScriptFunction();
|
||||
|
||||
function tryBadScripts() {
|
||||
var badScripts = [
|
||||
// Has a syntax error
|
||||
"importScripts_worker_imported3.js",
|
||||
// Throws an exception
|
||||
"importScripts_worker_imported4.js",
|
||||
// Shouldn't exist!
|
||||
"http://flippety.com/floppety/foo.js",
|
||||
// Not a valid url
|
||||
"http://flippety::foo_js ftw"
|
||||
];
|
||||
|
||||
for (var i = 0; i < badScripts.length; i++) {
|
||||
var caughtException = false;
|
||||
var url = badScripts[i];
|
||||
try {
|
||||
loadScripts(url);
|
||||
}
|
||||
catch (e) {
|
||||
caughtException = true;
|
||||
}
|
||||
if (!caughtException) {
|
||||
throw "Bad script didn't throw exception: " + url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryBadScripts();
|
|
@ -1,7 +0,0 @@
|
|||
// This caused security exceptions in the past, make sure it doesn't!
|
||||
var myConstructor = {}.constructor;
|
||||
|
||||
// Try to call a function defined in the imported script.
|
||||
function importedScriptFunction() {
|
||||
dump("running importedScriptFunction\n");
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// This caused security exceptions in the past, make sure it doesn't!
|
||||
var myConstructor2 = {}.constructor;
|
||||
|
||||
// Try to call a function defined in the imported script.
|
||||
function importedScriptFunction2() {
|
||||
dump("running importedScriptFunction2\n");
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// Deliberate syntax error, should generate a worker exception!
|
||||
for (var index = 0; index < 100) {}
|
|
@ -1,2 +0,0 @@
|
|||
// Deliberate throw, should generate a worker exception!
|
||||
throw new Error("Bah!");
|
|
@ -1,49 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Tests of DOM Worker Threads (Bug 437152)
|
||||
-->
|
||||
<head>
|
||||
<title>Test for DOM Worker Threads (Bug 437152)</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var pool = navigator.newWorkerPool();
|
||||
pool.messageListener = function(message, source) {
|
||||
switch (message) {
|
||||
case "started":
|
||||
source.postMessage("stop");
|
||||
break;
|
||||
case "stopped":
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unexpected message:" + message);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
pool.errorListener = function(error, source) {
|
||||
ok(false, "Worker had an error:" + error);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var worker = pool.createWorkerFromURL("importScripts_worker.js");
|
||||
worker.postMessage("start");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Загрузка…
Ссылка в новой задаче