Bug 1167409 - 3/4 - Change ScriptLoadRequeest::mLoading to mProgress. r=jandem
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 25 Aug 2015 17:52:27 -0400
changeset 259335 c467f1b2bc71b88399513455a2a6d62b2954b098
parent 259334 5afb8edd05f700fe50f889727008e96024f262f0
child 259336 49dbd2c38b9e96f9b3e24a2da8a0aa4bb5184e20
push id29277
push userryanvm@gmail.com
push dateWed, 26 Aug 2015 18:32:23 +0000
treeherdermozilla-central@fea87cbeaa6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1167409
milestone43.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 1167409 - 3/4 - Change ScriptLoadRequeest::mLoading to mProgress. r=jandem
dom/base/nsScriptLoader.cpp
dom/base/nsScriptLoader.h
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -631,52 +631,59 @@ nsScriptLoader::ProcessScriptElement(nsI
           SRICheck::IntegrityMetadata(integrity, mDocument, &sriMetadata);
         }
       }
 
       request = new nsScriptLoadRequest(aElement, version, ourCORSMode,
                                         sriMetadata);
       request->mURI = scriptURI;
       request->mIsInline = false;
-      request->mLoading = true;
+      request->mProgress = nsScriptLoadRequest::Progress_Loading;
       request->mReferrerPolicy = ourRefPolicy;
 
       // set aScriptFromHead to false so we don't treat non preloaded scripts as
       // blockers for full page load. See bug 792438.
       rv = StartLoad(request, type, false);
       if (NS_FAILED(rv)) {
         // Asynchronously report the load failure
         NS_DispatchToCurrentThread(
           NS_NewRunnableMethod(aElement,
                                &nsIScriptElement::FireErrorEvent));
         return false;
       }
     }
 
+    // Should still be in loading stage of script.
+    NS_ASSERTION(!request->InCompilingStage(),
+                 "Request should not yet be in compiling stage.");
+
     request->mJSVersion = version;
 
     if (aElement->GetScriptAsync()) {
       request->mIsAsync = true;
-      if (!request->mLoading) {
+      if (request->IsDoneLoading()) {
         mLoadedAsyncRequests.AppendElement(request);
         // 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 ProcessRequest if that fails.
         ProcessPendingRequestsAsync();
       } else {
         mLoadingAsyncRequests.AppendElement(request);
       }
       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->mLoading) {
+      if (request->IsDoneLoading()) {
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
         ProcessPendingRequestsAsync();
       }
       return false;
     }
     // we now have a parser-inserted request that may or may not be still
     // loading
@@ -695,24 +702,24 @@ nsScriptLoader::ProcessScriptElement(nsI
     }
 
     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->mLoading) {
+      if (request->IsDoneLoading()) {
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
         ProcessPendingRequestsAsync();
       }
       return true;
     }
-    if (!request->mLoading && ReadyToExecuteScripts()) {
+    if (request->IsDoneLoading() && ReadyToExecuteScripts()) {
       // 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
@@ -745,17 +752,17 @@ nsScriptLoader::ProcessScriptElement(nsI
   if (!CSPAllowsInlineScript(aElement, mDocument)) {
     return false;
   }
 
   // Inline scripts ignore ther CORS mode and are always CORS_NONE
   request = new nsScriptLoadRequest(aElement, version, CORS_NONE,
                                     SRIMetadata()); // SRI doesn't apply
   request->mJSVersion = version;
-  request->mLoading = false;
+  request->mProgress = nsScriptLoadRequest::Progress_DoneLoading;
   request->mIsInline = true;
   request->mURI = mDocument->GetDocumentURI();
   request->mLineNo = aElement->GetScriptLineNumber();
 
   if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
       (!ReadyToExecuteScripts() || !mXSLTRequests.isEmpty())) {
     // Need to maintain order for XSLT-inserted scripts
     NS_ASSERTION(!mParserBlockingRequest,
@@ -816,16 +823,18 @@ public:
   NS_DECL_NSIRUNNABLE
 };
 
 } /* anonymous namespace */
 
 nsresult
 nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest)
 {
+  MOZ_ASSERT(aRequest->mProgress == nsScriptLoadRequest::Progress_Compiling);
+  aRequest->mProgress = nsScriptLoadRequest::Progress_DoneCompiling;
   nsresult rv = ProcessRequest(aRequest);
   mDocument->UnblockOnload(false);
   return rv;
 }
 
 NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable()
 {
   if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) {
@@ -900,42 +909,47 @@ nsScriptLoader::AttemptAsyncScriptParse(
   if (!JS::CompileOffThread(cx, options,
                             aRequest->mScriptTextBuf, aRequest->mScriptTextLength,
                             OffThreadScriptLoaderCallback,
                             static_cast<void*>(runnable))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   mDocument->BlockOnload();
+  aRequest->mProgress = nsScriptLoadRequest::Progress_Compiling;
 
   unused << runnable.forget();
   return NS_OK;
 }
 
 nsresult
-nsScriptLoader::ParseOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest)
+nsScriptLoader::CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest)
 {
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
                "Processing requests when running scripts is unsafe.");
   NS_ASSERTION(!aRequest->mOffThreadToken,
-               "Candidate for off-thread parsing is already parsed off-thread");
+               "Candidate for off-thread compile is already parsed off-thread");
+  NS_ASSERTION(!aRequest->InCompilingStage(),
+               "Candidate for off-thread compile is already in compiling stage.");
 
   nsresult rv = AttemptAsyncScriptParse(aRequest);
   if (rv != NS_ERROR_FAILURE) {
     return rv;
   }
 
   return ProcessRequest(aRequest);
 }
 
 nsresult
 nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
 {
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
                "Processing requests when running scripts is unsafe.");
+  NS_ASSERTION(aRequest->IsReadyToRun(),
+               "Processing a request that is not ready to run.");
 
   NS_ENSURE_ARG(aRequest);
   nsAutoString textData;
   const char16_t* scriptBuf = nullptr;
   size_t scriptLength = 0;
   JS::SourceBufferHolder::Ownership giveScriptOwnership =
     JS::SourceBufferHolder::NoOwnership;
 
@@ -1198,49 +1212,50 @@ nsScriptLoader::ProcessPendingRequestsAs
     NS_DispatchToCurrentThread(ev);
   }
 }
 
 void
 nsScriptLoader::ProcessPendingRequests()
 {
   nsRefPtr<nsScriptLoadRequest> request;
+
   if (mParserBlockingRequest &&
-      !mParserBlockingRequest->mLoading &&
+      mParserBlockingRequest->IsReadyToRun() &&
       ReadyToExecuteScripts()) {
     request.swap(mParserBlockingRequest);
     UnblockParser(request);
     ProcessRequest(request);
     ContinueParserAsync(request);
   }
 
   while (ReadyToExecuteScripts() && 
          !mXSLTRequests.isEmpty() &&
-         !mXSLTRequests.getFirst()->mLoading) {
+         mXSLTRequests.getFirst()->IsReadyToRun()) {
     request = mXSLTRequests.StealFirst();
     ProcessRequest(request);
   }
 
   while (mEnabled && !mLoadedAsyncRequests.isEmpty()) {
     request = mLoadedAsyncRequests.StealFirst();
-    ParseOffThreadOrProcessRequest(request);
+    CompileOffThreadOrProcessRequest(request);
   }
 
   while (mEnabled && !mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
-         !mNonAsyncExternalScriptInsertedRequests.getFirst()->mLoading) {
+         mNonAsyncExternalScriptInsertedRequests.getFirst()->IsReadyToRun()) {
     // Violate the HTML5 spec and execute these in the insertion order 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 = mNonAsyncExternalScriptInsertedRequests.StealFirst();
     ProcessRequest(request);
   }
 
   if (mDocumentParsingDone && mXSLTRequests.isEmpty()) {
-    while (!mDeferRequests.isEmpty() && !mDeferRequests.getFirst()->mLoading) {
+    while (!mDeferRequests.isEmpty() && mDeferRequests.getFirst()->IsReadyToRun()) {
       request = mDeferRequests.StealFirst();
       ProcessRequest(request);
     }
   }
 
   while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
     nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
     mPendingChildLoaders.RemoveElementAt(0);
@@ -1605,17 +1620,17 @@ nsScriptLoader::PrepareLoadedRequest(nsS
                mLoadingAsyncRequests.Contains(aRequest) ||
                mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
                mXSLTRequests.Contains(aRequest)  ||
                mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
                mParserBlockingRequest,
                "aRequest should be pending!");
 
   // Mark this as loaded
-  aRequest->mLoading = false;
+  aRequest->mProgress = nsScriptLoadRequest::Progress_DoneLoading;
 
   // And if it's async, move it to the loaded list.  aRequest->mIsAsync really
   // _should_ be in a list, but the consequences if it's not are bad enough we
   // want to avoid trying to move it if it's not.
   if (aRequest->mIsAsync) {
     MOZ_ASSERT(aRequest->isInList());
     if (aRequest->isInList()) {
       nsRefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
@@ -1674,17 +1689,17 @@ nsScriptLoader::PreloadURI(nsIURI *aURI,
   }
 
   nsRefPtr<nsScriptLoadRequest> request =
     new nsScriptLoadRequest(nullptr, 0,
                             Element::StringToCORSMode(aCrossOrigin),
                             sriMetadata);
   request->mURI = aURI;
   request->mIsInline = false;
-  request->mLoading = true;
+  request->mProgress = nsScriptLoadRequest::Progress_Loading;
   request->mReferrerPolicy = aReferrerPolicy;
 
   nsresult rv = StartLoad(request, aType, aScriptFromHead);
   if (NS_FAILED(rv)) {
     return;
   }
 
   PreloadInfo *pi = mPreloads.AppendElement();
--- a/dom/base/nsScriptLoader.h
+++ b/dom/base/nsScriptLoader.h
@@ -55,17 +55,17 @@ class nsScriptLoadRequest final : public
   friend class nsScriptLoadRequestList;
 
 public:
   nsScriptLoadRequest(nsIScriptElement* aElement,
                       uint32_t aVersion,
                       mozilla::CORSMode aCORSMode,
                       const mozilla::dom::SRIMetadata &aIntegrity)
     : mElement(aElement),
-      mLoading(true),
+      mProgress(Progress_Loading),
       mIsInline(true),
       mHasSourceMapURL(false),
       mIsDefer(false),
       mIsAsync(false),
       mIsNonAsyncScriptInserted(false),
       mIsXSLT(false),
       mIsCanceled(false),
       mOffThreadToken(nullptr),
@@ -105,21 +105,38 @@ public:
     return mIsCanceled;
   }
 
   void** OffThreadTokenPtr()
   {
     return mOffThreadToken ?  &mOffThreadToken : nullptr;
   }
 
+  enum Progress {
+    Progress_Loading,
+    Progress_DoneLoading,
+    Progress_Compiling,
+    Progress_DoneCompiling
+  };
+  bool IsReadyToRun() {
+    return mProgress == Progress_DoneLoading ||
+           mProgress == Progress_DoneCompiling;
+  }
+  bool IsDoneLoading() {
+    return mProgress == Progress_DoneLoading;
+  }
+  bool InCompilingStage() {
+    return (mProgress == Progress_Compiling) || (mProgress == Progress_DoneCompiling);
+  }
+
   using super::getNext;
   using super::isInList;
 
   nsCOMPtr<nsIScriptElement> mElement;
-  bool mLoading;          // Are we still waiting for a load to complete?
+  Progress mProgress;     // Are we still waiting for a load to complete?
   bool mIsInline;         // Is the script inline or loaded?
   bool mHasSourceMapURL;  // Does the HTTP header have a source map url?
   bool mIsDefer;          // True if we live in mDeferRequests.
   bool mIsAsync;          // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests.
   bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests
   bool mIsXSLT;           // True if we live in mXSLTRequests.
   bool mIsCanceled;       // True if we have been explicitly canceled.
   void* mOffThreadToken;  // Off-thread parsing token.
@@ -448,17 +465,17 @@ private:
    */
   bool SelfReadyToExecuteScripts()
   {
     return mEnabled && !mBlockerCount;
   }
 
   nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest);
   nsresult ProcessRequest(nsScriptLoadRequest* aRequest);
-  nsresult ParseOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest);
+  nsresult CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest);
   void FireScriptAvailable(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   nsresult EvaluateScript(nsScriptLoadRequest* aRequest,
                           JS::SourceBufferHolder& aSrcBuf);
 
   already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();