Bug 959150 part 4 - Avoid using tree ops when parsing with nsHtml5StringParser. r=smaug.
☠☠ backed out by e420695e2079 ☠ ☠
authorHenri Sivonen <hsivonen@hsivonen.fi>
Wed, 05 Mar 2014 21:38:50 +0200
changeset 189299 14441e5285826c348ac560bad0d240b2f64f14a4
parent 189298 8d0ae8bffb08bc71bde1b7639f3b3b8959051def
child 189300 ebc67518a962c890acc5e1a8810a90ea8543a64d
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs959150
milestone30.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 959150 part 4 - Avoid using tree ops when parsing with nsHtml5StringParser. r=smaug.
parser/html/javasrc/TreeBuilder.java
parser/html/moz.build
parser/html/nsHtml5DocumentBuilder.cpp
parser/html/nsHtml5DocumentBuilder.h
parser/html/nsHtml5OplessBuilder.cpp
parser/html/nsHtml5OplessBuilder.h
parser/html/nsHtml5StringParser.cpp
parser/html/nsHtml5StringParser.h
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeBuilder.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/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -5132,25 +5132,28 @@ public abstract class TreeBuilder<T> imp
 
     private void appendToCurrentNodeAndPushFormattingElementMayFoster(
             ElementName elementName, HtmlAttributes attributes)
             throws SAXException {
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
         // ]NOCPP]
         // This method can't be called for custom elements
+        HtmlAttributes clone = attributes.cloneAttributes(null);
+        // Attributes must not be read after calling createElement, because
+        // createElement may delete attributes in C++.
         T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes);
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             insertIntoFosterParent(elt);
         } else {
             appendElement(elt, current.node);
         }
-        StackNode<T> node = new StackNode<T>(elementName, elt, attributes.cloneAttributes(null)
+        StackNode<T> node = new StackNode<T>(elementName, elt, clone
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
         append(node);
         node.retain(); // append doesn't retain itself
     }
@@ -5206,30 +5209,32 @@ public abstract class TreeBuilder<T> imp
             throws SAXException {
         @Local String popName = elementName.name;
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
         if (elementName.isCustom()) {
             popName = checkPopName(popName);
         }
         // ]NOCPP]
+        boolean markAsHtmlIntegrationPoint = false;
+        if (ElementName.ANNOTATION_XML == elementName
+                && annotationXmlEncodingPermitsHtml(attributes)) {
+            markAsHtmlIntegrationPoint = true;
+        }
+        // Attributes must not be read after calling createElement(), since
+        // createElement may delete the object in C++.
         T elt = createElement("http://www.w3.org/1998/Math/MathML", popName,
                 attributes);
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             insertIntoFosterParent(elt);
         } else {
             appendElement(elt, current.node);
         }
-        boolean markAsHtmlIntegrationPoint = false;
-        if (ElementName.ANNOTATION_XML == elementName
-                && annotationXmlEncodingPermitsHtml(attributes)) {
-            markAsHtmlIntegrationPoint = true;
-        }
         StackNode<T> node = new StackNode<T>(elementName, elt, popName,
                 markAsHtmlIntegrationPoint
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
--- a/parser/html/moz.build
+++ b/parser/html/moz.build
@@ -25,16 +25,17 @@ EXPORTS += [
     'nsHtml5DocumentMode.h',
     'nsHtml5HtmlAttributes.h',
     'nsHtml5Macros.h',
     'nsHtml5MetaScanner.h',
     'nsHtml5MetaScannerHSupplement.h',
     'nsHtml5Module.h',
     'nsHtml5NamedCharacters.h',
     'nsHtml5NamedCharactersAccel.h',
+    'nsHtml5OplessBuilder.h',
     'nsHtml5OwningUTF16Buffer.h',
     'nsHtml5Parser.h',
     'nsHtml5PendingNotification.h',
     'nsHtml5PlainTextUtils.h',
     'nsHtml5Speculation.h',
     'nsHtml5SpeculativeLoad.h',
     'nsHtml5StreamParser.h',
     'nsHtml5StringParser.h',
@@ -58,16 +59,17 @@ UNIFIED_SOURCES += [
     'nsHtml5DocumentBuilder.cpp',
     'nsHtml5ElementName.cpp',
     'nsHtml5Highlighter.cpp',
     'nsHtml5HtmlAttributes.cpp',
     'nsHtml5MetaScanner.cpp',
     'nsHtml5Module.cpp',
     'nsHtml5NamedCharacters.cpp',
     'nsHtml5NamedCharactersAccel.cpp',
+    'nsHtml5OplessBuilder.cpp',
     'nsHtml5OwningUTF16Buffer.cpp',
     'nsHtml5Parser.cpp',
     'nsHtml5PlainTextUtils.cpp',
     'nsHtml5Portability.cpp',
     'nsHtml5ReleasableAttributeName.cpp',
     'nsHtml5ReleasableElementName.cpp',
     'nsHtml5Speculation.cpp',
     'nsHtml5SpeculativeLoad.cpp',
--- a/parser/html/nsHtml5DocumentBuilder.cpp
+++ b/parser/html/nsHtml5DocumentBuilder.cpp
@@ -1,33 +1,144 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=78: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHtml5DocumentBuilder.h"
 
+#include "nsIStyleSheetLinkingElement.h"
+#include "nsStyleLinkElement.h"
 #include "nsScriptLoader.h"
-#include "mozilla/css/Loader.h"
-#include "nsIDocShell.h"
+#include "nsIHTMLDocument.h"
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsHtml5DocumentBuilder, nsContentSink,
                                      mOwnedElements)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsHtml5DocumentBuilder)
 NS_INTERFACE_MAP_END_INHERITING(nsContentSink)
 
 NS_IMPL_ADDREF_INHERITED(nsHtml5DocumentBuilder, nsContentSink)
 NS_IMPL_RELEASE_INHERITED(nsHtml5DocumentBuilder, nsContentSink)
 
-void
-nsHtml5DocumentBuilder::DropHeldElements()
+nsHtml5DocumentBuilder::nsHtml5DocumentBuilder(bool aRunsToCompletion)
+{
+  mRunsToCompletion = aRunsToCompletion;
+}
+
+nsresult
+nsHtml5DocumentBuilder::Init(nsIDocument* aDoc,
+                            nsIURI* aURI,
+                            nsISupports* aContainer,
+                            nsIChannel* aChannel)
+{
+  return nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
+}
+
+nsHtml5DocumentBuilder::~nsHtml5DocumentBuilder()
 {
-  mScriptLoader = nullptr;
-  mDocument = nullptr;
-  mNodeInfoManager = nullptr;
-  mCSSLoader = nullptr;
-  mDocumentURI = nullptr;
-  mDocShell = nullptr;
-  mOwnedElements.Clear();
+}
+
+nsresult
+nsHtml5DocumentBuilder::MarkAsBroken(nsresult aReason)
+{
+  mBroken = aReason;
+  return aReason;
+}
+
+void
+nsHtml5DocumentBuilder::SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource)
+{
+  if (mDocument) {
+    mDocument->SetDocumentCharacterSetSource(aCharsetSource);
+    mDocument->SetDocumentCharacterSet(aCharset);
+  }
 }
 
+void
+nsHtml5DocumentBuilder::UpdateStyleSheet(nsIContent* aElement)
+{
+  // Break out of the doc update created by Flush() to zap a runnable
+  // waiting to call UpdateStyleSheet without the right observer
+  EndDocUpdate();
+
+  if (MOZ_UNLIKELY(!mParser)) {
+    // EndDocUpdate ran stuff that called nsIParser::Terminate()
+    return;
+  }
+
+  nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aElement));
+  NS_ASSERTION(ssle, "Node didn't QI to style.");
+
+  ssle->SetEnableUpdates(true);
+
+  bool willNotify;
+  bool isAlternate;
+  nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this,
+                                       &willNotify,
+                                       &isAlternate);
+  if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) {
+    ++mPendingSheetCount;
+    mScriptLoader->AddExecuteBlocker();
+  }
+
+  if (aElement->IsHTML(nsGkAtoms::link)) {
+    // look for <link rel="next" href="url">
+    nsAutoString relVal;
+    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
+    if (!relVal.IsEmpty()) {
+      uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(relVal);
+      bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
+      if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
+        nsAutoString hrefVal;
+        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
+        if (!hrefVal.IsEmpty()) {
+          PrefetchHref(hrefVal, aElement, hasPrefetch);
+        }
+      }
+      if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) {
+        nsAutoString hrefVal;
+        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
+        if (!hrefVal.IsEmpty()) {
+          PrefetchDNS(hrefVal);
+        }
+      }
+    }
+  }
+
+  // Re-open update
+  BeginDocUpdate();
+}
+
+void
+nsHtml5DocumentBuilder::SetDocumentMode(nsHtml5DocumentMode m)
+{
+  nsCompatibility mode = eCompatibility_NavQuirks;
+  switch (m) {
+    case STANDARDS_MODE:
+      mode = eCompatibility_FullStandards;
+      break;
+    case ALMOST_STANDARDS_MODE:
+      mode = eCompatibility_AlmostStandards;
+      break;
+    case QUIRKS_MODE:
+      mode = eCompatibility_NavQuirks;
+      break;
+  }
+  nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(mDocument);
+  NS_ASSERTION(htmlDocument, "Document didn't QI into HTML document.");
+  htmlDocument->SetCompatibilityMode(mode);
+}
+
+// nsContentSink overrides
+
+void
+nsHtml5DocumentBuilder::UpdateChildCounts()
+{
+  // No-op
+}
+
+nsresult
+nsHtml5DocumentBuilder::FlushTags()
+{
+  return NS_OK;
+}
--- a/parser/html/nsHtml5DocumentBuilder.h
+++ b/parser/html/nsHtml5DocumentBuilder.h
@@ -3,16 +3,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsHtml5DocumentBuilder_h
 #define nsHtml5DocumentBuilder_h
 
 #include "nsHtml5PendingNotification.h"
+#include "nsContentSink.h"
+#include "nsHtml5DocumentMode.h"
+#include "nsIDocument.h"
 
 typedef nsIContent* nsIContentPtr;
 
 enum eHtml5FlushState {
   eNotFlushing = 0,  // not flushing
   eInFlush = 1,      // the Flush() method is on the call stack
   eInDocUpdate = 2,  // inside an update batch on the document
   eNotifying = 3     // flushing pending append notifications
@@ -87,35 +90,98 @@ public:
       sAppendBatchMaxSize = mElementsSeenInThisAppendBatch.Length();
     }
 #endif
     mElementsSeenInThisAppendBatch.Clear();
     NS_ASSERTION(mFlushState == eNotifying, "mFlushState out of sync");
     mFlushState = eInDocUpdate;
   }
 
-  void DropHeldElements();
+  nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
+                nsISupports* aContainer, nsIChannel* aChannel);
 
   // Getters and setters for fields from nsContentSink
   nsIDocument* GetDocument() {
     return mDocument;
   }
   nsNodeInfoManager* GetNodeInfoManager() {
     return mNodeInfoManager;
   }
 
-  virtual bool BelongsToStringParser() = 0;
+  /**
+   * Marks this parser as broken and tells the stream parser (if any) to
+   * terminate.
+   *
+   * @return aReason for convenience
+   */
+  virtual nsresult MarkAsBroken(nsresult aReason);
+
+  /**
+   * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0)
+   * value if broken.
+   */
+  inline nsresult IsBroken() {
+    return mBroken;
+  }
+
+  inline void BeginDocUpdate() {
+    NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update.");
+    NS_PRECONDITION(mParser, "Started update without parser.");
+    mFlushState = eInDocUpdate;
+    mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
+  }
+
+  inline void EndDocUpdate() {
+    NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync");
+    if (mFlushState == eInDocUpdate) {
+      FlushPendingAppendNotifications();
+      mFlushState = eInFlush;
+      mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
+    }
+  }
+
+  void SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource);
+
+  /**
+   * Sets up style sheet load / parse
+   */
+  void UpdateStyleSheet(nsIContent* aElement);
+
+  void SetDocumentMode(nsHtml5DocumentMode m);
+
+  void SetNodeInfoManager(nsNodeInfoManager* aManager) {
+    mNodeInfoManager = aManager;
+  }
+
+  // nsContentSink methods
+  virtual void UpdateChildCounts();
+  virtual nsresult FlushTags();
 
 protected:
   inline void SetAppendBatchCapacity(uint32_t aCapacity)
   {
     mElementsSeenInThisAppendBatch.SetCapacity(aCapacity);
   }
 
+  nsHtml5DocumentBuilder(bool aRunsToCompletion);
+  virtual ~nsHtml5DocumentBuilder();
+
 private:
   nsTArray<nsHtml5PendingNotification> mPendingNotifications;
-  nsTArray<nsCOMPtr<nsIContent> >      mOwnedElements;
   nsTArray<nsIContentPtr>              mElementsSeenInThisAppendBatch;
 protected:
+  nsTArray<nsCOMPtr<nsIContent> >      mOwnedElements;
+  /**
+   * Non-NS_OK if this parser should refuse to process any more input.
+   * For example, the parser needs to be marked as broken if it drops some
+   * input due to a memory allocation failure. In such a case, the whole
+   * parser needs to be marked as broken, because some input has been lost
+   * and parsing more input could lead to a DOM where pieces of HTML source
+   * that weren't supposed to become scripts become scripts.
+   *
+   * Since NS_OK is actually 0, zeroing operator new takes care of
+   * initializing this.
+   */
+  nsresult                             mBroken;
   eHtml5FlushState                     mFlushState;
 };
 
 #endif // nsHtml5DocumentBuilder_h
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5OplessBuilder.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsHtml5OplessBuilder.h"
+
+#include "nsScriptLoader.h"
+#include "mozilla/css/Loader.h"
+#include "nsIDocShell.h"
+#include "nsIHTMLDocument.h"
+
+nsHtml5OplessBuilder::nsHtml5OplessBuilder()
+ : nsHtml5DocumentBuilder(true)
+{
+}
+
+nsHtml5OplessBuilder::~nsHtml5OplessBuilder()
+{
+}
+
+void
+nsHtml5OplessBuilder::Start()
+{
+  mFlushState = eInFlush;
+  BeginDocUpdate();
+}
+
+void
+nsHtml5OplessBuilder::Finish()
+{
+  EndDocUpdate();
+  DropParserAndPerfHint();
+  mScriptLoader = nullptr;
+  mDocument = nullptr;
+  mNodeInfoManager = nullptr;
+  mCSSLoader = nullptr;
+  mDocumentURI = nullptr;
+  mDocShell = nullptr;
+  mOwnedElements.Clear();
+  mFlushState = eNotFlushing;
+}
+
+void
+nsHtml5OplessBuilder::SetParser(nsParserBase* aParser)
+{
+  mParser = aParser;
+}
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5OplessBuilder.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsHtml5OplessBuilder_h
+#define nsHtml5OplessBuilder_h
+
+#include "nsHtml5DocumentBuilder.h"
+
+class nsParserBase;
+
+class nsHtml5OplessBuilder : public nsHtml5DocumentBuilder
+{
+public:
+  NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
+
+  nsHtml5OplessBuilder();
+  ~nsHtml5OplessBuilder();
+  void Start();
+  void Finish();
+  void SetParser(nsParserBase* aParser);
+};
+
+#endif // nsHtml5OplessBuilder_h
--- a/parser/html/nsHtml5StringParser.cpp
+++ b/parser/html/nsHtml5StringParser.cpp
@@ -9,18 +9,18 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsHtml5DependentUTF16Buffer.h"
 
 NS_IMPL_ISUPPORTS0(nsHtml5StringParser)
 
 nsHtml5StringParser::nsHtml5StringParser()
-  : mExecutor(new nsHtml5TreeOpExecutor(true))
-  , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr))
+  : mBuilder(new nsHtml5OplessBuilder())
+  , mTreeBuilder(new nsHtml5TreeBuilder(mBuilder))
   , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, false))
 {
   MOZ_COUNT_CTOR(nsHtml5StringParser);
   mTokenizer->setInterner(&mAtomTable);
 }
 
 nsHtml5StringParser::~nsHtml5StringParser()
 {
@@ -37,37 +37,35 @@ nsHtml5StringParser::ParseFragment(const
 {
   NS_ENSURE_TRUE(aSourceBuffer.Length() <= INT32_MAX,
                  NS_ERROR_OUT_OF_MEMORY);
 
   nsIDocument* doc = aTargetNode->OwnerDoc();
   nsIURI* uri = doc->GetDocumentURI();
   NS_ENSURE_TRUE(uri, NS_ERROR_NOT_AVAILABLE);
 
-  nsIContent* target = aTargetNode;
   mTreeBuilder->setFragmentContext(aContextLocalName,
                                    aContextNamespace,
-                                   &target,
+                                   aTargetNode,
                                    aQuirks);
 
 #ifdef DEBUG
   if (!aPreventScriptExecution) {
     NS_ASSERTION(!aTargetNode->IsInDoc(),
                  "If script execution isn't prevented, "
                  "the target node must not be in doc.");
     nsCOMPtr<nsIDOMDocumentFragment> domFrag = do_QueryInterface(aTargetNode);
     NS_ASSERTION(domFrag,
       "If script execution isn't prevented, must parse to DOM fragment.");
   }
 #endif
 
   mTreeBuilder->SetPreventScriptExecution(aPreventScriptExecution);
 
-  Tokenize(aSourceBuffer, doc, true);
-  return NS_OK;
+  return Tokenize(aSourceBuffer, doc, true);
 }
 
 nsresult
 nsHtml5StringParser::ParseDocument(const nsAString& aSourceBuffer,
                                    nsIDocument* aTargetDoc,
                                    bool aScriptingEnabledForNoscriptParsing)
 {
   MOZ_ASSERT(!aTargetDoc->GetFirstChild());
@@ -77,60 +75,50 @@ nsHtml5StringParser::ParseDocument(const
 
   mTreeBuilder->setFragmentContext(nullptr,
                                    kNameSpaceID_None,
                                    nullptr,
                                    false);
 
   mTreeBuilder->SetPreventScriptExecution(true);
 
-  Tokenize(aSourceBuffer, aTargetDoc, aScriptingEnabledForNoscriptParsing);
-  return NS_OK;
+  return Tokenize(aSourceBuffer, aTargetDoc, aScriptingEnabledForNoscriptParsing);
 }
 
-void
+nsresult
 nsHtml5StringParser::Tokenize(const nsAString& aSourceBuffer,
                               nsIDocument* aDocument,
                               bool aScriptingEnabledForNoscriptParsing) {
 
   nsIURI* uri = aDocument->GetDocumentURI();
 
-  mExecutor->Init(aDocument, uri, nullptr, nullptr);
+  mBuilder->Init(aDocument, uri, nullptr, nullptr);
 
-  mExecutor->SetParser(this);
-  mExecutor->SetNodeInfoManager(aDocument->NodeInfoManager());
+  mBuilder->SetParser(this);
+  mBuilder->SetNodeInfoManager(aDocument->NodeInfoManager());
 
-  NS_PRECONDITION(!mExecutor->HasStarted(),
-                  "Tried to start parse without initializing the parser.");
+  // Mark the parser as *not* broken by passing NS_OK
+  nsresult rv = mBuilder->MarkAsBroken(NS_OK);
+
   mTreeBuilder->setScriptingEnabled(aScriptingEnabledForNoscriptParsing);
   mTreeBuilder->setIsSrcdocDocument(aDocument->IsSrcdocDocument()); 
+  mBuilder->Start();
   mTokenizer->start();
-  mExecutor->Start(); // Don't call WillBuildModel in fragment case
   if (!aSourceBuffer.IsEmpty()) {
     bool lastWasCR = false;
     nsHtml5DependentUTF16Buffer buffer(aSourceBuffer);
     while (buffer.hasMore()) {
       buffer.adjust(lastWasCR);
       lastWasCR = false;
       if (buffer.hasMore()) {
         lastWasCR = mTokenizer->tokenizeBuffer(&buffer);
-        if (mTreeBuilder->HasScript()) {
-          // If we come here, we are in createContextualFragment() or in the
-          // upcoming document.parse(). It's unclear if it's really necessary
-          // to flush here, but let's do so for consistency with other flushes
-          // to avoid different code paths on the executor side.
-          mTreeBuilder->Flush(); // Move ops to the executor
-          mExecutor->FlushDocumentWrite(); // run the ops
+        if (NS_FAILED(rv = mBuilder->IsBroken())) {
+          break;
         }
       }
     }
   }
   mTokenizer->eof();
-  mTreeBuilder->StreamEnded();
-  mTreeBuilder->Flush();
-  mExecutor->FlushDocumentWrite();
   mTokenizer->end();
-  mExecutor->DropParserAndPerfHint();
-  mExecutor->DropHeldElements();
-  mTreeBuilder->DropHandles();
+  mBuilder->Finish();
   mAtomTable.Clear();
-  mExecutor->Reset();
+  return rv;
 }
--- a/parser/html/nsHtml5StringParser.h
+++ b/parser/html/nsHtml5StringParser.h
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsHtml5StringParser_h
 #define nsHtml5StringParser_h
 
 #include "nsHtml5AtomTable.h"
 #include "nsParserBase.h"
 
-class nsHtml5TreeOpExecutor;
+class nsHtml5OplessBuilder;
 class nsHtml5TreeBuilder;
 class nsHtml5Tokenizer;
 class nsIContent;
 class nsIDocument;
 
 class nsHtml5StringParser : public nsParserBase
 {
   public:
@@ -53,24 +53,24 @@ class nsHtml5StringParser : public nsPar
      *
      */
     nsresult ParseDocument(const nsAString& aSourceBuffer,
                            nsIDocument* aTargetDoc,
                            bool aScriptingEnabledForNoscriptParsing);
 
   private:
 
-    void Tokenize(const nsAString& aSourceBuffer,
-                  nsIDocument* aDocument,
-                  bool aScriptingEnabledForNoscriptParsing);
+    nsresult Tokenize(const nsAString& aSourceBuffer,
+                      nsIDocument* aDocument,
+                      bool aScriptingEnabledForNoscriptParsing);
 
     /**
      * The tree operation executor
      */
-    nsRefPtr<nsHtml5TreeOpExecutor>     mExecutor;
+    nsRefPtr<nsHtml5OplessBuilder>      mBuilder;
 
     /**
      * The HTML5 tree builder
      */
     const nsAutoPtr<nsHtml5TreeBuilder> mTreeBuilder;
 
     /**
      * The HTML5 tokenizer
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -49,16 +49,17 @@
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5StreamParser.h"
 #include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Highlighter.h"
 #include "nsHtml5PlainTextUtils.h"
 #include "nsHtml5ViewSourceUtils.h"
 #include "mozilla/Likely.h"
 #include "nsIContentHandle.h"
+#include "nsHtml5OplessBuilder.h"
 
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
@@ -3894,25 +3895,26 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
   }
   nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_FORM, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
+  nsHtml5HtmlAttributes* clone = attributes->cloneAttributes(nullptr);
   nsIContentHandle* elt = createElement(kNameSpaceID_XHTML, elementName->name, attributes);
   nsHtml5StackNode* current = stack[currentPtr];
   if (current->isFosterParenting()) {
 
     insertIntoFosterParent(elt);
   } else {
     appendElement(elt, current->node);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, attributes->cloneAttributes(nullptr));
+  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, clone);
   push(node);
   append(node);
   node->retain();
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushElement(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
@@ -3940,28 +3942,28 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
   nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, popName);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterMathML(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
   nsIAtom* popName = elementName->name;
+  bool markAsHtmlIntegrationPoint = false;
+  if (nsHtml5ElementName::ELT_ANNOTATION_XML == elementName && annotationXmlEncodingPermitsHtml(attributes)) {
+    markAsHtmlIntegrationPoint = true;
+  }
   nsIContentHandle* elt = createElement(kNameSpaceID_MathML, popName, attributes);
   nsHtml5StackNode* current = stack[currentPtr];
   if (current->isFosterParenting()) {
 
     insertIntoFosterParent(elt);
   } else {
     appendElement(elt, current->node);
   }
-  bool markAsHtmlIntegrationPoint = false;
-  if (nsHtml5ElementName::ELT_ANNOTATION_XML == elementName && annotationXmlEncodingPermitsHtml(attributes)) {
-    markAsHtmlIntegrationPoint = true;
-  }
   nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, popName, markAsHtmlIntegrationPoint);
   push(node);
 }
 
 bool 
 nsHtml5TreeBuilder::annotationXmlEncodingPermitsHtml(nsHtml5HtmlAttributes* attributes)
 {
   nsString* encoding = attributes->getValue(nsHtml5AttributeName::ATTR_ENCODING);
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -50,16 +50,17 @@
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5StreamParser.h"
 #include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Highlighter.h"
 #include "nsHtml5PlainTextUtils.h"
 #include "nsHtml5ViewSourceUtils.h"
 #include "mozilla/Likely.h"
 #include "nsIContentHandle.h"
+#include "nsHtml5OplessBuilder.h"
 
 class nsHtml5StreamParser;
 
 class nsHtml5Tokenizer;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
 class nsHtml5HtmlAttributes;
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -8,23 +8,45 @@
 #include "nsIPresShell.h"
 #include "nsEventDispatcher.h"
 #include "nsNodeUtils.h"
 #include "nsIFrame.h"
 #include "mozilla/Likely.h"
 
 class nsPresContext;
 
+nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder)
+  : scriptingEnabled(false)
+  , fragment(false)
+  , contextNode(nullptr)
+  , formPointer(nullptr)
+  , headPointer(nullptr)
+  , mBuilder(aBuilder)
+  , mViewSource(nullptr)
+  , mOpSink(nullptr)
+  , mHandles(nullptr)
+  , mHandlesUsed(0)
+  , mSpeculativeLoadStage(nullptr)
+  , mCurrentHtmlScriptIsAsyncOrDefer(false)
+  , mPreventScriptExecution(false)
+#ifdef DEBUG
+  , mActive(false)
+#endif
+{
+  MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
+}
+
 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
                                        nsHtml5TreeOpStage* aStage)
   : scriptingEnabled(false)
   , fragment(false)
   , contextNode(nullptr)
   , formPointer(nullptr)
   , headPointer(nullptr)
+  , mBuilder(nullptr)
   , mViewSource(nullptr)
   , mOpSink(aOpSink)
   , mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH])
   , mHandlesUsed(0)
   , mSpeculativeLoadStage(aStage)
   , mCurrentHtmlScriptIsAsyncOrDefer(false)
   , mPreventScriptExecution(false)
 #ifdef DEBUG
@@ -46,16 +68,30 @@ nsHtml5TreeBuilder::createElement(int32_
 {
   NS_PRECONDITION(aAttributes, "Got null attributes.");
   NS_PRECONDITION(aName, "Got null name.");
   NS_PRECONDITION(aNamespace == kNameSpaceID_XHTML || 
                   aNamespace == kNameSpaceID_SVG || 
                   aNamespace == kNameSpaceID_MathML,
                   "Bogus namespace.");
 
+  if (mBuilder) {
+    nsCOMPtr<nsIAtom> name = nsHtml5TreeOperation::Reget(aName);
+    nsIContent* elem =
+      nsHtml5TreeOperation::CreateElement(aNamespace,
+                                          name,
+                                          aAttributes,
+                                          mozilla::dom::FROM_PARSER_FRAGMENT,
+                                          mBuilder);
+    if (aAttributes != nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
+      delete aAttributes;
+    }
+    return elem;
+  }
+
   nsIContentHandle* content = AllocateContentHandle();
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(aNamespace,
                aName,
                aAttributes,
                content,
                !!mSpeculativeLoadStage);
@@ -209,140 +245,257 @@ nsHtml5TreeBuilder::createElement(int32_
   return content;
 }
 
 nsIContentHandle*
 nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes, nsIContentHandle* aFormElement)
 {
   nsIContentHandle* content = createElement(aNamespace, aName, aAttributes);
   if (aFormElement) {
-    nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
-    NS_ASSERTION(treeOp, "Tree op allocation failed.");
-    treeOp->Init(eTreeOpSetFormElement, content, aFormElement);
+    if (mBuilder) {
+      nsHtml5TreeOperation::SetFormElement(static_cast<nsIContent*>(content),
+        static_cast<nsIContent*>(aFormElement));
+    } else {
+      nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+      NS_ASSERTION(treeOp, "Tree op allocation failed.");
+      treeOp->Init(eTreeOpSetFormElement, content, aFormElement);
+    }
   }
   return content;
 }
 
 nsIContentHandle*
 nsHtml5TreeBuilder::createHtmlElementSetAsRoot(nsHtml5HtmlAttributes* aAttributes)
 {
   nsIContentHandle* content = createElement(kNameSpaceID_XHTML, nsHtml5Atoms::html, aAttributes);
-  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
-  NS_ASSERTION(treeOp, "Tree op allocation failed.");
-  treeOp->Init(eTreeOpAppendToDocument, content);
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AppendToDocument(static_cast<nsIContent*>(content),
+                                                         mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+  } else {
+    nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+    NS_ASSERTION(treeOp, "Tree op allocation failed.");
+    treeOp->Init(eTreeOpAppendToDocument, content);
+  }
   return content;
 }
 
 void
 nsHtml5TreeBuilder::detachFromParent(nsIContentHandle* aElement)
 {
   NS_PRECONDITION(aElement, "Null element");
 
+  if (mBuilder) {
+    nsHtml5TreeOperation::Detach(static_cast<nsIContent*>(aElement),
+                                 mBuilder);
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpDetach, aElement);
 }
 
 void
 nsHtml5TreeBuilder::appendElement(nsIContentHandle* aChild, nsIContentHandle* aParent)
 {
   NS_PRECONDITION(aChild, "Null child");
   NS_PRECONDITION(aParent, "Null parent");
   if (deepTreeSurrogateParent) {
     return;
   }
+
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::Append(static_cast<nsIContent*>(aChild),
+                                               static_cast<nsIContent*>(aParent),
+                                               mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpAppend, aChild, aParent);
 }
 
 void
 nsHtml5TreeBuilder::appendChildrenToNewParent(nsIContentHandle* aOldParent, nsIContentHandle* aNewParent)
 {
   NS_PRECONDITION(aOldParent, "Null old parent");
   NS_PRECONDITION(aNewParent, "Null new parent");
 
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AppendChildrenToNewParent(
+      static_cast<nsIContent*>(aOldParent),
+      static_cast<nsIContent*>(aNewParent),
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpAppendChildrenToNewParent, aOldParent, aNewParent);
 }
 
 void
 nsHtml5TreeBuilder::insertFosterParentedCharacters(char16_t* aBuffer, int32_t aStart, int32_t aLength, nsIContentHandle* aTable, nsIContentHandle* aStackParent)
 {
   NS_PRECONDITION(aBuffer, "Null buffer");
   NS_PRECONDITION(aTable, "Null table");
   NS_PRECONDITION(aStackParent, "Null stack parent");
+  MOZ_ASSERT(!aStart, "aStart must always be zero.");
+
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::FosterParentText(
+      static_cast<nsIContent*>(aStackParent),
+      aBuffer, // XXX aStart always ignored???
+      aLength,
+      static_cast<nsIContent*>(aTable),
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
 
   char16_t* bufferCopy = new char16_t[aLength];
   memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
   
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpFosterParentText, bufferCopy, aLength, aStackParent, aTable);
 }
 
 void
 nsHtml5TreeBuilder::insertFosterParentedChild(nsIContentHandle* aChild, nsIContentHandle* aTable, nsIContentHandle* aStackParent)
 {
   NS_PRECONDITION(aChild, "Null child");
   NS_PRECONDITION(aTable, "Null table");
   NS_PRECONDITION(aStackParent, "Null stack parent");
 
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::FosterParent(
+      static_cast<nsIContent*>(aChild),
+      static_cast<nsIContent*>(aStackParent),
+      static_cast<nsIContent*>(aTable),
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpFosterParent, aChild, aStackParent, aTable);
 }
 
 void
 nsHtml5TreeBuilder::appendCharacters(nsIContentHandle* aParent, char16_t* aBuffer, int32_t aStart, int32_t aLength)
 {
   NS_PRECONDITION(aBuffer, "Null buffer");
   NS_PRECONDITION(aParent, "Null parent");
+  MOZ_ASSERT(!aStart, "aStart must always be zero.");
+
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AppendText(
+      aBuffer, // XXX aStart always ignored???
+      aLength,
+      static_cast<nsIContent*>(deepTreeSurrogateParent ?
+                               deepTreeSurrogateParent : aParent),
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
 
   char16_t* bufferCopy = new char16_t[aLength];
   memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
   
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpAppendText, bufferCopy, aLength,
       deepTreeSurrogateParent ? deepTreeSurrogateParent : aParent);
 }
 
 void
 nsHtml5TreeBuilder::appendIsindexPrompt(nsIContentHandle* aParent)
 {
   NS_PRECONDITION(aParent, "Null parent");
 
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AppendIsindexPrompt(
+      static_cast<nsIContent*>(aParent),
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpAppendIsindexPrompt, aParent);
 }
 
 void
 nsHtml5TreeBuilder::appendComment(nsIContentHandle* aParent, char16_t* aBuffer, int32_t aStart, int32_t aLength)
 {
   NS_PRECONDITION(aBuffer, "Null buffer");
   NS_PRECONDITION(aParent, "Null parent");
+  MOZ_ASSERT(!aStart, "aStart must always be zero.");
+
   if (deepTreeSurrogateParent) {
     return;
   }
 
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AppendComment(
+      static_cast<nsIContent*>(aParent),
+      aBuffer, // XXX aStart always ignored???
+      aLength,
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   char16_t* bufferCopy = new char16_t[aLength];
   memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
   
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpAppendComment, bufferCopy, aLength, aParent);
 }
 
 void
 nsHtml5TreeBuilder::appendCommentToDocument(char16_t* aBuffer, int32_t aStart, int32_t aLength)
 {
   NS_PRECONDITION(aBuffer, "Null buffer");
+  MOZ_ASSERT(!aStart, "aStart must always be zero.");
+
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AppendCommentToDocument(
+      aBuffer, // XXX aStart always ignored???
+      aLength,
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
 
   char16_t* bufferCopy = new char16_t[aLength];
   memcpy(bufferCopy, aBuffer, aLength * sizeof(char16_t));
   
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpAppendCommentToDocument, bufferCopy, aLength);
 }
@@ -351,26 +504,43 @@ void
 nsHtml5TreeBuilder::addAttributesToElement(nsIContentHandle* aElement, nsHtml5HtmlAttributes* aAttributes)
 {
   NS_PRECONDITION(aElement, "Null element");
   NS_PRECONDITION(aAttributes, "Null attributes");
 
   if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
     return;
   }
+
+  if (mBuilder) {
+    nsresult rv = nsHtml5TreeOperation::AddAttributes(
+      static_cast<nsIContent*>(aElement),
+      aAttributes,
+      mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(aElement, aAttributes);
 }
 
 void
 nsHtml5TreeBuilder::markMalformedIfScript(nsIContentHandle* aElement)
 {
   NS_PRECONDITION(aElement, "Null element");
 
+  if (mBuilder) {
+    // XXX innerHTML
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpMarkMalformedIfScript, aElement);
 }
 
 void
 nsHtml5TreeBuilder::start(bool fragment)
 {
@@ -390,16 +560,29 @@ nsHtml5TreeBuilder::end()
 #endif
 }
 
 void
 nsHtml5TreeBuilder::appendDoctypeToDocument(nsIAtom* aName, nsString* aPublicId, nsString* aSystemId)
 {
   NS_PRECONDITION(aName, "Null name");
 
+  if (mBuilder) {
+    nsCOMPtr<nsIAtom> name = nsHtml5TreeOperation::Reget(aName);
+    nsresult rv =
+      nsHtml5TreeOperation::AppendDoctypeToDocument(name,
+                                                    *aPublicId,
+                                                    *aSystemId,
+                                                    mBuilder);
+    if (NS_FAILED(rv)) {
+      MarkAsBrokenAndRequestSuspension(rv);
+    }
+    return;
+  }
+
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(aName, *aPublicId, *aSystemId);
   // nsXMLContentSink can flush here, but what's the point?
   // It can also interrupt here, but we can't.
 }
 
 void
@@ -437,36 +620,52 @@ nsHtml5TreeBuilder::elementPushed(int32_
         aName == nsHtml5Atoms::colgroup ||
         aName == nsHtml5Atoms::style)) {
     deepTreeSurrogateParent = aElement;
   }
   if (aNamespace != kNameSpaceID_XHTML) {
     return;
   }
   if (aName == nsHtml5Atoms::body || aName == nsHtml5Atoms::frameset) {
+    if (mBuilder) {
+      // InnerHTML and DOMParser shouldn't start layout anyway
+      return;
+    }
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpStartLayout);
     return;
   }
   if (aName == nsHtml5Atoms::input ||
       aName == nsHtml5Atoms::button) {
     if (!formPointer) {
       // If form inputs don't belong to a form, their state preservation
       // won't work right without an append notification flush at this
       // point. See bug 497861.
-      mOpQueue.AppendElement()->Init(eTreeOpFlushPendingAppendNotifications);
+      if (mBuilder) {
+        mBuilder->FlushPendingAppendNotifications();
+      } else {
+        mOpQueue.AppendElement()->Init(eTreeOpFlushPendingAppendNotifications);
+      }
     }
-    mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement);
+    if (mBuilder) {
+      nsHtml5TreeOperation::DoneCreatingElement(static_cast<nsIContent*>(aElement));
+    } else {
+      mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement);
+    }
     return;
   }
   if (aName == nsHtml5Atoms::audio ||
       aName == nsHtml5Atoms::video ||
       aName == nsHtml5Atoms::menuitem) {
-    mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement);
+    if (mBuilder) {
+      nsHtml5TreeOperation::DoneCreatingElement(static_cast<nsIContent*>(aElement));
+    } else {
+      mOpQueue.AppendElement()->Init(eTreeOpDoneCreatingElement, aElement);
+    }
     return;
   }
 }
 
 void
 nsHtml5TreeBuilder::elementPopped(int32_t aNamespace, nsIAtom* aName, nsIContentHandle* aElement)
 {
   NS_ASSERTION(aNamespace == kNameSpaceID_XHTML || aNamespace == kNameSpaceID_SVG || aNamespace == kNameSpaceID_MathML, "Element isn't HTML, SVG or MathML!");
@@ -476,81 +675,116 @@ nsHtml5TreeBuilder::elementPopped(int32_
     deepTreeSurrogateParent = nullptr;
   }
   if (aNamespace == kNameSpaceID_MathML) {
     return;
   }
   // we now have only SVG and HTML
   if (aName == nsHtml5Atoms::script) {
     if (mPreventScriptExecution) {
+      if (mBuilder) {
+        nsHtml5TreeOperation::PreventScriptExecution(static_cast<nsIContent*>(aElement));
+        return;
+      }
       mOpQueue.AppendElement()->Init(eTreeOpPreventScriptExecution, aElement);
       return;
     }
+    if (mBuilder) {
+      return;
+    }
     if (mCurrentHtmlScriptIsAsyncOrDefer) {
       NS_ASSERTION(aNamespace == kNameSpaceID_XHTML, 
                    "Only HTML scripts may be async/defer.");
       nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
       NS_ASSERTION(treeOp, "Tree op allocation failed.");
       treeOp->Init(eTreeOpRunScriptAsyncDefer, aElement);      
       mCurrentHtmlScriptIsAsyncOrDefer = false;
       return;
     }
     requestSuspension();
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->InitScript(aElement);
     return;
   }
   if (aName == nsHtml5Atoms::title) {
+    if (mBuilder) {
+      nsHtml5TreeOperation::DoneAddingChildren(static_cast<nsIContent*>(aElement), mBuilder);
+      return;
+    }
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpDoneAddingChildren, aElement);
     return;
   }
   if (aName == nsHtml5Atoms::style || (aNamespace == kNameSpaceID_XHTML && aName == nsHtml5Atoms::link)) {
+    if (mBuilder) {
+      MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
+        "Scripts must be blocked.");
+      mBuilder->FlushPendingAppendNotifications();
+      mBuilder->UpdateStyleSheet(static_cast<nsIContent*>(aElement));
+      return;
+    }
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpUpdateStyleSheet, aElement);
     return;
   }
   if (aNamespace == kNameSpaceID_SVG) {
+    if (mBuilder) {
+      // XXX innerHTML
+      // is this ever needed for the on-the-main-thread case
+      return;
+    }
     if (aName == nsHtml5Atoms::svg) {
       nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
       NS_ASSERTION(treeOp, "Tree op allocation failed.");
       treeOp->Init(eTreeOpSvgLoad, aElement);
     }
     return;
   }
   // we now have only HTML
   // Some HTML nodes need DoneAddingChildren() called to initialize
   // properly (e.g. form state restoration).
   // XXX expose ElementName group here and do switch
   if (aName == nsHtml5Atoms::object ||
       aName == nsHtml5Atoms::applet) {
+    if (mBuilder) {
+      nsHtml5TreeOperation::DoneAddingChildren(static_cast<nsIContent*>(aElement), mBuilder);
+      return;
+    }
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpDoneAddingChildren, aElement);
     return;
   }
   if (aName == nsHtml5Atoms::select || 
       aName == nsHtml5Atoms::textarea) {
     if (!formPointer) {
       // If form inputs don't belong to a form, their state preservation
       // won't work right without an append notification flush at this 
       // point. See bug 497861 and bug 539895.
-      nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
-      NS_ASSERTION(treeOp, "Tree op allocation failed.");
-      treeOp->Init(eTreeOpFlushPendingAppendNotifications);
+      if (mBuilder) {
+        mBuilder->FlushPendingAppendNotifications();
+      } else {
+        nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+        NS_ASSERTION(treeOp, "Tree op allocation failed.");
+        treeOp->Init(eTreeOpFlushPendingAppendNotifications);
+      }
+    }
+    if (mBuilder) {
+      nsHtml5TreeOperation::DoneAddingChildren(static_cast<nsIContent*>(aElement), mBuilder);
+      return;
     }
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpDoneAddingChildren, aElement);
     return;
   }
-  if (aName == nsHtml5Atoms::meta && !fragment) {
+  if (aName == nsHtml5Atoms::meta && !fragment && !mBuilder) {
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpProcessMeta, aElement);
     return;
   }
   return;
 }
 
@@ -566,16 +800,20 @@ nsHtml5TreeBuilder::accumulateCharacters
   }
   memcpy(charBuffer + charBufferLen, aBuf + aStart, sizeof(char16_t) * aLength);
   charBufferLen = newFillLen;
 }
 
 nsIContentHandle*
 nsHtml5TreeBuilder::AllocateContentHandle()
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must never allocate a handle with builder.");
+    return nullptr;
+  }
   if (mHandlesUsed == NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH) {
     mOldHandles.AppendElement(mHandles.forget());
     mHandles = new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH];
     mHandlesUsed = 0;
   }
 #ifdef DEBUG
   mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD;
 #endif
@@ -590,16 +828,20 @@ nsHtml5TreeBuilder::HasScript()
     return false;
   }
   return mOpQueue.ElementAt(len - 1).IsRunScript();
 }
 
 bool
 nsHtml5TreeBuilder::Flush(bool aDiscretionary)
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must never flush with builder.");
+    return false;
+  }
   if (!aDiscretionary ||
       !(charBufferLen &&
         currentPtr >= 0 &&
         stack[currentPtr]->isFosterParenting())) {
     // Don't flush text on discretionary flushes if the current element on
     // the stack is a foster-parenting element and there's pending text,
     // because flushing in that case would make the tree shape dependent on
     // where the flush points fall.
@@ -616,95 +858,114 @@ nsHtml5TreeBuilder::Flush(bool aDiscreti
   // no op sink: throw away ops
   mOpQueue.Clear();
   return false;
 }
 
 void
 nsHtml5TreeBuilder::FlushLoads()
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must never flush loads with builder.");
+    return;
+  }
   if (!mSpeculativeLoadQueue.IsEmpty()) {
     mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue);
   }
 }
 
 void
 nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset, 
                                        int32_t aCharsetSource)
 {
-  if (mSpeculativeLoadStage) {
+  if (mBuilder) {
+    mBuilder->SetDocumentCharsetAndSource(aCharset, aCharsetSource);
+  } else if (mSpeculativeLoadStage) {
     mSpeculativeLoadQueue.AppendElement()->InitSetDocumentCharset(
       aCharset, aCharsetSource);
   } else {
     mOpQueue.AppendElement()->Init(
       eTreeOpSetDocumentCharset, aCharset, aCharsetSource);
   }
 }
 
 void
 nsHtml5TreeBuilder::StreamEnded()
 {
-  // The fragment mode calls DidBuildModel from nsHtml5Parser. 
-  // Letting DidBuildModel be called from the executor in the fragment case
-  // confuses the EndLoad logic of nsHTMLDocument, since nsHTMLDocument
-  // thinks it is dealing with document.written content as opposed to 
-  // innerHTML content.
-  if (!fragment) {
-    nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
-    NS_ASSERTION(treeOp, "Tree op allocation failed.");
-    treeOp->Init(eTreeOpStreamEnded);
-  }
+  MOZ_ASSERT(!mBuilder, "Must not call StreamEnded with builder.");
+  MOZ_ASSERT(!fragment, "Must not parse fragments off the main thread.");
+  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+  NS_ASSERTION(treeOp, "Tree op allocation failed.");
+  treeOp->Init(eTreeOpStreamEnded);
 }
 
 void
 nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset,
                                          int32_t aCharsetSource,
                                          int32_t aLineNumber)
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must never switch charset with builder.");
+    return;
+  }
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(eTreeOpNeedsCharsetSwitchTo,
                aCharset,
                aCharsetSource,
                aLineNumber);
 }
 
 void
 nsHtml5TreeBuilder::MaybeComplainAboutCharset(const char* aMsgId,
                                               bool aError,
                                               int32_t aLineNumber)
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must never complain about charset with builder.");
+    return;
+  }
   mOpQueue.AppendElement()->Init(aMsgId, aError, aLineNumber);
 }
 
 void
 nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine)
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must never use snapshots with builder.");
+    return;
+  }
   NS_PRECONDITION(HasScript(), "No script to add a snapshot to!");
   NS_PRECONDITION(aSnapshot, "Got null snapshot.");
   mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
 }
 
 void
 nsHtml5TreeBuilder::DropHandles()
 {
+  MOZ_ASSERT(!mBuilder, "Must not drop handles with builder.");
   mOldHandles.Clear();
   mHandlesUsed = 0;
 }
 
 void
 nsHtml5TreeBuilder::MarkAsBroken()
 {
+  if (MOZ_UNLIKELY(mBuilder)) {
+    MOZ_ASSUME_UNREACHABLE("Must not call this with builder.");
+    return;
+  }
   mOpQueue.Clear(); // Previous ops don't matter anymore
   mOpQueue.AppendElement()->Init(eTreeOpMarkAsBroken);
 }
 
 void
 nsHtml5TreeBuilder::StartPlainTextViewSource(const nsAutoString& aTitle)
 {
+  MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
   startTag(nsHtml5ElementName::ELT_TITLE,
            nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
            false);
 
   // XUL will add the "Source of: " prefix.
   uint32_t length = aTitle.Length();
   if (length > INT32_MAX) {
     length = INT32_MAX;
@@ -721,63 +982,73 @@ nsHtml5TreeBuilder::StartPlainTextViewSo
            false);
 
   StartPlainTextBody();
 }
 
 void
 nsHtml5TreeBuilder::StartPlainText()
 {
+  MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
   startTag(nsHtml5ElementName::ELT_LINK,
            nsHtml5PlainTextUtils::NewLinkAttributes(),
            false);
 
   StartPlainTextBody();
 }
 
 void
 nsHtml5TreeBuilder::StartPlainTextBody()
 {
+  MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
   startTag(nsHtml5ElementName::ELT_PRE,
            nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES,
            false);
   needToDropLF = false;
 }
 
 // DocumentModeHandler
 void
 nsHtml5TreeBuilder::documentMode(nsHtml5DocumentMode m)
 {
+  if (mBuilder) {
+    mBuilder->SetDocumentMode(m);
+    return;
+  }
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   treeOp->Init(m);
 }
 
 nsIContentHandle*
 nsHtml5TreeBuilder::getDocumentFragmentForTemplate(nsIContentHandle* aTemplate)
 {
+  if (mBuilder) {
+    return nsHtml5TreeOperation::GetDocumentFragmentForTemplate(static_cast<nsIContent*>(aTemplate));
+  }
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
   nsIContentHandle* fragHandle = AllocateContentHandle();
   treeOp->Init(eTreeOpGetDocumentFragmentForTemplate, aTemplate, fragHandle);
   return fragHandle;
 }
 
 nsIContentHandle*
 nsHtml5TreeBuilder::getFormPointerForContext(nsIContentHandle* aContext)
 {
+  MOZ_ASSERT(mBuilder, "Must have builder.");
   if (!aContext) {
     return nullptr;
   }
 
   MOZ_ASSERT(NS_IsMainThread());
 
-  // aContext must always be a handle to an element that already exists
-  // in the document. It must never be an empty handle.
-  nsIContent* contextNode = *(static_cast<nsIContent**>(aContext));
+  // aContext must always be an element that already exists
+  // in the document.
+  nsIContent* contextNode = static_cast<nsIContent*>(aContext);
   nsIContent* currentAncestor = contextNode;
 
   // We traverse the ancestors of the context node to find the nearest
   // form pointer. This traversal is why aContext must not be an emtpy handle.
   nsIContent* nearestForm = nullptr;
   while (currentAncestor) {
     if (currentAncestor->IsHTML(nsGkAtoms::form)) {
       nearestForm = currentAncestor;
@@ -785,26 +1056,25 @@ nsHtml5TreeBuilder::getFormPointerForCon
     }
     currentAncestor = currentAncestor->GetParent();
   }
 
   if (!nearestForm) {
     return nullptr;
   }
 
-  nsIContentHandle* formPointer = AllocateContentHandle();
-  *(static_cast<nsIContent**>(formPointer)) = nearestForm;
-  return formPointer;
+  return nearestForm;
 }
 
 // Error reporting
 
 void
 nsHtml5TreeBuilder::EnableViewSource(nsHtml5Highlighter* aHighlighter)
 {
+  MOZ_ASSERT(!mBuilder, "Must not view source with builder.");
   mViewSource = aHighlighter;
 }
 
 void
 nsHtml5TreeBuilder::errStrayStartTag(nsIAtom* aName)
 {
   if (MOZ_UNLIKELY(mViewSource)) {
     mViewSource->AddErrorToCurrentRun("errStrayStartTag2", aName);
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -1,24 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #define NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH 512
 
   private:
+    nsHtml5OplessBuilder*                  mBuilder;
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    // If mBuilder is not null, the tree op machinery is not in use and
+    // the fields below aren't in use, either.
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     nsHtml5Highlighter*                    mViewSource;
     nsTArray<nsHtml5TreeOperation>         mOpQueue;
     nsTArray<nsHtml5SpeculativeLoad>       mSpeculativeLoadQueue;
     nsAHtml5TreeOpSink*                    mOpSink;
     nsAutoArrayPtr<nsIContent*>            mHandles;
     int32_t                                mHandlesUsed;
     nsTArray<nsAutoArrayPtr<nsIContent*> > mOldHandles;
     nsHtml5TreeOpStage*                    mSpeculativeLoadStage;
-    nsIContent**                           mDeepTreeSurrogateParent;
     bool                                   mCurrentHtmlScriptIsAsyncOrDefer;
     bool                                   mPreventScriptExecution;
 #ifdef DEBUG
     bool                                   mActive;
 #endif
 
     // DocumentModeHandler
     /**
@@ -55,18 +59,26 @@
      */
     nsIContentHandle* AllocateContentHandle();
     
     void accumulateCharactersForced(const char16_t* aBuf, int32_t aStart, int32_t aLength)
     {
       accumulateCharacters(aBuf, aStart, aLength);
     }
 
+    void MarkAsBrokenAndRequestSuspension(nsresult aRv)
+    {
+      mBuilder->MarkAsBroken(aRv);
+      requestSuspension();
+    }
+
   public:
 
+    nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder);
+
     nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
                        nsHtml5TreeOpStage* aStage);
 
     ~nsHtml5TreeBuilder();
     
     void StartPlainTextViewSource(const nsAutoString& aTitle);
 
     void StartPlainText();
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -8,18 +8,16 @@
 #include "mozilla/Likely.h"
 
 #include "nsError.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsScriptLoader.h"
 #include "nsIMarkupDocumentViewer.h"
 #include "nsIContentViewer.h"
 #include "nsIDocShellTreeItem.h"
-#include "nsIStyleSheetLinkingElement.h"
-#include "nsStyleLinkElement.h"
 #include "nsIDocShell.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIWebShellServices.h"
 #include "nsContentUtils.h"
 #include "mozAutoDocUpdate.h"
 #include "nsNetUtil.h"
 #include "nsHtml5Parser.h"
@@ -57,20 +55,20 @@ class nsHtml5ExecutorReflusher : public 
       mExecutor->RunFlushLoop();
       return NS_OK;
     }
 };
 
 static mozilla::LinkedList<nsHtml5TreeOpExecutor>* gBackgroundFlushList = nullptr;
 static nsITimer* gFlushTimer = nullptr;
 
-nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor(bool aRunsToCompletion)
-  : mPreloadedURLs(23)  // Mean # of preloadable resources per page on dmoz
+nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
+  : nsHtml5DocumentBuilder(false)
+  , mPreloadedURLs(23)  // Mean # of preloadable resources per page on dmoz
 {
-  mRunsToCompletion = aRunsToCompletion;
   // zeroing operator new for everything else
 }
 
 nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
 {
   if (gBackgroundFlushList && isInList()) {
     mOpQueue.Clear();
     removeFrom(*gBackgroundFlushList);
@@ -205,44 +203,26 @@ void
 nsHtml5TreeOpExecutor::FlushPendingNotifications(mozFlushType aType)
 {
   if (aType >= Flush_InterruptibleLayout) {
     // Bug 577508 / 253951
     nsContentSink::StartLayout(true);
   }
 }
 
-void
-nsHtml5TreeOpExecutor::SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource)
-{
-  if (mDocument) {
-    mDocument->SetDocumentCharacterSetSource(aCharsetSource);
-    mDocument->SetDocumentCharacterSet(aCharset);
-  }
-}
-
 nsISupports*
 nsHtml5TreeOpExecutor::GetTarget()
 {
   return mDocument;
 }
 
-// nsContentSink overrides
-
-void
-nsHtml5TreeOpExecutor::UpdateChildCounts()
-{
-  // No-op
-}
-
 nsresult
 nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!mRunsToCompletion, "Fragment parsers can't be broken!");
   mBroken = aReason;
   if (mStreamParser) {
     mStreamParser->Terminate();
   }
   // We are under memory pressure, but let's hope the following allocation
   // works out so that we get to terminate and clean up the parser from
   // a safer point.
   if (mParser) { // can mParser ever be null here?
@@ -250,22 +230,16 @@ nsHtml5TreeOpExecutor::MarkAsBroken(nsre
       NS_NewRunnableMethod(GetParser(), &nsHtml5Parser::Terminate);
     if (NS_FAILED(NS_DispatchToMainThread(terminator))) {
       NS_WARNING("failed to dispatch executor flush event");
     }
   }
   return aReason;
 }
 
-nsresult
-nsHtml5TreeOpExecutor::FlushTags()
-{
-  return NS_OK;
-}
-
 void
 FlushTimerCallback(nsITimer* aTimer, void* aClosure)
 {
   nsRefPtr<nsHtml5TreeOpExecutor> ex = gBackgroundFlushList->popFirst();
   if (ex) {
     ex->RunFlushLoop();
   }
   if (gBackgroundFlushList && gBackgroundFlushList->isEmpty()) {
@@ -299,71 +273,16 @@ nsHtml5TreeOpExecutor::ContinueInterrupt
       // See bug 734015.
       gFlushTimer->InitWithFuncCallback(FlushTimerCallback, nullptr,
                                         50, nsITimer::TYPE_REPEATING_SLACK);
     }
   }
 }
 
 void
-nsHtml5TreeOpExecutor::UpdateStyleSheet(nsIContent* aElement)
-{
-  // Break out of the doc update created by Flush() to zap a runnable 
-  // waiting to call UpdateStyleSheet without the right observer
-  EndDocUpdate();
-
-  if (MOZ_UNLIKELY(!mParser)) {
-    // EndDocUpdate ran stuff that called nsIParser::Terminate()
-    return;
-  }
-
-  nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aElement));
-  NS_ASSERTION(ssle, "Node didn't QI to style.");
-
-  ssle->SetEnableUpdates(true);
-
-  bool willNotify;
-  bool isAlternate;
-  nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this,
-                                       &willNotify,
-                                       &isAlternate);
-  if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) {
-    ++mPendingSheetCount;
-    mScriptLoader->AddExecuteBlocker();
-  }
-
-  if (aElement->IsHTML(nsGkAtoms::link)) {
-    // look for <link rel="next" href="url">
-    nsAutoString relVal;
-    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
-    if (!relVal.IsEmpty()) {
-      uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(relVal);
-      bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
-      if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
-        nsAutoString hrefVal;
-        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
-        if (!hrefVal.IsEmpty()) {
-          PrefetchHref(hrefVal, aElement, hasPrefetch);
-        }
-      }
-      if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) {
-        nsAutoString hrefVal;
-        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
-        if (!hrefVal.IsEmpty()) {
-          PrefetchDNS(hrefVal);
-        }
-      }
-    }
-  }
-
-  // Re-open update
-  BeginDocUpdate();
-}
-
-void
 nsHtml5TreeOpExecutor::FlushSpeculativeLoads()
 {
   nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
   mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
   const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
   const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
   for (nsHtml5SpeculativeLoad* iter = const_cast<nsHtml5SpeculativeLoad*>(start);
        iter < end;
@@ -661,36 +580,16 @@ nsHtml5TreeOpExecutor::IsScriptEnabled()
     NS_ENSURE_TRUE(globalObject, true);
   }
   NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), true);
   return nsContentUtils::GetSecurityManager()->
            ScriptAllowed(globalObject->GetGlobalJSObject());
 }
 
 void
-nsHtml5TreeOpExecutor::SetDocumentMode(nsHtml5DocumentMode m)
-{
-  nsCompatibility mode = eCompatibility_NavQuirks;
-  switch (m) {
-    case STANDARDS_MODE:
-      mode = eCompatibility_FullStandards;
-      break;
-    case ALMOST_STANDARDS_MODE:
-      mode = eCompatibility_AlmostStandards;
-      break;
-    case QUIRKS_MODE:
-      mode = eCompatibility_NavQuirks;
-      break;
-  }
-  nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(mDocument);
-  NS_ASSERTION(htmlDocument, "Document didn't QI into HTML document.");
-  htmlDocument->SetCompatibilityMode(mode);
-}
-
-void
 nsHtml5TreeOpExecutor::StartLayout() {
   if (mLayoutStarted || !mDocument) {
     return;
   }
 
   EndDocUpdate();
 
   if (MOZ_UNLIKELY(!mParser)) {
@@ -762,25 +661,16 @@ nsHtml5TreeOpExecutor::RunScript(nsICont
     // mParser may have been nulled out by now, but the flusher deals
 
     // If this event isn't needed, it doesn't do anything. It is sometimes
     // necessary for the parse to continue after complex situations.
     nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
   }
 }
 
-nsresult
-nsHtml5TreeOpExecutor::Init(nsIDocument* aDoc,
-                            nsIURI* aURI,
-                            nsISupports* aContainer,
-                            nsIChannel* aChannel)
-{
-  return nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
-}
-
 void
 nsHtml5TreeOpExecutor::Start()
 {
   NS_PRECONDITION(!mStarted, "Tried to start when already started.");
   mStarted = true;
 }
 
 void
@@ -878,29 +768,16 @@ nsHtml5TreeOpExecutor::ComplainAboutBogu
 nsHtml5Parser*
 nsHtml5TreeOpExecutor::GetParser()
 {
   MOZ_ASSERT(!mRunsToCompletion);
   return static_cast<nsHtml5Parser*>(mParser.get());
 }
 
 void
-nsHtml5TreeOpExecutor::Reset()
-{
-  MOZ_ASSERT(mRunsToCompletion);
-  DropHeldElements();
-  mOpQueue.Clear();
-  mStarted = false;
-  mFlushState = eNotFlushing;
-  mRunFlushLoopOnStack = false;
-  MOZ_ASSERT(!mReadingFromStage);
-  MOZ_ASSERT(NS_SUCCEEDED(mBroken));
-}
-
-void
 nsHtml5TreeOpExecutor::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
 {
   NS_PRECONDITION(mFlushState == eNotFlushing, "mOpQueue modified during tree op execution.");
   if (mOpQueue.IsEmpty()) {
     mOpQueue.SwapElements(aOpQueue);
     return;
   }
   mOpQueue.MoveElementsFrom(aOpQueue);
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -76,37 +76,24 @@ class nsHtml5TreeOpExecutor : public nsH
 
     nsHtml5TreeOpStage            mStage;
 
     bool                          mRunFlushLoopOnStack;
 
     bool                          mCallContinueInterruptedParsingIfEnabled;
 
     /**
-     * Non-NS_OK if this parser should refuse to process any more input.
-     * For example, the parser needs to be marked as broken if it drops some
-     * input due to a memory allocation failure. In such a case, the whole
-     * parser needs to be marked as broken, because some input has been lost
-     * and parsing more input could lead to a DOM where pieces of HTML source
-     * that weren't supposed to become scripts become scripts.
-     *
-     * Since NS_OK is actually 0, zeroing operator new takes care of
-     * initializing this.
-     */
-    nsresult                      mBroken;
-
-    /**
      * Whether this executor has already complained about matters related
      * to character encoding declarations.
      */
     bool                          mAlreadyComplainedAboutCharset;
 
   public:
   
-    nsHtml5TreeOpExecutor(bool aRunsToCompletion = false);
+    nsHtml5TreeOpExecutor();
     virtual ~nsHtml5TreeOpExecutor();
   
     // nsIContentSink
 
     /**
      * Unimplemented. For interface compat only.
      */
     NS_IMETHOD WillParse();
@@ -149,95 +136,41 @@ class nsHtml5TreeOpExecutor : public nsH
     	return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     /**
      * Returns the document.
      */
     virtual nsISupports *GetTarget();
   
-    // nsContentSink methods
-    virtual void UpdateChildCounts();
-    virtual nsresult FlushTags();
     virtual void ContinueInterruptedParsingAsync();
  
-    /**
-     * Sets up style sheet load / parse
-     */
-    void UpdateStyleSheet(nsIContent* aElement);
-
     // XXX Does anyone need this?
     nsIDocShell* GetDocShell() {
       return mDocShell;
     }
 
     bool IsScriptExecuting() {
       return IsScriptExecutingImpl();
     }
-    
-    void SetNodeInfoManager(nsNodeInfoManager* aManager) {
-      mNodeInfoManager = aManager;
-    }
-    
+
     // Not from interface
 
-    void SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource);
-
     void SetStreamParser(nsHtml5StreamParser* aStreamParser) {
       mStreamParser = aStreamParser;
     }
     
     void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, int32_t aLine);
 
     bool IsScriptEnabled();
 
-    bool BelongsToStringParser() {
-      return mRunsToCompletion;
-    }
-
-    /**
-     * Marks this parser as broken and tells the stream parser (if any) to
-     * terminate.
-     *
-     * @return aReason for convenience
-     */
-    nsresult MarkAsBroken(nsresult aReason);
+    virtual nsresult MarkAsBroken(nsresult aReason);
 
-    /**
-     * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0)
-     * value if broken.
-     */
-    inline nsresult IsBroken() {
-      return mBroken;
-    }
-
-    inline void BeginDocUpdate() {
-      NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update.");
-      NS_PRECONDITION(mParser, "Started update without parser.");
-      mFlushState = eInDocUpdate;
-      mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
-    }
-
-    inline void EndDocUpdate() {
-      NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync");
-      if (mFlushState == eInDocUpdate) {
-        FlushPendingAppendNotifications();
-        mFlushState = eInFlush;
-        mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
-      }
-    }
-
-    
     void StartLayout();
     
-    void SetDocumentMode(nsHtml5DocumentMode m);
-
-    nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
-                  nsISupports* aContainer, nsIChannel* aChannel);
-
     void FlushSpeculativeLoads();
                   
     void RunFlushLoop();
 
     void FlushDocumentWrite();
 
     void MaybeSuspend();
 
@@ -268,18 +201,16 @@ class nsHtml5TreeOpExecutor : public nsH
 #ifdef DEBUG
     bool IsInFlushLoop() {
       return mRunFlushLoopOnStack;
     }
 #endif
     
     void RunScript(nsIContent* aScriptElement);
     
-    void Reset();
-    
     /**
      * Flush the operations from the tree operations from the argument
      * queue unconditionally. (This is for the main thread case.)
      */
     virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
     
     nsHtml5TreeOpStage* GetStage() {
       return &mStage;
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -339,35 +339,31 @@ nsHtml5TreeOperation::AddAttributes(nsIC
   return NS_OK;
 }
 
 
 nsIContent*
 nsHtml5TreeOperation::CreateElement(int32_t aNs,
                                     nsIAtom* aName,
                                     nsHtml5HtmlAttributes* aAttributes,
-                                    bool aFromNetwork,
+                                    mozilla::dom::FromParser aFromParser,
                                     nsHtml5DocumentBuilder* aBuilder)
 {
   bool isKeygen = (aName == nsHtml5Atoms::keygen && aNs == kNameSpaceID_XHTML);
   if (MOZ_UNLIKELY(isKeygen)) {
     aName = nsHtml5Atoms::select;
   }
 
   nsCOMPtr<dom::Element> newContent;
   nsCOMPtr<nsINodeInfo> nodeInfo = aBuilder->GetNodeInfoManager()->
     GetNodeInfo(aName, nullptr, aNs, nsIDOMNode::ELEMENT_NODE);
   NS_ASSERTION(nodeInfo, "Got null nodeinfo.");
   NS_NewElement(getter_AddRefs(newContent),
                 nodeInfo.forget(),
-                (aFromNetwork ?
-                 dom::FROM_PARSER_NETWORK
-                 : (aBuilder->BelongsToStringParser() ?
-                    dom::FROM_PARSER_FRAGMENT :
-                    dom::FROM_PARSER_DOCUMENT_WRITE)));
+                aFromParser);
   NS_ASSERTION(newContent, "Element creation created null pointer.");
 
   aBuilder->HoldElement(newContent);
 
   if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
     nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
     if (ssle) {
       ssle->InitStyleLinkElement(false);
@@ -401,21 +397,17 @@ nsHtml5TreeOperation::CreateElement(int3
                                                   kNameSpaceID_XHTML,
                                                   nsIDOMNode::ELEMENT_NODE);
 
     for (uint32_t i = 0; i < theContent.Length(); ++i) {
       nsCOMPtr<dom::Element> optionElt;
       nsCOMPtr<nsINodeInfo> ni = optionNodeInfo;
       NS_NewElement(getter_AddRefs(optionElt),
                     ni.forget(),
-                    (aFromNetwork ?
-                     dom::FROM_PARSER_NETWORK
-                     : (aBuilder->BelongsToStringParser() ?
-                        dom::FROM_PARSER_FRAGMENT :
-                        dom::FROM_PARSER_DOCUMENT_WRITE)));
+                    aFromParser);
       nsRefPtr<nsTextNode> optionText =
         new nsTextNode(aBuilder->GetNodeInfoManager());
       (void) optionText->SetText(theContent[i], false);
       optionElt->AppendChildTo(optionText, false);
       newContent->AppendChildTo(optionElt, false);
       newContent->DoneAddingChildren(false);
     }
   }
@@ -669,17 +661,19 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
       nsIContent** target = mOne.node;
       int32_t ns = mFour.integer;
       nsCOMPtr<nsIAtom> name = Reget(mTwo.atom);
       nsHtml5HtmlAttributes* attributes = mThree.attributes;
 
       *target = CreateElement(ns,
                               name,
                               attributes,
-                              mOpCode == eTreeOpCreateElementNetwork,
+                              mOpCode == eTreeOpCreateElementNetwork ?
+                                dom::FROM_PARSER_NETWORK :
+                                dom::FROM_PARSER_DOCUMENT_WRITE,
                               aBuilder);
       return NS_OK;
     }
     case eTreeOpSetFormElement: {
       nsIContent* node = *(mOne.node);
       nsIContent* parent = *(mTwo.node);
       SetFormElement(node, parent);
       return NS_OK;
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
 #ifndef nsHtml5TreeOperation_h
 #define nsHtml5TreeOperation_h
 
 #include "nsHtml5DocumentMode.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsXPCOMStrings.h"
+#include "mozilla/dom/FromParser.h"
 
 class nsIContent;
 class nsHtml5TreeOpExecutor;
 class nsHtml5StateSnapshot;
 class nsHtml5DocumentBuilder;
 
 enum eHtml5TreeOperation {
 #ifdef DEBUG
@@ -141,17 +142,17 @@ class nsHtml5TreeOperation {
 
     static nsresult AddAttributes(nsIContent* aNode,
                                   nsHtml5HtmlAttributes* aAttributes,
                                   nsHtml5DocumentBuilder* aBuilder);
 
     static nsIContent* CreateElement(int32_t aNs,
                                      nsIAtom* aName,
                                      nsHtml5HtmlAttributes* aAttributes,
-                                     bool aFromNetwork,
+                                     mozilla::dom::FromParser aFromParser,
                                      nsHtml5DocumentBuilder* aBuilder);
 
     static void SetFormElement(nsIContent* aNode, nsIContent* aParent);
 
     static nsresult AppendIsindexPrompt(nsIContent* parent,
                                         nsHtml5DocumentBuilder* aBuilder);
 
     static nsresult FosterParentText(nsIContent* aStackParent,