Mozilla bug 256180 - Insert elements as siblings instead of children at the Blink-defined magic depth for compatibility. r=smaug.
authorHenri Sivonen <hsivonen@hsivonen.fi>
Thu, 13 Dec 2018 15:20:53 +0200
changeset 882 209cca99a3a2fb569ba3b5b904dfac32c314049a
parent 881 96e37ec9e2cdfc39479ea221c9c7e3a46a5ad9ca
child 883 0f79492cd9642591d5b07bc79a2926c7bb5292c8
push id197
push userhsivonen@mozilla.com
push dateMon, 14 Jan 2019 10:48:38 +0000
reviewerssmaug
bugs256180
Mozilla bug 256180 - Insert elements as siblings instead of children at the Blink-defined magic depth for compatibility. r=smaug.
src/nu/validator/htmlparser/impl/StateSnapshot.java
src/nu/validator/htmlparser/impl/TreeBuilder.java
src/nu/validator/htmlparser/impl/TreeBuilderState.java
--- a/src/nu/validator/htmlparser/impl/StateSnapshot.java
+++ b/src/nu/validator/htmlparser/impl/StateSnapshot.java
@@ -32,18 +32,16 @@ public class StateSnapshot<T> implements
     private final @Auto StackNode<T>[] listOfActiveFormattingElements;
 
     private final @Auto int[] templateModeStack;
 
     private final T formPointer;
 
     private final T headPointer;
 
-    private final T deepTreeSurrogateParent;
-
     private final int mode;
 
     private final int originalMode;
     
     private final boolean framesetOk;
 
     private final boolean needToDropLF;
 
@@ -59,24 +57,23 @@ public class StateSnapshot<T> implements
      * @param mode
      * @param originalMode
      * @param framesetOk
      * @param needToDropLF
      * @param quirks
      */
     StateSnapshot(StackNode<T>[] stack,
             StackNode<T>[] listOfActiveFormattingElements, int[] templateModeStack, T formPointer,
-            T headPointer, T deepTreeSurrogateParent, int mode, int originalMode,
+            T headPointer, int mode, int originalMode,
             boolean framesetOk, boolean needToDropLF, boolean quirks) {
         this.stack = stack;
         this.listOfActiveFormattingElements = listOfActiveFormattingElements;
         this.templateModeStack = templateModeStack;
         this.formPointer = formPointer;
         this.headPointer = headPointer;
-        this.deepTreeSurrogateParent = deepTreeSurrogateParent;
         this.mode = mode;
         this.originalMode = originalMode;
         this.framesetOk = framesetOk;
         this.needToDropLF = needToDropLF;
         this.quirks = quirks;
     }
     
     /**
@@ -117,26 +114,16 @@ public class StateSnapshot<T> implements
      * @return the headPointer
      */
     @Override
     public T getHeadPointer() {
         return headPointer;
     }
 
     /**
-     * Returns the deepTreeSurrogateParent.
-     * 
-     * @return the deepTreeSurrogateParent
-     */
-    @Override
-    public T getDeepTreeSurrogateParent() {
-        return deepTreeSurrogateParent;
-    }
-    
-    /**
      * Returns the mode.
      * 
      * @return the mode
      */
     @Override
     public int getMode() {
         return mode;
     }
--- a/src/nu/validator/htmlparser/impl/TreeBuilder.java
+++ b/src/nu/validator/htmlparser/impl/TreeBuilder.java
@@ -439,21 +439,16 @@ public abstract class TreeBuilder<T> imp
     private @Auto StackNode<T>[] listOfActiveFormattingElements;
 
     private int listPtr = -1;
 
     private T formPointer;
 
     private T headPointer;
 
-    /**
-     * Used to work around Gecko limitations. Not used in Java.
-     */
-    private T deepTreeSurrogateParent;
-
     protected @Auto char[] charBuffer;
 
     protected int charBufferLen = 0;
 
     private boolean quirks = false;
 
     private boolean isSrcdocDocument = false;
 
@@ -610,17 +605,16 @@ public abstract class TreeBuilder<T> imp
         originalMode = INITIAL;
         templateModePtr = -1;
         stackNodesIdx = 0;
         numStackNodes = 0;
         currentPtr = -1;
         listPtr = -1;
         formPointer = null;
         headPointer = null;
-        deepTreeSurrogateParent = null;
         // [NOCPP[
         html4 = false;
         idLocations.clear();
         wantingComments = wantsComments();
         firstCommentLocation = null;
         // ]NOCPP]
         start(fragment);
         charBufferLen = 0;
@@ -1639,17 +1633,16 @@ public abstract class TreeBuilder<T> imp
     }
 
     /**
      * @see nu.validator.htmlparser.common.TokenHandler#endTokenization()
      */
     public final void endTokenization() throws SAXException {
         formPointer = null;
         headPointer = null;
-        deepTreeSurrogateParent = null;
         templateModeStack = null;
         if (stack != null) {
             while (currentPtr > -1) {
                 stack[currentPtr].release(this);
                 currentPtr--;
             }
             stack = null;
         }
@@ -4737,17 +4730,21 @@ public abstract class TreeBuilder<T> imp
             if (furthestBlockPos > currentPtr) {
                 // no furthest block
                 while (currentPtr >= formattingEltStackPos) {
                     pop();
                 }
                 removeFromListOfActiveFormattingElements(formattingEltListPos);
                 return true;
             }
+            // commonAncestor is used for running the algorithm and
+            // insertionCommonAncestor is used for the actual insertions to
+            // keep them depth-limited.
             StackNode<T> commonAncestor = stack[formattingEltStackPos - 1]; // weak ref
+            T insertionCommonAncestor = nodeFromStackWithBlinkCompat(formattingEltStackPos - 1); // weak ref
             StackNode<T> furthestBlock = stack[furthestBlockPos]; // weak ref
             // detachFromParent(furthestBlock.node); XXX AAA CHANGE
             int bookmark = formattingEltListPos;
             int nodePos = furthestBlockPos;
             StackNode<T> lastNode = furthestBlock; // weak ref
             int j = 0;
             for (;;) {
                 ++j;
@@ -4785,17 +4782,17 @@ public abstract class TreeBuilder<T> imp
                 // now node is both on stack and in the list
                 if (nodePos == furthestBlockPos) {
                     bookmark = nodeListPos + 1;
                 }
                 // if (hasChildren(node.node)) { XXX AAA CHANGE
                 assert node == listOfActiveFormattingElements[nodeListPos];
                 assert node == stack[nodePos];
                 T clone = createElement("http://www.w3.org/1999/xhtml",
-                        node.name, node.attributes.cloneAttributes(), commonAncestor.node
+                        node.name, node.attributes.cloneAttributes(), insertionCommonAncestor
                         // CPPONLY: , htmlCreator(node.getHtmlCreator())
                         );
                 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
                         node.name, clone, node.popName, node.attributes
                         // CPPONLY: , node.getHtmlCreator()
                         // [NOCPP[
                         , node.getLocator()
                         // ]NOCPP]
@@ -4804,26 +4801,28 @@ public abstract class TreeBuilder<T> imp
                 stack[nodePos] = newNode;
                 newNode.retain(); // retain for list
                 listOfActiveFormattingElements[nodeListPos] = newNode;
                 node.release(this); // release from stack
                 node.release(this); // release from list
                 node = newNode;
                 // } XXX AAA CHANGE
                 detachFromParent(lastNode.node);
-                appendElement(lastNode.node, node.node);
+                appendElement(lastNode.node, nodeFromStackWithBlinkCompat(nodePos));
                 lastNode = node;
             }
+            // If we insert into a foster parent, for simplicity, we insert
+            // accoding to the spec without Blink's depth limit.
             if (commonAncestor.isFosterParenting()) {
                 fatal();
                 detachFromParent(lastNode.node);
                 insertIntoFosterParent(lastNode.node);
             } else {
                 detachFromParent(lastNode.node);
-                appendElement(lastNode.node, commonAncestor.node);
+                appendElement(lastNode.node, insertionCommonAncestor);
             }
             T clone = createElement("http://www.w3.org/1999/xhtml",
                     formattingElt.name,
                     formattingElt.attributes.cloneAttributes(), furthestBlock.node
                     // CPPONLY: , htmlCreator(formattingElt.getHtmlCreator())
                     );
             StackNode<T> formattingClone = createStackNode(
                     formattingElt.getFlags(), formattingElt.ns,
@@ -4999,30 +4998,31 @@ public abstract class TreeBuilder<T> imp
             }
             if (isInStack(listOfActiveFormattingElements[entryPos])) {
                 break;
             }
         }
         while (entryPos < listPtr) {
             entryPos++;
             StackNode<T> entry = listOfActiveFormattingElements[entryPos];
-            StackNode<T> currentNode = stack[currentPtr];
+            StackNode<T> current = stack[currentPtr];
 
             T clone;
-            if (currentNode.isFosterParenting()) {
+            if (current.isFosterParenting()) {
                 clone = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", entry.name,
                         entry.attributes.cloneAttributes()
                         // CPPONLY: , htmlCreator(entry.getHtmlCreator())
                         );
             } else {
+                T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
                 clone = createElement("http://www.w3.org/1999/xhtml", entry.name,
-                        entry.attributes.cloneAttributes(), currentNode.node
+                        entry.attributes.cloneAttributes(), currentNode
                         // CPPONLY: , htmlCreator(entry.getHtmlCreator())
                         );
-                appendElement(clone, currentNode.node);
+                appendElement(clone, currentNode);
             }
 
             StackNode<T> entryClone = createStackNode(entry.getFlags(),
                     entry.ns, entry.name, clone, entry.popName,
                     entry.attributes
                     // CPPONLY: , entry.getHtmlCreator()
                     // [NOCPP[
                     , entry.getLocator()
@@ -5366,17 +5366,17 @@ public abstract class TreeBuilder<T> imp
         appendHtmlElementToDocumentAndPush(tokenizer.emptyAttributes());
     }
 
     private void appendToCurrentNodeAndPushHeadElement(HtmlAttributes attributes)
             throws SAXException {
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
         // ]NOCPP]
-        T currentNode = stack[currentPtr].node;
+        T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
         T elt = createElement("http://www.w3.org/1999/xhtml", "head", attributes, currentNode
                 /*
                  * head uses NS_NewHTMLSharedElement creator
                  */
                 // CPPONLY: , htmlCreator(NS_NewHTMLSharedElement)
                 );
         appendElement(elt, currentNode);
         headPointer = elt;
@@ -5408,20 +5408,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", "form", attributes
                     // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
                     );
         } else {
-            elt = createElement("http://www.w3.org/1999/xhtml", "form", attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/1999/xhtml", "form", attributes, currentNode
                     // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
 
         if (!isTemplateContents()) {
             formPointer = elt;
         }
 
         StackNode<T> node = createStackNode(ElementName.FORM,
                 elt
@@ -5445,20 +5446,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
         } else {
-            elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         StackNode<T> node = createStackNode(elementName, elt, clone
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
         append(node);
@@ -5467,17 +5469,17 @@ public abstract class TreeBuilder<T> imp
 
     private void appendToCurrentNodeAndPushElement(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
-        T currentNode = stack[currentPtr].node;
+        T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
         T elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
                 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                 );
         appendElement(elt, currentNode);
         if (ElementName.TEMPLATE == elementName) {
             elt = getDocumentFragmentForTemplate(elt);
         }
         StackNode<T> node = createStackNode(elementName, elt
@@ -5501,20 +5503,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
         } else {
-            elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         StackNode<T> node = createStackNode(elementName, elt, popName
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
@@ -5539,20 +5542,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes
                     // CPPONLY: , htmlCreator(null)
                     );
         } else {
-            elt  = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt  = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, currentNode
                     // CPPONLY: , htmlCreator(null)
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         StackNode<T> node = createStackNode(elementName, elt, popName,
                 markAsHtmlIntegrationPoint
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
@@ -5592,20 +5596,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes
                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
                     );
         } else {
-            elt = createElement("http://www.w3.org/2000/svg", popName, attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/2000/svg", popName, attributes, currentNode
                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         StackNode<T> node = createStackNode(elementName, popName, elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
@@ -5622,21 +5627,22 @@ public abstract class TreeBuilder<T> imp
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(),
                     attributes, formOwner
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
         } else {
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
             elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(),
-                    attributes, formOwner, current.node
+                    attributes, formOwner, currentNode
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         StackNode<T> node = createStackNode(elementName, elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
@@ -5653,21 +5659,22 @@ public abstract class TreeBuilder<T> imp
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", name,
                     attributes, formOwner
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
         } else {
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
             elt = createElement("http://www.w3.org/1999/xhtml", name,
-                    attributes, formOwner, current.node
+                    attributes, formOwner, currentNode
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         elementPushed("http://www.w3.org/1999/xhtml", name, elt);
         elementPopped("http://www.w3.org/1999/xhtml", name, elt);
     }
 
     private void appendVoidElementToCurrentMayFoster(
             ElementName elementName, HtmlAttributes attributes)
             throws SAXException {
@@ -5681,20 +5688,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
         } else {
-            elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         elementPushed("http://www.w3.org/1999/xhtml", popName, elt);
         elementPopped("http://www.w3.org/1999/xhtml", popName, elt);
     }
 
     private void appendVoidElementToCurrentMayFosterSVG(
             ElementName elementName, HtmlAttributes attributes)
             throws SAXException {
@@ -5708,20 +5716,21 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes
                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
                     );
         } else {
-            elt = createElement("http://www.w3.org/2000/svg", popName, attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/2000/svg", popName, attributes, currentNode
                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         elementPushed("http://www.w3.org/2000/svg", popName, elt);
         elementPopped("http://www.w3.org/2000/svg", popName, elt);
     }
 
     private void appendVoidElementToCurrentMayFosterMathML(
             ElementName elementName, HtmlAttributes attributes)
             throws SAXException {
@@ -5735,45 +5744,46 @@ public abstract class TreeBuilder<T> imp
         T elt;
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes
                     // CPPONLY: , htmlCreator(null)
                     );
         } else {
-            elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, current.node
+            T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
+            elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, currentNode
                     // CPPONLY: , htmlCreator(null)
                     );
-            appendElement(elt, current.node);
+            appendElement(elt, currentNode);
         }
         elementPushed("http://www.w3.org/1998/Math/MathML", popName, elt);
         elementPopped("http://www.w3.org/1998/Math/MathML", popName, elt);
     }
 
     private void appendVoidInputToCurrent(HtmlAttributes attributes, T form) throws SAXException {
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
         // ]NOCPP]
         // Can't be called for custom elements
-        T currentNode = stack[currentPtr].node;
+        T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
         T elt = createElement("http://www.w3.org/1999/xhtml", "input", attributes,
                 form == null || fragment || isTemplateContents() ? null : form, currentNode
                         // CPPONLY: , htmlCreator(NS_NewHTMLInputElement)
                         );
         appendElement(elt, currentNode);
         elementPushed("http://www.w3.org/1999/xhtml", "input", elt);
         elementPopped("http://www.w3.org/1999/xhtml", "input", elt);
     }
 
     private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXException {
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
         // ]NOCPP]
-        T currentNode = stack[currentPtr].node;
+        T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
         T elt = createElement("http://www.w3.org/1999/xhtml", "form",
                 attributes, currentNode
                 // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
                 );
         formPointer = elt;
         // ownership transferred to form pointer
         appendElement(elt, currentNode);
         elementPushed("http://www.w3.org/1999/xhtml", "form", elt);
@@ -6175,34 +6185,33 @@ public abstract class TreeBuilder<T> imp
                 stackCopy[i] = listCopy[listIndex];
                 stackCopy[i].retain();
             }
         }
         int[] templateModeStackCopy = new int[templateModePtr + 1];
         System.arraycopy(templateModeStack, 0, templateModeStackCopy, 0,
                 templateModeStackCopy.length);
         return new StateSnapshot<T>(stackCopy, listCopy, templateModeStackCopy, formPointer,
-                headPointer, deepTreeSurrogateParent, mode, originalMode, framesetOk,
+                headPointer, mode, originalMode, framesetOk,
                 needToDropLF, quirks);
     }
 
     public boolean snapshotMatches(TreeBuilderState<T> snapshot) {
         StackNode<T>[] stackCopy = snapshot.getStack();
         int stackLen = snapshot.getStackLength();
         StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
         int listLen = snapshot.getListOfActiveFormattingElementsLength();
         int[] templateModeStackCopy = snapshot.getTemplateModeStack();
         int templateModeStackLen = snapshot.getTemplateModeStackLength();
 
         if (stackLen != currentPtr + 1
                 || listLen != listPtr + 1
                 || templateModeStackLen != templateModePtr + 1
                 || formPointer != snapshot.getFormPointer()
                 || headPointer != snapshot.getHeadPointer()
-                || deepTreeSurrogateParent != snapshot.getDeepTreeSurrogateParent()
                 || mode != snapshot.getMode()
                 || originalMode != snapshot.getOriginalMode()
                 || framesetOk != snapshot.isFramesetOk()
                 || needToDropLF != snapshot.isNeedToDropLF()
                 || quirks != snapshot.isQuirks()) { // maybe just assert quirks
             return false;
         }
         for (int i = listLen - 1; i >= 0; i--) {
@@ -6299,17 +6308,16 @@ public abstract class TreeBuilder<T> imp
             } else {
                 stack[i] = listOfActiveFormattingElements[listIndex];
                 stack[i].retain();
             }
         }
         System.arraycopy(templateModeStackCopy, 0, templateModeStack, 0, templateModeStackLen);
         formPointer = snapshot.getFormPointer();
         headPointer = snapshot.getHeadPointer();
-        deepTreeSurrogateParent = snapshot.getDeepTreeSurrogateParent();
         mode = snapshot.getMode();
         originalMode = snapshot.getOriginalMode();
         framesetOk = snapshot.isFramesetOk();
         needToDropLF = snapshot.isNeedToDropLF();
         quirks = snapshot.isQuirks();
     }
 
     private int findInArray(StackNode<T> node, StackNode<T>[] arr) {
@@ -6317,16 +6325,42 @@ public abstract class TreeBuilder<T> imp
             if (node == arr[i]) {
                 return i;
             }
         }
         return -1;
     }
 
     /**
+     * Returns <code>stack[stackPos].node</code> if <code>stackPos</code> is
+     * smaller than Blink's magic limit or the node at Blink's magic limit
+     * otherwise.
+     *
+     * In order to get Blink-compatible handling of excessive deeply-nested
+     * markup, this method must be used to obtain the node that is used as the
+     * parent node of an insertion.
+     *
+     * Blink's magic number is 512, but our counting is off by one compared to
+     * Blink's way of counting, so in order to get the same
+     * externally-observable outcome, we use 511 as our magic number.
+     *
+     * @param stackPos the stack position to attempt to read
+     * @return node at the position capped to Blink's magic number
+     * @throws SAXException
+     */
+    private T nodeFromStackWithBlinkCompat(int stackPos) throws SAXException {
+        // Magic number if off by one relative to Blink's magic number, but the
+        // outcome is the same, because the counting is different by one.
+        if (stackPos > 511) {
+            errDeepTree();
+            return stack[511].node;
+        }
+        return stack[stackPos].node;
+    }
+    /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getFormPointer()
      */
     @Override
     public T getFormPointer() {
         return formPointer;
     }
 
     /**
@@ -6335,26 +6369,16 @@ public abstract class TreeBuilder<T> imp
      * @return the headPointer
      */
     @Override
     public T getHeadPointer() {
         return headPointer;
     }
 
     /**
-     * Returns the deepTreeSurrogateParent.
-     *
-     * @return the deepTreeSurrogateParent
-     */
-    @Override
-    public T getDeepTreeSurrogateParent() {
-        return deepTreeSurrogateParent;
-    }
-
-    /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElements()
      */
     @Override
     public StackNode<T>[] getListOfActiveFormattingElements() {
         return listOfActiveFormattingElements;
     }
 
     /**
@@ -6443,16 +6467,26 @@ public abstract class TreeBuilder<T> imp
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStackLength()
      */
     @Override
     public int getTemplateModeStackLength() {
         return templateModePtr + 1;
     }
 
     /**
+     * Complains about an over-deep tree. Theoretically this should just be
+     * a warning, but in practice authors should take this as an error.
+     *
+     * @throws SAXException
+     */
+    private void errDeepTree() throws SAXException {
+        err("The document tree is more than 513 elements deep, which causes Firefox and Chrome flatten the tree.");
+    }
+
+    /**
      * Reports a stray start tag.
      * @param name the name of the stray tag
      *
      * @throws SAXException
      */
     private void errStrayStartTag(@Local String name) throws SAXException {
         err("Stray start tag \u201C" + name + "\u201D.");
     }
--- a/src/nu/validator/htmlparser/impl/TreeBuilderState.java
+++ b/src/nu/validator/htmlparser/impl/TreeBuilderState.java
@@ -62,23 +62,16 @@ public interface TreeBuilderState<T> {
     /**
      * Returns the headPointer.
      * 
      * @return the headPointer
      */
     public T getHeadPointer();
     
     /**
-     * Returns the deepTreeSurrogateParent.
-     * 
-     * @return the deepTreeSurrogateParent
-     */
-    public T getDeepTreeSurrogateParent();
-
-    /**
      * Returns the mode.
      * 
      * @return the mode
      */
     public int getMode();
 
     /**
      * Returns the originalMode.