Bug 1333990: Part 2a - Allow multiple concurrent parser blockers. r=hsivonen draft
authorKris Maglione <maglione.k@gmail.com>
Wed, 15 Mar 2017 17:31:00 -0700
changeset 499605 c3fdb20ac0442c327c88d0e61ecf5479adba1a67
parent 499604 757b8ca1b75a751e2f0c80c9e476ae6e309c7432
child 499606 9d471599ff42e7fd9a05875912eef65b57405e18
push id49456
push usermaglione.k@gmail.com
push dateThu, 16 Mar 2017 00:44:19 +0000
reviewershsivonen
bugs1333990
milestone54.0a1
Bug 1333990: Part 2a - Allow multiple concurrent parser blockers. r=hsivonen MozReview-Commit-ID: DYegic0RPWL
parser/html/nsHtml5Parser.cpp
parser/html/nsHtml5Parser.h
parser/htmlparser/nsParser.cpp
parser/htmlparser/nsParser.h
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -32,17 +32,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExecutor)
   tmp->DropStreamParser();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 nsHtml5Parser::nsHtml5Parser()
   : mLastWasCR(false)
   , mDocWriteSpeculativeLastWasCR(false)
-  , mBlocked(false)
+  , mBlocked(0)
   , mDocWriteSpeculatorActive(false)
   , mInsertionPointPushLevel(0)
   , mDocumentClosed(false)
   , mInDocumentWrite(false)
   , mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nullptr))
   , mLastBuffer(mFirstBuffer)
   , mExecutor(new nsHtml5TreeOpExecutor())
   , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr))
@@ -141,24 +141,29 @@ nsHtml5Parser::ContinueInterruptedParsin
 {
   NS_NOTREACHED("Don't call. For interface compat only.");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP_(void)
 nsHtml5Parser::BlockParser()
 {
-  mBlocked = true;
+  mBlocked++;
 }
 
 NS_IMETHODIMP_(void)
 nsHtml5Parser::UnblockParser()
 {
-  mBlocked = false;
-  mExecutor->ContinueInterruptedParsingAsync();
+  MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0);
+  if (MOZ_LIKELY(mBlocked > 0)) {
+    mBlocked--;
+  }
+  if (MOZ_LIKELY(mBlocked == 0)) {
+    mExecutor->ContinueInterruptedParsingAsync();
+  }
 }
 
 NS_IMETHODIMP_(void)
 nsHtml5Parser::ContinueInterruptedParsingAsync()
 {
   mExecutor->ContinueInterruptedParsingAsync();
 }
 
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -273,19 +273,20 @@ class nsHtml5Parser final : public nsIPa
 
     /**
      * Whether the last character tokenized was a carriage return (for CRLF)
      * when preparsing document.write.
      */
     bool                          mDocWriteSpeculativeLastWasCR;
 
     /**
-     * The parser is blocking on a script
+     * The parser is blocking on the load of an external script from a web
+     * page, or any number of extension content scripts.
      */
-    bool                          mBlocked;
+    uint32_t                      mBlocked;
 
     /**
      * Whether the document.write() speculator is already active.
      */
     bool                          mDocWriteSpeculatorActive;
     
     /**
      * The number of PushDefinedInsertionPoint calls we've seen without a
--- a/parser/htmlparser/nsParser.cpp
+++ b/parser/htmlparser/nsParser.cpp
@@ -40,17 +40,16 @@
 #include "nsIHTMLContentSink.h"
 
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/BinarySearch.h"
 
 using namespace mozilla;
 using mozilla::dom::EncodingUtils;
 
-#define NS_PARSER_FLAG_PARSER_ENABLED         0x00000002
 #define NS_PARSER_FLAG_OBSERVERS_ENABLED      0x00000004
 #define NS_PARSER_FLAG_PENDING_CONTINUE_EVENT 0x00000008
 #define NS_PARSER_FLAG_FLUSH_TOKENS           0x00000020
 #define NS_PARSER_FLAG_CAN_TOKENIZE           0x00000040
 
 //-------------- Begin ParseContinue Event Definition ------------------------
 /*
 The parser can be explicitly interrupted by passing a return value of
@@ -152,18 +151,18 @@ nsParser::Initialize(bool aConstructor)
   }
 
   mContinueEvent = nullptr;
   mCharsetSource = kCharsetUninitialized;
   mCharset.AssignLiteral("ISO-8859-1");
   mInternalState = NS_OK;
   mStreamStatus = NS_OK;
   mCommand = eViewNormal;
+  mBlocked = 0;
   mFlags = NS_PARSER_FLAG_OBSERVERS_ENABLED |
-           NS_PARSER_FLAG_PARSER_ENABLED |
            NS_PARSER_FLAG_CAN_TOKENIZE;
 
   mProcessingNetworkData = false;
   mIsAboutBlank = false;
 }
 
 void
 nsParser::Cleanup()
@@ -626,17 +625,17 @@ nsParser::ContinueInterruptedParsing()
   // that we might start closing things down when the parser
   // is reenabled. To make sure that we're not deleted across
   // the reenabling process, hold a reference to ourselves.
   nsresult result=NS_OK;
   nsCOMPtr<nsIParser> kungFuDeathGrip(this);
   nsCOMPtr<nsIContentSink> sinkDeathGrip(mSink);
 
 #ifdef DEBUG
-  if (!(mFlags & NS_PARSER_FLAG_PARSER_ENABLED)) {
+  if (mBlocked) {
     NS_WARNING("Don't call ContinueInterruptedParsing on a blocked parser.");
   }
 #endif
 
   bool isFinalChunk = mParserContext &&
                         mParserContext->mStreamListenerState == eOnStop;
 
   mProcessingNetworkData = true;
@@ -649,54 +648,55 @@ nsParser::ContinueInterruptedParsing()
   if (result != NS_OK) {
     result=mInternalState;
   }
 
   return result;
 }
 
 /**
- *  Stops parsing temporarily. That's it will prevent the
- *  parser from building up content model.
+ *  Stops parsing temporarily. That is, it will prevent the
+ *  parser from building up content model while scripts
+ *  are being loaded (either an external script from a web
+ *  page, or any number of extension content scripts).
  */
 NS_IMETHODIMP_(void)
 nsParser::BlockParser()
 {
-  mFlags &= ~NS_PARSER_FLAG_PARSER_ENABLED;
+  mBlocked++;
 }
 
 /**
  *  Open up the parser for tokenization, building up content
  *  model..etc. However, this method does not resume parsing
  *  automatically. It's the callers' responsibility to restart
  *  the parsing engine.
  */
 NS_IMETHODIMP_(void)
 nsParser::UnblockParser()
 {
-  if (!(mFlags & NS_PARSER_FLAG_PARSER_ENABLED)) {
-    mFlags |= NS_PARSER_FLAG_PARSER_ENABLED;
-  } else {
-    NS_WARNING("Trying to unblock an unblocked parser.");
+  MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0);
+  if (MOZ_LIKELY(mBlocked > 0)) {
+    mBlocked--;
   }
 }
 
 NS_IMETHODIMP_(void)
 nsParser::ContinueInterruptedParsingAsync()
 {
   mSink->ContinueInterruptedParsingAsync();
 }
 
 /**
  * Call this to query whether the parser is enabled or not.
  */
 NS_IMETHODIMP_(bool)
 nsParser::IsParserEnabled()
 {
-  return (mFlags & NS_PARSER_FLAG_PARSER_ENABLED) != 0;
+  return !mBlocked;
 }
 
 /**
  * Call this to query whether the parser thinks it's done with parsing.
  */
 NS_IMETHODIMP_(bool)
 nsParser::IsComplete()
 {
@@ -1019,18 +1019,17 @@ nsParser::ParseFragment(const nsAString&
  *  @return  error code -- 0 if ok, non-zero if error.
  */
 nsresult
 nsParser::ResumeParse(bool allowIteration, bool aIsFinalChunk,
                       bool aCanInterrupt)
 {
   nsresult result = NS_OK;
 
-  if ((mFlags & NS_PARSER_FLAG_PARSER_ENABLED) &&
-      mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
+  if (!mBlocked && mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
 
     result = WillBuildModel(mParserContext->mScanner->GetFilename());
     if (NS_FAILED(result)) {
       mFlags &= ~NS_PARSER_FLAG_CAN_TOKENIZE;
       return result;
     }
 
     if (mDTD) {
@@ -1065,17 +1064,17 @@ nsParser::ResumeParse(bool allowIteratio
         // down the parser, it's important to check whether the input buffer
         // has been scanned to completion (theTokenizerResult should be kEOF).
         // kEOF -> End of buffer.
 
         // If we're told to block the parser, we disable all further parsing
         // (and cache any data coming in) until the parser is re-enabled.
         if (NS_ERROR_HTMLPARSER_BLOCK == result) {
           mSink->WillInterrupt();
-          if (mFlags & NS_PARSER_FLAG_PARSER_ENABLED) {
+          if (!mBlocked) {
             // If we were blocked by a recursive invocation, don't re-block.
             BlockParser();
           }
           return NS_OK;
         }
         if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
           // Note: Parser Terminate() calls DidBuildModel.
           if (mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
--- a/parser/htmlparser/nsParser.h
+++ b/parser/htmlparser/nsParser.h
@@ -380,16 +380,17 @@ protected:
     nsIRunnable*                 mContinueEvent;  // weak ref
 
     eParserCommands     mCommand;
     nsresult            mInternalState;
     nsresult            mStreamStatus;
     int32_t             mCharsetSource;
     
     uint16_t            mFlags;
+    uint32_t            mBlocked;
 
     nsString            mUnusedInput;
     nsCString           mCharset;
     nsCString           mCommandStr;
 
     bool                mProcessingNetworkData;
     bool                mIsAboutBlank;
 };