Bug 450449 - Backing out, bad merge.

This commit is contained in:
Ben Turner 2008-09-08 00:36:19 -07:00
Родитель fc98f85b3d
Коммит 58ce8fe4b7
19 изменённых файлов: 60 добавлений и 1586 удалений

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

@ -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>