LCOV - code coverage report
Current view: directory - dom/workers - ScriptLoader.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 308 0 0.0 %
Date: 2012-06-02 Functions: 25 0 0.0 %

       1                 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Web Workers.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  *   The Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "ScriptLoader.h"
      40                 : 
      41                 : #include "nsIChannel.h"
      42                 : #include "nsIChannelPolicy.h"
      43                 : #include "nsIContentPolicy.h"
      44                 : #include "nsIContentSecurityPolicy.h"
      45                 : #include "nsIHttpChannel.h"
      46                 : #include "nsIIOService.h"
      47                 : #include "nsIProtocolHandler.h"
      48                 : #include "nsIScriptSecurityManager.h"
      49                 : #include "nsIStreamLoader.h"
      50                 : #include "nsIURI.h"
      51                 : 
      52                 : #include "jsapi.h"
      53                 : #include "nsChannelPolicy.h"
      54                 : #include "nsContentErrors.h"
      55                 : #include "nsContentPolicyUtils.h"
      56                 : #include "nsContentUtils.h"
      57                 : #include "nsDocShellCID.h"
      58                 : #include "nsISupportsPrimitives.h"
      59                 : #include "nsNetError.h"
      60                 : #include "nsNetUtil.h"
      61                 : #include "nsScriptLoader.h"
      62                 : #include "nsStringGlue.h"
      63                 : #include "nsTArray.h"
      64                 : #include "nsThreadUtils.h"
      65                 : #include "nsXPCOM.h"
      66                 : 
      67                 : #include "Principal.h"
      68                 : #include "WorkerFeature.h"
      69                 : #include "WorkerPrivate.h"
      70                 : 
      71                 : #define MAX_CONCURRENT_SCRIPTS 1000
      72                 : 
      73                 : USING_WORKERS_NAMESPACE
      74                 : 
      75                 : namespace {
      76                 : 
      77                 : class ScriptLoaderRunnable;
      78                 : 
      79                 : struct ScriptLoadInfo
      80               0 : {
      81               0 :   ScriptLoadInfo()
      82                 :   : mLoadResult(NS_ERROR_NOT_INITIALIZED), mExecutionScheduled(false),
      83               0 :     mExecutionResult(false)
      84               0 :   { }
      85                 : 
      86                 :   bool
      87                 :   ReadyToExecute()
      88                 :   {
      89                 :     return !mChannel && NS_SUCCEEDED(mLoadResult) && !mExecutionScheduled;
      90                 :   }
      91                 : 
      92                 :   nsString mURL;
      93                 :   nsCOMPtr<nsIChannel> mChannel;
      94                 :   nsString mScriptText;
      95                 : 
      96                 :   nsresult mLoadResult;
      97                 :   bool mExecutionScheduled;
      98                 :   bool mExecutionResult;
      99                 : };
     100                 : 
     101                 : class ScriptExecutorRunnable : public WorkerSyncRunnable
     102               0 : {
     103                 :   ScriptLoaderRunnable& mScriptLoader;
     104                 :   PRUint32 mFirstIndex;
     105                 :   PRUint32 mLastIndex;
     106                 : 
     107                 : public:
     108                 :   ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
     109                 :                          PRUint32 aSyncQueueKey, PRUint32 aFirstIndex,
     110                 :                          PRUint32 aLastIndex);
     111                 : 
     112                 :   bool
     113               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     114                 :   {
     115               0 :     AssertIsOnMainThread();
     116               0 :     return true;
     117                 :   }
     118                 : 
     119                 :   void
     120               0 :   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     121                 :                bool aDispatchResult)
     122                 :   {
     123               0 :     AssertIsOnMainThread();
     124               0 :   }
     125                 : 
     126                 :   bool
     127                 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
     128                 : 
     129                 :   void
     130                 :   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
     131                 : };
     132                 : 
     133                 : class ScriptLoaderRunnable : public WorkerFeature,
     134                 :                              public nsIRunnable,
     135                 :                              public nsIStreamLoaderObserver
     136               0 : {
     137                 :   friend class ScriptExecutorRunnable;
     138                 : 
     139                 :   WorkerPrivate* mWorkerPrivate;
     140                 :   PRUint32 mSyncQueueKey;
     141                 :   nsTArray<ScriptLoadInfo> mLoadInfos;
     142                 :   bool mIsWorkerScript;
     143                 :   bool mCanceled;
     144                 :   bool mCanceledMainThread;
     145                 : 
     146                 : public:
     147                 :   NS_DECL_ISUPPORTS
     148                 : 
     149               0 :   ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
     150                 :                        PRUint32 aSyncQueueKey,
     151                 :                        nsTArray<ScriptLoadInfo>& aLoadInfos,
     152                 :                        bool aIsWorkerScript)
     153                 :   : mWorkerPrivate(aWorkerPrivate), mSyncQueueKey(aSyncQueueKey),
     154                 :     mIsWorkerScript(aIsWorkerScript), mCanceled(false),
     155               0 :     mCanceledMainThread(false)
     156                 :   {
     157               0 :     aWorkerPrivate->AssertIsOnWorkerThread();
     158               0 :     NS_ASSERTION(!aIsWorkerScript || aLoadInfos.Length() == 1, "Bad args!");
     159                 : 
     160               0 :     if (!mLoadInfos.SwapElements(aLoadInfos)) {
     161               0 :       NS_ERROR("This should never fail!");
     162                 :     }
     163               0 :   }
     164                 : 
     165                 :   NS_IMETHOD
     166               0 :   Run()
     167                 :   {
     168               0 :     AssertIsOnMainThread();
     169                 : 
     170               0 :     if (NS_FAILED(RunInternal())) {
     171               0 :       CancelMainThread();
     172                 :     }
     173                 : 
     174               0 :     return NS_OK;
     175                 :   }
     176                 : 
     177                 :   NS_IMETHOD
     178               0 :   OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
     179                 :                    nsresult aStatus, PRUint32 aStringLen,
     180                 :                    const PRUint8* aString)
     181                 :   {
     182               0 :     AssertIsOnMainThread();
     183                 : 
     184               0 :     nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
     185               0 :     NS_ASSERTION(indexSupports, "This should never fail!");
     186                 : 
     187               0 :     PRUint32 index = PR_UINT32_MAX;
     188               0 :     if (NS_FAILED(indexSupports->GetData(&index)) ||
     189               0 :         index >= mLoadInfos.Length()) {
     190               0 :       NS_ERROR("Bad index!");
     191                 :     }
     192                 : 
     193               0 :     ScriptLoadInfo& loadInfo = mLoadInfos[index];
     194                 : 
     195                 :     loadInfo.mLoadResult = OnStreamCompleteInternal(aLoader, aContext, aStatus,
     196                 :                                                     aStringLen, aString,
     197               0 :                                                     loadInfo);
     198                 : 
     199               0 :     ExecuteFinishedScripts();
     200                 : 
     201               0 :     return NS_OK;
     202                 :   }
     203                 : 
     204                 :   bool
     205               0 :   Notify(JSContext* aCx, Status aStatus)
     206                 :   {
     207               0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     208                 : 
     209               0 :     if (aStatus >= Terminating && !mCanceled) {
     210               0 :       mCanceled = true;
     211                 : 
     212                 :       nsCOMPtr<nsIRunnable> runnable =
     213               0 :         NS_NewRunnableMethod(this, &ScriptLoaderRunnable::CancelMainThread);
     214               0 :       NS_ASSERTION(runnable, "This should never fail!");
     215                 : 
     216               0 :       if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
     217               0 :         JS_ReportError(aCx, "Failed to cancel script loader!");
     218               0 :         return false;
     219                 :       }
     220                 :     }
     221                 : 
     222               0 :     return true;
     223                 :   }
     224                 : 
     225                 :   void
     226               0 :   CancelMainThread()
     227                 :   {
     228               0 :     AssertIsOnMainThread();
     229                 : 
     230               0 :     if (mCanceledMainThread) {
     231               0 :       return;
     232                 :     }
     233                 : 
     234               0 :     mCanceledMainThread = true;
     235                 : 
     236                 :     // Cancel all the channels that were already opened.
     237               0 :     for (PRUint32 index = 0; index < mLoadInfos.Length(); index++) {
     238               0 :       ScriptLoadInfo& loadInfo = mLoadInfos[index];
     239                 : 
     240               0 :       if (loadInfo.mChannel &&
     241               0 :           NS_FAILED(loadInfo.mChannel->Cancel(NS_BINDING_ABORTED))) {
     242               0 :         NS_WARNING("Failed to cancel channel!");
     243               0 :         loadInfo.mChannel = nsnull;
     244               0 :         loadInfo.mLoadResult = NS_BINDING_ABORTED;
     245                 :       }
     246                 :     }
     247                 : 
     248               0 :     ExecuteFinishedScripts();
     249                 :   }
     250                 : 
     251                 :   nsresult
     252               0 :   RunInternal()
     253                 :   {
     254               0 :     AssertIsOnMainThread();
     255                 : 
     256               0 :     WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
     257                 : 
     258                 :     // Figure out which principal to use.
     259               0 :     nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
     260               0 :     if (!principal) {
     261               0 :       NS_ASSERTION(parentWorker, "Must have a principal!");
     262               0 :       NS_ASSERTION(mIsWorkerScript, "Must have a principal for importScripts!");
     263                 : 
     264               0 :       principal = parentWorker->GetPrincipal();
     265                 :     }
     266               0 :     NS_ASSERTION(principal, "This should never be null here!");
     267                 : 
     268                 :     // Figure out our base URI.
     269               0 :     nsCOMPtr<nsIURI> baseURI;
     270               0 :     if (mIsWorkerScript) {
     271               0 :       if (parentWorker) {
     272               0 :         baseURI = parentWorker->GetBaseURI();
     273               0 :         NS_ASSERTION(baseURI, "Should have been set already!");
     274                 :       }
     275                 :       else {
     276                 :         // May be null.
     277               0 :         baseURI = mWorkerPrivate->GetBaseURI();
     278                 :       }
     279                 :     }
     280                 :     else {
     281               0 :       baseURI = mWorkerPrivate->GetBaseURI();
     282               0 :       NS_ASSERTION(baseURI, "Should have been set already!");
     283                 :     }
     284                 : 
     285                 :     // May be null.
     286               0 :     nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
     287                 : 
     288                 :     // All of these can potentially be null, but that should be ok. We'll either
     289                 :     // succeed without them or fail below.
     290               0 :     nsCOMPtr<nsILoadGroup> loadGroup;
     291               0 :     if (parentDoc) {
     292               0 :       loadGroup = parentDoc->GetDocumentLoadGroup();
     293                 :     }
     294                 : 
     295               0 :     nsCOMPtr<nsIIOService> ios(do_GetIOService());
     296                 : 
     297               0 :     nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     298               0 :     NS_ASSERTION(secMan, "This should never be null!");
     299                 : 
     300               0 :     for (PRUint32 index = 0; index < mLoadInfos.Length(); index++) {
     301               0 :       ScriptLoadInfo& loadInfo = mLoadInfos[index];
     302               0 :       nsresult& rv = loadInfo.mLoadResult;
     303                 : 
     304               0 :       nsCOMPtr<nsIURI> uri;
     305               0 :       rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
     306                 :                                                      loadInfo.mURL, parentDoc,
     307               0 :                                                      baseURI);
     308               0 :       if (NS_FAILED(rv)) {
     309               0 :         return rv;
     310                 :       }
     311                 : 
     312                 :       // If we're part of a document then check the content load policy.
     313               0 :       if (parentDoc) {
     314               0 :         PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
     315                 :         rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
     316                 :                                        principal, parentDoc,
     317               0 :                                        NS_LITERAL_CSTRING("text/javascript"),
     318                 :                                        nsnull, &shouldLoad,
     319                 :                                        nsContentUtils::GetContentPolicy(),
     320               0 :                                        secMan);
     321               0 :         if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     322               0 :           if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
     323               0 :             return rv = NS_ERROR_CONTENT_BLOCKED;
     324                 :           }
     325               0 :           return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
     326                 :         }
     327                 :       }
     328                 : 
     329                 :       // If this script loader is being used to make a new worker then we need
     330                 :       // to do a same-origin check. Otherwise we need to clear the load with the
     331                 :       // security manager.
     332               0 :       if (mIsWorkerScript) {
     333               0 :         nsCString scheme;
     334               0 :         rv = uri->GetScheme(scheme);
     335               0 :         NS_ENSURE_SUCCESS(rv, rv);
     336                 : 
     337                 :         // We exempt data URLs from the same origin check.
     338               0 :         if (!scheme.EqualsLiteral("data")) {
     339               0 :           rv = principal->CheckMayLoad(uri, false);
     340               0 :           NS_ENSURE_SUCCESS(rv, rv);
     341                 :         }
     342                 :       }
     343                 :       else {
     344               0 :         rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
     345               0 :         NS_ENSURE_SUCCESS(rv, rv);
     346                 :       }
     347                 : 
     348                 :       // We need to know which index we're on in OnStreamComplete so we know
     349                 :       // where to put the result.
     350                 :       nsCOMPtr<nsISupportsPRUint32> indexSupports =
     351               0 :         do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
     352               0 :       NS_ENSURE_SUCCESS(rv, rv);
     353                 : 
     354               0 :       rv = indexSupports->SetData(index);
     355               0 :       NS_ENSURE_SUCCESS(rv, rv);
     356                 : 
     357                 :       // We don't care about progress so just use the simple stream loader for
     358                 :       // OnStreamComplete notification only.
     359               0 :       nsCOMPtr<nsIStreamLoader> loader;
     360               0 :       rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
     361               0 :       NS_ENSURE_SUCCESS(rv, rv);
     362                 : 
     363                 :       // Get Content Security Policy from parent document to pass into channel.
     364               0 :       nsCOMPtr<nsIContentSecurityPolicy> csp;
     365               0 :       rv = principal->GetCsp(getter_AddRefs(csp));
     366               0 :       NS_ENSURE_SUCCESS(rv, rv);
     367                 : 
     368               0 :       nsCOMPtr<nsIChannelPolicy> channelPolicy;
     369               0 :       if (csp) {
     370               0 :         channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
     371               0 :         NS_ENSURE_SUCCESS(rv, rv);
     372                 : 
     373               0 :         rv = channelPolicy->SetContentSecurityPolicy(csp);
     374               0 :         NS_ENSURE_SUCCESS(rv, rv);
     375                 : 
     376               0 :         rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
     377               0 :         NS_ENSURE_SUCCESS(rv, rv);
     378                 :       }
     379                 : 
     380               0 :       PRUint32 flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
     381                 : 
     382               0 :       nsCOMPtr<nsIChannel> channel;
     383               0 :       rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nsnull,
     384               0 :                          flags, channelPolicy);
     385               0 :       NS_ENSURE_SUCCESS(rv, rv);
     386                 : 
     387               0 :       rv = channel->AsyncOpen(loader, indexSupports);
     388               0 :       NS_ENSURE_SUCCESS(rv, rv);
     389                 : 
     390               0 :       loadInfo.mChannel.swap(channel);
     391                 :     }
     392                 : 
     393               0 :     return NS_OK;
     394                 :   }
     395                 : 
     396                 :   nsresult
     397               0 :   OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsISupports* aContext,
     398                 :                            nsresult aStatus, PRUint32 aStringLen,
     399                 :                            const PRUint8* aString, ScriptLoadInfo& aLoadInfo)
     400                 :   {
     401               0 :     AssertIsOnMainThread();
     402                 : 
     403               0 :     if (!aLoadInfo.mChannel) {
     404               0 :       return NS_BINDING_ABORTED;
     405                 :     }
     406                 : 
     407               0 :     aLoadInfo.mChannel = nsnull;
     408                 : 
     409               0 :     if (NS_FAILED(aStatus)) {
     410               0 :       return aStatus;
     411                 :     }
     412                 : 
     413               0 :     if (!aStringLen) {
     414               0 :       return NS_OK;
     415                 :     }
     416                 : 
     417               0 :     NS_ASSERTION(aString, "This should never be null!");
     418                 : 
     419                 :     // Make sure we're not seeing the result of a 404 or something by checking
     420                 :     // the 'requestSucceeded' attribute on the http channel.
     421               0 :     nsCOMPtr<nsIRequest> request;
     422               0 :     nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
     423               0 :     NS_ENSURE_SUCCESS(rv, rv);
     424                 : 
     425               0 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
     426               0 :     if (httpChannel) {
     427                 :       bool requestSucceeded;
     428               0 :       rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
     429               0 :       NS_ENSURE_SUCCESS(rv, rv);
     430                 : 
     431               0 :       if (!requestSucceeded) {
     432               0 :         return NS_ERROR_NOT_AVAILABLE;
     433                 :       }
     434                 :     }
     435                 : 
     436                 :     // May be null.
     437               0 :     nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
     438                 : 
     439                 :     // Use the regular nsScriptLoader for this grunt work! Should be just fine
     440                 :     // because we're running on the main thread.
     441                 :     rv = nsScriptLoader::ConvertToUTF16(aLoadInfo.mChannel, aString, aStringLen,
     442               0 :                                         EmptyString(), parentDoc,
     443               0 :                                         aLoadInfo.mScriptText);
     444               0 :     if (NS_FAILED(rv)) {
     445               0 :       return rv;
     446                 :     }
     447                 : 
     448               0 :     if (aLoadInfo.mScriptText.IsEmpty()) {
     449               0 :       return NS_ERROR_FAILURE;
     450                 :     }
     451                 : 
     452               0 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     453               0 :     NS_ASSERTION(channel, "This should never fail!");
     454                 : 
     455                 :     // Figure out what we actually loaded.
     456               0 :     nsCOMPtr<nsIURI> finalURI;
     457               0 :     rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
     458               0 :     NS_ENSURE_SUCCESS(rv, rv);
     459                 : 
     460               0 :     nsCString filename;
     461               0 :     rv = finalURI->GetSpec(filename);
     462               0 :     NS_ENSURE_SUCCESS(rv, rv);
     463                 : 
     464               0 :     if (!filename.IsEmpty()) {
     465                 :       // This will help callers figure out what their script url resolved to in
     466                 :       // case of errors.
     467               0 :       aLoadInfo.mURL.Assign(NS_ConvertUTF8toUTF16(filename));
     468                 :     }
     469                 : 
     470                 :     // Update the principal of the worker and its base URI if we just loaded the
     471                 :     // worker's primary script.
     472               0 :     if (mIsWorkerScript) {
     473                 :       // Take care of the base URI first.
     474               0 :       mWorkerPrivate->SetBaseURI(finalURI);
     475                 : 
     476                 :       // Now to figure out which principal to give this worker.
     477               0 :       WorkerPrivate* parent = mWorkerPrivate->GetParent();
     478                 : 
     479               0 :       NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
     480                 :                    "Must have one of these!");
     481                 : 
     482               0 :       nsCOMPtr<nsIPrincipal> loadPrincipal = mWorkerPrivate->GetPrincipal() ?
     483               0 :                                              mWorkerPrivate->GetPrincipal() :
     484               0 :                                              parent->GetPrincipal();
     485                 : 
     486               0 :       nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
     487               0 :       NS_ASSERTION(ssm, "Should never be null!");
     488                 : 
     489               0 :       nsCOMPtr<nsIPrincipal> channelPrincipal;
     490               0 :       rv = ssm->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
     491               0 :       NS_ENSURE_SUCCESS(rv, rv);
     492                 : 
     493                 :       // See if this is a resource URI. Since JSMs usually come from resource://
     494                 :       // URIs we're currently considering all URIs with the URI_IS_UI_RESOURCE
     495                 :       // flag as valid for creating privileged workers.
     496               0 :       if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
     497                 :         bool isResource;
     498                 :         rv = NS_URIChainHasFlags(finalURI,
     499                 :                                  nsIProtocolHandler::URI_IS_UI_RESOURCE,
     500               0 :                                  &isResource);
     501               0 :         NS_ENSURE_SUCCESS(rv, rv);
     502                 : 
     503               0 :         if (isResource) {
     504               0 :           rv = ssm->GetSystemPrincipal(getter_AddRefs(channelPrincipal));
     505               0 :           NS_ENSURE_SUCCESS(rv, rv);
     506                 :         }
     507                 :       }
     508                 : 
     509                 :       // If the load principal is the system principal then the channel
     510                 :       // principal must also be the system principal (we do not allow chrome
     511                 :       // code to create workers with non-chrome scripts). Otherwise this channel
     512                 :       // principal must be same origin with the load principal (we check again
     513                 :       // here in case redirects changed the location of the script).
     514               0 :       if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
     515               0 :         if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
     516               0 :           return NS_ERROR_DOM_BAD_URI;
     517                 :         }
     518                 :       }
     519                 :       else  {
     520               0 :         nsCString scheme;
     521               0 :         rv = finalURI->GetScheme(scheme);
     522               0 :         NS_ENSURE_SUCCESS(rv, rv);
     523                 : 
     524                 :         // We exempt data urls again.
     525               0 :         if (!scheme.EqualsLiteral("data") &&
     526               0 :             NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, false))) {
     527               0 :           return NS_ERROR_DOM_BAD_URI;
     528                 :         }
     529                 :       }
     530                 : 
     531               0 :       mWorkerPrivate->SetPrincipal(channelPrincipal);
     532                 :     }
     533                 : 
     534               0 :     return NS_OK;
     535                 :   }
     536                 : 
     537                 :   void
     538               0 :   ExecuteFinishedScripts()
     539                 :   {
     540               0 :     PRUint32 firstIndex = PR_UINT32_MAX;
     541               0 :     PRUint32 lastIndex = PR_UINT32_MAX;
     542                 : 
     543                 :     // Find firstIndex based on whether mExecutionScheduled is unset.
     544               0 :     for (PRUint32 index = 0; index < mLoadInfos.Length(); index++) {
     545               0 :       if (!mLoadInfos[index].mExecutionScheduled) {
     546               0 :         firstIndex = index;
     547               0 :         break;
     548                 :       }
     549                 :     }
     550                 : 
     551                 :     // Find lastIndex based on whether mChannel is set, and update
     552                 :     // mExecutionScheduled on the ones we're about to schedule.
     553               0 :     if (firstIndex != PR_UINT32_MAX) {
     554               0 :       for (PRUint32 index = firstIndex; index < mLoadInfos.Length(); index++) {
     555               0 :         ScriptLoadInfo& loadInfo = mLoadInfos[index];
     556                 : 
     557                 :         // If we still have a channel then the load is not complete.
     558               0 :         if (loadInfo.mChannel) {
     559               0 :           break;
     560                 :         }
     561                 : 
     562                 :         // We can execute this one.
     563               0 :         loadInfo.mExecutionScheduled = true;
     564                 : 
     565               0 :         lastIndex = index;
     566                 :       }
     567                 :     }
     568                 : 
     569               0 :     if (firstIndex != PR_UINT32_MAX && lastIndex != PR_UINT32_MAX) {
     570                 :       nsRefPtr<ScriptExecutorRunnable> runnable =
     571               0 :         new ScriptExecutorRunnable(*this, mSyncQueueKey, firstIndex, lastIndex);
     572               0 :       if (!runnable->Dispatch(nsnull)) {
     573               0 :         NS_ERROR("This should never fail!");
     574                 :       }
     575                 :     }
     576               0 :   }
     577                 : };
     578                 : 
     579               0 : NS_IMPL_THREADSAFE_ISUPPORTS2(ScriptLoaderRunnable, nsIRunnable,
     580                 :                                                     nsIStreamLoaderObserver)
     581                 : 
     582               0 : ScriptExecutorRunnable::ScriptExecutorRunnable(
     583                 :                                             ScriptLoaderRunnable& aScriptLoader,
     584                 :                                             PRUint32 aSyncQueueKey,
     585                 :                                             PRUint32 aFirstIndex,
     586                 :                                             PRUint32 aLastIndex)
     587                 : : WorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncQueueKey),
     588               0 :   mScriptLoader(aScriptLoader), mFirstIndex(aFirstIndex), mLastIndex(aLastIndex)
     589                 : {
     590               0 :   NS_ASSERTION(aFirstIndex <= aLastIndex, "Bad first index!");
     591               0 :   NS_ASSERTION(aLastIndex < aScriptLoader.mLoadInfos.Length(),
     592                 :                "Bad last index!");
     593               0 : }
     594                 : 
     595                 : bool
     596               0 : ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     597                 : {
     598               0 :   nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
     599                 : 
     600                 :   // Don't run if something else has already failed.
     601               0 :   for (PRUint32 index = 0; index < mFirstIndex; index++) {
     602               0 :     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
     603                 : 
     604               0 :     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
     605               0 :     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
     606                 : 
     607               0 :     if (!loadInfo.mExecutionResult) {
     608               0 :       return true;
     609                 :     }
     610                 :   }
     611                 : 
     612               0 :   JSObject* global = JS_GetGlobalObject(aCx);
     613               0 :   NS_ASSERTION(global, "Must have a global by now!");
     614                 : 
     615               0 :   JSPrincipals* principal = GetWorkerPrincipal();
     616               0 :   NS_ASSERTION(principal, "This should never be null!");
     617                 : 
     618               0 :   for (PRUint32 index = mFirstIndex; index <= mLastIndex; index++) {
     619               0 :     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
     620                 : 
     621               0 :     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
     622               0 :     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
     623               0 :     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
     624                 : 
     625               0 :     if (NS_FAILED(loadInfo.mLoadResult)) {
     626               0 :       NS_ConvertUTF16toUTF8 url(loadInfo.mURL);
     627                 : 
     628               0 :       switch (loadInfo.mLoadResult) {
     629                 :         case NS_BINDING_ABORTED:
     630                 :           // Canceled, don't set an exception.
     631               0 :           break;
     632                 : 
     633                 :         case NS_ERROR_MALFORMED_URI:
     634               0 :           JS_ReportError(aCx, "Malformed script URI: %s", url.get());
     635               0 :           break;
     636                 : 
     637                 :         case NS_ERROR_FILE_NOT_FOUND:
     638                 :         case NS_ERROR_NOT_AVAILABLE:
     639               0 :           JS_ReportError(aCx, "Script file not found: %s", url.get());
     640               0 :           break;
     641                 : 
     642                 :         default:
     643                 :           JS_ReportError(aCx, "Failed to load script: %s (nsresult = 0x%x)",
     644               0 :                          url.get(), loadInfo.mLoadResult);
     645                 :       }
     646               0 :       return true;
     647                 :     }
     648                 : 
     649               0 :     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
     650                 : 
     651               0 :     if (!JS_EvaluateUCScriptForPrincipals(aCx, global, principal,
     652                 :                                           loadInfo.mScriptText.get(),
     653                 :                                           loadInfo.mScriptText.Length(),
     654               0 :                                           filename.get(), 1, nsnull)) {
     655               0 :       return true;
     656                 :     }
     657                 : 
     658               0 :     loadInfo.mExecutionResult = true;
     659                 :   }
     660                 : 
     661               0 :   return true;
     662                 : }
     663                 : 
     664                 : void
     665               0 : ScriptExecutorRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     666                 :                                 bool aRunResult)
     667                 : {
     668               0 :   nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
     669                 : 
     670               0 :   if (mLastIndex == loadInfos.Length() - 1) {
     671                 :     // All done. If anything failed then return false.
     672               0 :     bool result = true;
     673               0 :     for (PRUint32 index = 0; index < loadInfos.Length(); index++) {
     674               0 :       if (!loadInfos[index].mExecutionResult) {
     675               0 :         result = false;
     676               0 :         break;
     677                 :       }
     678                 :     }
     679                 : 
     680               0 :     aWorkerPrivate->RemoveFeature(aCx, &mScriptLoader);
     681               0 :     aWorkerPrivate->StopSyncLoop(mSyncQueueKey, result);
     682                 :   }
     683               0 : }
     684                 : 
     685                 : bool
     686               0 : LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     687                 :                nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsWorkerScript)
     688                 : {
     689               0 :   aWorkerPrivate->AssertIsOnWorkerThread();
     690               0 :   NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
     691                 : 
     692               0 :   PRUint32 syncQueueKey = aWorkerPrivate->CreateNewSyncLoop();
     693                 : 
     694                 :   nsRefPtr<ScriptLoaderRunnable> loader =
     695                 :     new ScriptLoaderRunnable(aWorkerPrivate, syncQueueKey, aLoadInfos,
     696               0 :                              aIsWorkerScript);
     697                 : 
     698               0 :   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
     699                 : 
     700               0 :   if (!aWorkerPrivate->AddFeature(aCx, loader)) {
     701               0 :     return false;
     702                 :   }
     703                 : 
     704               0 :   if (NS_FAILED(NS_DispatchToMainThread(loader, NS_DISPATCH_NORMAL))) {
     705               0 :     NS_ERROR("Failed to dispatch!");
     706                 : 
     707               0 :     aWorkerPrivate->RemoveFeature(aCx, loader);
     708               0 :     return false;
     709                 :   }
     710                 : 
     711               0 :   return aWorkerPrivate->RunSyncLoop(aCx, syncQueueKey);
     712                 : }
     713                 : 
     714                 : } /* anonymous namespace */
     715                 : 
     716                 : BEGIN_WORKERS_NAMESPACE
     717                 : 
     718                 : namespace scriptloader {
     719                 : 
     720                 : bool
     721               0 : LoadWorkerScript(JSContext* aCx)
     722                 : {
     723               0 :   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
     724               0 :   NS_ASSERTION(worker, "This should never be null!");
     725                 : 
     726               0 :   nsTArray<ScriptLoadInfo> loadInfos;
     727                 : 
     728               0 :   ScriptLoadInfo* info = loadInfos.AppendElement();
     729               0 :   info->mURL = worker->ScriptURL();
     730                 : 
     731               0 :   return LoadAllScripts(aCx, worker, loadInfos, true);
     732                 : }
     733                 : 
     734                 : bool
     735               0 : Load(JSContext* aCx, unsigned aURLCount, jsval* aURLs)
     736                 : {
     737               0 :   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
     738               0 :   NS_ASSERTION(worker, "This should never be null!");
     739                 : 
     740               0 :   if (!aURLCount) {
     741               0 :     return true;
     742                 :   }
     743                 : 
     744               0 :   if (aURLCount > MAX_CONCURRENT_SCRIPTS) {
     745                 :     JS_ReportError(aCx, "Cannot load more than %d scripts at one time!",
     746               0 :                    MAX_CONCURRENT_SCRIPTS);
     747               0 :     return false;
     748                 :   }
     749                 : 
     750               0 :   nsTArray<ScriptLoadInfo> loadInfos;
     751               0 :   loadInfos.SetLength(PRUint32(aURLCount));
     752                 : 
     753               0 :   for (unsigned index = 0; index < aURLCount; index++) {
     754               0 :     JSString* str = JS_ValueToString(aCx, aURLs[index]);
     755               0 :     if (!str) {
     756               0 :       return false;
     757                 :     }
     758                 : 
     759                 :     size_t length;
     760               0 :     const jschar* buffer = JS_GetStringCharsAndLength(aCx, str, &length);
     761               0 :     if (!buffer) {
     762               0 :       return false;
     763                 :     }
     764                 : 
     765               0 :     loadInfos[index].mURL.Assign(buffer, length);
     766                 :   }
     767                 : 
     768               0 :   return LoadAllScripts(aCx, worker, loadInfos, false);
     769                 : }
     770                 : 
     771                 : } // namespace scriptloader
     772                 : 
     773                 : END_WORKERS_NAMESPACE

Generated by: LCOV version 1.7