Bug 1426140 - Factor out SRI handling from ScriptLoader::OnStreamComplete r=baku
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 03 Jan 2018 13:07:58 +0000
changeset 449376 54ba9609db7564bc6585388b482f20ff98405439
parent 449375 2b06fe3742603aad171371983451a51d19bdcb61
child 449377 a8f97458d0cc43a62b048a5ebb4a8b1663d18b20
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1426140
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 1426140 - Factor out SRI handling from ScriptLoader::OnStreamComplete r=baku
dom/script/ScriptLoader.cpp
dom/script/ScriptLoader.h
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1436,38 +1436,17 @@ ScriptLoader::ProcessScriptElement(nsISc
                                   ourRefPolicy);
       request->mTriggeringPrincipal = Move(principal);
       request->mIsInline = false;
       // 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)) {
-        const char* message;
-        bool isScript = scriptKind == ScriptKind::Classic;
-        if (rv == NS_ERROR_MALFORMED_URI) {
-          message =
-            isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
-        }
-        else if (rv == NS_ERROR_DOM_BAD_URI) {
-          message =
-            isScript ? "ScriptSourceNotAllowed" : "ModuleSourceNotAllowed";
-        } else {
-          message =
-            isScript ? "ScriptSourceLoadFailed" : "ModuleSourceLoadFailed";
-        }
-
-        NS_ConvertUTF8toUTF16 url(scriptURI->GetSpecOrDefault());
-        const char16_t* params[] = { url.get() };
-
-        nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-            NS_LITERAL_CSTRING("Script Loader"), mDocument,
-            nsContentUtils::eDOM_PROPERTIES, message,
-            params, ArrayLength(params), nullptr,
-            EmptyString(), aElement->GetScriptLineNumber());
+        ReportErrorToConsole(request, rv);
 
         // Asynchronously report the load failure
         NS_DispatchToCurrentThread(
           NewRunnableMethod("nsIScriptElement::FireErrorEvent",
                             aElement,
                             &nsIScriptElement::FireErrorEvent));
         return false;
       }
@@ -2760,21 +2739,61 @@ ScriptLoader::ConvertToUTF16(nsIChannel*
   return NS_OK;
 }
 
 nsresult
 ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
                                ScriptLoadRequest* aRequest,
                                nsresult aChannelStatus,
                                nsresult aSRIStatus,
-                               mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier)
+                               SRICheckDataVerifier* aSRIDataVerifier)
 {
   NS_ASSERTION(aRequest, "null request in stream complete handler");
   NS_ENSURE_TRUE(aRequest, NS_ERROR_FAILURE);
 
+  nsresult rv = VerifySRI(aRequest, aLoader, aSRIStatus, aSRIDataVerifier);
+
+  if (NS_SUCCEEDED(rv)) {
+    // If we are loading from source, save the computed SRI hash or a dummy SRI
+    // hash in case we are going to save the bytecode of this script in the cache.
+    if (aRequest->IsSource()) {
+      rv = SaveSRIHash(aRequest, aSRIDataVerifier);
+    }
+
+    if (NS_SUCCEEDED(rv)) {
+      rv = PrepareLoadedRequest(aRequest, aLoader, aChannelStatus);
+    }
+
+    if (NS_FAILED(rv)) {
+      ReportErrorToConsole(aRequest, rv);
+    }
+  }
+
+  if (NS_FAILED(rv)) {
+    // When loading bytecode, we verify the SRI hash. If it does not match the
+    // one from the document we restart the load, forcing us to load the source
+    // instead. If this happens do not remove the current request from script
+    // loader's data structures or fire any events.
+    if (aChannelStatus != NS_BINDING_RETARGETED) {
+      HandleLoadError(aRequest, rv);
+    }
+  }
+
+  // Process our request and/or any pending ones
+  ProcessPendingRequests();
+
+  return NS_OK;
+}
+
+nsresult
+ScriptLoader::VerifySRI(ScriptLoadRequest* aRequest,
+                        nsIIncrementalStreamLoader* aLoader,
+                        nsresult aSRIStatus,
+                        SRICheckDataVerifier* aSRIDataVerifier) const
+{
   nsCOMPtr<nsIRequest> channelRequest;
   aLoader->GetRequest(getter_AddRefs(channelRequest));
   nsCOMPtr<nsIChannel> channel;
   channel = do_QueryInterface(channelRequest);
 
   nsresult rv = NS_OK;
   if (!aRequest->mIntegrity.IsEmpty() &&
       NS_SUCCEEDED((rv = aSRIStatus))) {
@@ -2810,100 +2829,102 @@ ScriptLoader::OnStreamComplete(nsIIncrem
       csp->LogViolationDetails(
         nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
         NS_ConvertUTF8toUTF16(violationURISpec),
         EmptyString(), lineNo, EmptyString(), EmptyString());
       rv = NS_ERROR_SRI_CORRUPT;
     }
   }
 
-  // If we are loading from source, save the computed SRI hash or a dummy SRI
-  // hash in case we are going to save the bytecode of this script in the cache.
-  if (NS_SUCCEEDED(rv)) {
-    if (aRequest->IsSource()) {
-      MOZ_ASSERT(aRequest->mScriptBytecode.empty());
-      // If the integrity metadata does not correspond to a valid hash function,
-      // IsComplete would be false.
-      if (!aRequest->mIntegrity.IsEmpty() && aSRIDataVerifier->IsComplete()) {
-        // Encode the SRI computed hash.
-        uint32_t len = aSRIDataVerifier->DataSummaryLength();
-        if (!aRequest->mScriptBytecode.growBy(len)) {
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-        aRequest->mBytecodeOffset = len;
-
-        DebugOnly<nsresult> res = aSRIDataVerifier->ExportDataSummary(
-          aRequest->mScriptBytecode.length(),
-          aRequest->mScriptBytecode.begin());
-        MOZ_ASSERT(NS_SUCCEEDED(res));
-      } else {
-        // Encode a dummy SRI hash.
-        uint32_t len = SRICheckDataVerifier::EmptyDataSummaryLength();
-        if (!aRequest->mScriptBytecode.growBy(len)) {
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-        aRequest->mBytecodeOffset = len;
-
-        DebugOnly<nsresult> res = SRICheckDataVerifier::ExportEmptyDataSummary(
-          aRequest->mScriptBytecode.length(),
-          aRequest->mScriptBytecode.begin());
-        MOZ_ASSERT(NS_SUCCEEDED(res));
-      }
-
-      // Verify that the exported and predicted length correspond.
-      mozilla::DebugOnly<uint32_t> srilen;
-      MOZ_ASSERT(NS_SUCCEEDED(SRICheckDataVerifier::DataSummaryLength(
-                                aRequest->mScriptBytecode.length(),
-                                aRequest->mScriptBytecode.begin(),
-                                &srilen)));
-      MOZ_ASSERT(srilen == aRequest->mBytecodeOffset);
+  return rv;
+}
+
+nsresult
+ScriptLoader::SaveSRIHash(ScriptLoadRequest *aRequest,
+                          SRICheckDataVerifier* aSRIDataVerifier) const
+{
+  MOZ_ASSERT(aRequest->IsSource());
+  MOZ_ASSERT(aRequest->mScriptBytecode.empty());
+
+  // If the integrity metadata does not correspond to a valid hash function,
+  // IsComplete would be false.
+  if (!aRequest->mIntegrity.IsEmpty() && aSRIDataVerifier->IsComplete()) {
+    // Encode the SRI computed hash.
+    uint32_t len = aSRIDataVerifier->DataSummaryLength();
+    if (!aRequest->mScriptBytecode.growBy(len)) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
-
-    rv = PrepareLoadedRequest(aRequest, aLoader, aChannelStatus);
-
-    if (NS_FAILED(rv) && aRequest->mElement) {
-      uint32_t lineNo = aRequest->mElement->GetScriptLineNumber();
-
-      nsAutoString url;
-      if (aRequest->mURI) {
-        AppendUTF8toUTF16(aRequest->mURI->GetSpecOrDefault(), url);
-      }
-
-      const char* message = "ScriptSourceLoadFailed";
-      if (aRequest->IsModuleRequest()) {
-        message = "ModuleSourceLoadFailed";
-      }
-
-      const char16_t* params[] = { url.get() };
-
-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                      NS_LITERAL_CSTRING("Script Loader"), mDocument,
-                                      nsContentUtils::eDOM_PROPERTIES, message,
-                                      params, ArrayLength(params), nullptr,
-                                      EmptyString(), lineNo);
+    aRequest->mBytecodeOffset = len;
+
+    DebugOnly<nsresult> res = aSRIDataVerifier->ExportDataSummary(
+      aRequest->mScriptBytecode.length(),
+      aRequest->mScriptBytecode.begin());
+    MOZ_ASSERT(NS_SUCCEEDED(res));
+  } else {
+    // Encode a dummy SRI hash.
+    uint32_t len = SRICheckDataVerifier::EmptyDataSummaryLength();
+    if (!aRequest->mScriptBytecode.growBy(len)) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
+    aRequest->mBytecodeOffset = len;
+
+    DebugOnly<nsresult> res = SRICheckDataVerifier::ExportEmptyDataSummary(
+      aRequest->mScriptBytecode.length(),
+      aRequest->mScriptBytecode.begin());
+    MOZ_ASSERT(NS_SUCCEEDED(res));
   }
 
-  if (NS_FAILED(rv)) {
-    // When loading bytecode, we verify the SRI hash. If it does not match the
-    // one from the document we restart the load, forcing us to load the source
-    // instead. If this happens do not remove the current request from script
-    // loader's data structures or fire any events.
-    if (aChannelStatus != NS_BINDING_RETARGETED) {
-      HandleLoadError(aRequest, rv);
-    }
-  }
-
-  // Process our request and/or any pending ones
-  ProcessPendingRequests();
+  // Verify that the exported and predicted length correspond.
+  mozilla::DebugOnly<uint32_t> srilen;
+  MOZ_ASSERT(NS_SUCCEEDED(SRICheckDataVerifier::DataSummaryLength(
+                            aRequest->mScriptBytecode.length(),
+                            aRequest->mScriptBytecode.begin(),
+                            &srilen)));
+  MOZ_ASSERT(srilen == aRequest->mBytecodeOffset);
 
   return NS_OK;
 }
 
 void
+ScriptLoader::ReportErrorToConsole(ScriptLoadRequest *aRequest,
+                                   nsresult aResult) const
+{
+  MOZ_ASSERT(aRequest);
+
+  if (!aRequest->mElement) {
+    return;
+  }
+
+  bool isScript = !aRequest->IsModuleRequest();
+  const char* message;
+  if (aResult == NS_ERROR_MALFORMED_URI) {
+    message =
+      isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
+  }
+  else if (aResult == NS_ERROR_DOM_BAD_URI) {
+    message =
+      isScript ? "ScriptSourceNotAllowed" : "ModuleSourceNotAllowed";
+  } else {
+    message =
+      isScript ? "ScriptSourceLoadFailed" : "ModuleSourceLoadFailed";
+  }
+
+  NS_ConvertUTF8toUTF16 url(aRequest->mURI->GetSpecOrDefault());
+  const char16_t* params[] = { url.get() };
+
+  uint32_t lineNo = aRequest->mElement->GetScriptLineNumber();
+
+  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                  NS_LITERAL_CSTRING("Script Loader"), mDocument,
+                                  nsContentUtils::eDOM_PROPERTIES, message,
+                                  params, ArrayLength(params), nullptr,
+                                  EmptyString(), lineNo);
+}
+
+void
 ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult)
 {
   /*
    * Handle script not loading error because source was a tracking URL.
    * We make a note of this script node by including it in a dedicated
    * array of blocked tracking nodes under its parent document.
    */
   if (aResult == NS_ERROR_TRACKING_URI) {
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -237,19 +237,17 @@ public:
    * ScriptLoadHandler object which observes the IncrementalStreamLoader
    * loading the script. The streamed content is expected to be stored on the
    * aRequest argument.
    */
   nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
                             ScriptLoadRequest* aRequest,
                             nsresult aChannelStatus,
                             nsresult aSRIStatus,
-                            mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier);
-
-  void HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult);
+                            SRICheckDataVerifier* aSRIDataVerifier);
 
   /**
    * Returns wether any request is queued, and not executed yet.
    */
   bool HasPendingRequests();
 
   /**
    * Processes any pending requests that are ready for processing.
@@ -376,16 +374,18 @@ private:
 
   /**
    * Abort the current stream, and re-start with a new load request from scratch
    * without requesting any alternate data. Returns NS_BINDING_RETARGETED on
    * success, as this error code is used to abort the input stream.
    */
   nsresult RestartLoad(ScriptLoadRequest* aRequest);
 
+  void HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult);
+
   /**
    * Process any pending requests asynchronously (i.e. off an event) if there
    * are any. Note that this is a no-op if there aren't any currently pending
    * requests.
    *
    * This function is virtual to allow cross-library calls to SetEnabled()
    */
   virtual void ProcessPendingRequestsAsync();
@@ -410,16 +410,26 @@ private:
   /**
    * Return whether this loader is ready to execute scripts in general.
    */
   bool ReadyToExecuteScripts()
   {
     return mEnabled && !mBlockerCount;
   }
 
+  nsresult VerifySRI(ScriptLoadRequest *aRequest,
+                     nsIIncrementalStreamLoader* aLoader,
+                     nsresult aSRIStatus,
+                     SRICheckDataVerifier* aSRIDataVerifier) const;
+
+  nsresult SaveSRIHash(ScriptLoadRequest *aRequest,
+                       SRICheckDataVerifier* aSRIDataVerifier) const;
+
+  void ReportErrorToConsole(ScriptLoadRequest *aRequest, nsresult aResult) const;
+
   nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest);
   nsresult ProcessRequest(ScriptLoadRequest* aRequest);
   nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
   void FireScriptAvailable(nsresult aResult,
                            ScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult,
                            ScriptLoadRequest* aRequest);
   nsresult EvaluateScript(ScriptLoadRequest* aRequest);