LCOV - code coverage report
Current view: top level - dom/script - ScriptLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 527 1526 34.5 %
Date: 2017-07-14 16:53:18 Functions: 37 86 43.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ScriptLoader.h"
       8             : #include "ScriptLoadHandler.h"
       9             : #include "ScriptLoadRequest.h"
      10             : #include "ScriptTrace.h"
      11             : #include "ModuleLoadRequest.h"
      12             : #include "ModuleScript.h"
      13             : 
      14             : #include "prsystem.h"
      15             : #include "jsapi.h"
      16             : #include "jsfriendapi.h"
      17             : #include "js/Utility.h"
      18             : #include "xpcpublic.h"
      19             : #include "nsCycleCollectionParticipant.h"
      20             : #include "nsIContent.h"
      21             : #include "nsJSUtils.h"
      22             : #include "mozilla/dom/DocGroup.h"
      23             : #include "mozilla/dom/Element.h"
      24             : #include "mozilla/dom/ScriptSettings.h"
      25             : #include "mozilla/dom/SRILogHelper.h"
      26             : #include "nsGkAtoms.h"
      27             : #include "nsNetUtil.h"
      28             : #include "nsIScriptGlobalObject.h"
      29             : #include "nsIScriptContext.h"
      30             : #include "nsIScriptSecurityManager.h"
      31             : #include "nsIPrincipal.h"
      32             : #include "nsJSPrincipals.h"
      33             : #include "nsContentPolicyUtils.h"
      34             : #include "nsIHttpChannel.h"
      35             : #include "nsIHttpChannelInternal.h"
      36             : #include "nsIClassOfService.h"
      37             : #include "nsICacheInfoChannel.h"
      38             : #include "nsITimedChannel.h"
      39             : #include "nsIScriptElement.h"
      40             : #include "nsIDOMHTMLScriptElement.h"
      41             : #include "nsIDocShell.h"
      42             : #include "nsContentUtils.h"
      43             : #include "nsUnicharUtils.h"
      44             : #include "nsAutoPtr.h"
      45             : #include "nsIXPConnect.h"
      46             : #include "nsError.h"
      47             : #include "nsThreadUtils.h"
      48             : #include "nsDocShellCID.h"
      49             : #include "nsIContentSecurityPolicy.h"
      50             : #include "mozilla/Logging.h"
      51             : #include "nsCRT.h"
      52             : #include "nsContentCreatorFunctions.h"
      53             : #include "nsProxyRelease.h"
      54             : #include "nsSandboxFlags.h"
      55             : #include "nsContentTypeParser.h"
      56             : #include "nsINetworkPredictor.h"
      57             : #include "mozilla/ConsoleReportCollector.h"
      58             : 
      59             : #include "mozilla/AsyncEventDispatcher.h"
      60             : #include "mozilla/Attributes.h"
      61             : #include "mozilla/Telemetry.h"
      62             : #include "mozilla/TimeStamp.h"
      63             : #include "mozilla/Unused.h"
      64             : #include "nsIScriptError.h"
      65             : #include "nsIOutputStream.h"
      66             : 
      67             : using JS::SourceBufferHolder;
      68             : 
      69             : namespace mozilla {
      70             : namespace dom {
      71             : 
      72             : LazyLogModule ScriptLoader::gCspPRLog("CSP");
      73             : LazyLogModule ScriptLoader::gScriptLoaderLog("ScriptLoader");
      74             : 
      75             : #define LOG(args) \
      76             :   MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Debug, args)
      77             : 
      78             : 
      79             : // Alternate Data MIME type used by the ScriptLoader to register that we want to
      80             : // store bytecode without reading it.
      81           3 : static NS_NAMED_LITERAL_CSTRING(kNullMimeType, "javascript/null");
      82             : 
      83             : //////////////////////////////////////////////////////////////
      84             : // ScriptLoader::PreloadInfo
      85             : //////////////////////////////////////////////////////////////
      86             : 
      87             : inline void
      88             : ImplCycleCollectionUnlink(ScriptLoader::PreloadInfo& aField)
      89             : {
      90             :   ImplCycleCollectionUnlink(aField.mRequest);
      91             : }
      92             : 
      93             : inline void
      94           0 : ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
      95             :                             ScriptLoader::PreloadInfo& aField,
      96             :                             const char* aName,
      97             :                             uint32_t aFlags = 0)
      98             : {
      99           0 :   ImplCycleCollectionTraverse(aCallback, aField.mRequest, aName, aFlags);
     100           0 : }
     101             : 
     102             : //////////////////////////////////////////////////////////////
     103             : // ScriptLoader
     104             : //////////////////////////////////////////////////////////////
     105             : 
     106          55 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoader)
     107           0 : NS_INTERFACE_MAP_END
     108             : 
     109           0 : NS_IMPL_CYCLE_COLLECTION(ScriptLoader,
     110             :                          mNonAsyncExternalScriptInsertedRequests,
     111             :                          mLoadingAsyncRequests,
     112             :                          mLoadedAsyncRequests,
     113             :                          mDeferRequests,
     114             :                          mXSLTRequests,
     115             :                          mParserBlockingRequest,
     116             :                          mPreloads,
     117             :                          mPendingChildLoaders,
     118             :                          mFetchedModules)
     119             : 
     120          89 : NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader)
     121           9 : NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader)
     122             : 
     123          55 : ScriptLoader::ScriptLoader(nsIDocument *aDocument)
     124             :   : mDocument(aDocument),
     125             :     mParserBlockingBlockerCount(0),
     126             :     mBlockerCount(0),
     127             :     mNumberOfProcessors(0),
     128             :     mEnabled(true),
     129             :     mDeferEnabled(false),
     130             :     mDocumentParsingDone(false),
     131             :     mBlockingDOMContentLoaded(false),
     132             :     mLoadEventFired(false),
     133         110 :     mReporter(new ConsoleReportCollector())
     134             : {
     135          55 : }
     136             : 
     137           0 : ScriptLoader::~ScriptLoader()
     138             : {
     139           0 :   mObservers.Clear();
     140             : 
     141           0 :   if (mParserBlockingRequest) {
     142           0 :     mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
     143             :   }
     144             : 
     145           0 :   for (ScriptLoadRequest* req = mXSLTRequests.getFirst(); req;
     146           0 :        req = req->getNext()) {
     147           0 :     req->FireScriptAvailable(NS_ERROR_ABORT);
     148             :   }
     149             : 
     150           0 :   for (ScriptLoadRequest* req = mDeferRequests.getFirst(); req;
     151           0 :        req = req->getNext()) {
     152           0 :     req->FireScriptAvailable(NS_ERROR_ABORT);
     153             :   }
     154             : 
     155           0 :   for (ScriptLoadRequest* req = mLoadingAsyncRequests.getFirst(); req;
     156           0 :        req = req->getNext()) {
     157           0 :     req->FireScriptAvailable(NS_ERROR_ABORT);
     158             :   }
     159             : 
     160           0 :   for (ScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req;
     161           0 :        req = req->getNext()) {
     162           0 :     req->FireScriptAvailable(NS_ERROR_ABORT);
     163             :   }
     164             : 
     165           0 :   for(ScriptLoadRequest* req = mNonAsyncExternalScriptInsertedRequests.getFirst();
     166           0 :       req;
     167           0 :       req = req->getNext()) {
     168           0 :     req->FireScriptAvailable(NS_ERROR_ABORT);
     169             :   }
     170             : 
     171             :   // Unblock the kids, in case any of them moved to a different document
     172             :   // subtree in the meantime and therefore aren't actually going away.
     173           0 :   for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
     174           0 :     mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker();
     175             :   }
     176           0 : }
     177             : 
     178             : // Collect telemtry data about the cache information, and the kind of source
     179             : // which are being loaded, and where it is being loaded from.
     180             : static void
     181           5 : CollectScriptTelemetry(nsIIncrementalStreamLoader* aLoader,
     182             :                        ScriptLoadRequest* aRequest)
     183             : {
     184             :   using namespace mozilla::Telemetry;
     185             : 
     186             :   // Skip this function if we are not running telemetry.
     187           5 :   if (!CanRecordExtended()) {
     188          10 :     return;
     189             :   }
     190             : 
     191             :   // Report the type of source, as well as the size of the source.
     192           0 :   if (aRequest->IsLoadingSource()) {
     193           0 :     if (aRequest->mIsInline) {
     194           0 :       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline);
     195           0 :       nsAutoString inlineData;
     196           0 :       aRequest->mElement->GetScriptText(inlineData);
     197           0 :       Accumulate(DOM_SCRIPT_INLINE_SIZE, inlineData.Length());
     198             :     } else {
     199           0 :       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback);
     200           0 :       Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->mScriptText.length());
     201             :     }
     202             :   } else {
     203           0 :     MOZ_ASSERT(aRequest->IsLoading());
     204           0 :     if (aRequest->IsSource()) {
     205           0 :       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Source);
     206           0 :       Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->mScriptText.length());
     207             :     } else {
     208           0 :       MOZ_ASSERT(aRequest->IsBytecode());
     209           0 :       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::AltData);
     210           0 :       Accumulate(DOM_SCRIPT_BYTECODE_SIZE, aRequest->mScriptBytecode.length());
     211             :     }
     212             :   }
     213             : 
     214             :   // Skip if we do not have any cache information for the given script.
     215           0 :   if (!aLoader) {
     216           0 :     return;
     217             :   }
     218           0 :   nsCOMPtr<nsIRequest> channel;
     219           0 :   aLoader->GetRequest(getter_AddRefs(channel));
     220           0 :   nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(channel));
     221           0 :   if (!cic) {
     222           0 :     return;
     223             :   }
     224             : 
     225           0 :   int32_t fetchCount = 0;
     226           0 :   if (NS_SUCCEEDED(cic->GetCacheTokenFetchCount(&fetchCount))) {
     227           0 :     Accumulate(DOM_SCRIPT_FETCH_COUNT, fetchCount);
     228             :   }
     229             : }
     230             : 
     231             : // Helper method for checking if the script element is an event-handler
     232             : // This means that it has both a for-attribute and a event-attribute.
     233             : // Also, if the for-attribute has a value that matches "\s*window\s*",
     234             : // and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an
     235             : // eventhandler. (both matches are case insensitive).
     236             : // This is how IE seems to filter out a window's onload handler from a
     237             : // <script for=... event=...> element.
     238             : 
     239             : static bool
     240           5 : IsScriptEventHandler(nsIContent* aScriptElement)
     241             : {
     242           5 :   if (!aScriptElement->IsHTMLElement()) {
     243           0 :     return false;
     244             :   }
     245             : 
     246          10 :   nsAutoString forAttr, eventAttr;
     247           5 :   if (!aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_for, forAttr) ||
     248           0 :       !aScriptElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, eventAttr)) {
     249           5 :     return false;
     250             :   }
     251             : 
     252             :   const nsAString& for_str =
     253           0 :     nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(forAttr);
     254           0 :   if (!for_str.LowerCaseEqualsLiteral("window")) {
     255           0 :     return true;
     256             :   }
     257             : 
     258             :   // We found for="window", now check for event="onload".
     259             :   const nsAString& event_str =
     260           0 :     nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(eventAttr, false);
     261           0 :   if (!StringBeginsWith(event_str, NS_LITERAL_STRING("onload"),
     262           0 :                         nsCaseInsensitiveStringComparator())) {
     263             :     // It ain't "onload.*".
     264             : 
     265           0 :     return true;
     266             :   }
     267             : 
     268           0 :   nsAutoString::const_iterator start, end;
     269           0 :   event_str.BeginReading(start);
     270           0 :   event_str.EndReading(end);
     271             : 
     272           0 :   start.advance(6); // advance past "onload"
     273             : 
     274           0 :   if (start != end && *start != '(' && *start != ' ') {
     275             :     // We got onload followed by something other than space or
     276             :     // '('. Not good enough.
     277             : 
     278           0 :     return true;
     279             :   }
     280             : 
     281           0 :   return false;
     282             : }
     283             : 
     284             : nsresult
     285           4 : ScriptLoader::CheckContentPolicy(nsIDocument* aDocument,
     286             :                                  nsISupports* aContext,
     287             :                                  nsIURI* aURI,
     288             :                                  const nsAString& aType,
     289             :                                  bool aIsPreLoad)
     290             : {
     291             :   nsContentPolicyType contentPolicyType = aIsPreLoad
     292           4 :                                           ? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD
     293           4 :                                           : nsIContentPolicy::TYPE_INTERNAL_SCRIPT;
     294             : 
     295           4 :   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
     296           8 :   nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType,
     297             :                                           aURI,
     298             :                                           aDocument->NodePrincipal(),
     299             :                                           aContext,
     300           8 :                                           NS_LossyConvertUTF16toASCII(aType),
     301             :                                           nullptr,    //extra
     302             :                                           &shouldLoad,
     303             :                                           nsContentUtils::GetContentPolicy(),
     304           4 :                                           nsContentUtils::GetSecurityManager());
     305           4 :   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     306           0 :     if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
     307           0 :       return NS_ERROR_CONTENT_BLOCKED;
     308             :     }
     309           0 :     return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
     310             :   }
     311             : 
     312           4 :   return NS_OK;
     313             : }
     314             : 
     315             : bool
     316          14 : ScriptLoader::ModuleScriptsEnabled()
     317             : {
     318             :   static bool sEnabledForContent = false;
     319             :   static bool sCachedPref = false;
     320          14 :   if (!sCachedPref) {
     321           1 :     sCachedPref = true;
     322           1 :     Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
     323             :   }
     324             : 
     325          14 :   return nsContentUtils::IsChromeDoc(mDocument) || sEnabledForContent;
     326             : }
     327             : 
     328             : bool
     329           0 : ScriptLoader::ModuleMapContainsModule(ModuleLoadRequest* aRequest) const
     330             : {
     331             :   // Returns whether we have fetched, or are currently fetching, a module script
     332             :   // for the request's URL.
     333           0 :   return mFetchingModules.Contains(aRequest->mURI) ||
     334           0 :          mFetchedModules.Contains(aRequest->mURI);
     335             : }
     336             : 
     337             : bool
     338           0 : ScriptLoader::IsFetchingModule(ModuleLoadRequest* aRequest) const
     339             : {
     340           0 :   bool fetching = mFetchingModules.Contains(aRequest->mURI);
     341           0 :   MOZ_ASSERT_IF(fetching, !mFetchedModules.Contains(aRequest->mURI));
     342           0 :   return fetching;
     343             : }
     344             : 
     345             : void
     346           0 : ScriptLoader::SetModuleFetchStarted(ModuleLoadRequest* aRequest)
     347             : {
     348             :   // Update the module map to indicate that a module is currently being fetched.
     349             : 
     350           0 :   MOZ_ASSERT(aRequest->IsLoading());
     351           0 :   MOZ_ASSERT(!ModuleMapContainsModule(aRequest));
     352           0 :   mFetchingModules.Put(aRequest->mURI, nullptr);
     353           0 : }
     354             : 
     355             : void
     356           0 : ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest* aRequest,
     357             :                                                              nsresult aResult)
     358             : {
     359             :   // Update module map with the result of fetching a single module script.  The
     360             :   // module script pointer is nullptr on error.
     361             : 
     362           0 :   MOZ_ASSERT(!aRequest->IsReadyToRun());
     363             : 
     364           0 :   RefPtr<GenericPromise::Private> promise;
     365           0 :   MOZ_ALWAYS_TRUE(mFetchingModules.Remove(aRequest->mURI, getter_AddRefs(promise)));
     366             : 
     367           0 :   RefPtr<ModuleScript> ms(aRequest->mModuleScript);
     368           0 :   MOZ_ASSERT(NS_SUCCEEDED(aResult) == (ms != nullptr));
     369           0 :   mFetchedModules.Put(aRequest->mURI, ms);
     370             : 
     371           0 :   if (promise) {
     372           0 :     if (ms) {
     373           0 :       promise->Resolve(true, __func__);
     374             :     } else {
     375           0 :       promise->Reject(aResult, __func__);
     376             :     }
     377             :   }
     378           0 : }
     379             : 
     380             : RefPtr<GenericPromise>
     381           0 : ScriptLoader::WaitForModuleFetch(ModuleLoadRequest* aRequest)
     382             : {
     383           0 :   MOZ_ASSERT(ModuleMapContainsModule(aRequest));
     384             : 
     385           0 :   if (auto entry = mFetchingModules.Lookup(aRequest->mURI)) {
     386           0 :     if (!entry.Data()) {
     387           0 :       entry.Data() = new GenericPromise::Private(__func__);
     388             :     }
     389           0 :     return entry.Data();
     390             :   }
     391             : 
     392           0 :   RefPtr<ModuleScript> ms;
     393           0 :   MOZ_ALWAYS_TRUE(mFetchedModules.Get(aRequest->mURI, getter_AddRefs(ms)));
     394           0 :   if (!ms || ms->InstantiationFailed()) {
     395           0 :     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
     396             :   }
     397             : 
     398           0 :   return GenericPromise::CreateAndResolve(true, __func__);
     399             : }
     400             : 
     401             : ModuleScript*
     402           0 : ScriptLoader::GetFetchedModule(nsIURI* aURL) const
     403             : {
     404             :   bool found;
     405           0 :   ModuleScript* ms = mFetchedModules.GetWeak(aURL, &found);
     406           0 :   MOZ_ASSERT(found);
     407           0 :   return ms;
     408             : }
     409             : 
     410             : nsresult
     411           0 : ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest)
     412             : {
     413           0 :   MOZ_ASSERT(!aRequest->mModuleScript);
     414             : 
     415           0 :   nsresult rv = CreateModuleScript(aRequest);
     416           0 :   SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
     417             : 
     418           0 :   aRequest->mScriptText.clearAndFree();
     419             : 
     420           0 :   if (NS_SUCCEEDED(rv)) {
     421           0 :     StartFetchingModuleDependencies(aRequest);
     422             :   }
     423             : 
     424           0 :   return rv;
     425             : }
     426             : 
     427             : nsresult
     428           0 : ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
     429             : {
     430           0 :   MOZ_ASSERT(!aRequest->mModuleScript);
     431           0 :   MOZ_ASSERT(aRequest->mBaseURL);
     432             : 
     433           0 :   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
     434           0 :   if (!globalObject) {
     435           0 :     return NS_ERROR_FAILURE;
     436             :   }
     437             : 
     438           0 :   nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
     439           0 :   if (!context) {
     440           0 :     return NS_ERROR_FAILURE;
     441             :   }
     442             : 
     443           0 :   nsAutoMicroTask mt;
     444           0 :   AutoEntryScript aes(globalObject, "CompileModule", true);
     445             : 
     446           0 :   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
     447           0 :   context->SetProcessingScriptTag(true);
     448             : 
     449             :   nsresult rv;
     450             :   {
     451             :     // Update our current script.
     452           0 :     AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement);
     453             : 
     454           0 :     JSContext* cx = aes.cx();
     455           0 :     JS::Rooted<JSObject*> module(cx);
     456             : 
     457           0 :     if (aRequest->mWasCompiledOMT) {
     458           0 :       module = JS::FinishOffThreadModule(cx, aRequest->mOffThreadToken);
     459           0 :       aRequest->mOffThreadToken = nullptr;
     460           0 :       rv = module ? NS_OK : NS_ERROR_FAILURE;
     461             :     } else {
     462           0 :       JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
     463             : 
     464           0 :       JS::CompileOptions options(cx);
     465           0 :       rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
     466             : 
     467           0 :       if (NS_SUCCEEDED(rv)) {
     468           0 :         nsAutoString inlineData;
     469           0 :         SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
     470           0 :         rv = nsJSUtils::CompileModule(cx, srcBuf, global, options, &module);
     471             :       }
     472             :     }
     473           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
     474           0 :     if (module) {
     475             :       aRequest->mModuleScript =
     476           0 :         new ModuleScript(this, aRequest->mBaseURL, module);
     477             :     }
     478             :   }
     479             : 
     480           0 :   context->SetProcessingScriptTag(oldProcessingScriptTag);
     481             : 
     482           0 :   return rv;
     483             : }
     484             : 
     485             : static bool
     486           0 : ThrowTypeError(JSContext* aCx, ModuleScript* aScript,
     487             :                const nsString& aMessage)
     488             : {
     489           0 :   JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord());
     490           0 :   JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module));
     491           0 :   JS::Rooted<JSString*> filename(aCx);
     492           0 :   filename = JS_NewStringCopyZ(aCx, JS_GetScriptFilename(script));
     493           0 :   if (!filename) {
     494           0 :     return false;
     495             :   }
     496             : 
     497           0 :   JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyZ(aCx, aMessage.get()));
     498           0 :   if (!message) {
     499           0 :     return false;
     500             :   }
     501             : 
     502           0 :   JS::Rooted<JS::Value> error(aCx);
     503           0 :   if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr,
     504             :                        message, &error)) {
     505           0 :     return false;
     506             :   }
     507             : 
     508           0 :   JS_SetPendingException(aCx, error);
     509           0 :   return false;
     510             : }
     511             : 
     512             : static bool
     513           0 : HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
     514             :                      const nsAString& aSpecifier)
     515             : {
     516             :   // TODO: How can we get the line number of the failed import?
     517             : 
     518           0 :   nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: "));
     519           0 :   message.Append(aSpecifier);
     520             : 
     521           0 :   return ThrowTypeError(aCx, aScript, message);
     522             : }
     523             : 
     524             : static bool
     525           0 : HandleModuleNotFound(JSContext* aCx, ModuleScript* aScript,
     526             :                      const nsAString& aSpecifier)
     527             : {
     528             :   // TODO: How can we get the line number of the failed import?
     529             : 
     530           0 :   nsAutoString message(NS_LITERAL_STRING("Resolved module not found in map: "));
     531           0 :   message.Append(aSpecifier);
     532             : 
     533           0 :   return ThrowTypeError(aCx, aScript, message);
     534             : }
     535             : 
     536             : static already_AddRefed<nsIURI>
     537           0 : ResolveModuleSpecifier(ModuleScript* aScript,
     538             :                        const nsAString& aSpecifier)
     539             : {
     540             :   // The following module specifiers are allowed by the spec:
     541             :   //  - a valid absolute URL
     542             :   //  - a valid relative URL that starts with "/", "./" or "../"
     543             :   //
     544             :   // Bareword module specifiers are currently disallowed as these may be given
     545             :   // special meanings in the future.
     546             : 
     547           0 :   nsCOMPtr<nsIURI> uri;
     548           0 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), aSpecifier);
     549           0 :   if (NS_SUCCEEDED(rv)) {
     550           0 :     return uri.forget();
     551             :   }
     552             : 
     553           0 :   if (rv != NS_ERROR_MALFORMED_URI) {
     554           0 :     return nullptr;
     555             :   }
     556             : 
     557           0 :   if (!StringBeginsWith(aSpecifier, NS_LITERAL_STRING("/")) &&
     558           0 :       !StringBeginsWith(aSpecifier, NS_LITERAL_STRING("./")) &&
     559           0 :       !StringBeginsWith(aSpecifier, NS_LITERAL_STRING("../"))) {
     560           0 :     return nullptr;
     561             :   }
     562             : 
     563           0 :   rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, aScript->BaseURL());
     564           0 :   if (NS_SUCCEEDED(rv)) {
     565           0 :     return uri.forget();
     566             :   }
     567             : 
     568           0 :   return nullptr;
     569             : }
     570             : 
     571             : static nsresult
     572           0 : RequestedModuleIsInAncestorList(ModuleLoadRequest* aRequest, nsIURI* aURL, bool* aResult)
     573             : {
     574           0 :   const size_t ImportDepthLimit = 100;
     575             : 
     576           0 :   *aResult = false;
     577           0 :   size_t depth = 0;
     578           0 :   while (aRequest) {
     579           0 :     if (depth++ == ImportDepthLimit) {
     580           0 :       return NS_ERROR_FAILURE;
     581             :     }
     582             : 
     583             :     bool equal;
     584           0 :     nsresult rv = aURL->Equals(aRequest->mURI, &equal);
     585           0 :     NS_ENSURE_SUCCESS(rv, rv);
     586           0 :     if (equal) {
     587           0 :       *aResult = true;
     588           0 :       return NS_OK;
     589             :     }
     590             : 
     591           0 :     aRequest = aRequest->mParent;
     592             :   }
     593             : 
     594           0 :   return NS_OK;
     595             : }
     596             : 
     597             : static nsresult
     598           0 : ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>& aUrls)
     599             : {
     600           0 :   ModuleScript* ms = aRequest->mModuleScript;
     601             : 
     602           0 :   AutoJSAPI jsapi;
     603           0 :   if (!jsapi.Init(ms->ModuleRecord())) {
     604           0 :     return NS_ERROR_FAILURE;
     605             :   }
     606             : 
     607           0 :   JSContext* cx = jsapi.cx();
     608           0 :   JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
     609           0 :   JS::Rooted<JSObject*> specifiers(cx, JS::GetRequestedModules(cx, moduleRecord));
     610             : 
     611             :   uint32_t length;
     612           0 :   if (!JS_GetArrayLength(cx, specifiers, &length)) {
     613           0 :     return NS_ERROR_FAILURE;
     614             :   }
     615             : 
     616           0 :   JS::Rooted<JS::Value> val(cx);
     617           0 :   for (uint32_t i = 0; i < length; i++) {
     618           0 :     if (!JS_GetElement(cx, specifiers, i, &val)) {
     619           0 :       return NS_ERROR_FAILURE;
     620             :     }
     621             : 
     622           0 :     nsAutoJSString specifier;
     623           0 :     if (!specifier.init(cx, val)) {
     624           0 :       return NS_ERROR_FAILURE;
     625             :     }
     626             : 
     627             :     // Let url be the result of resolving a module specifier given module script and requested.
     628           0 :     ModuleScript* ms = aRequest->mModuleScript;
     629           0 :     nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
     630           0 :     if (!uri) {
     631           0 :       HandleResolveFailure(cx, ms, specifier);
     632           0 :       return NS_ERROR_FAILURE;
     633             :     }
     634             : 
     635             :     bool isAncestor;
     636           0 :     nsresult rv = RequestedModuleIsInAncestorList(aRequest, uri, &isAncestor);
     637           0 :     NS_ENSURE_SUCCESS(rv, rv);
     638           0 :     if (!isAncestor) {
     639           0 :       aUrls.AppendElement(uri.forget());
     640             :     }
     641             :   }
     642             : 
     643           0 :   return NS_OK;
     644             : }
     645             : 
     646             : void
     647           0 : ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest)
     648             : {
     649           0 :   MOZ_ASSERT(aRequest->mModuleScript);
     650           0 :   MOZ_ASSERT(!aRequest->mModuleScript->InstantiationFailed());
     651           0 :   MOZ_ASSERT(!aRequest->IsReadyToRun());
     652           0 :   aRequest->mProgress = ModuleLoadRequest::Progress::FetchingImports;
     653             : 
     654           0 :   nsCOMArray<nsIURI> urls;
     655           0 :   nsresult rv = ResolveRequestedModules(aRequest, urls);
     656           0 :   if (NS_FAILED(rv)) {
     657           0 :     aRequest->LoadFailed();
     658           0 :     return;
     659             :   }
     660             : 
     661           0 :   if (urls.Length() == 0) {
     662             :     // There are no descendents to load so this request is ready.
     663           0 :     aRequest->DependenciesLoaded();
     664           0 :     return;
     665             :   }
     666             : 
     667             :   // For each url in urls, fetch a module script tree given url, module script's
     668             :   // CORS setting, and module script's settings object.
     669           0 :   nsTArray<RefPtr<GenericPromise>> importsReady;
     670           0 :   for (size_t i = 0; i < urls.Length(); i++) {
     671             :     RefPtr<GenericPromise> childReady =
     672           0 :       StartFetchingModuleAndDependencies(aRequest, urls[i]);
     673           0 :     importsReady.AppendElement(childReady);
     674             :   }
     675             : 
     676             :   // Wait for all imports to become ready.
     677             :   RefPtr<GenericPromise::AllPromiseType> allReady =
     678           0 :     GenericPromise::All(GetMainThreadSerialEventTarget(), importsReady);
     679             :   allReady->Then(GetMainThreadSerialEventTarget(), __func__, aRequest,
     680             :                  &ModuleLoadRequest::DependenciesLoaded,
     681           0 :                  &ModuleLoadRequest::LoadFailed);
     682             : }
     683             : 
     684             : RefPtr<GenericPromise>
     685           0 : ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest,
     686             :                                                  nsIURI* aURI)
     687             : {
     688           0 :   MOZ_ASSERT(aURI);
     689             : 
     690             :   RefPtr<ModuleLoadRequest> childRequest =
     691             :     new ModuleLoadRequest(aRequest->mElement, aRequest->mJSVersion,
     692           0 :                             aRequest->mCORSMode, aRequest->mIntegrity, this);
     693             : 
     694           0 :   childRequest->mIsTopLevel = false;
     695           0 :   childRequest->mURI = aURI;
     696           0 :   childRequest->mIsInline = false;
     697           0 :   childRequest->mReferrerPolicy = aRequest->mReferrerPolicy;
     698           0 :   childRequest->mParent = aRequest;
     699             : 
     700           0 :   RefPtr<GenericPromise> ready = childRequest->mReady.Ensure(__func__);
     701             : 
     702           0 :   nsresult rv = StartLoad(childRequest);
     703           0 :   if (NS_FAILED(rv)) {
     704           0 :     childRequest->mReady.Reject(rv, __func__);
     705           0 :     return ready;
     706             :   }
     707             : 
     708           0 :   aRequest->mImports.AppendElement(childRequest);
     709           0 :   return ready;
     710             : }
     711             : 
     712             : bool
     713           0 : HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp)
     714             : {
     715           0 :   MOZ_ASSERT(argc == 2);
     716           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     717           0 :   JS::Rooted<JSObject*> module(aCx, &args[0].toObject());
     718           0 :   JS::Rooted<JSString*> specifier(aCx, args[1].toString());
     719             : 
     720             :   // Let referencing module script be referencingModule.[[HostDefined]].
     721           0 :   JS::Value value = JS::GetModuleHostDefinedField(module);
     722           0 :   auto script = static_cast<ModuleScript*>(value.toPrivate());
     723           0 :   MOZ_ASSERT(script->ModuleRecord() == module);
     724             : 
     725             :   // Let url be the result of resolving a module specifier given referencing
     726             :   // module script and specifier. If the result is failure, throw a TypeError
     727             :   // exception and abort these steps.
     728           0 :   nsAutoJSString string;
     729           0 :   if (!string.init(aCx, specifier)) {
     730           0 :     return false;
     731             :   }
     732             : 
     733           0 :   nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
     734           0 :   if (!uri) {
     735           0 :     return HandleResolveFailure(aCx, script, string);
     736             :   }
     737             : 
     738             :   // Let resolved module script be the value of the entry in module map whose
     739             :   // key is url. If no such entry exists, throw a TypeError exception and abort
     740             :   // these steps.
     741           0 :   ModuleScript* ms = script->Loader()->GetFetchedModule(uri);
     742           0 :   if (!ms) {
     743           0 :     return HandleModuleNotFound(aCx, script, string);
     744             :   }
     745             : 
     746           0 :   if (ms->InstantiationFailed()) {
     747           0 :     JS::Rooted<JS::Value> exception(aCx, ms->Exception());
     748           0 :     JS_SetPendingException(aCx, exception);
     749           0 :     return false;
     750             :   }
     751             : 
     752           0 :   *vp = JS::ObjectValue(*ms->ModuleRecord());
     753           0 :   return true;
     754             : }
     755             : 
     756             : static nsresult
     757           0 : EnsureModuleResolveHook(JSContext* aCx)
     758             : {
     759           0 :   if (JS::GetModuleResolveHook(aCx)) {
     760           0 :     return NS_OK;
     761             :   }
     762             : 
     763           0 :   JS::Rooted<JSFunction*> func(aCx);
     764           0 :   func = JS_NewFunction(aCx, HostResolveImportedModule, 2, 0,
     765           0 :                         "HostResolveImportedModule");
     766           0 :   if (!func) {
     767           0 :     return NS_ERROR_FAILURE;
     768             :   }
     769             : 
     770           0 :   JS::SetModuleResolveHook(aCx, func);
     771           0 :   return NS_OK;
     772             : }
     773             : 
     774             : void
     775           0 : ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
     776             : {
     777           0 :   if (aRequest->IsTopLevel()) {
     778           0 :     MaybeMoveToLoadedList(aRequest);
     779           0 :     ProcessPendingRequests();
     780             :   }
     781             : 
     782           0 :   if (aRequest->mWasCompiledOMT) {
     783           0 :     mDocument->UnblockOnload(false);
     784             :   }
     785           0 : }
     786             : 
     787             : bool
     788           0 : ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest)
     789             : {
     790             :   // Perform eager instantiation of the loaded module tree.
     791             : 
     792           0 :   MOZ_ASSERT(aRequest);
     793             : 
     794           0 :   ModuleScript* ms = aRequest->mModuleScript;
     795           0 :   MOZ_ASSERT(ms);
     796           0 :   if (!ms->ModuleRecord()) {
     797           0 :     return false;
     798             :   }
     799             : 
     800           0 :   AutoJSAPI jsapi;
     801           0 :   if (NS_WARN_IF(!jsapi.Init(ms->ModuleRecord()))) {
     802           0 :     return false;
     803             :   }
     804             : 
     805           0 :   nsresult rv = EnsureModuleResolveHook(jsapi.cx());
     806           0 :   NS_ENSURE_SUCCESS(rv, false);
     807             : 
     808           0 :   JS::Rooted<JSObject*> module(jsapi.cx(), ms->ModuleRecord());
     809           0 :   bool ok = NS_SUCCEEDED(nsJSUtils::ModuleDeclarationInstantiation(jsapi.cx(), module));
     810             : 
     811           0 :   JS::RootedValue exception(jsapi.cx());
     812           0 :   if (!ok) {
     813           0 :     MOZ_ASSERT(jsapi.HasException());
     814           0 :     if (!jsapi.StealException(&exception)) {
     815           0 :       return false;
     816             :     }
     817           0 :     MOZ_ASSERT(!exception.isUndefined());
     818             :   }
     819             : 
     820             :   // Mark this module and any uninstantiated dependencies found via depth-first
     821             :   // search as instantiated and record any error.
     822             : 
     823           0 :   mozilla::Vector<ModuleLoadRequest*, 1> requests;
     824           0 :   if (!requests.append(aRequest)) {
     825           0 :     return false;
     826             :   }
     827             : 
     828           0 :   while (!requests.empty()) {
     829           0 :     ModuleLoadRequest* request = requests.popCopy();
     830           0 :     ModuleScript* ms = request->mModuleScript;
     831           0 :     if (!ms->IsUninstantiated()) {
     832           0 :       continue;
     833             :     }
     834             : 
     835           0 :     ms->SetInstantiationResult(exception);
     836             : 
     837           0 :     for (auto import : request->mImports) {
     838           0 :       if (import->mModuleScript->IsUninstantiated() &&
     839           0 :           !requests.append(import))
     840             :       {
     841           0 :         return false;
     842             :       }
     843             :     }
     844             :   }
     845             : 
     846           0 :   return true;
     847             : }
     848             : 
     849             : nsresult
     850           0 : ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest)
     851             : {
     852           0 :   MOZ_ASSERT(aRequest->IsBytecode());
     853           0 :   aRequest->mScriptBytecode.clearAndFree();
     854           0 :   TRACE_FOR_TEST(aRequest->mElement, "scriptloader_fallback");
     855             : 
     856             :   // Start a new channel from which we explicitly request to stream the source
     857             :   // instead of the bytecode.
     858           0 :   aRequest->mProgress = ScriptLoadRequest::Progress::Loading_Source;
     859           0 :   nsresult rv = StartLoad(aRequest);
     860           0 :   if (NS_FAILED(rv)) {
     861           0 :     return rv;
     862             :   }
     863             : 
     864             :   // Close the current channel and this ScriptLoadHandler as we created a new
     865             :   // one for the same request.
     866           0 :   return NS_BINDING_RETARGETED;
     867             : }
     868             : 
     869             : nsresult
     870           4 : ScriptLoader::StartLoad(ScriptLoadRequest* aRequest)
     871             : {
     872           4 :   MOZ_ASSERT(aRequest->IsLoading());
     873           4 :   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
     874           4 :   aRequest->mDataType = ScriptLoadRequest::DataType::Unknown;
     875             : 
     876             :   // If this document is sandboxed without 'allow-scripts', abort.
     877           4 :   if (mDocument->HasScriptsBlockedBySandbox()) {
     878           0 :     return NS_OK;
     879             :   }
     880             : 
     881           4 :   if (aRequest->IsModuleRequest()) {
     882             :     // Check whether the module has been fetched or is currently being fetched,
     883             :     // and if so wait for it.
     884           0 :     ModuleLoadRequest* request = aRequest->AsModuleRequest();
     885           0 :     if (ModuleMapContainsModule(request)) {
     886           0 :       WaitForModuleFetch(request)
     887             :         ->Then(GetMainThreadSerialEventTarget(), __func__, request,
     888             :                &ModuleLoadRequest::ModuleLoaded,
     889           0 :                &ModuleLoadRequest::LoadFailed);
     890           0 :       return NS_OK;
     891             :     }
     892             : 
     893             :     // Otherwise put the URL in the module map and mark it as fetching.
     894           0 :     SetModuleFetchStarted(request);
     895             :   }
     896             : 
     897           4 :   nsContentPolicyType contentPolicyType = aRequest->IsPreload()
     898           4 :                                           ? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD
     899           4 :                                           : nsIContentPolicy::TYPE_INTERNAL_SCRIPT;
     900           8 :   nsCOMPtr<nsINode> context;
     901           4 :   if (aRequest->mElement) {
     902           0 :     context = do_QueryInterface(aRequest->mElement);
     903             :   }
     904             :   else {
     905           4 :     context = mDocument;
     906             :   }
     907             : 
     908           8 :   nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
     909           8 :   nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
     910           4 :   NS_ENSURE_TRUE(window, NS_ERROR_NULL_POINTER);
     911           4 :   nsIDocShell* docshell = window->GetDocShell();
     912           8 :   nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
     913             : 
     914             :   nsSecurityFlags securityFlags;
     915           4 :   if (aRequest->IsModuleRequest()) {
     916             :     // According to the spec, module scripts have different behaviour to classic
     917             :     // scripts and always use CORS.
     918           0 :     securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
     919           0 :     if (aRequest->mCORSMode == CORS_NONE) {
     920           0 :       securityFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
     921           0 :     } else if (aRequest->mCORSMode == CORS_ANONYMOUS) {
     922           0 :       securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
     923             :     } else {
     924           0 :       MOZ_ASSERT(aRequest->mCORSMode == CORS_USE_CREDENTIALS);
     925           0 :       securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
     926             :     }
     927             :   } else {
     928           8 :     securityFlags = aRequest->mCORSMode == CORS_NONE
     929           4 :       ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
     930             :       : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
     931           4 :     if (aRequest->mCORSMode == CORS_ANONYMOUS) {
     932           0 :       securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
     933           4 :     } else if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) {
     934           0 :       securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
     935             :     }
     936             :   }
     937           4 :   securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
     938             : 
     939           8 :   nsCOMPtr<nsIChannel> channel;
     940           8 :   nsresult rv = NS_NewChannel(getter_AddRefs(channel),
     941             :                               aRequest->mURI,
     942             :                               context,
     943             :                               securityFlags,
     944             :                               contentPolicyType,
     945             :                               loadGroup,
     946             :                               prompter,
     947             :                               nsIRequest::LOAD_NORMAL |
     948           4 :                               nsIChannel::LOAD_CLASSIFY_URI);
     949             : 
     950           4 :   NS_ENSURE_SUCCESS(rv, rv);
     951             : 
     952             :   // To avoid decoding issues, the JSVersion is explicitly guarded here, and the
     953             :   // build-id is part of the JSBytecodeMimeType constant.
     954           4 :   aRequest->mCacheInfo = nullptr;
     955           8 :   nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(channel));
     956           4 :   if (cic && nsContentUtils::IsBytecodeCacheEnabled() &&
     957           0 :       aRequest->mJSVersion == JSVERSION_DEFAULT) {
     958           0 :     if (!aRequest->IsLoadingSource()) {
     959             :       // Inform the HTTP cache that we prefer to have information coming from the
     960             :       // bytecode cache instead of the sources, if such entry is already registered.
     961           0 :       LOG(("ScriptLoadRequest (%p): Maybe request bytecode", aRequest));
     962           0 :       cic->PreferAlternativeDataType(nsContentUtils::JSBytecodeMimeType());
     963             :     } else {
     964             :       // If we are explicitly loading from the sources, such as after a
     965             :       // restarted request, we might still want to save the bytecode after.
     966             :       //
     967             :       // The following tell the cache to look for an alternative data type which
     968             :       // does not exist, such that we can later save the bytecode with a
     969             :       // different alternative data type.
     970           0 :       LOG(("ScriptLoadRequest (%p): Request saving bytecode later", aRequest));
     971           0 :       cic->PreferAlternativeDataType(kNullMimeType);
     972             :     }
     973             :   }
     974             : 
     975           4 :   nsIScriptElement* script = aRequest->mElement;
     976           8 :   nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
     977             : 
     978           4 :   if (cos) {
     979           4 :     if (aRequest->mScriptFromHead &&
     980           0 :         !(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
     981             :       // synchronous head scripts block loading of most other non js/css
     982             :       // content such as images
     983           2 :       cos->AddClassFlags(nsIClassOfService::Leader);
     984           0 :     } else if (!(script && script->GetScriptDeferred())) {
     985             :       // other scripts are neither blocked nor prioritized unless marked deferred
     986           0 :       cos->AddClassFlags(nsIClassOfService::Unblocked);
     987             :     }
     988             :   }
     989             : 
     990           8 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
     991           4 :   if (httpChannel) {
     992             :     // HTTP content negotation has little value in this context.
     993           8 :     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
     994           4 :                                        NS_LITERAL_CSTRING("*/*"),
     995           8 :                                        false);
     996           2 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     997           4 :     rv = httpChannel->SetReferrerWithPolicy(mDocument->GetDocumentURI(),
     998           4 :                                             aRequest->mReferrerPolicy);
     999           2 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1000             : 
    1001           4 :     nsCOMPtr<nsIHttpChannelInternal> internalChannel(do_QueryInterface(httpChannel));
    1002           2 :     if (internalChannel) {
    1003           2 :       rv = internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());
    1004           2 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    1005             :     }
    1006             :   }
    1007             : 
    1008           4 :   mozilla::net::PredictorLearn(aRequest->mURI, mDocument->GetDocumentURI(),
    1009             :                                nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
    1010           8 :                                mDocument->NodePrincipal()->OriginAttributesRef());
    1011             : 
    1012             :   // Set the initiator type
    1013           8 :   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
    1014           4 :   if (timedChannel) {
    1015           2 :     timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
    1016             :   }
    1017             : 
    1018           8 :   nsAutoPtr<mozilla::dom::SRICheckDataVerifier> sriDataVerifier;
    1019           4 :   if (!aRequest->mIntegrity.IsEmpty()) {
    1020           0 :     nsAutoCString sourceUri;
    1021           0 :     if (mDocument->GetDocumentURI()) {
    1022           0 :       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    1023             :     }
    1024             :     sriDataVerifier = new SRICheckDataVerifier(aRequest->mIntegrity, sourceUri,
    1025           0 :                                                mReporter);
    1026             :   }
    1027             : 
    1028             :   RefPtr<ScriptLoadHandler> handler =
    1029          12 :       new ScriptLoadHandler(this, aRequest, sriDataVerifier.forget());
    1030             : 
    1031           8 :   nsCOMPtr<nsIIncrementalStreamLoader> loader;
    1032           4 :   rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler);
    1033           4 :   NS_ENSURE_SUCCESS(rv, rv);
    1034             : 
    1035           4 :   return channel->AsyncOpen2(loader);
    1036             : }
    1037             : 
    1038             : bool
    1039           4 : ScriptLoader::PreloadURIComparator::Equals(const PreloadInfo& aPi,
    1040             :                                            nsIURI* const& aURI) const
    1041             : {
    1042             :   bool same;
    1043           4 :   return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) &&
    1044           4 :          same;
    1045             : }
    1046             : 
    1047           0 : class ScriptRequestProcessor : public Runnable
    1048             : {
    1049             : private:
    1050             :   RefPtr<ScriptLoader> mLoader;
    1051             :   RefPtr<ScriptLoadRequest> mRequest;
    1052             : public:
    1053           0 :   ScriptRequestProcessor(ScriptLoader* aLoader, ScriptLoadRequest* aRequest)
    1054           0 :     : Runnable("dom::ScriptRequestProcessor")
    1055             :     , mLoader(aLoader)
    1056           0 :     , mRequest(aRequest)
    1057           0 :   {}
    1058           0 :   NS_IMETHOD Run() override
    1059             :   {
    1060           0 :     return mLoader->ProcessRequest(mRequest);
    1061             :   }
    1062             : };
    1063             : 
    1064             : static inline bool
    1065           5 : ParseTypeAttribute(const nsAString& aType, JSVersion* aVersion)
    1066             : {
    1067           5 :   MOZ_ASSERT(!aType.IsEmpty());
    1068           5 :   MOZ_ASSERT(aVersion);
    1069           5 :   MOZ_ASSERT(*aVersion == JSVERSION_DEFAULT);
    1070             : 
    1071          10 :   nsContentTypeParser parser(aType);
    1072             : 
    1073          10 :   nsAutoString mimeType;
    1074           5 :   nsresult rv = parser.GetType(mimeType);
    1075           5 :   NS_ENSURE_SUCCESS(rv, false);
    1076             : 
    1077           5 :   if (!nsContentUtils::IsJavascriptMIMEType(mimeType)) {
    1078           0 :     return false;
    1079             :   }
    1080             : 
    1081             :   // Get the version string, and ensure the language supports it.
    1082          10 :   nsAutoString versionName;
    1083           5 :   rv = parser.GetParameter("version", versionName);
    1084             : 
    1085           5 :   if (NS_SUCCEEDED(rv)) {
    1086           0 :     *aVersion = nsContentUtils::ParseJavascriptVersion(versionName);
    1087           5 :   } else if (rv != NS_ERROR_INVALID_ARG) {
    1088           0 :     return false;
    1089             :   }
    1090             : 
    1091           5 :   return true;
    1092             : }
    1093             : 
    1094             : static bool
    1095           1 : CSPAllowsInlineScript(nsIScriptElement* aElement, nsIDocument* aDocument)
    1096             : {
    1097           2 :   nsCOMPtr<nsIContentSecurityPolicy> csp;
    1098             :   // Note: For imports NodePrincipal and the principal of the master are
    1099             :   // the same.
    1100           1 :   nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
    1101           1 :   NS_ENSURE_SUCCESS(rv, false);
    1102             : 
    1103           1 :   if (!csp) {
    1104             :     // no CSP --> allow
    1105           1 :     return true;
    1106             :   }
    1107             : 
    1108             :   // query the nonce
    1109           0 :   nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
    1110           0 :   nsAutoString nonce;
    1111           0 :   scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
    1112           0 :   bool parserCreated = aElement->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER;
    1113             : 
    1114             :   // query the scripttext
    1115           0 :   nsAutoString scriptText;
    1116           0 :   aElement->GetScriptText(scriptText);
    1117             : 
    1118           0 :   bool allowInlineScript = false;
    1119           0 :   rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
    1120             :                             nonce, parserCreated, scriptText,
    1121             :                             aElement->GetScriptLineNumber(),
    1122           0 :                             &allowInlineScript);
    1123           0 :   return allowInlineScript;
    1124             : }
    1125             : 
    1126             : ScriptLoadRequest*
    1127           5 : ScriptLoader::CreateLoadRequest(ScriptKind aKind,
    1128             :                                 nsIScriptElement* aElement,
    1129             :                                 uint32_t aVersion, CORSMode aCORSMode,
    1130             :                                 const SRIMetadata& aIntegrity)
    1131             : {
    1132           5 :   if (aKind == ScriptKind::Classic) {
    1133             :     return new ScriptLoadRequest(aKind, aElement, aVersion, aCORSMode,
    1134           5 :                                  aIntegrity);
    1135             :   }
    1136             : 
    1137           0 :   MOZ_ASSERT(aKind == ScriptKind::Module);
    1138           0 :   return new ModuleLoadRequest(aElement, aVersion, aCORSMode, aIntegrity, this);
    1139             : }
    1140             : 
    1141             : bool
    1142           5 : ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement)
    1143             : {
    1144             :   // We need a document to evaluate scripts.
    1145           5 :   NS_ENSURE_TRUE(mDocument, false);
    1146             : 
    1147             :   // Check to see if scripts has been turned off.
    1148           5 :   if (!mEnabled || !mDocument->IsScriptEnabled()) {
    1149           0 :     return false;
    1150             :   }
    1151             : 
    1152           5 :   NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script");
    1153             : 
    1154          10 :   nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
    1155             : 
    1156             :   // Step 13. Check that the script is not an eventhandler
    1157           5 :   if (IsScriptEventHandler(scriptContent)) {
    1158           0 :     return false;
    1159             :   }
    1160             : 
    1161           5 :   JSVersion version = JSVERSION_DEFAULT;
    1162             : 
    1163             :   // Check the type attribute to determine language and version.
    1164             :   // If type exists, it trumps the deprecated 'language='
    1165          10 :   nsAutoString type;
    1166           5 :   bool hasType = aElement->GetScriptType(type);
    1167             : 
    1168           5 :   ScriptKind scriptKind = ScriptKind::Classic;
    1169           5 :   if (!type.IsEmpty()) {
    1170           5 :     if (ModuleScriptsEnabled() && type.LowerCaseEqualsASCII("module")) {
    1171           0 :       scriptKind = ScriptKind::Module;
    1172             :     } else {
    1173           5 :       NS_ENSURE_TRUE(ParseTypeAttribute(type, &version), false);
    1174             :     }
    1175           0 :   } else if (!hasType) {
    1176             :     // no 'type=' element
    1177             :     // "language" is a deprecated attribute of HTML, so we check it only for
    1178             :     // HTML script elements.
    1179           0 :     if (scriptContent->IsHTMLElement()) {
    1180           0 :       nsAutoString language;
    1181           0 :       scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language);
    1182           0 :       if (!language.IsEmpty()) {
    1183           0 :         if (!nsContentUtils::IsJavaScriptLanguage(language)) {
    1184           0 :           return false;
    1185             :         }
    1186             :       }
    1187             :     }
    1188             :   }
    1189             : 
    1190             :   // "In modern user agents that support module scripts, the script element with
    1191             :   // the nomodule attribute will be ignored".
    1192             :   // "The nomodule attribute must not be specified on module scripts (and will
    1193             :   // be ignored if it is)."
    1194          10 :   if (ModuleScriptsEnabled() &&
    1195           0 :       scriptKind == ScriptKind::Classic &&
    1196           5 :       scriptContent->IsHTMLElement() &&
    1197           0 :       scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) {
    1198           0 :     return false;
    1199             :   }
    1200             : 
    1201             :   // Step 15. and later in the HTML5 spec
    1202           5 :   nsresult rv = NS_OK;
    1203          10 :   RefPtr<ScriptLoadRequest> request;
    1204           5 :   if (aElement->GetScriptExternal()) {
    1205             :     // external script
    1206           8 :     nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
    1207           4 :     if (!scriptURI) {
    1208             :       // Asynchronously report the failure to create a URI object
    1209           0 :       NS_DispatchToCurrentThread(
    1210           0 :         NewRunnableMethod("nsIScriptElement::FireErrorEvent",
    1211             :                           aElement,
    1212           0 :                           &nsIScriptElement::FireErrorEvent));
    1213           0 :       return false;
    1214             :     }
    1215             : 
    1216             :     // Double-check that the preload matches what we're asked to load now.
    1217           4 :     mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy();
    1218           4 :     CORSMode ourCORSMode = aElement->GetCORSMode();
    1219             :     nsTArray<PreloadInfo>::index_type i =
    1220           4 :       mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
    1221           4 :     if (i != nsTArray<PreloadInfo>::NoIndex) {
    1222             :       // preloaded
    1223             :       // note that a script-inserted script can steal a preload!
    1224           4 :       request = mPreloads[i].mRequest;
    1225           4 :       request->mElement = aElement;
    1226           8 :       nsString preloadCharset(mPreloads[i].mCharset);
    1227           4 :       mPreloads.RemoveElementAt(i);
    1228             : 
    1229             :       // Double-check that the charset the preload used is the same as
    1230             :       // the charset we have now.
    1231           8 :       nsAutoString elementCharset;
    1232           4 :       aElement->GetScriptCharset(elementCharset);
    1233          12 :       if (elementCharset.Equals(preloadCharset) &&
    1234           8 :           ourCORSMode == request->mCORSMode &&
    1235          12 :           ourRefPolicy == request->mReferrerPolicy &&
    1236           4 :           scriptKind == request->mKind) {
    1237           4 :         rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false);
    1238           4 :         if (NS_FAILED(rv)) {
    1239             :           // probably plans have changed; even though the preload was allowed seems
    1240             :           // like the actual load is not; let's cancel the preload request.
    1241           0 :           request->Cancel();
    1242           0 :           return false;
    1243             :         }
    1244             :       } else {
    1245             :         // Drop the preload
    1246           0 :         request = nullptr;
    1247             :       }
    1248             :     }
    1249             : 
    1250           4 :     if (!request) {
    1251             :       // no usable preload
    1252             : 
    1253           0 :       SRIMetadata sriMetadata;
    1254             :       {
    1255           0 :         nsAutoString integrity;
    1256           0 :         scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity,
    1257           0 :                                integrity);
    1258           0 :         if (!integrity.IsEmpty()) {
    1259           0 :           MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
    1260             :                   ("ScriptLoader::ProcessScriptElement, integrity=%s",
    1261             :                    NS_ConvertUTF16toUTF8(integrity).get()));
    1262           0 :           nsAutoCString sourceUri;
    1263           0 :           if (mDocument->GetDocumentURI()) {
    1264           0 :             mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    1265             :           }
    1266           0 :           SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter,
    1267           0 :                                       &sriMetadata);
    1268             :         }
    1269             :       }
    1270             : 
    1271             :       request = CreateLoadRequest(scriptKind, aElement, version, ourCORSMode,
    1272           0 :                                   sriMetadata);
    1273           0 :       request->mURI = scriptURI;
    1274           0 :       request->mIsInline = false;
    1275           0 :       request->mReferrerPolicy = ourRefPolicy;
    1276             :       // keep request->mScriptFromHead to false so we don't treat non preloaded
    1277             :       // scripts as blockers for full page load. See bug 792438.
    1278             : 
    1279           0 :       rv = StartLoad(request);
    1280           0 :       if (NS_FAILED(rv)) {
    1281           0 :         const char* message = "ScriptSourceLoadFailed";
    1282             : 
    1283           0 :         if (rv == NS_ERROR_MALFORMED_URI) {
    1284           0 :             message = "ScriptSourceMalformed";
    1285             :         }
    1286           0 :         else if (rv == NS_ERROR_DOM_BAD_URI) {
    1287           0 :             message = "ScriptSourceNotAllowed";
    1288             :         }
    1289             : 
    1290           0 :         NS_ConvertUTF8toUTF16 url(scriptURI->GetSpecOrDefault());
    1291           0 :         const char16_t* params[] = { url.get() };
    1292             : 
    1293           0 :         nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    1294           0 :             NS_LITERAL_CSTRING("Script Loader"), mDocument,
    1295             :             nsContentUtils::eDOM_PROPERTIES, message,
    1296           0 :             params, ArrayLength(params), nullptr,
    1297           0 :             EmptyString(), aElement->GetScriptLineNumber());
    1298             : 
    1299             :         // Asynchronously report the load failure
    1300           0 :         NS_DispatchToCurrentThread(
    1301           0 :           NewRunnableMethod("nsIScriptElement::FireErrorEvent",
    1302             :                             aElement,
    1303           0 :                             &nsIScriptElement::FireErrorEvent));
    1304           0 :         return false;
    1305             :       }
    1306             :     }
    1307             : 
    1308             :     // Should still be in loading stage of script.
    1309           4 :     NS_ASSERTION(!request->InCompilingStage(),
    1310             :                  "Request should not yet be in compiling stage.");
    1311             : 
    1312           4 :     request->mJSVersion = version;
    1313             : 
    1314           4 :     if (aElement->GetScriptAsync()) {
    1315           0 :       request->mIsAsync = true;
    1316           0 :       if (request->IsReadyToRun()) {
    1317           0 :         mLoadedAsyncRequests.AppendElement(request);
    1318             :         // The script is available already. Run it ASAP when the event
    1319             :         // loop gets a chance to spin.
    1320             : 
    1321             :         // KVKV TODO: Instead of processing immediately, try off-thread-parsing
    1322             :         // it and only schedule a pending ProcessRequest if that fails.
    1323           0 :         ProcessPendingRequestsAsync();
    1324             :       } else {
    1325           0 :         mLoadingAsyncRequests.AppendElement(request);
    1326             :       }
    1327           0 :       return false;
    1328             :     }
    1329           4 :     if (!aElement->GetParserCreated()) {
    1330             :       // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
    1331             :       // for RequireJS work with their Gecko-sniffed code path. See
    1332             :       // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
    1333           0 :       request->mIsNonAsyncScriptInserted = true;
    1334           0 :       mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
    1335           0 :       if (request->IsReadyToRun()) {
    1336             :         // The script is available already. Run it ASAP when the event
    1337             :         // loop gets a chance to spin.
    1338           0 :         ProcessPendingRequestsAsync();
    1339             :       }
    1340           0 :       return false;
    1341             :     }
    1342             :     // we now have a parser-inserted request that may or may not be still
    1343             :     // loading
    1344           4 :     if (aElement->GetScriptDeferred() || request->IsModuleRequest()) {
    1345             :       // We don't want to run this yet.
    1346             :       // If we come here, the script is a parser-created script and it has
    1347             :       // the defer attribute but not the async attribute. Since a
    1348             :       // a parser-inserted script is being run, we came here by the parser
    1349             :       // running the script, which means the parser is still alive and the
    1350             :       // parse is ongoing.
    1351           0 :       NS_ASSERTION(mDocument->GetCurrentContentSink() ||
    1352             :                    aElement->GetParserCreated() == FROM_PARSER_XSLT,
    1353             :           "Non-XSLT Defer script on a document without an active parser; bug 592366.");
    1354           0 :       AddDeferRequest(request);
    1355           0 :       return false;
    1356             :     }
    1357             : 
    1358           4 :     if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
    1359             :       // Need to maintain order for XSLT-inserted scripts
    1360           0 :       NS_ASSERTION(!mParserBlockingRequest,
    1361             :           "Parser-blocking scripts and XSLT scripts in the same doc!");
    1362           0 :       request->mIsXSLT = true;
    1363           0 :       mXSLTRequests.AppendElement(request);
    1364           0 :       if (request->IsReadyToRun()) {
    1365             :         // The script is available already. Run it ASAP when the event
    1366             :         // loop gets a chance to spin.
    1367           0 :         ProcessPendingRequestsAsync();
    1368             :       }
    1369           0 :       return true;
    1370             :     }
    1371             : 
    1372           4 :     if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) {
    1373             :       // The request has already been loaded and there are no pending style
    1374             :       // sheets. If the script comes from the network stream, cheat for
    1375             :       // performance reasons and avoid a trip through the event loop.
    1376           1 :       if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
    1377           1 :         return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
    1378             :       }
    1379             :       // Otherwise, we've got a document.written script, make a trip through
    1380             :       // the event loop to hide the preload effects from the scripts on the
    1381             :       // Web page.
    1382           0 :       NS_ASSERTION(!mParserBlockingRequest,
    1383             :           "There can be only one parser-blocking script at a time");
    1384           0 :       NS_ASSERTION(mXSLTRequests.isEmpty(),
    1385             :           "Parser-blocking scripts and XSLT scripts in the same doc!");
    1386           0 :       mParserBlockingRequest = request;
    1387           0 :       ProcessPendingRequestsAsync();
    1388           0 :       return true;
    1389             :     }
    1390             : 
    1391             :     // The script hasn't loaded yet or there's a style sheet blocking it.
    1392             :     // The script will be run when it loads or the style sheet loads.
    1393           3 :     NS_ASSERTION(!mParserBlockingRequest,
    1394             :         "There can be only one parser-blocking script at a time");
    1395           3 :     NS_ASSERTION(mXSLTRequests.isEmpty(),
    1396             :         "Parser-blocking scripts and XSLT scripts in the same doc!");
    1397           3 :     mParserBlockingRequest = request;
    1398           3 :     return true;
    1399             :   }
    1400             : 
    1401             :   // inline script
    1402             :   // Is this document sandboxed without 'allow-scripts'?
    1403           1 :   if (mDocument->HasScriptsBlockedBySandbox()) {
    1404           0 :     return false;
    1405             :   }
    1406             : 
    1407             :   // Does CSP allow this inline script to run?
    1408           1 :   if (!CSPAllowsInlineScript(aElement, mDocument)) {
    1409           0 :     return false;
    1410             :   }
    1411             : 
    1412             :   // Inline scripts ignore ther CORS mode and are always CORS_NONE
    1413             :   request = CreateLoadRequest(scriptKind, aElement, version, CORS_NONE,
    1414           1 :                               SRIMetadata()); // SRI doesn't apply
    1415           1 :   request->mJSVersion = version;
    1416           1 :   request->mIsInline = true;
    1417           1 :   request->mURI = mDocument->GetDocumentURI();
    1418           1 :   request->mLineNo = aElement->GetScriptLineNumber();
    1419           1 :   request->mProgress = ScriptLoadRequest::Progress::Loading_Source;
    1420           1 :   request->mDataType = ScriptLoadRequest::DataType::Source;
    1421           1 :   TRACE_FOR_TEST_BOOL(request->mElement, "scriptloader_load_source");
    1422           1 :   CollectScriptTelemetry(nullptr, request);
    1423             : 
    1424           1 :   if (request->IsModuleRequest()) {
    1425           0 :     ModuleLoadRequest* modReq = request->AsModuleRequest();
    1426           0 :     modReq->mBaseURL = mDocument->GetDocBaseURI();
    1427           0 :     rv = CreateModuleScript(modReq);
    1428           0 :     NS_ENSURE_SUCCESS(rv, false);
    1429           0 :     StartFetchingModuleDependencies(modReq);
    1430           0 :     if (aElement->GetScriptAsync()) {
    1431           0 :       mLoadingAsyncRequests.AppendElement(request);
    1432             :     } else {
    1433           0 :       AddDeferRequest(request);
    1434             :     }
    1435           0 :     return false;
    1436             :   }
    1437           1 :   request->mProgress = ScriptLoadRequest::Progress::Ready;
    1438           1 :   if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
    1439           0 :       (!ReadyToExecuteParserBlockingScripts() || !mXSLTRequests.isEmpty())) {
    1440             :     // Need to maintain order for XSLT-inserted scripts
    1441           0 :     NS_ASSERTION(!mParserBlockingRequest,
    1442             :         "Parser-blocking scripts and XSLT scripts in the same doc!");
    1443           0 :     mXSLTRequests.AppendElement(request);
    1444           0 :     return true;
    1445             :   }
    1446           1 :   if (aElement->GetParserCreated() == NOT_FROM_PARSER) {
    1447           0 :     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
    1448             :         "A script-inserted script is inserted without an update batch?");
    1449             :     nsContentUtils::AddScriptRunner(new ScriptRequestProcessor(this,
    1450           0 :                                                                  request));
    1451           0 :     return false;
    1452             :   }
    1453           2 :   if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
    1454           1 :       !ReadyToExecuteParserBlockingScripts()) {
    1455           0 :     NS_ASSERTION(!mParserBlockingRequest,
    1456             :         "There can be only one parser-blocking script at a time");
    1457           0 :     mParserBlockingRequest = request;
    1458           0 :     NS_ASSERTION(mXSLTRequests.isEmpty(),
    1459             :         "Parser-blocking scripts and XSLT scripts in the same doc!");
    1460           0 :     return true;
    1461             :   }
    1462             :   // We now have a document.written inline script or we have an inline script
    1463             :   // from the network but there is no style sheet that is blocking scripts.
    1464             :   // Don't check for style sheets blocking scripts in the document.write
    1465             :   // case to avoid style sheet network activity affecting when
    1466             :   // document.write returns. It's not really necessary to do this if
    1467             :   // there's no document.write currently on the call stack. However,
    1468             :   // this way matches IE more closely than checking if document.write
    1469             :   // is on the call stack.
    1470           1 :   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
    1471             :       "Not safe to run a parser-inserted script?");
    1472           1 :   return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
    1473             : }
    1474             : 
    1475             : namespace {
    1476             : 
    1477             : class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable
    1478             : {
    1479             :   RefPtr<ScriptLoadRequest> mRequest;
    1480             :   RefPtr<ScriptLoader> mLoader;
    1481             :   RefPtr<DocGroup> mDocGroup;
    1482             :   void* mToken;
    1483             : 
    1484             : public:
    1485           0 :   NotifyOffThreadScriptLoadCompletedRunnable(ScriptLoadRequest* aRequest,
    1486             :                                              ScriptLoader* aLoader)
    1487           0 :     : Runnable("dom::NotifyOffThreadScriptLoadCompletedRunnable")
    1488             :     , mRequest(aRequest)
    1489             :     , mLoader(aLoader)
    1490             :     , mDocGroup(aLoader->GetDocGroup())
    1491           0 :     , mToken(nullptr)
    1492             :   {
    1493           0 :     MOZ_ASSERT(NS_IsMainThread());
    1494           0 :   }
    1495             : 
    1496             :   virtual ~NotifyOffThreadScriptLoadCompletedRunnable();
    1497             : 
    1498           0 :   void SetToken(void* aToken) {
    1499           0 :     MOZ_ASSERT(aToken && !mToken);
    1500           0 :     mToken = aToken;
    1501           0 :   }
    1502             : 
    1503           0 :   static void Dispatch(already_AddRefed<NotifyOffThreadScriptLoadCompletedRunnable>&& aSelf) {
    1504           0 :     RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> self = aSelf;
    1505           0 :     RefPtr<DocGroup> docGroup = self->mDocGroup;
    1506           0 :     docGroup->Dispatch("NotifyOffThreadScriptLoadCompletedRunnable",
    1507           0 :                        TaskCategory::Other, self.forget());
    1508           0 :   }
    1509             : 
    1510             :   NS_DECL_NSIRUNNABLE
    1511             : };
    1512             : 
    1513             : } /* anonymous namespace */
    1514             : 
    1515             : nsresult
    1516           0 : ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest)
    1517             : {
    1518           0 :   MOZ_ASSERT(aRequest->mProgress == ScriptLoadRequest::Progress::Compiling);
    1519           0 :   MOZ_ASSERT(!aRequest->mWasCompiledOMT);
    1520             : 
    1521           0 :   aRequest->mWasCompiledOMT = true;
    1522             : 
    1523           0 :   if (aRequest->IsModuleRequest()) {
    1524           0 :     MOZ_ASSERT(aRequest->mOffThreadToken);
    1525           0 :     ModuleLoadRequest* request = aRequest->AsModuleRequest();
    1526           0 :     nsresult rv = ProcessFetchedModuleSource(request);
    1527           0 :     if (NS_FAILED(rv)) {
    1528           0 :       request->LoadFailed();
    1529             :     }
    1530           0 :     return rv;
    1531             :   }
    1532             : 
    1533           0 :   aRequest->SetReady();
    1534             : 
    1535           0 :   if (aRequest == mParserBlockingRequest) {
    1536           0 :     if (!ReadyToExecuteParserBlockingScripts()) {
    1537             :       // If not ready to execute scripts, schedule an async call to
    1538             :       // ProcessPendingRequests to handle it.
    1539           0 :       ProcessPendingRequestsAsync();
    1540           0 :       return NS_OK;
    1541             :     }
    1542             : 
    1543             :     // Same logic as in top of ProcessPendingRequests.
    1544           0 :     mParserBlockingRequest = nullptr;
    1545           0 :     UnblockParser(aRequest);
    1546           0 :     ProcessRequest(aRequest);
    1547           0 :     mDocument->UnblockOnload(false);
    1548           0 :     ContinueParserAsync(aRequest);
    1549           0 :     return NS_OK;
    1550             :   }
    1551             : 
    1552           0 :   nsresult rv = ProcessRequest(aRequest);
    1553           0 :   mDocument->UnblockOnload(false);
    1554           0 :   return rv;
    1555             : }
    1556             : 
    1557           0 : NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable()
    1558             : {
    1559           0 :   if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) {
    1560             :     NS_ReleaseOnMainThread(
    1561           0 :       "NotifyOffThreadScriptLoadCompletedRunnable::mRequest", mRequest.forget());
    1562             :     NS_ReleaseOnMainThread(
    1563           0 :       "NotifyOffThreadScriptLoadCompletedRunnable::mLoader", mLoader.forget());
    1564             :   }
    1565           0 : }
    1566             : 
    1567             : NS_IMETHODIMP
    1568           0 : NotifyOffThreadScriptLoadCompletedRunnable::Run()
    1569             : {
    1570           0 :   MOZ_ASSERT(NS_IsMainThread());
    1571             : 
    1572             :   // We want these to be dropped on the main thread, once we return from this
    1573             :   // function.
    1574           0 :   RefPtr<ScriptLoadRequest> request = mRequest.forget();
    1575           0 :   RefPtr<ScriptLoader> loader = mLoader.forget();
    1576             : 
    1577           0 :   request->mOffThreadToken = mToken;
    1578           0 :   nsresult rv = loader->ProcessOffThreadRequest(request);
    1579             : 
    1580           0 :   return rv;
    1581             : }
    1582             : 
    1583             : static void
    1584           0 : OffThreadScriptLoaderCallback(void* aToken, void* aCallbackData)
    1585             : {
    1586             :   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable =
    1587           0 :     dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));
    1588           0 :   aRunnable->SetToken(aToken);
    1589           0 :   NotifyOffThreadScriptLoadCompletedRunnable::Dispatch(aRunnable.forget());
    1590           0 : }
    1591             : 
    1592             : nsresult
    1593           3 : ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest)
    1594             : {
    1595           3 :   MOZ_ASSERT_IF(!aRequest->IsModuleRequest(), aRequest->IsReadyToRun());
    1596           3 :   MOZ_ASSERT(!aRequest->mWasCompiledOMT);
    1597             : 
    1598             :   // Don't off-thread compile inline scripts.
    1599           3 :   if (aRequest->mIsInline) {
    1600           0 :     return NS_ERROR_FAILURE;
    1601             :   }
    1602             : 
    1603           6 :   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
    1604           3 :   if (!globalObject) {
    1605           0 :     return NS_ERROR_FAILURE;
    1606             :   }
    1607             : 
    1608           6 :   AutoJSAPI jsapi;
    1609           3 :   if (!jsapi.Init(globalObject)) {
    1610           0 :     return NS_ERROR_FAILURE;
    1611             :   }
    1612             : 
    1613           3 :   JSContext* cx = jsapi.cx();
    1614           6 :   JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
    1615           6 :   JS::CompileOptions options(cx);
    1616             : 
    1617           3 :   nsresult rv = FillCompileOptionsForRequest(jsapi, aRequest, global, &options);
    1618           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1619           0 :     return rv;
    1620             :   }
    1621             : 
    1622           3 :   size_t len = aRequest->IsSource()
    1623           3 :     ? aRequest->mScriptText.length()
    1624           3 :     : aRequest->mScriptBytecode.length();
    1625           3 :   if (!JS::CanCompileOffThread(cx, options, len)) {
    1626           3 :     return NS_ERROR_FAILURE;
    1627             :   }
    1628             : 
    1629             :   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
    1630           0 :     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
    1631             : 
    1632           0 :   if (aRequest->IsModuleRequest()) {
    1633           0 :     MOZ_ASSERT(aRequest->IsSource());
    1634           0 :     if (!JS::CompileOffThreadModule(cx, options,
    1635           0 :                                     aRequest->mScriptText.begin(),
    1636             :                                     aRequest->mScriptText.length(),
    1637             :                                     OffThreadScriptLoaderCallback,
    1638           0 :                                     static_cast<void*>(runnable))) {
    1639           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1640             :     }
    1641           0 :   } else if (aRequest->IsSource()) {
    1642           0 :     if (!JS::CompileOffThread(cx, options,
    1643           0 :                               aRequest->mScriptText.begin(),
    1644             :                               aRequest->mScriptText.length(),
    1645             :                               OffThreadScriptLoaderCallback,
    1646           0 :                               static_cast<void*>(runnable))) {
    1647           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1648             :     }
    1649             :   } else {
    1650           0 :     MOZ_ASSERT(aRequest->IsBytecode());
    1651           0 :     if (!JS::DecodeOffThreadScript(cx, options,
    1652             :                                    aRequest->mScriptBytecode,
    1653           0 :                                    aRequest->mBytecodeOffset,
    1654             :                                    OffThreadScriptLoaderCallback,
    1655           0 :                                    static_cast<void*>(runnable))) {
    1656           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1657             :     }
    1658             :   }
    1659             : 
    1660           0 :   mDocument->BlockOnload();
    1661             : 
    1662             :   // Once the compilation is finished, an event would be added to the event loop
    1663             :   // to call ScriptLoader::ProcessOffThreadRequest with the same request.
    1664           0 :   aRequest->mProgress = ScriptLoadRequest::Progress::Compiling;
    1665             : 
    1666           0 :   Unused << runnable.forget();
    1667           0 :   return NS_OK;
    1668             : }
    1669             : 
    1670             : nsresult
    1671           0 : ScriptLoader::CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest)
    1672             : {
    1673           0 :   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
    1674             :                "Processing requests when running scripts is unsafe.");
    1675           0 :   NS_ASSERTION(!aRequest->mOffThreadToken,
    1676             :                "Candidate for off-thread compile is already parsed off-thread");
    1677           0 :   NS_ASSERTION(!aRequest->InCompilingStage(),
    1678             :                "Candidate for off-thread compile is already in compiling stage.");
    1679             : 
    1680           0 :   nsresult rv = AttemptAsyncScriptCompile(aRequest);
    1681           0 :   if (NS_SUCCEEDED(rv)) {
    1682           0 :     return rv;
    1683             :   }
    1684             : 
    1685           0 :   return ProcessRequest(aRequest);
    1686             : }
    1687             : 
    1688             : SourceBufferHolder
    1689           5 : ScriptLoader::GetScriptSource(ScriptLoadRequest* aRequest, nsAutoString& inlineData)
    1690             : {
    1691             :   // Return a SourceBufferHolder object holding the script's source text.
    1692             :   // |inlineData| is used to hold the text for inline objects.
    1693             : 
    1694             :   // If there's no script text, we try to get it from the element
    1695           5 :   if (aRequest->mIsInline) {
    1696             :     // XXX This is inefficient - GetText makes multiple
    1697             :     // copies.
    1698           1 :     aRequest->mElement->GetScriptText(inlineData);
    1699             :     return SourceBufferHolder(inlineData.get(),
    1700           1 :                               inlineData.Length(),
    1701           1 :                               SourceBufferHolder::NoOwnership);
    1702             :   }
    1703             : 
    1704           4 :   return SourceBufferHolder(aRequest->mScriptText.begin(),
    1705             :                             aRequest->mScriptText.length(),
    1706           8 :                             SourceBufferHolder::NoOwnership);
    1707             : }
    1708             : 
    1709             : nsresult
    1710           5 : ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
    1711             : {
    1712           5 :   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
    1713             :                "Processing requests when running scripts is unsafe.");
    1714           5 :   NS_ASSERTION(aRequest->IsReadyToRun(),
    1715             :                "Processing a request that is not ready to run.");
    1716             : 
    1717           5 :   NS_ENSURE_ARG(aRequest);
    1718             : 
    1719           5 :   if (aRequest->IsModuleRequest() &&
    1720           0 :       !aRequest->AsModuleRequest()->mModuleScript)
    1721             :   {
    1722             :     // There was an error parsing a module script.  Nothing to do here.
    1723           0 :     FireScriptAvailable(NS_ERROR_FAILURE, aRequest);
    1724           0 :     return NS_OK;
    1725             :   }
    1726             : 
    1727          10 :   nsCOMPtr<nsINode> scriptElem = do_QueryInterface(aRequest->mElement);
    1728             : 
    1729          10 :   nsCOMPtr<nsIDocument> doc;
    1730           5 :   if (!aRequest->mIsInline) {
    1731           4 :     doc = scriptElem->OwnerDoc();
    1732             :   }
    1733             : 
    1734          10 :   nsCOMPtr<nsIScriptElement> oldParserInsertedScript;
    1735           5 :   uint32_t parserCreated = aRequest->mElement->GetParserCreated();
    1736           5 :   if (parserCreated) {
    1737           5 :     oldParserInsertedScript = mCurrentParserInsertedScript;
    1738           5 :     mCurrentParserInsertedScript = aRequest->mElement;
    1739             :   }
    1740             : 
    1741           5 :   aRequest->mElement->BeginEvaluating();
    1742             : 
    1743           5 :   FireScriptAvailable(NS_OK, aRequest);
    1744             : 
    1745             :   // The window may have gone away by this point, in which case there's no point
    1746             :   // in trying to run the script.
    1747             : 
    1748             :   {
    1749             :     // Try to perform a microtask checkpoint
    1750           5 :     nsAutoMicroTask mt;
    1751             :   }
    1752             : 
    1753           5 :   nsPIDOMWindowInner* pwin = mDocument->GetInnerWindow();
    1754           5 :   bool runScript = !!pwin;
    1755           5 :   if (runScript) {
    1756          10 :     nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
    1757             :                                          scriptElem,
    1758          10 :                                          NS_LITERAL_STRING("beforescriptexecute"),
    1759          15 :                                          true, true, &runScript);
    1760             :   }
    1761             : 
    1762             :   // Inner window could have gone away after firing beforescriptexecute
    1763           5 :   pwin = mDocument->GetInnerWindow();
    1764           5 :   if (!pwin) {
    1765           0 :     runScript = false;
    1766             :   }
    1767             : 
    1768           5 :   nsresult rv = NS_OK;
    1769           5 :   if (runScript) {
    1770           5 :     if (doc) {
    1771           4 :       doc->BeginEvaluatingExternalScript();
    1772             :     }
    1773           5 :     rv = EvaluateScript(aRequest);
    1774           5 :     if (doc) {
    1775           4 :       doc->EndEvaluatingExternalScript();
    1776             :     }
    1777             : 
    1778          10 :     nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
    1779             :                                          scriptElem,
    1780          10 :                                          NS_LITERAL_STRING("afterscriptexecute"),
    1781          15 :                                          true, false);
    1782             :   }
    1783             : 
    1784           5 :   FireScriptEvaluated(rv, aRequest);
    1785             : 
    1786           5 :   aRequest->mElement->EndEvaluating();
    1787             : 
    1788           5 :   if (parserCreated) {
    1789           5 :     mCurrentParserInsertedScript = oldParserInsertedScript;
    1790             :   }
    1791             : 
    1792           5 :   if (aRequest->mOffThreadToken) {
    1793             :     // The request was parsed off-main-thread, but the result of the off
    1794             :     // thread parse was not actually needed to process the request
    1795             :     // (disappearing window, some other error, ...). Finish the
    1796             :     // request to avoid leaks in the JS engine.
    1797           0 :     MOZ_ASSERT(!aRequest->IsModuleRequest());
    1798           0 :     aRequest->MaybeCancelOffThreadScript();
    1799             :   }
    1800             : 
    1801             :   // Free any source data, but keep the bytecode content as we might have to
    1802             :   // save it later.
    1803           5 :   aRequest->mScriptText.clearAndFree();
    1804             : 
    1805           5 :   return rv;
    1806             : }
    1807             : 
    1808             : void
    1809           5 : ScriptLoader::FireScriptAvailable(nsresult aResult,
    1810             :                                   ScriptLoadRequest* aRequest)
    1811             : {
    1812           5 :   for (int32_t i = 0; i < mObservers.Count(); i++) {
    1813           0 :     nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
    1814           0 :     obs->ScriptAvailable(aResult, aRequest->mElement,
    1815           0 :                          aRequest->mIsInline, aRequest->mURI,
    1816           0 :                          aRequest->mLineNo);
    1817             :   }
    1818             : 
    1819           5 :   aRequest->FireScriptAvailable(aResult);
    1820           5 : }
    1821             : 
    1822             : void
    1823           5 : ScriptLoader::FireScriptEvaluated(nsresult aResult,
    1824             :                                   ScriptLoadRequest* aRequest)
    1825             : {
    1826           5 :   for (int32_t i = 0; i < mObservers.Count(); i++) {
    1827           0 :     nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
    1828           0 :     obs->ScriptEvaluated(aResult, aRequest->mElement,
    1829           0 :                          aRequest->mIsInline);
    1830             :   }
    1831             : 
    1832           5 :   aRequest->FireScriptEvaluated(aResult);
    1833           5 : }
    1834             : 
    1835             : already_AddRefed<nsIScriptGlobalObject>
    1836           8 : ScriptLoader::GetScriptGlobalObject()
    1837             : {
    1838           8 :   if (!mDocument) {
    1839           0 :     return nullptr;
    1840             :   }
    1841             : 
    1842           8 :   nsPIDOMWindowInner* pwin = mDocument->GetInnerWindow();
    1843           8 :   if (!pwin) {
    1844           0 :     return nullptr;
    1845             :   }
    1846             : 
    1847          16 :   nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin);
    1848           8 :   NS_ASSERTION(globalObject, "windows must be global objects");
    1849             : 
    1850             :   // and make sure we are setup for this type of script.
    1851           8 :   nsresult rv = globalObject->EnsureScriptEnvironment();
    1852           8 :   if (NS_FAILED(rv)) {
    1853           0 :     return nullptr;
    1854             :   }
    1855             : 
    1856           8 :   return globalObject.forget();
    1857             : }
    1858             : 
    1859             : nsresult
    1860           8 : ScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI&jsapi,
    1861             :                                            ScriptLoadRequest* aRequest,
    1862             :                                            JS::Handle<JSObject*> aScopeChain,
    1863             :                                            JS::CompileOptions* aOptions)
    1864             : {
    1865             :   // It's very important to use aRequest->mURI, not the final URI of the channel
    1866             :   // aRequest ended up getting script data from, as the script filename.
    1867             :   nsresult rv;
    1868           8 :   nsContentUtils::GetWrapperSafeScriptFilename(mDocument, aRequest->mURI,
    1869           8 :                                                aRequest->mURL, &rv);
    1870           8 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1871           0 :     return rv;
    1872             :   }
    1873             : 
    1874           8 :   if (mDocument) {
    1875           8 :     mDocument->NoteScriptTrackingStatus(aRequest->mURL, aRequest->IsTracking());
    1876             :   }
    1877             : 
    1878           8 :   bool isScriptElement = !aRequest->IsModuleRequest() ||
    1879           8 :                          aRequest->AsModuleRequest()->IsTopLevel();
    1880             :   aOptions->setIntroductionType(isScriptElement ? "scriptElement"
    1881           8 :                                                 : "importedModule");
    1882           8 :   aOptions->setFileAndLine(aRequest->mURL.get(), aRequest->mLineNo);
    1883           8 :   aOptions->setVersion(JSVersion(aRequest->mJSVersion));
    1884           8 :   aOptions->setIsRunOnce(true);
    1885           8 :   aOptions->setNoScriptRval(true);
    1886           8 :   if (aRequest->mHasSourceMapURL) {
    1887           0 :     aOptions->setSourceMapURL(aRequest->mSourceMapURL.get());
    1888             :   }
    1889           8 :   if (aRequest->mOriginPrincipal) {
    1890           7 :     nsIPrincipal* scriptPrin = nsContentUtils::ObjectPrincipal(aScopeChain);
    1891           7 :     bool subsumes = scriptPrin->Subsumes(aRequest->mOriginPrincipal);
    1892           7 :     aOptions->setMutedErrors(!subsumes);
    1893             :   }
    1894             : 
    1895           8 :   JSContext* cx = jsapi.cx();
    1896          16 :   JS::Rooted<JS::Value> elementVal(cx);
    1897           8 :   MOZ_ASSERT(aRequest->mElement);
    1898           8 :   if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, aRequest->mElement,
    1899             :                                               &elementVal,
    1900             :                                               /* aAllowWrapping = */ true))) {
    1901           8 :     MOZ_ASSERT(elementVal.isObject());
    1902           8 :     aOptions->setElement(&elementVal.toObject());
    1903             :   }
    1904             : 
    1905           8 :   return NS_OK;
    1906             : }
    1907             : 
    1908             : /* static */ bool
    1909           5 : ScriptLoader::ShouldCacheBytecode(ScriptLoadRequest* aRequest)
    1910             : {
    1911             :   using mozilla::TimeStamp;
    1912             :   using mozilla::TimeDuration;
    1913             : 
    1914             :   // We need the nsICacheInfoChannel to exist to be able to open the alternate
    1915             :   // data output stream. This pointer would only be non-null if the bytecode was
    1916             :   // activated at the time the channel got created in StartLoad.
    1917           5 :   if (!aRequest->mCacheInfo) {
    1918           5 :     LOG(("ScriptLoadRequest (%p): Cannot cache anything (cacheInfo = %p)",
    1919             :          aRequest, aRequest->mCacheInfo.get()));
    1920           5 :     return false;
    1921             :   }
    1922             : 
    1923             :   // Look at the preference to know which strategy (parameters) should be used
    1924             :   // when the bytecode cache is enabled.
    1925           0 :   int32_t strategy = nsContentUtils::BytecodeCacheStrategy();
    1926             : 
    1927             :   // List of parameters used by the strategies.
    1928           0 :   bool hasSourceLengthMin = false;
    1929           0 :   bool hasFetchCountMin = false;
    1930           0 :   bool hasTimeSinceLastFetched = false;
    1931           0 :   size_t sourceLengthMin = 100;
    1932           0 :   int32_t fetchCountMin = 5;
    1933           0 :   TimeDuration timeSinceLastFetched;
    1934             : 
    1935           0 :   LOG(("ScriptLoadRequest (%p): Bytecode-cache: strategy = %d.", aRequest, strategy));
    1936           0 :   switch (strategy) {
    1937             :     case -2: {
    1938             :       // Reader mode, keep requesting alternate data but no longer save it.
    1939           0 :       LOG(("ScriptLoadRequest (%p): Bytecode-cache: Encoding disabled.", aRequest));
    1940           0 :       return false;
    1941             :     }
    1942             :     case -1: {
    1943             :       // Eager mode, skip heuristics!
    1944           0 :       hasSourceLengthMin = false;
    1945           0 :       hasFetchCountMin = false;
    1946           0 :       hasTimeSinceLastFetched = false;
    1947           0 :       break;
    1948             :     }
    1949             :     default:
    1950             :     case 0: {
    1951           0 :       hasSourceLengthMin = true;
    1952           0 :       hasFetchCountMin = true;
    1953           0 :       hasTimeSinceLastFetched = true;
    1954           0 :       sourceLengthMin = 1024;
    1955           0 :       fetchCountMin = 5;
    1956           0 :       timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
    1957           0 :       break;
    1958             :     }
    1959             : 
    1960             :     // The following strategies are made-up to study what impact each parameter
    1961             :     // has when compared to the default case.
    1962             :     case 1: {
    1963           0 :       hasSourceLengthMin = true;
    1964           0 :       hasFetchCountMin = true;
    1965           0 :       hasTimeSinceLastFetched = false;
    1966           0 :       sourceLengthMin = 1024;
    1967           0 :       fetchCountMin = 5;
    1968           0 :       break;
    1969             :     }
    1970             :     case 2: {
    1971           0 :       hasSourceLengthMin = true;
    1972           0 :       hasFetchCountMin = false;
    1973           0 :       hasTimeSinceLastFetched = true;
    1974           0 :       sourceLengthMin = 1024;
    1975           0 :       timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
    1976           0 :       break;
    1977             :     }
    1978             :     case 3: {
    1979           0 :       hasSourceLengthMin = false;
    1980           0 :       hasFetchCountMin = true;
    1981           0 :       hasTimeSinceLastFetched = true;
    1982           0 :       fetchCountMin = 5;
    1983           0 :       timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
    1984           0 :       break;
    1985             :     }
    1986             : 
    1987             :     // The following strategies are made-up to study what impact each parameter
    1988             :     // has individually.
    1989             :     case 4: {
    1990           0 :       hasSourceLengthMin = false;
    1991           0 :       hasFetchCountMin = false;
    1992           0 :       hasTimeSinceLastFetched = true;
    1993           0 :       timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
    1994           0 :       break;
    1995             :     }
    1996             :     case 5: {
    1997           0 :       hasSourceLengthMin = false;
    1998           0 :       hasFetchCountMin = true;
    1999           0 :       hasTimeSinceLastFetched = false;
    2000           0 :       fetchCountMin = 5;
    2001           0 :       break;
    2002             :     }
    2003             :     case 6: {
    2004           0 :       hasSourceLengthMin = true;
    2005           0 :       hasFetchCountMin = false;
    2006           0 :       hasTimeSinceLastFetched = false;
    2007           0 :       sourceLengthMin = 1024;
    2008           0 :       break;
    2009             :     }
    2010             :   }
    2011             : 
    2012             :   // If the script is too small/large, do not attempt at creating a bytecode
    2013             :   // cache for this script, as the overhead of parsing it might not be worth the
    2014             :   // effort.
    2015           0 :   if (hasSourceLengthMin && aRequest->mScriptText.length() < sourceLengthMin) {
    2016           0 :     LOG(("ScriptLoadRequest (%p): Bytecode-cache: Script is too small.", aRequest));
    2017           0 :     return false;
    2018             :   }
    2019             : 
    2020             :   // Check that we loaded the cache entry a few times before attempting any
    2021             :   // bytecode-cache optimization, such that we do not waste time on entry which
    2022             :   // are going to be dropped soon.
    2023           0 :   if (hasFetchCountMin) {
    2024           0 :     int32_t fetchCount = 0;
    2025           0 :     if (NS_FAILED(aRequest->mCacheInfo->GetCacheTokenFetchCount(&fetchCount))) {
    2026           0 :       LOG(("ScriptLoadRequest (%p): Bytecode-cache: Cannot get fetchCount.", aRequest));
    2027           0 :       return false;
    2028             :     }
    2029           0 :     LOG(("ScriptLoadRequest (%p): Bytecode-cache: fetchCount = %d.", aRequest, fetchCount));
    2030           0 :     if (fetchCount < fetchCountMin) {
    2031           0 :       return false;
    2032             :     }
    2033             :   }
    2034             : 
    2035             :   // Check that the cache entry got accessed recently, before caching it.
    2036           0 :   if (hasTimeSinceLastFetched) {
    2037           0 :     uint32_t lastFetched = 0;
    2038           0 :     if (NS_FAILED(aRequest->mCacheInfo->GetCacheTokenLastFetched(&lastFetched))) {
    2039           0 :       LOG(("ScriptLoadRequest (%p): Bytecode-cache: Cannot get lastFetched.", aRequest));
    2040           0 :       return false;
    2041             :     }
    2042           0 :     uint32_t now = PR_Now() / PR_USEC_PER_SEC;
    2043           0 :     if (now < lastFetched) {
    2044           0 :       LOG(("ScriptLoadRequest (%p): Bytecode-cache: (What?) lastFetched set in the future.", aRequest));
    2045           0 :       return false;
    2046             :     }
    2047           0 :     TimeDuration since = TimeDuration::FromSeconds(now - lastFetched);
    2048           0 :     LOG(("ScriptLoadRequest (%p): Bytecode-cache: lastFetched = %f sec. ago.",
    2049             :          aRequest, since.ToSeconds()));
    2050           0 :     if (since >= timeSinceLastFetched) {
    2051           0 :       return false;
    2052             :     }
    2053             :   }
    2054             : 
    2055           0 :   LOG(("ScriptLoadRequest (%p): Bytecode-cache: Trigger encoding.", aRequest));
    2056           0 :   return true;
    2057             : }
    2058             : 
    2059             : nsresult
    2060           5 : ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
    2061             : {
    2062             :   using namespace mozilla::Telemetry;
    2063           5 :   MOZ_ASSERT(aRequest->IsReadyToRun());
    2064             : 
    2065             :   // We need a document to evaluate scripts.
    2066           5 :   if (!mDocument) {
    2067           0 :     return NS_ERROR_FAILURE;
    2068             :   }
    2069             : 
    2070          10 :   nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
    2071           5 :   nsIDocument* ownerDoc = scriptContent->OwnerDoc();
    2072           5 :   if (ownerDoc != mDocument) {
    2073             :     // Willful violation of HTML5 as of 2010-12-01
    2074           0 :     return NS_ERROR_FAILURE;
    2075             :   }
    2076             : 
    2077             :   // Report telemetry results of the number of scripts evaluated.
    2078           5 :   mDocument->SetDocumentIncCounter(IncCounter::eIncCounter_ScriptTag);
    2079             : 
    2080             :   // Get the script-type to be used by this element.
    2081           5 :   NS_ASSERTION(scriptContent, "no content - what is default script-type?");
    2082             : 
    2083          10 :   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
    2084           5 :   if (!globalObject) {
    2085           0 :     return NS_ERROR_FAILURE;
    2086             :   }
    2087             : 
    2088             :   // Make sure context is a strong reference since we access it after
    2089             :   // we've executed a script, which may cause all other references to
    2090             :   // the context to go away.
    2091          10 :   nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
    2092           5 :   if (!context) {
    2093           0 :     return NS_ERROR_FAILURE;
    2094             :   }
    2095             : 
    2096           5 :   JSVersion version = JSVersion(aRequest->mJSVersion);
    2097           5 :   if (version == JSVERSION_UNKNOWN) {
    2098           0 :     return NS_OK;
    2099             :   }
    2100             : 
    2101             :   // New script entry point required, due to the "Create a script" sub-step of
    2102             :   // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
    2103          10 :   nsAutoMicroTask mt;
    2104          10 :   AutoEntryScript aes(globalObject, "<script> element", true);
    2105          10 :   JS::Rooted<JSObject*> global(aes.cx(),
    2106          20 :                                globalObject->GetGlobalJSObject());
    2107             : 
    2108           5 :   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
    2109           5 :   context->SetProcessingScriptTag(true);
    2110             :   nsresult rv;
    2111             :   {
    2112             :     // Update our current script.
    2113          10 :     AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement);
    2114             : 
    2115           5 :     if (aRequest->IsModuleRequest()) {
    2116             :       // When a module is already loaded, it is not feched a second time and the
    2117             :       // mDataType of the request might remain set to DataType::Unknown.
    2118           0 :       MOZ_ASSERT(!aRequest->IsBytecode());
    2119           0 :       LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
    2120           0 :       ModuleLoadRequest* request = aRequest->AsModuleRequest();
    2121           0 :       MOZ_ASSERT(request->mModuleScript);
    2122           0 :       MOZ_ASSERT(!request->mOffThreadToken);
    2123           0 :       ModuleScript* ms = request->mModuleScript;
    2124           0 :       MOZ_ASSERT(!ms->IsUninstantiated());
    2125           0 :       if (ms->InstantiationFailed()) {
    2126           0 :         JS::Rooted<JS::Value> exception(aes.cx(), ms->Exception());
    2127           0 :         JS_SetPendingException(aes.cx(), exception);
    2128           0 :         rv = NS_ERROR_FAILURE;
    2129             :       } else {
    2130           0 :         JS::Rooted<JSObject*> module(aes.cx(), ms->ModuleRecord());
    2131           0 :         MOZ_ASSERT(module);
    2132           0 :         rv = nsJSUtils::ModuleEvaluation(aes.cx(), module);
    2133             :       }
    2134           0 :       aRequest->mCacheInfo = nullptr;
    2135             :     } else {
    2136          10 :       JS::CompileOptions options(aes.cx());
    2137           5 :       rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
    2138             : 
    2139           5 :       if (NS_SUCCEEDED(rv)) {
    2140           5 :         if (aRequest->IsBytecode()) {
    2141           0 :           TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
    2142           0 :           nsJSUtils::ExecutionContext exec(aes.cx(), global);
    2143           0 :           if (aRequest->mOffThreadToken) {
    2144           0 :             LOG(("ScriptLoadRequest (%p): Decode Bytecode & Join and Execute", aRequest));
    2145           0 :             AutoTimer<DOM_SCRIPT_OFF_THREAD_DECODE_EXEC_MS> timer;
    2146           0 :             rv = exec.DecodeJoinAndExec(&aRequest->mOffThreadToken);
    2147             :           } else {
    2148           0 :             LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
    2149           0 :             AutoTimer<DOM_SCRIPT_MAIN_THREAD_DECODE_EXEC_MS> timer;
    2150           0 :             rv = exec.DecodeAndExec(options, aRequest->mScriptBytecode,
    2151           0 :                                     aRequest->mBytecodeOffset);
    2152             :           }
    2153             :           // We do not expect to be saving anything when we already have some
    2154             :           // bytecode.
    2155           0 :           MOZ_ASSERT(!aRequest->mCacheInfo);
    2156             :         } else {
    2157           5 :           MOZ_ASSERT(aRequest->IsSource());
    2158          10 :           JS::Rooted<JSScript*> script(aes.cx());
    2159           5 :           bool encodeBytecode = ShouldCacheBytecode(aRequest);
    2160             : 
    2161           5 :           TimeStamp start;
    2162           5 :           if (Telemetry::CanRecordExtended()) {
    2163             :             // Only record telemetry for scripts which are above the threshold.
    2164           0 :             if (aRequest->mCacheInfo && aRequest->mScriptText.length() >= 1024) {
    2165           0 :               start = TimeStamp::Now();
    2166             :             }
    2167             :           }
    2168             : 
    2169             :           {
    2170          10 :             nsJSUtils::ExecutionContext exec(aes.cx(), global);
    2171           5 :             exec.SetEncodeBytecode(encodeBytecode);
    2172           5 :             TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
    2173           5 :             if (aRequest->mOffThreadToken) {
    2174             :               // Off-main-thread parsing.
    2175           0 :               LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
    2176             :                    aRequest));
    2177           0 :               rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
    2178           0 :               if (start) {
    2179           0 :                 AccumulateTimeDelta(encodeBytecode
    2180             :                                     ? DOM_SCRIPT_OFF_THREAD_PARSE_ENCODE_EXEC_MS
    2181             :                                     : DOM_SCRIPT_OFF_THREAD_PARSE_EXEC_MS,
    2182           0 :                                     start);
    2183             :               }
    2184             :             } else {
    2185             :               // Main thread parsing (inline and small scripts)
    2186           5 :               LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
    2187          10 :               nsAutoString inlineData;
    2188          10 :               SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
    2189           5 :               rv = exec.CompileAndExec(options, srcBuf, &script);
    2190           5 :               if (start) {
    2191           0 :                 AccumulateTimeDelta(encodeBytecode
    2192             :                                     ? DOM_SCRIPT_MAIN_THREAD_PARSE_ENCODE_EXEC_MS
    2193             :                                     : DOM_SCRIPT_MAIN_THREAD_PARSE_EXEC_MS,
    2194           0 :                                     start);
    2195             :               }
    2196             :             }
    2197             :           }
    2198             : 
    2199             :           // Queue the current script load request to later save the bytecode.
    2200           5 :           if (script && encodeBytecode) {
    2201           0 :             aRequest->mScript = script;
    2202           0 :             HoldJSObjects(aRequest);
    2203           0 :             TRACE_FOR_TEST(aRequest->mElement, "scriptloader_encode");
    2204           0 :             MOZ_ASSERT(aRequest->mBytecodeOffset == aRequest->mScriptBytecode.length());
    2205           0 :             RegisterForBytecodeEncoding(aRequest);
    2206             :           } else {
    2207           5 :             LOG(("ScriptLoadRequest (%p): Bytecode-cache: disabled (rv = %X, script = %p)",
    2208             :                  aRequest, unsigned(rv), script.get()));
    2209           5 :             TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_no_encode");
    2210           5 :             aRequest->mCacheInfo = nullptr;
    2211             :           }
    2212             :         }
    2213             :       }
    2214             :     }
    2215             : 
    2216             :     // Even if we are not saving the bytecode of the current script, we have
    2217             :     // to trigger the encoding of the bytecode, as the current script can
    2218             :     // call functions of a script for which we are recording the bytecode.
    2219           5 :     MaybeTriggerBytecodeEncoding();
    2220             :   }
    2221             : 
    2222           5 :   context->SetProcessingScriptTag(oldProcessingScriptTag);
    2223           5 :   return rv;
    2224             : }
    2225             : 
    2226             : void
    2227           0 : ScriptLoader::RegisterForBytecodeEncoding(ScriptLoadRequest* aRequest)
    2228             : {
    2229           0 :   MOZ_ASSERT(aRequest->mCacheInfo);
    2230           0 :   MOZ_ASSERT(aRequest->mScript);
    2231           0 :   mBytecodeEncodingQueue.AppendElement(aRequest);
    2232           0 : }
    2233             : 
    2234             : void
    2235           6 : ScriptLoader::LoadEventFired()
    2236             : {
    2237           6 :   mLoadEventFired = true;
    2238           6 :   MaybeTriggerBytecodeEncoding();
    2239           6 : }
    2240             : 
    2241             : void
    2242          11 : ScriptLoader::MaybeTriggerBytecodeEncoding()
    2243             : {
    2244             :   // We wait for the load event to be fired before saving the bytecode of
    2245             :   // any script to the cache. It is quite common to have load event
    2246             :   // listeners trigger more JavaScript execution, that we want to save as
    2247             :   // part of this start-up bytecode cache.
    2248          11 :   if (!mLoadEventFired) {
    2249          16 :     return;
    2250             :   }
    2251             : 
    2252             :   // No need to fire any event if there is no bytecode to be saved.
    2253           6 :   if (mBytecodeEncodingQueue.isEmpty()) {
    2254           6 :     return;
    2255             :   }
    2256             : 
    2257             :   // Wait until all scripts are loaded before saving the bytecode, such that
    2258             :   // we capture most of the intialization of the page.
    2259           0 :   if (HasPendingRequests()) {
    2260           0 :     return;
    2261             :   }
    2262             : 
    2263             :   // Create a new runnable dedicated to encoding the content of the bytecode of
    2264             :   // all enqueued scripts when the document is idle. In case of failure, we
    2265             :   // give-up on encoding the bytecode.
    2266             :   nsCOMPtr<nsIRunnable> encoder =
    2267           0 :     NewRunnableMethod("ScriptLoader::EncodeBytecode",
    2268           0 :                       this, &ScriptLoader::EncodeBytecode);
    2269           0 :   if (NS_FAILED(NS_IdleDispatchToCurrentThread(encoder.forget()))) {
    2270           0 :     GiveUpBytecodeEncoding();
    2271             :   }
    2272             : }
    2273             : 
    2274             : void
    2275           0 : ScriptLoader::EncodeBytecode()
    2276             : {
    2277             :   // If any script got added in the previous loop cycle, wait until all
    2278             :   // remaining script executions are completed, such that we capture most of
    2279             :   // the initialization.
    2280           0 :   if (HasPendingRequests()) {
    2281           0 :     return;
    2282             :   }
    2283             : 
    2284           0 :   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
    2285           0 :   if (!globalObject) {
    2286           0 :     GiveUpBytecodeEncoding();
    2287           0 :     return;
    2288             :   }
    2289             : 
    2290           0 :   nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
    2291           0 :   if (!context) {
    2292           0 :     GiveUpBytecodeEncoding();
    2293           0 :     return;
    2294             :   }
    2295             : 
    2296           0 :   Telemetry::AutoTimer<Telemetry::DOM_SCRIPT_ENCODING_MS_PER_DOCUMENT> timer;
    2297           0 :   AutoEntryScript aes(globalObject, "encode bytecode", true);
    2298           0 :   RefPtr<ScriptLoadRequest> request;
    2299           0 :   while (!mBytecodeEncodingQueue.isEmpty()) {
    2300           0 :     request = mBytecodeEncodingQueue.StealFirst();
    2301           0 :     EncodeRequestBytecode(aes.cx(), request);
    2302           0 :     request->mScriptBytecode.clearAndFree();
    2303           0 :     request->DropBytecodeCacheReferences();
    2304             :   }
    2305             : }
    2306             : 
    2307             : void
    2308           0 : ScriptLoader::EncodeRequestBytecode(JSContext* aCx, ScriptLoadRequest* aRequest)
    2309             : {
    2310             :   using namespace mozilla::Telemetry;
    2311           0 :   nsresult rv = NS_OK;
    2312           0 :   MOZ_ASSERT(aRequest->mCacheInfo);
    2313           0 :   auto bytecodeFailed = mozilla::MakeScopeExit([&]() {
    2314           0 :     TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_failed");
    2315           0 :   });
    2316             : 
    2317           0 :   JS::RootedScript script(aCx, aRequest->mScript);
    2318           0 :   if (!JS::FinishIncrementalEncoding(aCx, script, aRequest->mScriptBytecode)) {
    2319           0 :     LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode",
    2320             :          aRequest));
    2321           0 :     AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::EncodingFailure);
    2322           0 :     return;
    2323             :   }
    2324             : 
    2325           0 :   if (aRequest->mScriptBytecode.length() >= UINT32_MAX) {
    2326           0 :     LOG(("ScriptLoadRequest (%p): Bytecode cache is too large to be decoded correctly.",
    2327             :          aRequest));
    2328           0 :     AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::BufferTooLarge);
    2329           0 :     return;
    2330             :   }
    2331             : 
    2332             :   // Open the output stream to the cache entry alternate data storage. This
    2333             :   // might fail if the stream is already open by another request, in which
    2334             :   // case, we just ignore the current one.
    2335           0 :   nsCOMPtr<nsIOutputStream> output;
    2336           0 :   rv = aRequest->mCacheInfo->OpenAlternativeOutputStream(nsContentUtils::JSBytecodeMimeType(),
    2337           0 :                                                          getter_AddRefs(output));
    2338           0 :   if (NS_FAILED(rv)) {
    2339           0 :     LOG(("ScriptLoadRequest (%p): Cannot open bytecode cache (rv = %X, output = %p)",
    2340             :          aRequest, unsigned(rv), output.get()));
    2341           0 :     AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::OpenFailure);
    2342           0 :     return;
    2343             :   }
    2344           0 :   MOZ_ASSERT(output);
    2345           0 :   auto closeOutStream = mozilla::MakeScopeExit([&]() {
    2346           0 :     nsresult rv = output->Close();
    2347           0 :     LOG(("ScriptLoadRequest (%p): Closing (rv = %X)",
    2348             :          aRequest, unsigned(rv)));
    2349           0 :     if (NS_FAILED(rv)) {
    2350           0 :       AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::CloseFailure);
    2351             :     }
    2352           0 :   });
    2353             : 
    2354             :   uint32_t n;
    2355           0 :   rv = output->Write(reinterpret_cast<char*>(aRequest->mScriptBytecode.begin()),
    2356           0 :                      aRequest->mScriptBytecode.length(), &n);
    2357           0 :   LOG(("ScriptLoadRequest (%p): Write bytecode cache (rv = %X, length = %u, written = %u)",
    2358             :        aRequest, unsigned(rv), unsigned(aRequest->mScriptBytecode.length()), n));
    2359           0 :   if (NS_FAILED(rv)) {
    2360           0 :     AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::WriteFailure);
    2361           0 :     return;
    2362             :   }
    2363             : 
    2364           0 :   bytecodeFailed.release();
    2365           0 :   TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_saved");
    2366           0 :   AccumulateCategorical(LABELS_DOM_SCRIPT_ENCODING_STATUS::EncodingSuccess);
    2367             : }
    2368             : 
    2369             : void
    2370           0 : ScriptLoader::GiveUpBytecodeEncoding()
    2371             : {
    2372             :   // Ideally we prefer to properly end the incremental encoder, such that we
    2373             :   // would not keep a large buffer around.  If we cannot, we fallback on the
    2374             :   // removal of all request from the current list and these large buffers would
    2375             :   // be removed at the same time as the source object.
    2376           0 :   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
    2377           0 :   Maybe<AutoEntryScript> aes;
    2378             : 
    2379           0 :   if (globalObject) {
    2380           0 :     nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
    2381           0 :     if (context) {
    2382           0 :       aes.emplace(globalObject, "give-up bytecode encoding", true);
    2383             :     }
    2384             :   }
    2385             : 
    2386           0 :   while (!mBytecodeEncodingQueue.isEmpty()) {
    2387           0 :     RefPtr<ScriptLoadRequest> request = mBytecodeEncodingQueue.StealFirst();
    2388           0 :     LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode", request.get()));
    2389           0 :     TRACE_FOR_TEST_NONE(request->mElement, "scriptloader_bytecode_failed");
    2390             : 
    2391           0 :     if (aes.isSome()) {
    2392           0 :       JS::RootedScript script(aes->cx(), request->mScript);
    2393           0 :       Unused << JS::FinishIncrementalEncoding(aes->cx(), script,
    2394           0 :                                               request->mScriptBytecode);
    2395             :     }
    2396             : 
    2397           0 :     request->mScriptBytecode.clearAndFree();
    2398           0 :     request->DropBytecodeCacheReferences();
    2399             :   }
    2400           0 : }
    2401             : 
    2402             : bool
    2403           0 : ScriptLoader::HasPendingRequests()
    2404             : {
    2405           0 :   return mParserBlockingRequest ||
    2406           0 :          !mXSLTRequests.isEmpty() ||
    2407           0 :          !mLoadedAsyncRequests.isEmpty() ||
    2408           0 :          !mNonAsyncExternalScriptInsertedRequests.isEmpty() ||
    2409           0 :          !mDeferRequests.isEmpty() ||
    2410           0 :          !mPendingChildLoaders.IsEmpty();
    2411             : }
    2412             : 
    2413             : void
    2414           0 : ScriptLoader::ProcessPendingRequestsAsync()
    2415             : {
    2416           0 :   if (HasPendingRequests()) {
    2417             :     nsCOMPtr<nsIRunnable> task =
    2418           0 :       NewRunnableMethod("dom::ScriptLoader::ProcessPendingRequests",
    2419             :                         this,
    2420           0 :                         &ScriptLoader::ProcessPendingRequests);
    2421           0 :     if (mDocument) {
    2422           0 :       mDocument->Dispatch("ScriptLoader", TaskCategory::Other, task.forget());
    2423             :     } else {
    2424           0 :       NS_DispatchToCurrentThread(task.forget());
    2425             :     }
    2426             :   }
    2427           0 : }
    2428             : 
    2429             : void
    2430          29 : ScriptLoader::ProcessPendingRequests()
    2431             : {
    2432          58 :   RefPtr<ScriptLoadRequest> request;
    2433             : 
    2434          32 :   if (mParserBlockingRequest &&
    2435          32 :       mParserBlockingRequest->IsReadyToRun() &&
    2436           3 :       ReadyToExecuteParserBlockingScripts()) {
    2437           3 :     request.swap(mParserBlockingRequest);
    2438           3 :     UnblockParser(request);
    2439           3 :     ProcessRequest(request);
    2440           3 :     if (request->mWasCompiledOMT) {
    2441           0 :       mDocument->UnblockOnload(false);
    2442             :     }
    2443           3 :     ContinueParserAsync(request);
    2444             :   }
    2445             : 
    2446          66 :   while (ReadyToExecuteParserBlockingScripts() &&
    2447          29 :          !mXSLTRequests.isEmpty() &&
    2448           0 :          mXSLTRequests.getFirst()->IsReadyToRun()) {
    2449           0 :     request = mXSLTRequests.StealFirst();
    2450           0 :     ProcessRequest(request);
    2451             :   }
    2452             : 
    2453          29 :   while (ReadyToExecuteScripts() && !mLoadedAsyncRequests.isEmpty()) {
    2454           0 :     request = mLoadedAsyncRequests.StealFirst();
    2455           0 :     if (request->IsModuleRequest()) {
    2456           0 :       ProcessRequest(request);
    2457             :     } else {
    2458           0 :       CompileOffThreadOrProcessRequest(request);
    2459             :     }
    2460             :   }
    2461             : 
    2462          37 :   while (ReadyToExecuteScripts() &&
    2463          29 :          !mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
    2464           0 :          mNonAsyncExternalScriptInsertedRequests.getFirst()->IsReadyToRun()) {
    2465             :     // Violate the HTML5 spec and execute these in the insertion order in
    2466             :     // order to make LABjs and the "order" plug-in for RequireJS work with
    2467             :     // their Gecko-sniffed code path. See
    2468             :     // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
    2469           0 :     request = mNonAsyncExternalScriptInsertedRequests.StealFirst();
    2470           0 :     ProcessRequest(request);
    2471             :   }
    2472             : 
    2473          29 :   if (mDocumentParsingDone && mXSLTRequests.isEmpty()) {
    2474          54 :     while (ReadyToExecuteScripts() &&
    2475          25 :            !mDeferRequests.isEmpty() &&
    2476           0 :            mDeferRequests.getFirst()->IsReadyToRun()) {
    2477           0 :       request = mDeferRequests.StealFirst();
    2478           0 :       ProcessRequest(request);
    2479             :     }
    2480             :   }
    2481             : 
    2482          29 :   while (!mPendingChildLoaders.IsEmpty() &&
    2483           0 :          ReadyToExecuteParserBlockingScripts()) {
    2484           0 :     RefPtr<ScriptLoader> child = mPendingChildLoaders[0];
    2485           0 :     mPendingChildLoaders.RemoveElementAt(0);
    2486           0 :     child->RemoveParserBlockingScriptExecutionBlocker();
    2487             :   }
    2488             : 
    2489         108 :   if (mDocumentParsingDone && mDocument && !mParserBlockingRequest &&
    2490          50 :       mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
    2491         104 :       mXSLTRequests.isEmpty() && mDeferRequests.isEmpty() &&
    2492          25 :       MaybeRemovedDeferRequests()) {
    2493           0 :     return ProcessPendingRequests();
    2494             :   }
    2495             : 
    2496         108 :   if (mDocumentParsingDone && mDocument &&
    2497          75 :       !mParserBlockingRequest && mLoadingAsyncRequests.isEmpty() &&
    2498          50 :       mLoadedAsyncRequests.isEmpty() &&
    2499          50 :       mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
    2500          79 :       mXSLTRequests.isEmpty() && mDeferRequests.isEmpty()) {
    2501             :     // No more pending scripts; time to unblock onload.
    2502             :     // OK to unblock onload synchronously here, since callers must be
    2503             :     // prepared for the world changing anyway.
    2504          25 :     mDocumentParsingDone = false;
    2505          25 :     mDocument->UnblockOnload(true);
    2506             :   }
    2507             : }
    2508             : 
    2509             : bool
    2510          34 : ScriptLoader::ReadyToExecuteParserBlockingScripts()
    2511             : {
    2512             :   // Make sure the SelfReadyToExecuteParserBlockingScripts check is first, so
    2513             :   // that we don't block twice on an ancestor.
    2514          34 :   if (!SelfReadyToExecuteParserBlockingScripts()) {
    2515          21 :     return false;
    2516             :   }
    2517             : 
    2518          27 :   for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) {
    2519          14 :     ScriptLoader* ancestor = doc->ScriptLoader();
    2520          14 :     if (!ancestor->SelfReadyToExecuteParserBlockingScripts() &&
    2521           0 :         ancestor->AddPendingChildLoader(this)) {
    2522           0 :       AddParserBlockingScriptExecutionBlocker();
    2523           0 :       return false;
    2524             :     }
    2525             :   }
    2526             : 
    2527          13 :   return true;
    2528             : }
    2529             : 
    2530             : /* static */ nsresult
    2531          36 : ScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
    2532             :                              uint32_t aLength, const nsAString& aHintCharset,
    2533             :                              nsIDocument* aDocument,
    2534             :                              char16_t*& aBufOut, size_t& aLengthOut)
    2535             : {
    2536          36 :   if (!aLength) {
    2537           0 :     aBufOut = nullptr;
    2538           0 :     aLengthOut = 0;
    2539           0 :     return NS_OK;
    2540             :   }
    2541             : 
    2542          36 :   auto data = MakeSpan(aData, aLength);
    2543             : 
    2544             :   // The encoding info precedence is as follows from high to low:
    2545             :   // The BOM
    2546             :   // HTTP Content-Type (if name recognized)
    2547             :   // charset attribute (if name recognized)
    2548             :   // The encoding of the document
    2549             : 
    2550          72 :   UniquePtr<Decoder> unicodeDecoder;
    2551             : 
    2552             :   const Encoding* encoding;
    2553             :   size_t bomLength;
    2554          36 :   Tie(encoding, bomLength) = Encoding::ForBOM(data);
    2555          36 :   if (encoding) {
    2556           0 :     unicodeDecoder = encoding->NewDecoderWithBOMRemoval();
    2557             :   }
    2558             : 
    2559          36 :   if (!unicodeDecoder && aChannel) {
    2560          56 :     nsAutoCString label;
    2561          56 :     if (NS_SUCCEEDED(aChannel->GetContentCharset(label)) &&
    2562          28 :         (encoding = Encoding::ForLabel(label))) {
    2563          19 :       unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
    2564             :     }
    2565             :   }
    2566             : 
    2567          36 :   if (!unicodeDecoder && (encoding = Encoding::ForLabel(aHintCharset))) {
    2568           8 :     unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
    2569             :   }
    2570             : 
    2571          36 :   if (!unicodeDecoder && aDocument) {
    2572           0 :     unicodeDecoder = aDocument->GetDocumentCharacterSet()
    2573           0 :                        ->NewDecoderWithoutBOMHandling();
    2574             :   }
    2575             : 
    2576          36 :   if (!unicodeDecoder) {
    2577             :     // Curiously, there are various callers that don't pass aDocument. The
    2578             :     // fallback in the old code was ISO-8859-1, which behaved like
    2579             :     // windows-1252.
    2580           9 :     unicodeDecoder = WINDOWS_1252_ENCODING->NewDecoderWithoutBOMHandling();
    2581             :   }
    2582             : 
    2583             :   CheckedInt<size_t> unicodeLength =
    2584          36 :     unicodeDecoder->MaxUTF16BufferLength(aLength);
    2585          36 :   if (!unicodeLength.isValid()) {
    2586           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2587             :   }
    2588             : 
    2589          36 :   aBufOut =
    2590          36 :     static_cast<char16_t*>(js_malloc(unicodeLength.value() * sizeof(char16_t)));
    2591          36 :   if (!aBufOut) {
    2592           0 :     aLengthOut = 0;
    2593           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2594             :   }
    2595             : 
    2596             :   uint32_t result;
    2597             :   size_t read;
    2598             :   size_t written;
    2599             :   bool hadErrors;
    2600          72 :   Tie(result, read, written, hadErrors) = unicodeDecoder->DecodeToUTF16(
    2601          36 :     data, MakeSpan(aBufOut, unicodeLength.value()), true);
    2602          36 :   MOZ_ASSERT(result == kInputEmpty);
    2603          36 :   MOZ_ASSERT(read == aLength);
    2604          36 :   MOZ_ASSERT(written <= unicodeLength.value());
    2605             :   Unused << hadErrors;
    2606          36 :   aLengthOut = written;
    2607             : 
    2608          72 :   nsAutoCString charset;
    2609          36 :   unicodeDecoder->Encoding()->Name(charset);
    2610             :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::DOM_SCRIPT_SRC_ENCODING,
    2611          36 :     charset);
    2612          36 :   return NS_OK;
    2613             : }
    2614             : 
    2615             : nsresult
    2616           4 : ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
    2617             :                                ScriptLoadRequest* aRequest,
    2618             :                                nsresult aChannelStatus,
    2619             :                                nsresult aSRIStatus,
    2620             :                                mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier)
    2621             : {
    2622           4 :   NS_ASSERTION(aRequest, "null request in stream complete handler");
    2623           4 :   NS_ENSURE_TRUE(aRequest, NS_ERROR_FAILURE);
    2624             : 
    2625           8 :   nsCOMPtr<nsIRequest> channelRequest;
    2626           4 :   aLoader->GetRequest(getter_AddRefs(channelRequest));
    2627           8 :   nsCOMPtr<nsIChannel> channel;
    2628           4 :   channel = do_QueryInterface(channelRequest);
    2629             : 
    2630           4 :   nsresult rv = NS_OK;
    2631           4 :   if (!aRequest->mIntegrity.IsEmpty() &&
    2632           0 :       NS_SUCCEEDED((rv = aSRIStatus))) {
    2633           0 :     MOZ_ASSERT(aSRIDataVerifier);
    2634           0 :     MOZ_ASSERT(mReporter);
    2635             : 
    2636           0 :     nsAutoCString sourceUri;
    2637           0 :     if (mDocument && mDocument->GetDocumentURI()) {
    2638           0 :       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    2639             :     }
    2640           0 :     rv = aSRIDataVerifier->Verify(aRequest->mIntegrity, channel, sourceUri,
    2641           0 :                                   mReporter);
    2642           0 :     if (channelRequest) {
    2643           0 :       mReporter->FlushReportsToConsole(
    2644           0 :         nsContentUtils::GetInnerWindowID(channelRequest));
    2645             :     } else {
    2646           0 :       mReporter->FlushConsoleReports(mDocument);
    2647             :     }
    2648           0 :     if (NS_FAILED(rv)) {
    2649           0 :       rv = NS_ERROR_SRI_CORRUPT;
    2650             :     }
    2651             :   } else {
    2652           8 :     nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
    2653             : 
    2654           4 :     if (loadInfo && loadInfo->GetEnforceSRI()) {
    2655           0 :       MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
    2656             :               ("ScriptLoader::OnStreamComplete, required SRI not found"));
    2657           0 :       nsCOMPtr<nsIContentSecurityPolicy> csp;
    2658           0 :       loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
    2659           0 :       nsAutoCString violationURISpec;
    2660           0 :       mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec);
    2661           0 :       uint32_t lineNo = aRequest->mElement ? aRequest->mElement->GetScriptLineNumber() : 0;
    2662           0 :       csp->LogViolationDetails(
    2663             :         nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
    2664           0 :         NS_ConvertUTF8toUTF16(violationURISpec),
    2665           0 :         EmptyString(), lineNo, EmptyString(), EmptyString());
    2666           0 :       rv = NS_ERROR_SRI_CORRUPT;
    2667             :     }
    2668             :   }
    2669             : 
    2670           4 :   bool sriOk = NS_SUCCEEDED(rv);
    2671             : 
    2672             :   // If we are loading from source, save the computed SRI hash or a dummy SRI
    2673             :   // hash in case we are going to save the bytecode of this script in the cache.
    2674           4 :   if (sriOk && aRequest->IsSource()) {
    2675           4 :     MOZ_ASSERT(aRequest->mScriptBytecode.empty());
    2676             :     // If the integrity metadata does not correspond to a valid hash function,
    2677             :     // IsComplete would be false.
    2678           4 :     if (!aRequest->mIntegrity.IsEmpty() && aSRIDataVerifier->IsComplete()) {
    2679             :       // Encode the SRI computed hash.
    2680           0 :       uint32_t len = aSRIDataVerifier->DataSummaryLength();
    2681           0 :       if (!aRequest->mScriptBytecode.growBy(len)) {
    2682           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2683             :       }
    2684           0 :       aRequest->mBytecodeOffset = len;
    2685             : 
    2686           0 :       DebugOnly<nsresult> res = aSRIDataVerifier->ExportDataSummary(
    2687           0 :         aRequest->mScriptBytecode.length(),
    2688           0 :         aRequest->mScriptBytecode.begin());
    2689           0 :       MOZ_ASSERT(NS_SUCCEEDED(res));
    2690             :     } else {
    2691             :       // Encode a dummy SRI hash.
    2692           4 :       uint32_t len = SRICheckDataVerifier::EmptyDataSummaryLength();
    2693           4 :       if (!aRequest->mScriptBytecode.growBy(len)) {
    2694           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2695             :       }
    2696           4 :       aRequest->mBytecodeOffset = len;
    2697             : 
    2698          12 :       DebugOnly<nsresult> res = SRICheckDataVerifier::ExportEmptyDataSummary(
    2699           4 :         aRequest->mScriptBytecode.length(),
    2700          12 :         aRequest->mScriptBytecode.begin());
    2701           4 :       MOZ_ASSERT(NS_SUCCEEDED(res));
    2702             :     }
    2703             : 
    2704             :     // Verify that the exported and predicted length correspond.
    2705           8 :     mozilla::DebugOnly<uint32_t> srilen;
    2706           4 :     MOZ_ASSERT(NS_SUCCEEDED(SRICheckDataVerifier::DataSummaryLength(
    2707             :                               aRequest->mScriptBytecode.length(),
    2708             :                               aRequest->mScriptBytecode.begin(),
    2709             :                               &srilen)));
    2710           4 :     MOZ_ASSERT(srilen == aRequest->mBytecodeOffset);
    2711             :   }
    2712             : 
    2713           4 :   if (sriOk) {
    2714           4 :     rv = PrepareLoadedRequest(aRequest, aLoader, aChannelStatus);
    2715             :   }
    2716             : 
    2717           4 :   if (NS_FAILED(rv)) {
    2718           0 :     if (sriOk && aRequest->mElement) {
    2719             : 
    2720           0 :       uint32_t lineNo = aRequest->mElement->GetScriptLineNumber();
    2721             : 
    2722           0 :       nsAutoString url;
    2723           0 :       if (aRequest->mURI) {
    2724           0 :         AppendUTF8toUTF16(aRequest->mURI->GetSpecOrDefault(), url);
    2725             :       }
    2726             : 
    2727           0 :       const char16_t* params[] = { url.get() };
    2728             : 
    2729           0 :       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    2730           0 :         NS_LITERAL_CSTRING("Script Loader"), mDocument,
    2731             :         nsContentUtils::eDOM_PROPERTIES, "ScriptSourceLoadFailed",
    2732           0 :         params, ArrayLength(params), nullptr,
    2733           0 :         EmptyString(), lineNo);
    2734             :     }
    2735             : 
    2736             :     /*
    2737             :      * Handle script not loading error because source was a tracking URL.
    2738             :      * We make a note of this script node by including it in a dedicated
    2739             :      * array of blocked tracking nodes under its parent document.
    2740             :      */
    2741           0 :     if (rv == NS_ERROR_TRACKING_URI) {
    2742           0 :       nsCOMPtr<nsIContent> cont = do_QueryInterface(aRequest->mElement);
    2743           0 :       mDocument->AddBlockedTrackingNode(cont);
    2744             :     }
    2745             : 
    2746           0 :     if (aChannelStatus == NS_BINDING_RETARGETED) {
    2747             :       // When loading bytecode, we verify the SRI hash. If it does not match the
    2748             :       // one from the document we restart the load, forcing us to load the
    2749             :       // source instead. This test makes sure we do not remove the current
    2750             :       // request from the following lists when we restart the load.
    2751           0 :     } else if (aRequest->mIsDefer) {
    2752           0 :       MOZ_ASSERT_IF(aRequest->IsModuleRequest(),
    2753             :                     aRequest->AsModuleRequest()->IsTopLevel());
    2754           0 :       if (aRequest->isInList()) {
    2755           0 :         RefPtr<ScriptLoadRequest> req = mDeferRequests.Steal(aRequest);
    2756           0 :         FireScriptAvailable(rv, req);
    2757             :       }
    2758           0 :     } else if (aRequest->mIsAsync) {
    2759           0 :       MOZ_ASSERT_IF(aRequest->IsModuleRequest(),
    2760             :                     aRequest->AsModuleRequest()->IsTopLevel());
    2761           0 :       if (aRequest->isInList()) {
    2762           0 :         RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
    2763           0 :         FireScriptAvailable(rv, req);
    2764             :       }
    2765           0 :     } else if (aRequest->mIsNonAsyncScriptInserted) {
    2766           0 :       if (aRequest->isInList()) {
    2767             :         RefPtr<ScriptLoadRequest> req =
    2768           0 :           mNonAsyncExternalScriptInsertedRequests.Steal(aRequest);
    2769           0 :         FireScriptAvailable(rv, req);
    2770             :       }
    2771           0 :     } else if (aRequest->mIsXSLT) {
    2772           0 :       if (aRequest->isInList()) {
    2773           0 :         RefPtr<ScriptLoadRequest> req = mXSLTRequests.Steal(aRequest);
    2774           0 :         FireScriptAvailable(rv, req);
    2775             :       }
    2776           0 :     } else if (aRequest->IsModuleRequest()) {
    2777           0 :       ModuleLoadRequest* modReq = aRequest->AsModuleRequest();
    2778           0 :       MOZ_ASSERT(!modReq->IsTopLevel());
    2779           0 :       MOZ_ASSERT(!modReq->isInList());
    2780           0 :       modReq->Cancel();
    2781           0 :       FireScriptAvailable(rv, aRequest);
    2782           0 :     } else if (mParserBlockingRequest == aRequest) {
    2783           0 :       MOZ_ASSERT(!aRequest->isInList());
    2784           0 :       mParserBlockingRequest = nullptr;
    2785           0 :       UnblockParser(aRequest);
    2786             : 
    2787             :       // Ensure that we treat aRequest->mElement as our current parser-inserted
    2788             :       // script while firing onerror on it.
    2789           0 :       MOZ_ASSERT(aRequest->mElement->GetParserCreated());
    2790             :       nsCOMPtr<nsIScriptElement> oldParserInsertedScript =
    2791           0 :         mCurrentParserInsertedScript;
    2792           0 :       mCurrentParserInsertedScript = aRequest->mElement;
    2793           0 :       FireScriptAvailable(rv, aRequest);
    2794           0 :       ContinueParserAsync(aRequest);
    2795           0 :       mCurrentParserInsertedScript = oldParserInsertedScript;
    2796             :     } else {
    2797           0 :       mPreloads.RemoveElement(aRequest, PreloadRequestComparator());
    2798             :     }
    2799             :   }
    2800             : 
    2801             :   // Process our request and/or any pending ones
    2802           4 :   ProcessPendingRequests();
    2803             : 
    2804           4 :   return NS_OK;
    2805             : }
    2806             : 
    2807             : void
    2808           3 : ScriptLoader::UnblockParser(ScriptLoadRequest* aParserBlockingRequest)
    2809             : {
    2810           3 :   aParserBlockingRequest->mElement->UnblockParser();
    2811           3 : }
    2812             : 
    2813             : void
    2814           3 : ScriptLoader::ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest)
    2815             : {
    2816           3 :   aParserBlockingRequest->mElement->ContinueParserAsync();
    2817           3 : }
    2818             : 
    2819             : uint32_t
    2820           3 : ScriptLoader::NumberOfProcessors()
    2821             : {
    2822           3 :   if (mNumberOfProcessors > 0)
    2823           2 :     return mNumberOfProcessors;
    2824             : 
    2825           1 :   int32_t numProcs = PR_GetNumberOfProcessors();
    2826           1 :   if (numProcs > 0)
    2827           1 :     mNumberOfProcessors = numProcs;
    2828           1 :   return mNumberOfProcessors;
    2829             : }
    2830             : 
    2831             : void
    2832           4 : ScriptLoader::MaybeMoveToLoadedList(ScriptLoadRequest* aRequest)
    2833             : {
    2834           4 :   MOZ_ASSERT(aRequest->IsReadyToRun());
    2835             : 
    2836             :   // If it's async, move it to the loaded list.  aRequest->mIsAsync really
    2837             :   // _should_ be in a list, but the consequences if it's not are bad enough we
    2838             :   // want to avoid trying to move it if it's not.
    2839           4 :   if (aRequest->mIsAsync) {
    2840           0 :     MOZ_ASSERT(aRequest->isInList());
    2841           0 :     if (aRequest->isInList()) {
    2842           0 :       RefPtr<ScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
    2843           0 :       mLoadedAsyncRequests.AppendElement(req);
    2844             :     }
    2845             :   }
    2846           4 : }
    2847             : 
    2848             : nsresult
    2849           4 : ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest,
    2850             :                                    nsIIncrementalStreamLoader* aLoader,
    2851             :                                    nsresult aStatus)
    2852             : {
    2853           4 :   if (NS_FAILED(aStatus)) {
    2854           0 :     return aStatus;
    2855             :   }
    2856             : 
    2857           4 :   if (aRequest->IsCanceled()) {
    2858           0 :     return NS_BINDING_ABORTED;
    2859             :   }
    2860           4 :   MOZ_ASSERT(aRequest->IsLoading());
    2861           4 :   CollectScriptTelemetry(aLoader, aRequest);
    2862             : 
    2863             :   // If we don't have a document, then we need to abort further
    2864             :   // evaluation.
    2865           4 :   if (!mDocument) {
    2866           0 :     return NS_ERROR_NOT_AVAILABLE;
    2867             :   }
    2868             : 
    2869             :   // If the load returned an error page, then we need to abort
    2870           8 :   nsCOMPtr<nsIRequest> req;
    2871           4 :   nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
    2872           4 :   NS_ASSERTION(req, "StreamLoader's request went away prematurely");
    2873           4 :   NS_ENSURE_SUCCESS(rv, rv);
    2874             : 
    2875           8 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req);
    2876           4 :   if (httpChannel) {
    2877             :     bool requestSucceeded;
    2878           2 :     rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
    2879           2 :     if (NS_SUCCEEDED(rv) && !requestSucceeded) {
    2880           0 :       return NS_ERROR_NOT_AVAILABLE;
    2881             :     }
    2882             : 
    2883           4 :     nsAutoCString sourceMapURL;
    2884           2 :     if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
    2885           0 :       aRequest->mHasSourceMapURL = true;
    2886           0 :       aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL);
    2887             :     }
    2888             : 
    2889           2 :     if (httpChannel->GetIsTrackingResource()) {
    2890           0 :       aRequest->SetIsTracking();
    2891             :     }
    2892             :   }
    2893             : 
    2894           8 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
    2895             :   // If this load was subject to a CORS check; don't flag it with a
    2896             :   // separate origin principal, so that it will treat our document's
    2897             :   // principal as the origin principal
    2898           4 :   if (aRequest->mCORSMode == CORS_NONE) {
    2899           4 :     rv = nsContentUtils::GetSecurityManager()->
    2900           4 :       GetChannelResultPrincipal(channel, getter_AddRefs(aRequest->mOriginPrincipal));
    2901           4 :     NS_ENSURE_SUCCESS(rv, rv);
    2902             :   }
    2903             : 
    2904             :   // This assertion could fire errorously if we ran out of memory when
    2905             :   // inserting the request in the array. However it's an unlikely case
    2906             :   // so if you see this assertion it is likely something else that is
    2907             :   // wrong, especially if you see it more than once.
    2908           4 :   NS_ASSERTION(mDeferRequests.Contains(aRequest) ||
    2909             :                mLoadingAsyncRequests.Contains(aRequest) ||
    2910             :                mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
    2911             :                mXSLTRequests.Contains(aRequest)  ||
    2912             :                (aRequest->IsModuleRequest() &&
    2913             :                 !aRequest->AsModuleRequest()->IsTopLevel() &&
    2914             :                 !aRequest->isInList()) ||
    2915             :                mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
    2916             :                mParserBlockingRequest,
    2917             :                "aRequest should be pending!");
    2918             : 
    2919           4 :   if (aRequest->IsModuleRequest()) {
    2920           0 :     MOZ_ASSERT(aRequest->IsSource());
    2921           0 :     ModuleLoadRequest* request = aRequest->AsModuleRequest();
    2922             : 
    2923             :     // When loading a module, only responses with a JavaScript MIME type are
    2924             :     // acceptable.
    2925           0 :     nsAutoCString mimeType;
    2926           0 :     channel->GetContentType(mimeType);
    2927           0 :     NS_ConvertUTF8toUTF16 typeString(mimeType);
    2928           0 :     if (!nsContentUtils::IsJavascriptMIMEType(typeString)) {
    2929           0 :       return NS_ERROR_FAILURE;
    2930             :     }
    2931             : 
    2932           0 :     channel->GetURI(getter_AddRefs(request->mBaseURL));
    2933             : 
    2934             :     // Attempt to compile off main thread.
    2935           0 :     rv = AttemptAsyncScriptCompile(request);
    2936           0 :     if (NS_SUCCEEDED(rv)) {
    2937           0 :       return rv;
    2938             :     }
    2939             : 
    2940             :     // Otherwise compile it right away and start fetching descendents.
    2941           0 :     return ProcessFetchedModuleSource(request);
    2942             :   }
    2943             : 
    2944             :   // The script is now loaded and ready to run.
    2945           4 :   aRequest->SetReady();
    2946             : 
    2947             :   // If this is currently blocking the parser, attempt to compile it off-main-thread.
    2948           4 :   if (aRequest == mParserBlockingRequest && NumberOfProcessors() > 1) {
    2949           3 :     MOZ_ASSERT(!aRequest->IsModuleRequest());
    2950           3 :     nsresult rv = AttemptAsyncScriptCompile(aRequest);
    2951           3 :     if (rv == NS_OK) {
    2952           0 :       MOZ_ASSERT(aRequest->mProgress == ScriptLoadRequest::Progress::Compiling,
    2953             :                  "Request should be off-thread compiling now.");
    2954           0 :       return NS_OK;
    2955             :     }
    2956             : 
    2957             :     // If off-thread compile errored, return the error.
    2958           3 :     if (rv != NS_ERROR_FAILURE) {
    2959           0 :       return rv;
    2960             :     }
    2961             : 
    2962             :     // If off-thread compile was rejected, continue with regular processing.
    2963             :   }
    2964             : 
    2965           4 :   MaybeMoveToLoadedList(aRequest);
    2966             : 
    2967           4 :   return NS_OK;
    2968             : }
    2969             : 
    2970             : void
    2971          25 : ScriptLoader::ParsingComplete(bool aTerminated)
    2972             : {
    2973          25 :   if (mDeferEnabled) {
    2974             :     // Have to check because we apparently get ParsingComplete
    2975             :     // without BeginDeferringScripts in some cases
    2976          25 :     mDocumentParsingDone = true;
    2977             :   }
    2978          25 :   mDeferEnabled = false;
    2979          25 :   if (aTerminated) {
    2980           0 :     mDeferRequests.Clear();
    2981           0 :     mLoadingAsyncRequests.Clear();
    2982           0 :     mLoadedAsyncRequests.Clear();
    2983           0 :     mNonAsyncExternalScriptInsertedRequests.Clear();
    2984           0 :     mXSLTRequests.Clear();
    2985           0 :     if (mParserBlockingRequest) {
    2986           0 :       mParserBlockingRequest->Cancel();
    2987           0 :       mParserBlockingRequest = nullptr;
    2988             :     }
    2989             :   }
    2990             : 
    2991             :   // Have to call this even if aTerminated so we'll correctly unblock
    2992             :   // onload and all.
    2993          25 :   ProcessPendingRequests();
    2994          25 : }
    2995             : 
    2996             : void
    2997           4 : ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
    2998             :                          const nsAString& aType,
    2999             :                          const nsAString& aCrossOrigin,
    3000             :                          const nsAString& aIntegrity,
    3001             :                          bool aScriptFromHead,
    3002             :                          const mozilla::net::ReferrerPolicy aReferrerPolicy)
    3003             : {
    3004           4 :   NS_ENSURE_TRUE_VOID(mDocument);
    3005             :   // Check to see if scripts has been turned off.
    3006           4 :   if (!mEnabled || !mDocument->IsScriptEnabled()) {
    3007           0 :     return;
    3008             :   }
    3009             : 
    3010             :   // TODO: Preload module scripts.
    3011           4 :   if (ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) {
    3012           0 :     return;
    3013             :   }
    3014             : 
    3015           8 :   SRIMetadata sriMetadata;
    3016           4 :   if (!aIntegrity.IsEmpty()) {
    3017           0 :     MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
    3018             :             ("ScriptLoader::PreloadURI, integrity=%s",
    3019             :              NS_ConvertUTF16toUTF8(aIntegrity).get()));
    3020           0 :     nsAutoCString sourceUri;
    3021           0 :     if (mDocument->GetDocumentURI()) {
    3022           0 :       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    3023             :     }
    3024           0 :     SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
    3025             :   }
    3026             : 
    3027             :   RefPtr<ScriptLoadRequest> request =
    3028             :     CreateLoadRequest(ScriptKind::Classic, nullptr, 0,
    3029           8 :                       Element::StringToCORSMode(aCrossOrigin), sriMetadata);
    3030           4 :   request->mURI = aURI;
    3031           4 :   request->mIsInline = false;
    3032           4 :   request->mReferrerPolicy = aReferrerPolicy;
    3033           4 :   request->mScriptFromHead = aScriptFromHead;
    3034             : 
    3035           4 :   nsresult rv = StartLoad(request);
    3036           4 :   if (NS_FAILED(rv)) {
    3037           0 :     return;
    3038             :   }
    3039             : 
    3040           4 :   PreloadInfo* pi = mPreloads.AppendElement();
    3041           4 :   pi->mRequest = request;
    3042           4 :   pi->mCharset = aCharset;
    3043             : }
    3044             : 
    3045             : void
    3046           0 : ScriptLoader::AddDeferRequest(ScriptLoadRequest* aRequest)
    3047             : {
    3048           0 :   aRequest->mIsDefer = true;
    3049           0 :   mDeferRequests.AppendElement(aRequest);
    3050           0 :   if (mDeferEnabled && aRequest == mDeferRequests.getFirst() &&
    3051           0 :       mDocument && !mBlockingDOMContentLoaded) {
    3052           0 :     MOZ_ASSERT(mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING);
    3053           0 :     mBlockingDOMContentLoaded = true;
    3054           0 :     mDocument->BlockDOMContentLoaded();
    3055             :   }
    3056           0 : }
    3057             : 
    3058             : bool
    3059          25 : ScriptLoader::MaybeRemovedDeferRequests()
    3060             : {
    3061          50 :   if (mDeferRequests.isEmpty() && mDocument &&
    3062          25 :       mBlockingDOMContentLoaded) {
    3063           0 :     mBlockingDOMContentLoaded = false;
    3064           0 :     mDocument->UnblockDOMContentLoaded();
    3065           0 :     return true;
    3066             :   }
    3067          25 :   return false;
    3068             : }
    3069             : 
    3070             : #undef TRACE_FOR_TEST
    3071             : #undef TRACE_FOR_TEST_BOOL
    3072             : #undef TRACE_FOR_TEST_NONE
    3073             : 
    3074             : #undef LOG
    3075             : 
    3076             : } // dom namespace
    3077             : } // mozilla namespace

Generated by: LCOV version 1.13