Bug 1088635. r=smaug, a=dveditz.
authorHenri Sivonen <hsivonen@hsivonen.fi>
Wed, 05 Nov 2014 16:37:17 +0200
changeset 225926 2be3d4150683
parent 225925 2ad92b68de0b
child 225927 fdb8b52bea5c
push id4071
push userhsivonen@mozilla.com
push date2014-11-05 15:58 +0000
treeherdermozilla-beta@2be3d4150683 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, dveditz
bugs1088635
milestone34.0
Bug 1088635. r=smaug, a=dveditz.
content/base/src/nsDocument.cpp
parser/html/nsHtml5Parser.cpp
parser/html/nsHtml5Parser.h
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeBuilderHSupplement.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
parser/html/nsHtml5TreeOperation.cpp
parser/html/nsHtml5TreeOperation.h
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3923,17 +3923,17 @@ nsDocument::GetChildArray(uint32_t* aChi
 }
 
 
 nsresult
 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                           bool aNotify)
 {
   if (aKid->IsElement() && GetRootElement()) {
-    NS_ERROR("Inserting element child when we already have one");
+    NS_WARNING("Inserting root element when we already have one");
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
   return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
 }
 
 void
 nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -233,17 +233,18 @@ nsHtml5Parser::Parse(const nsAString& aS
       mTreeBuilder->StartPlainText();
       mTokenizer->StartPlainText();
     }
     /*
      * If you move the following line, be very careful not to cause 
      * WillBuildModel to be called before the document has had its 
      * script global object set.
      */
-    mExecutor->WillBuildModel(eDTDMode_unknown);
+    rv = mExecutor->WillBuildModel(eDTDMode_unknown);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Return early if the parser has processed EOF
   if (mExecutor->IsComplete()) {
     return NS_OK;
   }
 
   if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
@@ -251,17 +252,17 @@ nsHtml5Parser::Parse(const nsAString& aS
     NS_ASSERTION(!GetStreamParser(),
                  "Had stream parser but got document.close().");
     if (mDocumentClosed) {
       // already closed
       return NS_OK;
     }
     mDocumentClosed = true;
     if (!mBlocked && !mInDocumentWrite) {
-      ParseUntilBlocked();
+      return ParseUntilBlocked();
     }
     return NS_OK;
   }
 
   // If we got this far, we are dealing with a document.write or
   // document.writeln call--not document.close().
 
   NS_ASSERTION(IsInsertionPointDefined(),
@@ -374,17 +375,18 @@ nsHtml5Parser::Parse(const nsAString& aS
       if (inRootContext) {
         mRootContextLineNumber = mTokenizer->getLineNumber();
       } else {
         mTokenizer->setLineNumber(lineNumberSave);
       }
 
       if (mTreeBuilder->HasScript()) {
         mTreeBuilder->Flush(); // Move ops to the executor
-        mExecutor->FlushDocumentWrite(); // run the ops
+        rv = mExecutor->FlushDocumentWrite(); // run the ops
+        NS_ENSURE_SUCCESS(rv, rv);
         // Flushing tree ops can cause all sorts of things.
         // Return early if the parser got terminated.
         if (mExecutor->IsComplete()) {
           return NS_OK;
         }
       }
       // Ignore suspension requests
     }
@@ -433,17 +435,18 @@ nsHtml5Parser::Parse(const nsAString& aS
     }
   }
 
   if (!mBlocked) { // buffer was tokenized to completion
     NS_ASSERTION(!stackBuffer.hasMore(),
       "Buffer wasn't tokenized to completion?");
     // Scripting semantics require a forced tree builder flush here
     mTreeBuilder->Flush(); // Move ops to the executor
-    mExecutor->FlushDocumentWrite(); // run the ops
+    rv = mExecutor->FlushDocumentWrite(); // run the ops
+    NS_ENSURE_SUCCESS(rv, rv);
   } else if (stackBuffer.hasMore()) {
     // The buffer wasn't tokenized to completion. Tokenize the untokenized
     // content in order to preload stuff. This content will be retokenized
     // later for normal parsing.
     if (!mDocWriteSpeculatorActive) {
       mDocWriteSpeculatorActive = true;
       if (!mDocWriteSpeculativeTreeBuilder) {
         // Lazily initialize if uninitialized
@@ -591,44 +594,48 @@ bool
 nsHtml5Parser::IsScriptCreated()
 {
   return !GetStreamParser();
 }
 
 /* End nsIParser  */
 
 // not from interface
-void
+nsresult
 nsHtml5Parser::ParseUntilBlocked()
 {
-  if (mBlocked || mExecutor->IsComplete() || NS_FAILED(mExecutor->IsBroken())) {
-    return;
+  nsresult rv = mExecutor->IsBroken();
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (mBlocked || mExecutor->IsComplete()) {
+    return NS_OK;
   }
   NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
   NS_ASSERTION(!mInDocumentWrite,
     "ParseUntilBlocked entered while in doc.write!");
 
   mDocWriteSpeculatorActive = false;
 
   for (;;) {
     if (!mFirstBuffer->hasMore()) {
       if (mFirstBuffer == mLastBuffer) {
         if (mExecutor->IsComplete()) {
           // something like cache manisfests stopped the parse in mid-flight
-          return;
+          return NS_OK;
         }
         if (mDocumentClosed) {
           NS_ASSERTION(!GetStreamParser(),
                        "This should only happen with script-created parser.");
           mTokenizer->eof();
           mTreeBuilder->StreamEnded();
           mTreeBuilder->Flush();
           mExecutor->FlushDocumentWrite();
+          // The below call does memory cleanup, so call it even if the
+          // parser has been marked as broken.
           mTokenizer->end();
-          return;            
+          return NS_OK;
         }
         // never release the last buffer.
         NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
                      "Sentinel buffer had its indeces changed.");
         if (GetStreamParser()) {
           if (mReturnToStreamParserPermitted &&
               !mExecutor->IsScriptExecuting()) {
             mTreeBuilder->Flush();
@@ -640,44 +647,45 @@ nsHtml5Parser::ParseUntilBlocked()
         } else {
           // Script-created parser
           mTreeBuilder->Flush();
           // No need to flush the executor, because the executor is already
           // in a flush
           NS_ASSERTION(mExecutor->IsInFlushLoop(),
               "How did we come here without being in the flush loop?");
         }
-        return; // no more data for now but expecting more
+        return NS_OK; // no more data for now but expecting more
       }
       mFirstBuffer = mFirstBuffer->next;
       continue;
     }
 
     if (mBlocked || mExecutor->IsComplete()) {
-      return;
+      return NS_OK;
     }
 
     // now we have a non-empty buffer
     mFirstBuffer->adjust(mLastWasCR);
     mLastWasCR = false;
     if (mFirstBuffer->hasMore()) {
       bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
       if (inRootContext) {
         mTokenizer->setLineNumber(mRootContextLineNumber);
       }
       mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
       if (inRootContext) {
         mRootContextLineNumber = mTokenizer->getLineNumber();
       }
       if (mTreeBuilder->HasScript()) {
         mTreeBuilder->Flush();
-        mExecutor->FlushDocumentWrite();
+        nsresult rv = mExecutor->FlushDocumentWrite();
+        NS_ENSURE_SUCCESS(rv, rv);
       }
       if (mBlocked) {
-        return;
+        return NS_OK;
       }
     }
     continue;
   }
 }
 
 nsresult
 nsHtml5Parser::Initialize(nsIDocument* aDoc,
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -256,17 +256,17 @@ class nsHtml5Parser : public nsIParser,
         return nullptr;
       }
       return mStreamListener->GetDelegate();
     }
 
     /**
      * Parse until pending data is exhausted or a script blocks the parser
      */
-    void ParseUntilBlocked();
+    nsresult ParseUntilBlocked();
 
   private:
 
     virtual ~nsHtml5Parser();
 
     // State variables
 
     /**
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -788,17 +788,17 @@ nsHtml5StreamParser::WriteStreamBytes(co
                                       uint32_t aCount,
                                       uint32_t* aWriteCount)
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   // mLastBuffer should always point to a buffer of the size
   // NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
   if (!mLastBuffer) {
     NS_WARNING("mLastBuffer should not be null!");
-    MarkAsBroken();
+    MarkAsBroken(NS_ERROR_NULL_POINTER);
     return NS_ERROR_NULL_POINTER;
   }
   if (mLastBuffer->getEnd() == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
     nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
       nsHtml5OwningUTF16Buffer::FalliblyCreate(
         NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
     if (!newBuf) {
       return NS_ERROR_OUT_OF_MEMORY;
@@ -894,17 +894,18 @@ nsHtml5StreamParser::OnStartRequest(nsIR
     mTokenizer->StartPlainText();
   }
 
   /*
    * If you move the following line, be very careful not to cause 
    * WillBuildModel to be called before the document has had its 
    * script global object set.
    */
-  mExecutor->WillBuildModel(eDTDMode_unknown);
+  rv = mExecutor->WillBuildModel(eDTDMode_unknown);
+  NS_ENSURE_SUCCESS(rv, rv);
   
   nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
     nsHtml5OwningUTF16Buffer::FalliblyCreate(
       NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
   if (!newBuf) {
     // marks this stream parser as terminated,
     // which prevents entry to code paths that
     // would use mFirstBuffer or mLastBuffer.
@@ -999,18 +1000,19 @@ nsHtml5StreamParser::DoStopRequest()
   if (IsTerminated()) {
     return;
   }
 
   mStreamState = STREAM_ENDED;
 
   if (!mUnicodeDecoder) {
     uint32_t writeCount;
-    if (NS_FAILED(FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
-      MarkAsBroken();
+    nsresult rv;
+    if (NS_FAILED(rv = FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
+      MarkAsBroken(rv);
       return;
     }
   } else if (mFeedChardet) {
     mChardet->Done();
   }
 
   if (IsTerminatedOrInterrupted()) {
     return;
@@ -1072,17 +1074,17 @@ nsHtml5StreamParser::DoDataAvailable(con
       mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
       mFeedChardet = !dontFeed;
     }
     rv = WriteStreamBytes(aBuffer, aLength, &writeCount);
   } else {
     rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
   }
   if (NS_FAILED(rv)) {
-    MarkAsBroken();
+    MarkAsBroken(rv);
     return;
   }
   NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
 
   if (IsTerminatedOrInterrupted()) {
     return;
   }
 
@@ -1658,21 +1660,21 @@ nsHtml5StreamParser::TimerFlush()
       if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
         NS_WARNING("failed to dispatch executor flush event");
       }
     }
   }
 }
 
 void
-nsHtml5StreamParser::MarkAsBroken()
+nsHtml5StreamParser::MarkAsBroken(nsresult aRv)
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
   Terminate();
-  mTreeBuilder->MarkAsBroken();
+  mTreeBuilder->MarkAsBroken(aRv);
   mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(false);
   NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
   if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
     NS_WARNING("failed to dispatch executor flush event");
   }
 }
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -212,17 +212,17 @@ class nsHtml5StreamParser : public nsICh
 #ifdef DEBUG
     bool IsParserThread() {
       bool ret;
       mThread->IsOnCurrentThread(&ret);
       return ret;
     }
 #endif
 
-    void MarkAsBroken();
+    void MarkAsBroken(nsresult aRv);
 
     /**
      * Marks the stream parser as interrupted. If you ever add calls to this
      * method, be sure to review Uninterrupt usage very, very carefully to
      * avoid having a previous in-flight runnable cancel your Interrupt()
      * call on the other thread too soon.
      */
     void Interrupt()
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -913,24 +913,24 @@ void
 nsHtml5TreeBuilder::DropHandles()
 {
   MOZ_ASSERT(!mBuilder, "Must not drop handles with builder.");
   mOldHandles.Clear();
   mHandlesUsed = 0;
 }
 
 void
-nsHtml5TreeBuilder::MarkAsBroken()
+nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv)
 {
   if (MOZ_UNLIKELY(mBuilder)) {
     MOZ_ASSERT_UNREACHABLE("Must not call this with builder.");
     return;
   }
   mOpQueue.Clear(); // Previous ops don't matter anymore
-  mOpQueue.AppendElement()->Init(eTreeOpMarkAsBroken);
+  mOpQueue.AppendElement()->Init(aRv);
 }
 
 void
 nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle)
 {
   MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
   startTag(nsHtml5ElementName::ELT_TITLE,
            nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -218,9 +218,9 @@
     void errNoCheckUnclosedElementsOnStack();
 
     void errEndTagDidNotMatchCurrentOpenElement(nsIAtom* aName, nsIAtom* aOther);
 
     void errEndTagViolatesNestingRules(nsIAtom* aName);
 
     void errEndWithUnclosedElements(nsIAtom* aName);
 
-    void MarkAsBroken();
+    void MarkAsBroken(nsresult aRv);
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -407,17 +407,21 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
         return;
       }
       // Not sure if this grip is still needed, but previously, the code
       // gripped before calling ParseUntilBlocked();
       nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip = 
         GetParser()->GetStreamParser();
       // Now parse content left in the document.write() buffer queue if any.
       // This may generate tree ops on its own or dequeue a speculation.
-      GetParser()->ParseUntilBlocked();
+      nsresult rv = GetParser()->ParseUntilBlocked();
+      if (NS_FAILED(rv)) {
+        MarkAsBroken(rv);
+        return;
+      }
     }
 
     if (mOpQueue.IsEmpty()) {
       // Avoid bothering the rest of the engine with a doc update if there's 
       // nothing to do.
       return;
     }
 
@@ -490,31 +494,34 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
         #endif
         nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
         return;      
       }
     }
   }
 }
 
-void
+nsresult
 nsHtml5TreeOpExecutor::FlushDocumentWrite()
 {
+  nsresult rv = IsBroken();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   FlushSpeculativeLoads(); // Make sure speculative loads never start after the
                 // corresponding normal loads for the same URLs.
 
   if (MOZ_UNLIKELY(!mParser)) {
     // The parse has ended.
     mOpQueue.Clear(); // clear in order to be able to assert in destructor
-    return;
+    return rv;
   }
   
   if (mFlushState != eNotFlushing) {
     // XXX Can this happen? In case it can, let's avoid crashing.
-    return;
+    return rv;
   }
 
   mFlushState = eInFlush;
 
   // avoid crashing near EOF
   nsRefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
   nsRefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
 
@@ -537,38 +544,39 @@ nsHtml5TreeOpExecutor::FlushDocumentWrit
        iter < end;
        ++iter) {
     if (MOZ_UNLIKELY(!mParser)) {
       // The previous tree op caused a call to nsIParser::Terminate().
       break;
     }
     NS_ASSERTION(mFlushState == eInDocUpdate, 
       "Tried to perform tree op outside update batch.");
-    nsresult rv = iter->Perform(this, &scriptElement);
+    rv = iter->Perform(this, &scriptElement);
     if (NS_FAILED(rv)) {
       MarkAsBroken(rv);
       break;
     }
   }
 
   mOpQueue.Clear();
   
   EndDocUpdate();
 
   mFlushState = eNotFlushing;
 
   if (MOZ_UNLIKELY(!mParser)) {
     // Ending the doc update caused a call to nsIParser::Terminate().
-    return;
+    return rv;
   }
 
   if (scriptElement) {
     // must be tail call when mFlushState is eNotFlushing
     RunScript(scriptElement);
   }
+  return rv;
 }
 
 // copied from HTML content sink
 bool
 nsHtml5TreeOpExecutor::IsScriptEnabled()
 {
   if (!mDocument || !mDocShell)
     return true;
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -173,17 +173,17 @@ class nsHtml5TreeOpExecutor MOZ_FINAL : 
     virtual nsresult MarkAsBroken(nsresult aReason);
 
     void StartLayout();
     
     void FlushSpeculativeLoads();
                   
     void RunFlushLoop();
 
-    void FlushDocumentWrite();
+    nsresult FlushDocumentWrite();
 
     void MaybeSuspend();
 
     void Start();
 
     void NeedsCharsetSwitchTo(const char* aEncoding,
                               int32_t aSource,
                               uint32_t aLineNumber);
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -193,16 +193,20 @@ nsHtml5TreeOperation::AppendToDocument(n
   MOZ_ASSERT(aBuilder);
   MOZ_ASSERT(aBuilder->GetDocument() == aNode->OwnerDoc());
   MOZ_ASSERT(aBuilder->IsInDocUpdate());
   nsresult rv = NS_OK;
 
   nsIDocument* doc = aBuilder->GetDocument();
   uint32_t childCount = doc->GetChildCount();
   rv = doc->AppendChildTo(aNode, false);
+  if (rv == NS_ERROR_DOM_HIERARCHY_REQUEST_ERR) {
+    aNode->SetParserHasNotified();
+    return NS_OK;
+  }
   NS_ENSURE_SUCCESS(rv, rv);
   aNode->SetParserHasNotified();
   nsNodeUtils::ContentInserted(doc, aNode, childCount);
 
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                "Someone forgot to block scripts");
   if (aNode->IsElement()) {
     nsContentUtils::AddScriptRunner(
@@ -722,18 +726,17 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
       return AppendDoctypeToDocument(name, publicId, systemId, aBuilder);
     }
     case eTreeOpGetDocumentFragmentForTemplate: {
       nsIContent* node = *(mOne.node);
       *mTwo.node = GetDocumentFragmentForTemplate(node);
       return NS_OK;
     }
     case eTreeOpMarkAsBroken: {
-      aBuilder->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
-      return NS_OK;
+      return mOne.result;
     }
     case eTreeOpRunScript: {
       nsIContent* node = *(mOne.node);
       nsAHtml5TreeBuilderState* snapshot = mTwo.state;
       if (snapshot) {
         aBuilder->InitializeDocWriteParserState(snapshot, mFour.integer);
       }
       *aScriptElement = node;
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -428,16 +428,25 @@ class nsHtml5TreeOperation {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
       NS_PRECONDITION(aNode, "Initialized tree op with null node.");
       mOpCode = aOpCode;
       mOne.node = static_cast<nsIContent**>(aNode);
       mFour.integer = aInt;
     }
 
+    inline void Init(nsresult aRv)
+    {
+      NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+        "Op code must be uninitialized when initializing.");
+      NS_PRECONDITION(NS_FAILED(aRv), "Initialized tree op with non-failure.");
+      mOpCode = eTreeOpMarkAsBroken;
+      mOne.result = aRv;
+    }
+
     inline void InitAddClass(nsIContentHandle* aNode, const char16_t* aClass)
     {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
       NS_PRECONDITION(aNode, "Initialized tree op with null node.");
       NS_PRECONDITION(aClass, "Initialized tree op with null string.");
       // aClass must be a literal string that does not need freeing
       mOpCode = eTreeOpAddClass;
@@ -480,17 +489,18 @@ class nsHtml5TreeOperation {
     // Make the queue take items the size of pointer and make the op code
     // decide how many operands it dequeues after it.
     eHtml5TreeOperation mOpCode;
     union {
       nsIContent**                    node;
       nsIAtom*                        atom;
       nsHtml5HtmlAttributes*          attributes;
       nsHtml5DocumentMode             mode;
-      char16_t*                      unicharPtr;
+      char16_t*                       unicharPtr;
       char*                           charPtr;
       nsHtml5TreeOperationStringPair* stringPair;
       nsAHtml5TreeBuilderState*       state;
       int32_t                         integer;
+      nsresult                        result;
     }                   mOne, mTwo, mThree, mFour;
 };
 
 #endif // nsHtml5TreeOperation_h