forked from mirrors/gecko-dev
		
	 820e368a57
			
		
	
	
		820e368a57
		
	
	
	
	
		
			
			--HG-- rename : dom/src/threads/Makefile.in => dom/workers/Makefile.in rename : dom/src/threads/test/Makefile.in => dom/workers/test/Makefile.in rename : dom/src/threads/test/WorkerTest.jsm => dom/workers/test/WorkerTest.jsm rename : dom/src/threads/test/WorkerTest_badworker.js => dom/workers/test/WorkerTest_badworker.js rename : dom/src/threads/test/WorkerTest_subworker.js => dom/workers/test/WorkerTest_subworker.js rename : dom/src/threads/test/WorkerTest_worker.js => dom/workers/test/WorkerTest_worker.js rename : dom/src/threads/test/atob_worker.js => dom/workers/test/atob_worker.js rename : dom/src/threads/test/chromeWorker_subworker.js => dom/workers/test/chromeWorker_subworker.js rename : dom/src/threads/test/chromeWorker_worker.js => dom/workers/test/chromeWorker_worker.js rename : dom/src/threads/test/closeOnGC_server.sjs => dom/workers/test/closeOnGC_server.sjs rename : dom/src/threads/test/closeOnGC_worker.js => dom/workers/test/closeOnGC_worker.js rename : dom/src/threads/test/close_worker.js => dom/workers/test/close_worker.js rename : dom/src/threads/test/fibonacci_worker.js => dom/workers/test/fibonacci_worker.js rename : dom/src/threads/test/importScripts_worker.js => dom/workers/test/importScripts_worker.js rename : dom/src/threads/test/importScripts_worker_imported1.js => dom/workers/test/importScripts_worker_imported1.js rename : dom/src/threads/test/importScripts_worker_imported2.js => dom/workers/test/importScripts_worker_imported2.js rename : dom/src/threads/test/importScripts_worker_imported3.js => dom/workers/test/importScripts_worker_imported3.js rename : dom/src/threads/test/importScripts_worker_imported4.js => dom/workers/test/importScripts_worker_imported4.js rename : dom/src/threads/test/json_worker.js => dom/workers/test/json_worker.js rename : dom/src/threads/test/location_worker.js => dom/workers/test/location_worker.js rename : dom/src/threads/test/longThread_worker.js => dom/workers/test/longThread_worker.js rename : dom/src/threads/test/navigator_worker.js => dom/workers/test/navigator_worker.js rename : dom/src/threads/test/newError_worker.js => dom/workers/test/newError_worker.js rename : dom/src/threads/test/recursion_worker.js => dom/workers/test/recursion_worker.js rename : dom/src/threads/test/relativeLoad_import.js => dom/workers/test/relativeLoad_import.js rename : dom/src/threads/test/relativeLoad_sub_import.js => dom/workers/test/relativeLoad_sub_import.js rename : dom/src/threads/test/relativeLoad_sub_worker.js => dom/workers/test/relativeLoad_sub_worker.js rename : dom/src/threads/test/relativeLoad_sub_worker2.js => dom/workers/test/relativeLoad_sub_worker2.js rename : dom/src/threads/test/relativeLoad_worker.js => dom/workers/test/relativeLoad_worker.js rename : dom/src/threads/test/relativeLoad_worker2.js => dom/workers/test/relativeLoad_worker2.js rename : dom/src/threads/test/simpleThread_worker.js => dom/workers/test/simpleThread_worker.js rename : dom/src/threads/test/suspend_iframe.html => dom/workers/test/suspend_iframe.html rename : dom/src/threads/test/suspend_worker.js => dom/workers/test/suspend_worker.js rename : dom/src/threads/test/terminate_worker.js => dom/workers/test/terminate_worker.js rename : dom/src/threads/test/testXHR.txt => dom/workers/test/testXHR.txt rename : dom/src/threads/test/test_404.html => dom/workers/test/test_404.html rename : dom/src/threads/test/test_atob.html => dom/workers/test/test_atob.html rename : dom/src/threads/test/test_chromeWorker.html => dom/workers/test/test_chromeWorker.html rename : dom/src/threads/test/test_chromeWorker.xul => dom/workers/test/test_chromeWorker.xul rename : dom/src/threads/test/test_chromeWorkerJSM.xul => dom/workers/test/test_chromeWorkerJSM.xul rename : dom/src/threads/test/test_close.html => dom/workers/test/test_close.html rename : dom/src/threads/test/test_closeOnGC.html => dom/workers/test/test_closeOnGC.html rename : dom/src/threads/test/test_errorPropagation.html => dom/workers/test/test_errorPropagation.html rename : dom/src/threads/test/test_fibonacci.html => dom/workers/test/test_fibonacci.html rename : dom/src/threads/test/test_importScripts.html => dom/workers/test/test_importScripts.html rename : dom/src/threads/test/test_json.html => dom/workers/test/test_json.html rename : dom/src/threads/test/test_location.html => dom/workers/test/test_location.html rename : dom/src/threads/test/test_longThread.html => dom/workers/test/test_longThread.html rename : dom/src/threads/test/test_navigator.html => dom/workers/test/test_navigator.html rename : dom/src/threads/test/test_newError.html => dom/workers/test/test_newError.html rename : dom/src/threads/test/test_recursion.html => dom/workers/test/test_recursion.html rename : dom/src/threads/test/test_relativeLoad.html => dom/workers/test/test_relativeLoad.html rename : dom/src/threads/test/test_simpleThread.html => dom/workers/test/test_simpleThread.html rename : dom/src/threads/test/test_suspend.html => dom/workers/test/test_suspend.html rename : dom/src/threads/test/test_terminate.html => dom/workers/test/test_terminate.html rename : dom/src/threads/test/test_threadErrors.html => dom/workers/test/test_threadErrors.html rename : dom/src/threads/test/test_threadTimeouts.html => dom/workers/test/test_threadTimeouts.html rename : dom/src/threads/test/test_throwingOnerror.html => dom/workers/test/test_throwingOnerror.html rename : dom/src/threads/test/test_xhr.html => dom/workers/test/test_xhr.html rename : dom/src/threads/test/test_xhrAbort.html => dom/workers/test/test_xhrAbort.html rename : dom/src/threads/test/threadErrors_worker1.js => dom/workers/test/threadErrors_worker1.js rename : dom/src/threads/test/threadErrors_worker2.js => dom/workers/test/threadErrors_worker2.js rename : dom/src/threads/test/threadErrors_worker3.js => dom/workers/test/threadErrors_worker3.js rename : dom/src/threads/test/threadErrors_worker4.js => dom/workers/test/threadErrors_worker4.js rename : dom/src/threads/test/threadTimeouts_worker.js => dom/workers/test/threadTimeouts_worker.js rename : dom/src/threads/test/throwingOnerror_worker.js => dom/workers/test/throwingOnerror_worker.js rename : dom/src/threads/test/xhrAbort_worker.js => dom/workers/test/xhrAbort_worker.js rename : dom/src/threads/test/xhr_worker.js => dom/workers/test/xhr_worker.js
		
			
				
	
	
		
			755 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			755 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: c++; c-basic-offset: 2; 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 Web Workers.
 | |
|  *
 | |
|  * The Initial Developer of the Original Code is
 | |
|  *   The Mozilla Foundation.
 | |
|  * Portions created by the Initial Developer are Copyright (C) 2011
 | |
|  * 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 "ScriptLoader.h"
 | |
| 
 | |
| #include "nsIChannel.h"
 | |
| #include "nsIChannelPolicy.h"
 | |
| #include "nsIContentPolicy.h"
 | |
| #include "nsIContentSecurityPolicy.h"
 | |
| #include "nsIHttpChannel.h"
 | |
| #include "nsIIOService.h"
 | |
| #include "nsIProtocolHandler.h"
 | |
| #include "nsIScriptSecurityManager.h"
 | |
| #include "nsIStreamLoader.h"
 | |
| #include "nsIURI.h"
 | |
| 
 | |
| #include "jsapi.h"
 | |
| #include "nsChannelPolicy.h"
 | |
| #include "nsContentErrors.h"
 | |
| #include "nsContentPolicyUtils.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsDocShellCID.h"
 | |
| #include "nsISupportsPrimitives.h"
 | |
| #include "nsNetError.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsScriptLoader.h"
 | |
| #include "nsStringGlue.h"
 | |
| #include "nsTArray.h"
 | |
| #include "nsThreadUtils.h"
 | |
| #include "nsXPCOM.h"
 | |
| 
 | |
| #include "Principal.h"
 | |
| #include "WorkerFeature.h"
 | |
| #include "WorkerPrivate.h"
 | |
| 
 | |
| #define MAX_CONCURRENT_SCRIPTS 1000
 | |
| 
 | |
| USING_WORKERS_NAMESPACE
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class ScriptLoaderRunnable;
 | |
| 
 | |
| struct ScriptLoadInfo
 | |
| {
 | |
|   ScriptLoadInfo()
 | |
|   : mLoadResult(NS_ERROR_NOT_INITIALIZED), mExecutionScheduled(false),
 | |
|     mExecutionResult(false)
 | |
|   { }
 | |
| 
 | |
|   bool
 | |
|   ReadyToExecute()
 | |
|   {
 | |
|     return !mChannel && NS_SUCCEEDED(mLoadResult) && !mExecutionScheduled;
 | |
|   }
 | |
| 
 | |
|   nsString mURL;
 | |
|   nsCOMPtr<nsIChannel> mChannel;
 | |
|   nsString mScriptText;
 | |
| 
 | |
|   nsresult mLoadResult;
 | |
|   bool mExecutionScheduled;
 | |
|   bool mExecutionResult;
 | |
| };
 | |
| 
 | |
| class ScriptExecutorRunnable : public WorkerSyncRunnable
 | |
| {
 | |
|   ScriptLoaderRunnable& mScriptLoader;
 | |
|   PRUint32 mFirstIndex;
 | |
|   PRUint32 mLastIndex;
 | |
| 
 | |
| public:
 | |
|   ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
 | |
|                          PRUint32 aSyncQueueKey, PRUint32 aFirstIndex,
 | |
|                          PRUint32 aLastIndex);
 | |
| 
 | |
|   bool
 | |
|   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
 | |
|                bool aDispatchResult)
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
|   }
 | |
| 
 | |
|   bool
 | |
|   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 | |
| 
 | |
|   void
 | |
|   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
 | |
| };
 | |
| 
 | |
| class ScriptLoaderRunnable : public WorkerFeature,
 | |
|                              public nsIRunnable,
 | |
|                              public nsIStreamLoaderObserver
 | |
| {
 | |
|   friend class ScriptExecutorRunnable;
 | |
| 
 | |
|   WorkerPrivate* mWorkerPrivate;
 | |
|   PRUint32 mSyncQueueKey;
 | |
|   nsTArray<ScriptLoadInfo> mLoadInfos;
 | |
|   bool mIsWorkerScript;
 | |
|   bool mCanceled;
 | |
|   bool mCanceledMainThread;
 | |
| 
 | |
| public:
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
 | |
|                        PRUint32 aSyncQueueKey,
 | |
|                        nsTArray<ScriptLoadInfo>& aLoadInfos,
 | |
|                        bool aIsWorkerScript)
 | |
|   : mWorkerPrivate(aWorkerPrivate), mSyncQueueKey(aSyncQueueKey),
 | |
|     mIsWorkerScript(aIsWorkerScript), mCanceled(false),
 | |
|     mCanceledMainThread(false)
 | |
|   {
 | |
|     aWorkerPrivate->AssertIsOnWorkerThread();
 | |
|     NS_ASSERTION(!aIsWorkerScript || aLoadInfos.Length() == 1, "Bad args!");
 | |
| 
 | |
|     if (!mLoadInfos.SwapElements(aLoadInfos)) {
 | |
|       NS_ERROR("This should never fail!");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   NS_IMETHOD
 | |
|   Run()
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
| 
 | |
|     if (NS_FAILED(RunInternal())) {
 | |
|       CancelMainThread();
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   NS_IMETHOD
 | |
|   OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
 | |
|                    nsresult aStatus, PRUint32 aStringLen,
 | |
|                    const PRUint8* aString)
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
| 
 | |
|     nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
 | |
|     NS_ASSERTION(indexSupports, "This should never fail!");
 | |
| 
 | |
|     PRUint32 index = PR_UINT32_MAX;
 | |
|     if (NS_FAILED(indexSupports->GetData(&index)) ||
 | |
|         index >= mLoadInfos.Length()) {
 | |
|       NS_ERROR("Bad index!");
 | |
|     }
 | |
| 
 | |
|     ScriptLoadInfo& loadInfo = mLoadInfos[index];
 | |
| 
 | |
|     loadInfo.mLoadResult = OnStreamCompleteInternal(aLoader, aContext, aStatus,
 | |
|                                                     aStringLen, aString,
 | |
|                                                     loadInfo);
 | |
| 
 | |
|     ExecuteFinishedScripts();
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   bool
 | |
|   Notify(JSContext* aCx, Status aStatus)
 | |
|   {
 | |
|     mWorkerPrivate->AssertIsOnWorkerThread();
 | |
| 
 | |
|     if (aStatus >= Terminating && !mCanceled) {
 | |
|       mCanceled = true;
 | |
| 
 | |
|       nsCOMPtr<nsIRunnable> runnable =
 | |
|         NS_NewRunnableMethod(this, &ScriptLoaderRunnable::CancelMainThread);
 | |
|       NS_ASSERTION(runnable, "This should never fail!");
 | |
| 
 | |
|       if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
 | |
|         JS_ReportError(aCx, "Failed to cancel script loader!");
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   CancelMainThread()
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
| 
 | |
|     if (mCanceledMainThread) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     mCanceledMainThread = true;
 | |
| 
 | |
|     // Cancel all the channels that were already opened.
 | |
|     for (PRUint32 index = 0; index < mLoadInfos.Length(); index++) {
 | |
|       ScriptLoadInfo& loadInfo = mLoadInfos[index];
 | |
| 
 | |
|       if (loadInfo.mChannel &&
 | |
|           NS_FAILED(loadInfo.mChannel->Cancel(NS_BINDING_ABORTED))) {
 | |
|         NS_WARNING("Failed to cancel channel!");
 | |
|         loadInfo.mChannel = nsnull;
 | |
|         loadInfo.mLoadResult = NS_BINDING_ABORTED;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     ExecuteFinishedScripts();
 | |
|   }
 | |
| 
 | |
|   nsresult
 | |
|   RunInternal()
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
| 
 | |
|     WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
 | |
| 
 | |
|     // Figure out which principal to use.
 | |
|     nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
 | |
|     if (!principal) {
 | |
|       NS_ASSERTION(parentWorker, "Must have a principal!");
 | |
|       NS_ASSERTION(mIsWorkerScript, "Must have a principal for importScripts!");
 | |
| 
 | |
|       principal = parentWorker->GetPrincipal();
 | |
|     }
 | |
|     NS_ASSERTION(principal, "This should never be null here!");
 | |
| 
 | |
|     // Figure out our base URI.
 | |
|     nsCOMPtr<nsIURI> baseURI;
 | |
|     if (mIsWorkerScript) {
 | |
|       if (parentWorker) {
 | |
|         baseURI = parentWorker->GetBaseURI();
 | |
|         NS_ASSERTION(baseURI, "Should have been set already!");
 | |
|       }
 | |
|       else {
 | |
|         // May be null.
 | |
|         baseURI = mWorkerPrivate->GetBaseURI();
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       baseURI = mWorkerPrivate->GetBaseURI();
 | |
|       NS_ASSERTION(baseURI, "Should have been set already!");
 | |
|     }
 | |
| 
 | |
|     // May be null.
 | |
|     nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
 | |
| 
 | |
|     // All of these can potentially be null, but that should be ok. We'll either
 | |
|     // succeed without them or fail below.
 | |
|     nsCOMPtr<nsILoadGroup> loadGroup;
 | |
|     if (parentDoc) {
 | |
|       loadGroup = parentDoc->GetDocumentLoadGroup();
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIIOService> ios(do_GetIOService());
 | |
| 
 | |
|     nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
 | |
|     NS_ASSERTION(secMan, "This should never be null!");
 | |
| 
 | |
|     for (PRUint32 index = 0; index < mLoadInfos.Length(); index++) {
 | |
|       ScriptLoadInfo& loadInfo = mLoadInfos[index];
 | |
|       nsresult& rv = loadInfo.mLoadResult;
 | |
| 
 | |
|       nsCOMPtr<nsIURI> uri;
 | |
|       rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
 | |
|                                                      loadInfo.mURL, parentDoc,
 | |
|                                                      baseURI);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         return rv;
 | |
|       }
 | |
| 
 | |
|       // If we're part of a document then check the content load policy.
 | |
|       if (parentDoc) {
 | |
|         PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
 | |
|         rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
 | |
|                                        principal, 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 rv = NS_ERROR_CONTENT_BLOCKED;
 | |
|           }
 | |
|           return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // If this script loader is being used to make a new worker then we need
 | |
|       // to do a same-origin check. Otherwise we need to clear the load with the
 | |
|       // security manager.
 | |
|       rv = mIsWorkerScript ?
 | |
|            principal->CheckMayLoad(uri, PR_FALSE):
 | |
|            secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       // 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);
 | |
| 
 | |
|       // Get Content Security Policy from parent document to pass into channel.
 | |
|       nsCOMPtr<nsIContentSecurityPolicy> csp;
 | |
|       rv = principal->GetCsp(getter_AddRefs(csp));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       nsCOMPtr<nsIChannelPolicy> channelPolicy;
 | |
|       if (csp) {
 | |
|         channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         rv = channelPolicy->SetContentSecurityPolicy(csp);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       }
 | |
| 
 | |
|       PRUint32 flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
 | |
| 
 | |
|       nsCOMPtr<nsIChannel> channel;
 | |
|       rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nsnull,
 | |
|                          flags, channelPolicy);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       rv = channel->AsyncOpen(loader, indexSupports);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       loadInfo.mChannel.swap(channel);
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsresult
 | |
|   OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsISupports* aContext,
 | |
|                            nsresult aStatus, PRUint32 aStringLen,
 | |
|                            const PRUint8* aString, ScriptLoadInfo& aLoadInfo)
 | |
|   {
 | |
|     AssertIsOnMainThread();
 | |
| 
 | |
|     if (!aLoadInfo.mChannel) {
 | |
|       return NS_BINDING_ABORTED;
 | |
|     }
 | |
| 
 | |
|     aLoadInfo.mChannel = nsnull;
 | |
| 
 | |
|     if (NS_FAILED(aStatus)) {
 | |
|       return aStatus;
 | |
|     }
 | |
| 
 | |
|     if (!aStringLen) {
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     NS_ASSERTION(aString, "This should never be null!");
 | |
| 
 | |
|     // Make sure we're not seeing the result of a 404 or something by checking
 | |
|     // the 'requestSucceeded' attribute on the http channel.
 | |
|     nsCOMPtr<nsIRequest> request;
 | |
|     nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
 | |
|     if (httpChannel) {
 | |
|       PRBool requestSucceeded;
 | |
|       rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       if (!requestSucceeded) {
 | |
|         return NS_ERROR_NOT_AVAILABLE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // May be null.
 | |
|     nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
 | |
| 
 | |
|     // Use the regular nsScriptLoader for this grunt work! Should be just fine
 | |
|     // because we're running on the main thread.
 | |
|     rv = nsScriptLoader::ConvertToUTF16(aLoadInfo.mChannel, aString, aStringLen,
 | |
|                                         EmptyString(), parentDoc,
 | |
|                                         aLoadInfo.mScriptText);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return rv;
 | |
|     }
 | |
| 
 | |
|     if (aLoadInfo.mScriptText.IsEmpty()) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
 | |
|     NS_ASSERTION(channel, "This should never fail!");
 | |
| 
 | |
|     // Figure out what we actually loaded.
 | |
|     nsCOMPtr<nsIURI> finalURI;
 | |
|     rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     nsCString filename;
 | |
|     rv = finalURI->GetSpec(filename);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     if (!filename.IsEmpty()) {
 | |
|       // This will help callers figure out what their script url resolved to in
 | |
|       // case of errors.
 | |
|       aLoadInfo.mURL.Assign(NS_ConvertUTF8toUTF16(filename));
 | |
|     }
 | |
| 
 | |
|     // Update the principal of the worker and its base URI if we just loaded the
 | |
|     // worker's primary script.
 | |
|     if (mIsWorkerScript) {
 | |
|       // Take care of the base URI first.
 | |
|       rv = mWorkerPrivate->SetBaseURI(finalURI);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       // Now to figure out which principal to give this worker.
 | |
|       WorkerPrivate* parent = mWorkerPrivate->GetParent();
 | |
| 
 | |
|       NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
 | |
|                    "Must have one of these!");
 | |
| 
 | |
|       nsCOMPtr<nsIPrincipal> loadPrincipal = mWorkerPrivate->GetPrincipal() ?
 | |
|                                              mWorkerPrivate->GetPrincipal() :
 | |
|                                              parent->GetPrincipal();
 | |
| 
 | |
|       nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
 | |
|       NS_ASSERTION(ssm, "Should never be null!");
 | |
| 
 | |
|       nsCOMPtr<nsIPrincipal> channelPrincipal;
 | |
|       rv = ssm->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|       // See if this is a resource URI. Since JSMs usually come from resource://
 | |
|       // URIs we're currently considering all URIs with the URI_IS_UI_RESOURCE
 | |
|       // flag as valid for creating privileged workers.
 | |
|       if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
 | |
|         PRBool isResource;
 | |
|         rv = NS_URIChainHasFlags(finalURI,
 | |
|                                  nsIProtocolHandler::URI_IS_UI_RESOURCE,
 | |
|                                  &isResource);
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|         if (isResource) {
 | |
|           rv = ssm->GetSystemPrincipal(getter_AddRefs(channelPrincipal));
 | |
|           NS_ENSURE_SUCCESS(rv, rv);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // If the load principal is the system principal then the channel
 | |
|       // principal must also be the system principal (we do not allow chrome
 | |
|       // code to create workers with non-chrome scripts). Otherwise this channel
 | |
|       // principal must be same origin with the load principal (we check again
 | |
|       // here in case redirects changed the location of the script).
 | |
|       if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
 | |
|         if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
 | |
|           return NS_ERROR_DOM_BAD_URI;
 | |
|         }
 | |
|       }
 | |
|       else if (NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, PR_FALSE))) {
 | |
|         return NS_ERROR_DOM_BAD_URI;
 | |
|       }
 | |
| 
 | |
|       mWorkerPrivate->SetPrincipal(channelPrincipal);
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   void
 | |
|   ExecuteFinishedScripts()
 | |
|   {
 | |
|     PRUint32 firstIndex = PR_UINT32_MAX;
 | |
|     PRUint32 lastIndex = PR_UINT32_MAX;
 | |
| 
 | |
|     // Find firstIndex based on whether mExecutionScheduled is unset.
 | |
|     for (PRUint32 index = 0; index < mLoadInfos.Length(); index++) {
 | |
|       if (!mLoadInfos[index].mExecutionScheduled) {
 | |
|         firstIndex = index;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Find lastIndex based on whether mChannel is set, and update
 | |
|     // mExecutionScheduled on the ones we're about to schedule.
 | |
|     if (firstIndex != PR_UINT32_MAX) {
 | |
|       for (PRUint32 index = firstIndex; index < mLoadInfos.Length(); index++) {
 | |
|         ScriptLoadInfo& loadInfo = mLoadInfos[index];
 | |
| 
 | |
|         // If we still have a channel then the load is not complete.
 | |
|         if (loadInfo.mChannel) {
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         // We can execute this one.
 | |
|         loadInfo.mExecutionScheduled = true;
 | |
| 
 | |
|         lastIndex = index;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (firstIndex != PR_UINT32_MAX && lastIndex != PR_UINT32_MAX) {
 | |
|       nsRefPtr<ScriptExecutorRunnable> runnable =
 | |
|         new ScriptExecutorRunnable(*this, mSyncQueueKey, firstIndex, lastIndex);
 | |
|       if (!runnable->Dispatch(nsnull)) {
 | |
|         NS_ERROR("This should never fail!");
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| NS_IMPL_THREADSAFE_ISUPPORTS2(ScriptLoaderRunnable, nsIRunnable,
 | |
|                                                     nsIStreamLoaderObserver)
 | |
| 
 | |
| ScriptExecutorRunnable::ScriptExecutorRunnable(
 | |
|                                             ScriptLoaderRunnable& aScriptLoader,
 | |
|                                             PRUint32 aSyncQueueKey,
 | |
|                                             PRUint32 aFirstIndex,
 | |
|                                             PRUint32 aLastIndex)
 | |
| : WorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncQueueKey),
 | |
|   mScriptLoader(aScriptLoader), mFirstIndex(aFirstIndex), mLastIndex(aLastIndex)
 | |
| {
 | |
|   NS_ASSERTION(aFirstIndex <= aLastIndex, "Bad first index!");
 | |
|   NS_ASSERTION(aLastIndex < aScriptLoader.mLoadInfos.Length(),
 | |
|                "Bad last index!");
 | |
| }
 | |
| 
 | |
| bool
 | |
| ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 | |
| {
 | |
|   nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
 | |
| 
 | |
|   // Don't run if something else has already failed.
 | |
|   for (PRUint32 index = 0; index < mFirstIndex; index++) {
 | |
|     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
 | |
| 
 | |
|     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
 | |
|     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
 | |
| 
 | |
|     if (!loadInfo.mExecutionResult) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   JSObject* global = JS_GetGlobalObject(aCx);
 | |
|   NS_ASSERTION(global, "Must have a global by now!");
 | |
| 
 | |
|   JSPrincipals* principal = GetWorkerPrincipal();
 | |
|   NS_ASSERTION(principal, "This should never be null!");
 | |
| 
 | |
|   for (PRUint32 index = mFirstIndex; index <= mLastIndex; index++) {
 | |
|     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
 | |
| 
 | |
|     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
 | |
|     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
 | |
|     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
 | |
| 
 | |
|     if (NS_FAILED(loadInfo.mLoadResult)) {
 | |
|       NS_ConvertUTF16toUTF8 url(loadInfo.mURL);
 | |
| 
 | |
|       switch (loadInfo.mLoadResult) {
 | |
|         case NS_BINDING_ABORTED:
 | |
|           // Canceled, don't set an exception.
 | |
|           break;
 | |
| 
 | |
|         case NS_ERROR_MALFORMED_URI:
 | |
|           JS_ReportError(aCx, "Malformed script URI: %s", url.get());
 | |
|           break;
 | |
| 
 | |
|         case NS_ERROR_FILE_NOT_FOUND:
 | |
|         case NS_ERROR_NOT_AVAILABLE:
 | |
|           JS_ReportError(aCx, "Script file not found: %s", url.get());
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           JS_ReportError(aCx, "Failed to load script: %s (nsresult = 0x%x)",
 | |
|                          url.get(), loadInfo.mLoadResult);
 | |
|       }
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
 | |
| 
 | |
|     if (!JS_EvaluateUCScriptForPrincipals(aCx, global, principal,
 | |
|                                           loadInfo.mScriptText.get(),
 | |
|                                           loadInfo.mScriptText.Length(),
 | |
|                                           filename.get(), 1, nsnull)) {
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     loadInfo.mExecutionResult = true;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| ScriptExecutorRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
 | |
|                                 bool aRunResult)
 | |
| {
 | |
|   nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
 | |
| 
 | |
|   if (mLastIndex == loadInfos.Length() - 1) {
 | |
|     // All done. If anything failed then return false.
 | |
|     bool result = true;
 | |
|     for (PRUint32 index = 0; index < loadInfos.Length(); index++) {
 | |
|       if (!loadInfos[index].mExecutionResult) {
 | |
|         result = false;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     aWorkerPrivate->RemoveFeature(aCx, &mScriptLoader);
 | |
|     aWorkerPrivate->StopSyncLoop(mSyncQueueKey, result);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
 | |
|                nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsWorkerScript)
 | |
| {
 | |
|   aWorkerPrivate->AssertIsOnWorkerThread();
 | |
|   NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
 | |
| 
 | |
|   PRUint32 syncQueueKey = aWorkerPrivate->CreateNewSyncLoop();
 | |
| 
 | |
|   nsRefPtr<ScriptLoaderRunnable> loader =
 | |
|     new ScriptLoaderRunnable(aWorkerPrivate, syncQueueKey, aLoadInfos,
 | |
|                              aIsWorkerScript);
 | |
| 
 | |
|   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
 | |
| 
 | |
|   if (!aWorkerPrivate->AddFeature(aCx, loader)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (NS_FAILED(NS_DispatchToMainThread(loader, NS_DISPATCH_NORMAL))) {
 | |
|     NS_ERROR("Failed to dispatch!");
 | |
| 
 | |
|     aWorkerPrivate->RemoveFeature(aCx, loader);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return aWorkerPrivate->RunSyncLoop(aCx, syncQueueKey);
 | |
| }
 | |
| 
 | |
| } /* anonymous namespace */
 | |
| 
 | |
| BEGIN_WORKERS_NAMESPACE
 | |
| 
 | |
| namespace scriptloader {
 | |
| 
 | |
| bool
 | |
| LoadWorkerScript(JSContext* aCx)
 | |
| {
 | |
|   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
 | |
|   NS_ASSERTION(worker, "This should never be null!");
 | |
| 
 | |
|   nsTArray<ScriptLoadInfo> loadInfos;
 | |
| 
 | |
|   ScriptLoadInfo* info = loadInfos.AppendElement();
 | |
|   info->mURL = worker->ScriptURL();
 | |
| 
 | |
|   return LoadAllScripts(aCx, worker, loadInfos, true);
 | |
| }
 | |
| 
 | |
| bool
 | |
| Load(JSContext* aCx, uintN aURLCount, jsval* aURLs)
 | |
| {
 | |
|   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
 | |
|   NS_ASSERTION(worker, "This should never be null!");
 | |
| 
 | |
|   if (!aURLCount) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (aURLCount > MAX_CONCURRENT_SCRIPTS) {
 | |
|     JS_ReportError(aCx, "Cannot load more than %d scripts at one time!",
 | |
|                    MAX_CONCURRENT_SCRIPTS);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsTArray<ScriptLoadInfo> loadInfos;
 | |
|   loadInfos.SetLength(PRUint32(aURLCount));
 | |
| 
 | |
|   for (uintN index = 0; index < aURLCount; index++) {
 | |
|     JSString* str = JS_ValueToString(aCx, aURLs[index]);
 | |
|     if (!str) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     size_t length;
 | |
|     const jschar* buffer = JS_GetStringCharsAndLength(aCx, str, &length);
 | |
|     if (!buffer) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     loadInfos[index].mURL.Assign(buffer, length);
 | |
|   }
 | |
| 
 | |
|   return LoadAllScripts(aCx, worker, loadInfos, false);
 | |
| }
 | |
| 
 | |
| } // namespace scriptloader
 | |
| 
 | |
| END_WORKERS_NAMESPACE
 |