Bug 1429535 - Split up ScriptLoader::ProcessScriptElement method r=baku
☠☠ backed out by 7e52c1fdef97 ☠ ☠
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 11 Jan 2018 12:05:26 +0000
changeset 450576 ef258af455925d856d340defb2d9f1ed3e3b9ce6
parent 450575 ec70d962c006a9efec5ed9c4baa807f5e91b9f77
child 450577 59a7ff5e790dac72e89aef826db19640a69ac5b3
push id8528
push userryanvm@gmail.com
push dateFri, 12 Jan 2018 02:17:15 +0000
treeherdermozilla-beta@713625f4b2f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1429535
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1429535 - Split up ScriptLoader::ProcessScriptElement method r=baku
dom/script/ScriptLoader.cpp
dom/script/ScriptLoader.h
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1281,229 +1281,211 @@ ScriptLoader::ProcessScriptElement(nsISc
   if (mDocument->ModuleScriptsEnabled() &&
       scriptKind == ScriptKind::eClassic &&
       scriptContent->IsHTMLElement() &&
       scriptContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) {
     return false;
   }
 
   // Step 15. and later in the HTML5 spec
-  nsresult rv = NS_OK;
-  RefPtr<ScriptLoadRequest> request;
-  mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy();
   if (aElement->GetScriptExternal()) {
-    // external script
-    nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
-    if (!scriptURI) {
-      // Asynchronously report the failure to create a URI object
+    return ProcessExternalScript(aElement, scriptKind, type, scriptContent);
+  }
+
+  return ProcessInlineScript(aElement, scriptKind);
+}
+
+bool
+ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
+                                    ScriptKind aScriptKind,
+                                    nsAutoString aTypeAttr,
+                                    nsIContent* aScriptContent)
+{
+  nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
+  if (!scriptURI) {
+    // Asynchronously report the failure to create a URI object
+    NS_DispatchToCurrentThread(
+      NewRunnableMethod("nsIScriptElement::FireErrorEvent",
+                        aElement,
+                        &nsIScriptElement::FireErrorEvent));
+    return false;
+  }
+
+  RefPtr<ScriptLoadRequest> request = LookupPreloadRequest(aElement, aScriptKind);
+
+  if (request && NS_FAILED(CheckContentPolicy(mDocument, aElement, request->mURI,
+                                              aTypeAttr, false))) {
+    // Probably plans have changed; even though the preload was allowed seems
+    // like the actual load is not; let's cancel the preload request.
+    request->Cancel();
+    return false;
+  }
+
+  if (request) {
+    // Use the preload request.
+    request->mElement = aElement;
+
+    // It's possible these attributes changed since we started the preload so
+    // update them here.
+    request->SetScriptMode(aElement->GetScriptDeferred(),
+                           aElement->GetScriptAsync());
+  } else {
+    // No usable preload found.
+
+    SRIMetadata sriMetadata;
+    {
+      nsAutoString integrity;
+      aScriptContent->AsElement()->GetAttr(kNameSpaceID_None,
+                                          nsGkAtoms::integrity,
+                                          integrity);
+      GetSRIMetadata(integrity, &sriMetadata);
+    }
+
+    nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
+    if (!principal) {
+      principal = aScriptContent->NodePrincipal();
+    }
+
+    CORSMode ourCORSMode = aElement->GetCORSMode();
+    mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy();
+    request = CreateLoadRequest(aScriptKind, scriptURI, aElement,
+                                ourCORSMode, sriMetadata, ourRefPolicy);
+    request->mTriggeringPrincipal = Move(principal);
+    request->mIsInline = false;
+    request->SetScriptMode(aElement->GetScriptDeferred(),
+                           aElement->GetScriptAsync());
+    // keep request->mScriptFromHead to false so we don't treat non preloaded
+    // scripts as blockers for full page load. See bug 792438.
+
+    nsresult rv = StartLoad(request);
+    if (NS_FAILED(rv)) {
+      ReportErrorToConsole(request, rv);
+
+      // Asynchronously report the load failure
       NS_DispatchToCurrentThread(
         NewRunnableMethod("nsIScriptElement::FireErrorEvent",
                           aElement,
                           &nsIScriptElement::FireErrorEvent));
       return false;
     }
-
-    // Double-check that the preload matches what we're asked to load now.
-    CORSMode ourCORSMode = aElement->GetCORSMode();
-    nsTArray<PreloadInfo>::index_type i =
-      mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
-    if (i != nsTArray<PreloadInfo>::NoIndex) {
-      // preloaded
-      // note that a script-inserted script can steal a preload!
-      request = mPreloads[i].mRequest;
-      request->mElement = aElement;
-      nsString preloadCharset(mPreloads[i].mCharset);
-      mPreloads.RemoveElementAt(i);
-
-      // Double-check that the charset the preload used is the same as
-      // the charset we have now.
-      nsAutoString elementCharset;
-      aElement->GetScriptCharset(elementCharset);
-      if (elementCharset.Equals(preloadCharset) &&
-          ourCORSMode == request->mCORSMode &&
-          ourRefPolicy == request->mReferrerPolicy &&
-          scriptKind == request->mKind) {
-        rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false);
-        if (NS_FAILED(rv)) {
-          // probably plans have changed; even though the preload was allowed seems
-          // like the actual load is not; let's cancel the preload request.
-          request->Cancel();
-          return false;
-        }
-      } else {
-        // Drop the preload
-        request = nullptr;
-      }
+  }
+
+  // Should still be in loading stage of script.
+  NS_ASSERTION(!request->InCompilingStage(),
+               "Request should not yet be in compiling stage.");
+
+  if (request->IsAsyncScript()) {
+    AddAsyncRequest(request);
+    if (request->IsReadyToRun()) {
+      // The script is available already. Run it ASAP when the event
+      // loop gets a chance to spin.
+
+      // KVKV TODO: Instead of processing immediately, try off-thread-parsing
+      // it and only schedule a pending ProcessRequest if that fails.
+      ProcessPendingRequestsAsync();
+    }
+    return false;
+  }
+  if (!aElement->GetParserCreated()) {
+    // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
+    // for RequireJS work with their Gecko-sniffed code path. See
+    // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
+    request->mIsNonAsyncScriptInserted = true;
+    mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
+    if (request->IsReadyToRun()) {
+      // The script is available already. Run it ASAP when the event
+      // loop gets a chance to spin.
+      ProcessPendingRequestsAsync();
     }
-
-    if (request) {
-      // Use a preload request.
-
-      // It's possible these attributes changed since we started the preload so
-      // update them here.
-      request->SetScriptMode(aElement->GetScriptDeferred(),
-                             aElement->GetScriptAsync());
-    } else {
-      // No usable preload found.
-
-      SRIMetadata sriMetadata;
-      {
-        nsAutoString integrity;
-        scriptContent->AsElement()->GetAttr(kNameSpaceID_None,
-                                            nsGkAtoms::integrity,
-                                            integrity);
-        if (!integrity.IsEmpty()) {
-          MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
-                  ("ScriptLoader::ProcessScriptElement, integrity=%s",
-                   NS_ConvertUTF16toUTF8(integrity).get()));
-          nsAutoCString sourceUri;
-          if (mDocument->GetDocumentURI()) {
-            mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
-          }
-          SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter,
-                                      &sriMetadata);
-        }
-      }
-
-      nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
-      if (!principal) {
-        principal = scriptContent->NodePrincipal();
-      }
-
-      request = CreateLoadRequest(scriptKind, scriptURI, aElement, ourCORSMode,
-                                  sriMetadata, ourRefPolicy);
-      request->mTriggeringPrincipal = Move(principal);
-      request->mIsInline = false;
-      request->SetScriptMode(aElement->GetScriptDeferred(),
-                             aElement->GetScriptAsync());
-      // keep request->mScriptFromHead to false so we don't treat non preloaded
-      // scripts as blockers for full page load. See bug 792438.
-
-      rv = StartLoad(request);
-      if (NS_FAILED(rv)) {
-        ReportErrorToConsole(request, rv);
-
-        // Asynchronously report the load failure
-        NS_DispatchToCurrentThread(
-          NewRunnableMethod("nsIScriptElement::FireErrorEvent",
-                            aElement,
-                            &nsIScriptElement::FireErrorEvent));
-        return false;
-      }
+    return false;
+  }
+  // we now have a parser-inserted request that may or may not be still
+  // loading
+  if (request->IsDeferredScript()) {
+    // We don't want to run this yet.
+    // If we come here, the script is a parser-created script and it has
+    // the defer attribute but not the async attribute. Since a
+    // a parser-inserted script is being run, we came here by the parser
+    // running the script, which means the parser is still alive and the
+    // parse is ongoing.
+    NS_ASSERTION(mDocument->GetCurrentContentSink() ||
+                 aElement->GetParserCreated() == FROM_PARSER_XSLT,
+        "Non-XSLT Defer script on a document without an active parser; bug 592366.");
+    AddDeferRequest(request);
+    return false;
+  }
+
+  if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
+    // Need to maintain order for XSLT-inserted scripts
+    NS_ASSERTION(!mParserBlockingRequest,
+        "Parser-blocking scripts and XSLT scripts in the same doc!");
+    request->mIsXSLT = true;
+    mXSLTRequests.AppendElement(request);
+    if (request->IsReadyToRun()) {
+      // The script is available already. Run it ASAP when the event
+      // loop gets a chance to spin.
+      ProcessPendingRequestsAsync();
     }
-
-    // Should still be in loading stage of script.
-    NS_ASSERTION(!request->InCompilingStage(),
-                 "Request should not yet be in compiling stage.");
-
-    if (request->IsAsyncScript()) {
-      AddAsyncRequest(request);
-      if (request->IsReadyToRun()) {
-        // The script is available already. Run it ASAP when the event
-        // loop gets a chance to spin.
-
-        // KVKV TODO: Instead of processing immediately, try off-thread-parsing
-        // it and only schedule a pending ProcessRequest if that fails.
-        ProcessPendingRequestsAsync();
-      }
-      return false;
-    }
-    if (!aElement->GetParserCreated()) {
-      // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
-      // for RequireJS work with their Gecko-sniffed code path. See
-      // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
-      request->mIsNonAsyncScriptInserted = true;
-      mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
-      if (request->IsReadyToRun()) {
-        // The script is available already. Run it ASAP when the event
-        // loop gets a chance to spin.
-        ProcessPendingRequestsAsync();
-      }
-      return false;
+    return true;
+  }
+
+  if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) {
+    // The request has already been loaded and there are no pending style
+    // sheets. If the script comes from the network stream, cheat for
+    // performance reasons and avoid a trip through the event loop.
+    if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
+      return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
     }
-    // we now have a parser-inserted request that may or may not be still
-    // loading
-    if (request->IsDeferredScript()) {
-      // We don't want to run this yet.
-      // If we come here, the script is a parser-created script and it has
-      // the defer attribute but not the async attribute. Since a
-      // a parser-inserted script is being run, we came here by the parser
-      // running the script, which means the parser is still alive and the
-      // parse is ongoing.
-      NS_ASSERTION(mDocument->GetCurrentContentSink() ||
-                   aElement->GetParserCreated() == FROM_PARSER_XSLT,
-          "Non-XSLT Defer script on a document without an active parser; bug 592366.");
-      AddDeferRequest(request);
-      return false;
-    }
-
-    if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
-      // Need to maintain order for XSLT-inserted scripts
-      NS_ASSERTION(!mParserBlockingRequest,
-          "Parser-blocking scripts and XSLT scripts in the same doc!");
-      request->mIsXSLT = true;
-      mXSLTRequests.AppendElement(request);
-      if (request->IsReadyToRun()) {
-        // The script is available already. Run it ASAP when the event
-        // loop gets a chance to spin.
-        ProcessPendingRequestsAsync();
-      }
-      return true;
-    }
-
-    if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) {
-      // The request has already been loaded and there are no pending style
-      // sheets. If the script comes from the network stream, cheat for
-      // performance reasons and avoid a trip through the event loop.
-      if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
-        return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
-      }
-      // Otherwise, we've got a document.written script, make a trip through
-      // the event loop to hide the preload effects from the scripts on the
-      // Web page.
-      NS_ASSERTION(!mParserBlockingRequest,
-          "There can be only one parser-blocking script at a time");
-      NS_ASSERTION(mXSLTRequests.isEmpty(),
-          "Parser-blocking scripts and XSLT scripts in the same doc!");
-      mParserBlockingRequest = request;
-      ProcessPendingRequestsAsync();
-      return true;
-    }
-
-    // The script hasn't loaded yet or there's a style sheet blocking it.
-    // The script will be run when it loads or the style sheet loads.
+    // Otherwise, we've got a document.written script, make a trip through
+    // the event loop to hide the preload effects from the scripts on the
+    // Web page.
     NS_ASSERTION(!mParserBlockingRequest,
         "There can be only one parser-blocking script at a time");
     NS_ASSERTION(mXSLTRequests.isEmpty(),
         "Parser-blocking scripts and XSLT scripts in the same doc!");
     mParserBlockingRequest = request;
+    ProcessPendingRequestsAsync();
     return true;
   }
 
-  // inline script
+  // The script hasn't loaded yet or there's a style sheet blocking it.
+  // The script will be run when it loads or the style sheet loads.
+  NS_ASSERTION(!mParserBlockingRequest,
+      "There can be only one parser-blocking script at a time");
+  NS_ASSERTION(mXSLTRequests.isEmpty(),
+      "Parser-blocking scripts and XSLT scripts in the same doc!");
+  mParserBlockingRequest = request;
+  return true;
+}
+
+bool
+ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
+                                  ScriptKind aScriptKind)
+{
   // Is this document sandboxed without 'allow-scripts'?
   if (mDocument->HasScriptsBlockedBySandbox()) {
     return false;
   }
 
   // Does CSP allow this inline script to run?
   if (!CSPAllowsInlineScript(aElement, mDocument)) {
     return false;
   }
 
   // Inline classic scripts ignore their CORS mode and are always CORS_NONE.
   CORSMode corsMode = CORS_NONE;
-  if (scriptKind == ScriptKind::eModule) {
+  if (aScriptKind == ScriptKind::eModule) {
     corsMode = aElement->GetCORSMode();
   }
 
-  request = CreateLoadRequest(scriptKind, mDocument->GetDocumentURI(), aElement,
-                              corsMode,
-                              SRIMetadata(), // SRI doesn't apply
-                              ourRefPolicy);
+  RefPtr<ScriptLoadRequest> request =
+    CreateLoadRequest(aScriptKind, mDocument->GetDocumentURI(), aElement,
+                      corsMode,
+                      SRIMetadata(), // SRI doesn't apply
+                      mDocument->GetReferrerPolicy());
   request->mIsInline = true;
   request->mTriggeringPrincipal = mDocument->NodePrincipal();
   request->mLineNo = aElement->GetScriptLineNumber();
   request->mProgress = ScriptLoadRequest::Progress::eLoading_Source;
   request->mDataType = ScriptLoadRequest::DataType::eSource;
   TRACE_FOR_TEST_BOOL(request->mElement, "scriptloader_load_source");
   CollectScriptTelemetry(nullptr, request);
 
@@ -1569,16 +1551,69 @@ ScriptLoader::ProcessScriptElement(nsISc
   // there's no document.write currently on the call stack. However,
   // this way matches IE more closely than checking if document.write
   // is on the call stack.
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
       "Not safe to run a parser-inserted script?");
   return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
 }
 
+ScriptLoadRequest*
+ScriptLoader::LookupPreloadRequest(nsIScriptElement* aElement,
+                                   ScriptKind aScriptKind)
+{
+  nsTArray<PreloadInfo>::index_type i =
+    mPreloads.IndexOf(aElement->GetScriptURI(), 0, PreloadURIComparator());
+  if (i == nsTArray<PreloadInfo>::NoIndex) {
+    return nullptr;
+  }
+
+  // Found preloaded request. Note that a script-inserted script can steal a
+  // preload!
+  RefPtr<ScriptLoadRequest> request = mPreloads[i].mRequest;
+  nsString preloadCharset(mPreloads[i].mCharset);
+  mPreloads.RemoveElementAt(i);
+
+  // Double-check that the charset the preload used is the same as the charset
+  // we have now.
+  nsAutoString elementCharset;
+  aElement->GetScriptCharset(elementCharset);
+  if (!elementCharset.Equals(preloadCharset) ||
+      aElement->GetCORSMode() != request->mCORSMode ||
+      mDocument->GetReferrerPolicy() != request->mReferrerPolicy ||
+      aScriptKind != request->mKind) {
+    // Drop the preload.
+    return nullptr;
+  }
+
+  return request;
+}
+
+void
+ScriptLoader::GetSRIMetadata(const nsAString& aIntegrityAttr,
+                             SRIMetadata *aMetadataOut)
+{
+  MOZ_ASSERT(aMetadataOut->IsEmpty());
+
+  if (aIntegrityAttr.IsEmpty()) {
+    return;
+  }
+
+  MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
+          ("ScriptLoader::GetSRIMetadata, integrity=%s",
+           NS_ConvertUTF16toUTF8(aIntegrityAttr).get()));
+
+  nsAutoCString sourceUri;
+  if (mDocument->GetDocumentURI()) {
+    mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
+  }
+  SRICheck::IntegrityMetadata(aIntegrityAttr, sourceUri, mReporter,
+                              aMetadataOut);
+}
+
 namespace {
 
 class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable
 {
   RefPtr<ScriptLoadRequest> mRequest;
   RefPtr<ScriptLoader> mLoader;
   RefPtr<DocGroup> mDocGroup;
   void* mToken;
@@ -3124,26 +3159,17 @@ ScriptLoader::PreloadURI(nsIURI* aURI, c
   }
 
   // TODO: Preload module scripts.
   if (mDocument->ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) {
     return;
   }
 
   SRIMetadata sriMetadata;
-  if (!aIntegrity.IsEmpty()) {
-    MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
-            ("ScriptLoader::PreloadURI, integrity=%s",
-             NS_ConvertUTF16toUTF8(aIntegrity).get()));
-    nsAutoCString sourceUri;
-    if (mDocument->GetDocumentURI()) {
-      mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
-    }
-    SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
-  }
+  GetSRIMetadata(aIntegrity, &sriMetadata);
 
   RefPtr<ScriptLoadRequest> request =
     CreateLoadRequest(ScriptKind::eClassic, aURI, nullptr,
                       Element::StringToCORSMode(aCrossOrigin), sriMetadata,
                       aReferrerPolicy);
   request->mTriggeringPrincipal = mDocument->NodePrincipal();
   request->mIsInline = false;
   request->mScriptFromHead = aScriptFromHead;
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -352,16 +352,30 @@ private:
   void UnblockParser(ScriptLoadRequest* aParserBlockingRequest);
 
   /**
    * Asynchronously resumes the creator parser of the parser-blocking scripts.
    */
   void ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest);
 
 
+  bool ProcessExternalScript(nsIScriptElement* aElement,
+                             ScriptKind aScriptKind,
+                             nsAutoString aTypeAttr,
+                             nsIContent* aScriptContent);
+
+  bool ProcessInlineScript(nsIScriptElement* aElement,
+                           ScriptKind aScriptKind);
+
+  ScriptLoadRequest* LookupPreloadRequest(nsIScriptElement* aElement,
+                                          ScriptKind aScriptKind);
+
+  void GetSRIMetadata(const nsAString& aIntegrityAttr,
+                      SRIMetadata *aMetadataOut);
+
   /**
    * Helper function to check the content policy for a given request.
    */
   static nsresult CheckContentPolicy(nsIDocument* aDocument,
                                      nsISupports* aContext,
                                      nsIURI* aURI,
                                      const nsAString& aType,
                                      bool aIsPreLoad);