Bug 1355441 - Reuse StackNode in TreeBuilder to avoid malloc. r=hsivonen.
authorWilliam Chen <wchen@mozilla.com>
Mon, 15 May 2017 17:18:20 +0300
changeset 358402 d8c78b2b736bc41c59247c7a47bea70d396258b3
parent 358401 26f5d5167d2c502932b251dbedd3341914d1b589
child 358403 f65355fbb8712d4eff1071a69a9269fc0b4e0a86
push id90318
push userhsivonen@mozilla.com
push dateMon, 15 May 2017 14:20:52 +0000
treeherdermozilla-inbound@d8c78b2b736b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1355441
milestone55.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 1355441 - Reuse StackNode in TreeBuilder to avoid malloc. r=hsivonen. MozReview-Commit-ID: 4QwQwISCKPk
parser/html/javasrc/StackNode.java
parser/html/javasrc/StateSnapshot.java
parser/html/javasrc/TreeBuilder.java
parser/html/nsHtml5ElementName.cpp
parser/html/nsHtml5StackNode.cpp
parser/html/nsHtml5StackNode.h
parser/html/nsHtml5StateSnapshot.cpp
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeBuilder.h
--- a/parser/html/javasrc/StackNode.java
+++ b/parser/html/javasrc/StackNode.java
@@ -23,34 +23,39 @@
 
 package nu.validator.htmlparser.impl;
 
 import nu.validator.htmlparser.annotation.Inline;
 import nu.validator.htmlparser.annotation.Local;
 import nu.validator.htmlparser.annotation.NsUri;
 
 final class StackNode<T> {
-    final int flags;
+    // Index where this stack node is stored in the tree builder's list of stack nodes.
+    // A value of -1 indicates that the stack node is not owned by a tree builder and
+    // must delete itself when its refcount reaches 0.
+    final int idxInTreeBuilder;
 
-    final @Local String name;
+    int flags;
 
-    final @Local String popName;
+    @Local String name;
+
+    @Local String popName;
 
-    final @NsUri String ns;
+    @NsUri String ns;
 
-    final T node;
+    T node;
 
     // Only used on the list of formatting elements
     HtmlAttributes attributes;
 
-    private int refcount = 1;
+    private int refcount = 0;
 
     // [NOCPP[
 
-    private final TaintableLocatorImpl locator;
+    private TaintableLocatorImpl locator;
 
     public TaintableLocatorImpl getLocator() {
         return locator;
     }
 
     // ]NOCPP]
 
     @Inline public int getFlags() {
@@ -80,34 +85,40 @@ final class StackNode<T> {
     // [NOCPP[
 
     public boolean isOptionalEndTag() {
         return (flags & ElementName.OPTIONAL_END_TAG) != 0;
     }
 
     // ]NOCPP]
 
+    StackNode(int idxInTreeBuilder) {
+        this.idxInTreeBuilder = idxInTreeBuilder;
+        this.refcount = 0;
+    }
+
     /**
-     * Constructor for copying. This doesn't take another <code>StackNode</code>
-     * because in C++ the caller is reponsible for reobtaining the local names
+     * Setter for copying. This doesn't take another <code>StackNode</code>
+     * because in C++ the caller is responsible for reobtaining the local names
      * from another interner.
      *
      * @param flags
      * @param ns
      * @param name
      * @param node
      * @param popName
      * @param attributes
      */
-    StackNode(int flags, @NsUri String ns, @Local String name, T node,
+    void setValues(int flags, @NsUri String ns, @Local String name, T node,
             @Local String popName, HtmlAttributes attributes
             // [NOCPP[
             , TaintableLocatorImpl locator
-    // ]NOCPP]
+            // ]NOCPP]
     ) {
+        assert isUnused();
         this.flags = flags;
         this.name = name;
         this.popName = popName;
         this.ns = ns;
         this.node = node;
         this.attributes = attributes;
         this.refcount = 1;
         // [NOCPP[
@@ -116,124 +127,129 @@ final class StackNode<T> {
     }
 
     /**
      * Short hand for well-known HTML elements.
      *
      * @param elementName
      * @param node
      */
-    StackNode(ElementName elementName, T node
-    // [NOCPP[
+    void setValues(ElementName elementName, T node
+            // [NOCPP[
             , TaintableLocatorImpl locator
-    // ]NOCPP]
+            // ]NOCPP]
     ) {
+        assert isUnused();
         this.flags = elementName.getFlags();
         this.name = elementName.getName();
         this.popName = elementName.getName();
         this.ns = "http://www.w3.org/1999/xhtml";
         this.node = node;
         this.attributes = null;
         this.refcount = 1;
         assert elementName.isInterned() : "Don't use this constructor for custom elements.";
         // [NOCPP[
         this.locator = locator;
         // ]NOCPP]
     }
 
     /**
-     * Constructor for HTML formatting elements.
+     * Setter for HTML formatting elements.
      *
      * @param elementName
      * @param node
      * @param attributes
      */
-    StackNode(ElementName elementName, T node, HtmlAttributes attributes
-    // [NOCPP[
+    void setValues(ElementName elementName, T node, HtmlAttributes attributes
+            // [NOCPP[
             , TaintableLocatorImpl locator
-    // ]NOCPP]
+            // ]NOCPP]
     ) {
+        assert isUnused();
         this.flags = elementName.getFlags();
         this.name = elementName.getName();
         this.popName = elementName.getName();
         this.ns = "http://www.w3.org/1999/xhtml";
         this.node = node;
         this.attributes = attributes;
         this.refcount = 1;
         assert elementName.isInterned() : "Don't use this constructor for custom elements.";
         // [NOCPP[
         this.locator = locator;
         // ]NOCPP]
     }
 
     /**
-     * The common-case HTML constructor.
+     * The common-case HTML setter.
      *
      * @param elementName
      * @param node
      * @param popName
      */
-    StackNode(ElementName elementName, T node, @Local String popName
-    // [NOCPP[
+    void setValues(ElementName elementName, T node, @Local String popName
+            // [NOCPP[
             , TaintableLocatorImpl locator
-    // ]NOCPP]
+            // ]NOCPP]
     ) {
+        assert isUnused();
         this.flags = elementName.getFlags();
         this.name = elementName.getName();
         this.popName = popName;
         this.ns = "http://www.w3.org/1999/xhtml";
         this.node = node;
         this.attributes = null;
         this.refcount = 1;
         // [NOCPP[
         this.locator = locator;
         // ]NOCPP]
     }
 
     /**
-     * Constructor for SVG elements. Note that the order of the arguments is
-     * what distinguishes this from the HTML constructor. This is ugly, but
+     * Setter for SVG elements. Note that the order of the arguments is
+     * what distinguishes this from the HTML setter. This is ugly, but
      * AFAICT the least disruptive way to make this work with Java's generics
      * and without unnecessary branches. :-(
      *
      * @param elementName
      * @param popName
      * @param node
      */
-    StackNode(ElementName elementName, @Local String popName, T node
-    // [NOCPP[
+    void setValues(ElementName elementName, @Local String popName, T node
+            // [NOCPP[
             , TaintableLocatorImpl locator
-    // ]NOCPP]
+            // ]NOCPP]
     ) {
+        assert isUnused();
         this.flags = prepareSvgFlags(elementName.getFlags());
         this.name = elementName.getName();
         this.popName = popName;
         this.ns = "http://www.w3.org/2000/svg";
         this.node = node;
         this.attributes = null;
         this.refcount = 1;
         // [NOCPP[
         this.locator = locator;
         // ]NOCPP]
     }
 
     /**
-     * Constructor for MathML.
+     * Setter for MathML.
      *
      * @param elementName
      * @param node
      * @param popName
      * @param markAsIntegrationPoint
      */
-    StackNode(ElementName elementName, T node, @Local String popName,
+    void setValues(ElementName elementName, T node, @Local String popName,
             boolean markAsIntegrationPoint
             // [NOCPP[
             , TaintableLocatorImpl locator
-    // ]NOCPP]
+            // ]NOCPP]
     ) {
+        assert isUnused();
         this.flags = prepareMathFlags(elementName.getFlags(),
                 markAsIntegrationPoint);
         this.name = elementName.getName();
         this.popName = popName;
         this.ns = "http://www.w3.org/1998/Math/MathML";
         this.node = node;
         this.attributes = null;
         this.refcount = 1;
@@ -260,17 +276,17 @@ final class StackNode<T> {
         }
         if (markAsIntegrationPoint) {
             flags |= ElementName.HTML_INTEGRATION_POINT;
         }
         return flags;
     }
 
     @SuppressWarnings("unused") private void destructor() {
-        Portability.delete(attributes);
+        // The translator adds refcount debug code here.
     }
 
     public void dropAttributes() {
         attributes = null;
     }
 
     // [NOCPP[
     /**
@@ -281,15 +297,26 @@ final class StackNode<T> {
     }
 
     // ]NOCPP]
 
     public void retain() {
         refcount++;
     }
 
-    public void release() {
+    public void release(TreeBuilder<T> owningTreeBuilder) {
         refcount--;
+        assert refcount >= 0;
         if (refcount == 0) {
-            Portability.delete(this);
+            Portability.delete(attributes);
+            if (idxInTreeBuilder >= 0) {
+                owningTreeBuilder.notifyUnusedStackNode(idxInTreeBuilder);
+            } else {
+                assert owningTreeBuilder == null;
+                Portability.delete(this);
+            }
         }
     }
+
+    boolean isUnused() {
+        return refcount == 0;
+    }
 }
--- a/parser/html/javasrc/StateSnapshot.java
+++ b/parser/html/javasrc/StateSnapshot.java
@@ -188,17 +188,17 @@ public class StateSnapshot<T> implements
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStackLength()
      */
     public int getTemplateModeStackLength() {
         return templateModeStack.length;
     }
 
     @SuppressWarnings("unused") private void destructor() {
         for (int i = 0; i < stack.length; i++) {
-            stack[i].release();
+            stack[i].release(null);
         }
         for (int i = 0; i < listOfActiveFormattingElements.length; i++) {
             if (listOfActiveFormattingElements[i] != null) {
-                listOfActiveFormattingElements[i].release();                
+                listOfActiveFormattingElements[i].release(null);
             }
         }
     }
 }
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -420,16 +420,25 @@ public abstract class TreeBuilder<T> imp
      */
     private @Auto int[] templateModeStack;
 
     /**
      * Current template mode stack pointer.
      */
     private int templateModePtr = -1;
 
+    private @Auto StackNode<T>[] stackNodes;
+
+    /**
+     * Index of the earliest possible unused or empty element in stackNodes.
+     */
+    private int stackNodesIdx = -1;
+
+    private int numStackNodes = 0;
+
     private @Auto StackNode<T>[] stack;
 
     private int currentPtr = -1;
 
     private @Auto StackNode<T>[] listOfActiveFormattingElements;
 
     private int listPtr = -1;
 
@@ -578,22 +587,25 @@ public abstract class TreeBuilder<T> imp
         SAXParseException spe = new SAXParseException(message, locator);
         errorHandler.warning(spe);
     }
 
     // ]NOCPP]
 
     @SuppressWarnings("unchecked") public final void startTokenization(Tokenizer self) throws SAXException {
         tokenizer = self;
+        stackNodes = new StackNode[64];
         stack = new StackNode[64];
         templateModeStack = new int[64];
         listOfActiveFormattingElements = new StackNode[64];
         needToDropLF = false;
         originalMode = INITIAL;
         templateModePtr = -1;
+        stackNodesIdx = 0;
+        numStackNodes = 0;
         currentPtr = -1;
         listPtr = -1;
         formPointer = null;
         headPointer = null;
         deepTreeSurrogateParent = null;
         // [NOCPP[
         html4 = false;
         idLocations.clear();
@@ -626,17 +638,17 @@ public abstract class TreeBuilder<T> imp
                 ElementName elementName = ElementName.SVG;
                 if ("title" == contextName || "desc" == contextName
                         || "foreignObject" == contextName) {
                     // These elements are all alike and we don't care about
                     // the exact name.
                     elementName = ElementName.FOREIGNOBJECT;
                 }
                 // This is the SVG variant of the StackNode constructor.
-                StackNode<T> node = new StackNode<T>(elementName,
+                StackNode<T> node = createStackNode(elementName,
                         elementName.getCamelCaseName(), elt
                         // [NOCPP[
                         , errorHandler == null ? null
                                 : new TaintableLocatorImpl(tokenizer)
                 // ]NOCPP]
                 );
                 currentPtr++;
                 stack[currentPtr] = node;
@@ -657,32 +669,32 @@ public abstract class TreeBuilder<T> imp
                     elementName = ElementName.ANNOTATION_XML;
                     // Blink does not check the encoding attribute of the
                     // annotation-xml element innerHTML is being set on.
                     // Let's do the same at least until
                     // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26783
                     // is resolved.
                 }
                 // This is the MathML variant of the StackNode constructor.
-                StackNode<T> node = new StackNode<T>(elementName, elt,
+                StackNode<T> node = createStackNode(elementName, elt,
                         elementName.getName(), false
                         // [NOCPP[
                         , errorHandler == null ? null
                                 : new TaintableLocatorImpl(tokenizer)
                 // ]NOCPP]
                 );
                 currentPtr++;
                 stack[currentPtr] = node;
                 tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA,
                         contextName);
                 // The frameset-ok flag is set even though <frameset> never
                 // ends up being allowed as HTML frameset in the fragment case.
                 mode = FRAMESET_OK;
             } else { // html
-                StackNode<T> node = new StackNode<T>(ElementName.HTML, elt
+                StackNode<T> node = createStackNode(ElementName.HTML, elt
                 // [NOCPP[
                         , errorHandler == null ? null
                                 : new TaintableLocatorImpl(tokenizer)
                 // ]NOCPP]
                 );
                 currentPtr++;
                 stack[currentPtr] = node;
                 if ("template" == contextName) {
@@ -715,17 +727,17 @@ public abstract class TreeBuilder<T> imp
         } else {
             mode = INITIAL;
             // If we are viewing XML source, put a foreign element permanently
             // on the stack so that cdataSectionAllowed() returns true.
             // CPPONLY: if (tokenizer.isViewingXmlSource()) {
             // CPPONLY: T elt = createElement("http://www.w3.org/2000/svg",
             // CPPONLY: "svg",
             // CPPONLY: tokenizer.emptyAttributes(), null);
-            // CPPONLY: StackNode<T> node = new StackNode<T>(ElementName.SVG,
+            // CPPONLY: StackNode<T> node = createStackNode(ElementName.SVG,
             // CPPONLY: "svg",
             // CPPONLY: elt);
             // CPPONLY: currentPtr++;
             // CPPONLY: stack[currentPtr] = node;
             // CPPONLY: }
         }
     }
 
@@ -1618,30 +1630,39 @@ public abstract class TreeBuilder<T> imp
      */
     public final void endTokenization() throws SAXException {
         formPointer = null;
         headPointer = null;
         deepTreeSurrogateParent = null;
         templateModeStack = null;
         if (stack != null) {
             while (currentPtr > -1) {
-                stack[currentPtr].release();
+                stack[currentPtr].release(this);
                 currentPtr--;
             }
             stack = null;
         }
         if (listOfActiveFormattingElements != null) {
             while (listPtr > -1) {
                 if (listOfActiveFormattingElements[listPtr] != null) {
-                    listOfActiveFormattingElements[listPtr].release();
+                    listOfActiveFormattingElements[listPtr].release(this);
                 }
                 listPtr--;
             }
             listOfActiveFormattingElements = null;
         }
+        if (stackNodes != null) {
+            for (int i = 0; i < numStackNodes; i++) {
+                assert stackNodes[i].isUnused();
+                Portability.delete(stackNodes[i]);
+            }
+            numStackNodes = 0;
+            stackNodesIdx = 0;
+            stackNodes = null;
+        }
         // [NOCPP[
         idLocations.clear();
         // ]NOCPP]
         charBuffer = null;
         end();
     }
 
     public final void startTag(ElementName elementName,
@@ -2213,17 +2234,17 @@ public abstract class TreeBuilder<T> imp
                                     StackNode<T> activeA = listOfActiveFormattingElements[activeAPos];
                                     activeA.retain();
                                     adoptionAgencyEndTag("a");
                                     removeFromStack(activeA);
                                     activeAPos = findInListOfActiveFormattingElements(activeA);
                                     if (activeAPos != -1) {
                                         removeFromListOfActiveFormattingElements(activeAPos);
                                     }
-                                    activeA.release();
+                                    activeA.release(this);
                                 }
                                 reconstructTheActiveFormattingElements();
                                 appendToCurrentNodeAndPushFormattingElementMayFoster(
                                         elementName,
                                         attributes);
                                 attributes = null; // CPP
                                 break starttagloop;
                             case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
@@ -4610,32 +4631,32 @@ public abstract class TreeBuilder<T> imp
     }
 
     private void clearTheListOfActiveFormattingElementsUpToTheLastMarker() {
         while (listPtr > -1) {
             if (listOfActiveFormattingElements[listPtr] == null) {
                 --listPtr;
                 return;
             }
-            listOfActiveFormattingElements[listPtr].release();
+            listOfActiveFormattingElements[listPtr].release(this);
             --listPtr;
         }
     }
 
     @Inline private boolean isCurrent(@Local String name) {
         return stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" &&
                 name == stack[currentPtr].name;
     }
 
     private void removeFromStack(int pos) throws SAXException {
         if (currentPtr == pos) {
             pop();
         } else {
             fatal();
-            stack[pos].release();
+            stack[pos].release(this);
             System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
             assert debugOnlyClearLastStackSlot();
             currentPtr--;
         }
     }
 
     private void removeFromStack(StackNode<T> node) throws SAXException {
         if (stack[currentPtr] == node) {
@@ -4645,25 +4666,25 @@ public abstract class TreeBuilder<T> imp
             while (pos >= 0 && stack[pos] != node) {
                 pos--;
             }
             if (pos == -1) {
                 // dead code?
                 return;
             }
             fatal();
-            node.release();
+            node.release(this);
             System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
             currentPtr--;
         }
     }
 
     private void removeFromListOfActiveFormattingElements(int pos) {
         assert listOfActiveFormattingElements[pos] != null;
-        listOfActiveFormattingElements[pos].release();
+        listOfActiveFormattingElements[pos].release(this);
         if (pos == listPtr) {
             assert debugOnlyClearLastListSlot();
             listPtr--;
             return;
         }
         assert pos < listPtr;
         System.arraycopy(listOfActiveFormattingElements, pos + 1,
                 listOfActiveFormattingElements, pos, listPtr - pos);
@@ -4799,28 +4820,28 @@ public abstract class TreeBuilder<T> imp
                 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(null), commonAncestor.node);
-                StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
+                StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
                         node.name, clone, node.popName, node.attributes
                         // [NOCPP[
                         , node.getLocator()
                         // ]NOCPP]
                 ); // creation ownership goes to stack
                 node.dropAttributes(); // adopt ownership to newNode
                 stack[nodePos] = newNode;
                 newNode.retain(); // retain for list
                 listOfActiveFormattingElements[nodeListPos] = newNode;
-                node.release(); // release from stack
-                node.release(); // release from list
+                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);
                 lastNode = node;
             }
             if (commonAncestor.isFosterParenting()) {
                 fatal();
@@ -4828,17 +4849,17 @@ public abstract class TreeBuilder<T> imp
                 insertIntoFosterParent(lastNode.node);
             } else {
                 detachFromParent(lastNode.node);
                 appendElement(lastNode.node, commonAncestor.node);
             }
             T clone = createElement("http://www.w3.org/1999/xhtml",
                     formattingElt.name,
                     formattingElt.attributes.cloneAttributes(null), furthestBlock.node);
-            StackNode<T> formattingClone = new StackNode<T>(
+            StackNode<T> formattingClone = createStackNode(
                     formattingElt.getFlags(), formattingElt.ns,
                     formattingElt.name, clone, formattingElt.popName,
                     formattingElt.attributes
                     // [NOCPP[
                     , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
                     // ]NOCPP]
             ); // Ownership transfers to stack below
             formattingElt.dropAttributes(); // transfer ownership to
@@ -4971,17 +4992,17 @@ public abstract class TreeBuilder<T> imp
         // ]NOCPP]
         addAttributesToElement(stack[0].node, attributes);
     }
 
     private void pushHeadPointerOntoStack() throws SAXException {
         assert headPointer != null;
         assert mode == AFTER_HEAD;
         fatal();
-        silentPush(new StackNode<T>(ElementName.HEAD, headPointer
+        silentPush(createStackNode(ElementName.HEAD, headPointer
         // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         ));
     }
 
     /**
      * @throws SAXException
@@ -5018,35 +5039,156 @@ public abstract class TreeBuilder<T> imp
                 clone = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", entry.name,
                         entry.attributes.cloneAttributes(null));
             } else {
                 clone = createElement("http://www.w3.org/1999/xhtml", entry.name,
                         entry.attributes.cloneAttributes(null), currentNode.node);
                 appendElement(clone, currentNode.node);
             }
 
-            StackNode<T> entryClone = new StackNode<T>(entry.getFlags(),
+            StackNode<T> entryClone = createStackNode(entry.getFlags(),
                     entry.ns, entry.name, clone, entry.popName,
                     entry.attributes
                     // [NOCPP[
                     , entry.getLocator()
                     // ]NOCPP]
             );
 
             entry.dropAttributes(); // transfer ownership to entryClone
 
             push(entryClone);
             // stack takes ownership of the local variable
             listOfActiveFormattingElements[entryPos] = entryClone;
             // overwriting the old entry on the list, so release & retain
-            entry.release();
+            entry.release(this);
             entryClone.retain();
         }
     }
 
+    void notifyUnusedStackNode(int idxInStackNodes) {
+        // stackNodesIdx is the earliest possible index of a stack node that might be unused,
+        // so update the index if necessary.
+        if (idxInStackNodes < stackNodesIdx) {
+            stackNodesIdx = idxInStackNodes;
+        }
+    }
+
+    private StackNode<T> getUnusedStackNode() {
+        // Search for an unused stack node.
+        while (stackNodesIdx < numStackNodes) {
+            if (stackNodes[stackNodesIdx].isUnused()) {
+                return stackNodes[stackNodesIdx++];
+            }
+            stackNodesIdx++;
+        }
+
+        if (stackNodesIdx < stackNodes.length) {
+            // No unused stack nodes, but there is still space in the storage array.
+            stackNodes[stackNodesIdx] = new StackNode<T>(stackNodesIdx);
+            numStackNodes++;
+            return stackNodes[stackNodesIdx++];
+        }
+
+        // Could not find an unused stack node and storage array is full.
+        StackNode<T>[] newStack = new StackNode[stackNodes.length + 64];
+        System.arraycopy(stackNodes, 0, newStack, 0, stackNodes.length);
+        stackNodes = newStack;
+
+        // Create a new stack node and return it.
+        stackNodes[stackNodesIdx] = new StackNode<T>(stackNodesIdx);
+        numStackNodes++;
+        return stackNodes[stackNodesIdx++];
+    }
+
+    private StackNode<T> createStackNode(int flags, @NsUri String ns, @Local String name, T node,
+            @Local String popName, HtmlAttributes attributes
+            // [NOCPP[
+            , TaintableLocatorImpl locator
+            // ]NOCPP]
+    ) {
+        StackNode<T> instance = getUnusedStackNode();
+        instance.setValues(flags, ns, name, node, popName, attributes
+                // [NOCPP[
+                , locator
+                // ]NOCPP]
+        );
+        return instance;
+    }
+
+    private StackNode<T> createStackNode(ElementName elementName, T node
+            // [NOCPP[
+            , TaintableLocatorImpl locator
+            // ]NOCPP]
+    ) {
+        StackNode<T> instance = getUnusedStackNode();
+        instance.setValues(elementName, node
+                // [NOCPP[
+                , locator
+                // ]NOCPP]
+        );
+        return instance;
+    }
+
+    private StackNode<T> createStackNode(ElementName elementName, T node, HtmlAttributes attributes
+            // [NOCPP[
+            , TaintableLocatorImpl locator
+            // ]NOCPP]
+    ) {
+        StackNode<T> instance = getUnusedStackNode();
+        instance.setValues(elementName, node, attributes
+                // [NOCPP[
+                , locator
+                // ]NOCPP]
+        );
+        return instance;
+    }
+
+    private StackNode<T> createStackNode(ElementName elementName, T node, @Local String popName
+            // [NOCPP[
+            , TaintableLocatorImpl locator
+            // ]NOCPP]
+    ) {
+        StackNode<T> instance = getUnusedStackNode();
+        instance.setValues(elementName, node, popName
+                // [NOCPP[
+                , locator
+                // ]NOCPP]
+        );
+        return instance;
+    }
+
+    private StackNode<T> createStackNode(ElementName elementName, @Local String popName, T node
+            // [NOCPP[
+            , TaintableLocatorImpl locator
+            // ]NOCPP]
+    ) {
+        StackNode<T> instance = getUnusedStackNode();
+        instance.setValues(elementName, popName, node
+                // [NOCPP[
+                , locator
+                // ]NOCPP]
+        );
+        return instance;
+    }
+
+    private StackNode<T> createStackNode(ElementName elementName, T node, @Local String popName,
+            boolean markAsIntegrationPoint
+            // [NOCPP[
+            , TaintableLocatorImpl locator
+            // ]NOCPP]
+    ) {
+        StackNode<T> instance = getUnusedStackNode();
+        instance.setValues(elementName, node, popName, markAsIntegrationPoint
+                // [NOCPP[
+                , locator
+                // ]NOCPP]
+        );
+        return instance;
+    }
+
     private void insertIntoFosterParent(T child) throws SAXException {
         int tablePos = findLastOrRoot(TreeBuilder.TABLE);
         int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
 
         if (templatePos >= tablePos) {
             appendElement(child, stack[templatePos].node);
             return;
         }
@@ -5088,33 +5230,33 @@ public abstract class TreeBuilder<T> imp
         templateModePtr--;
     }
 
     private void pop() throws SAXException {
         StackNode<T> node = stack[currentPtr];
         assert debugOnlyClearLastStackSlot();
         currentPtr--;
         elementPopped(node.ns, node.popName, node.node);
-        node.release();
+        node.release(this);
     }
 
     private void silentPop() throws SAXException {
         StackNode<T> node = stack[currentPtr];
         assert debugOnlyClearLastStackSlot();
         currentPtr--;
-        node.release();
+        node.release(this);
     }
 
     private void popOnEof() throws SAXException {
         StackNode<T> node = stack[currentPtr];
         assert debugOnlyClearLastStackSlot();
         currentPtr--;
         markMalformedIfScript(node.node);
         elementPopped(node.ns, node.popName, node.node);
-        node.release();
+        node.release(this);
     }
 
     // [NOCPP[
     private void checkAttributes(HtmlAttributes attributes, @NsUri String ns)
             throws SAXException {
         if (errorHandler != null) {
             int len = attributes.getXmlnsLength();
             for (int i = 0; i < len; i++) {
@@ -5206,17 +5348,17 @@ public abstract class TreeBuilder<T> imp
     // ]NOCPP]
 
     private void appendHtmlElementToDocumentAndPush(HtmlAttributes attributes)
             throws SAXException {
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
         // ]NOCPP]
         T elt = createHtmlElementSetAsRoot(attributes);
-        StackNode<T> node = new StackNode<T>(ElementName.HTML,
+        StackNode<T> node = createStackNode(ElementName.HTML,
                 elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
@@ -5228,17 +5370,17 @@ public abstract class TreeBuilder<T> imp
             throws SAXException {
         // [NOCPP[
         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
         // ]NOCPP]
         T currentNode = stack[currentPtr].node;
         T elt = createElement("http://www.w3.org/1999/xhtml", "head", attributes, currentNode);
         appendElement(elt, currentNode);
         headPointer = elt;
-        StackNode<T> node = new StackNode<T>(ElementName.HEAD,
+        StackNode<T> node = createStackNode(ElementName.HEAD,
                 elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
@@ -5267,17 +5409,17 @@ public abstract class TreeBuilder<T> imp
             elt = createElement("http://www.w3.org/1999/xhtml", "form", attributes, current.node);
             appendElement(elt, current.node);
         }
 
         if (!isTemplateContents()) {
             formPointer = elt;
         }
 
-        StackNode<T> node = new StackNode<T>(ElementName.FORM,
+        StackNode<T> node = createStackNode(ElementName.FORM,
                 elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
                 // ]NOCPP]
         );
         push(node);
     }
 
@@ -5295,17 +5437,17 @@ 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);
         } else {
             elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, current.node);
             appendElement(elt, current.node);
         }
-        StackNode<T> node = new StackNode<T>(elementName, elt, clone
+        StackNode<T> node = createStackNode(elementName, elt, clone
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
         append(node);
         node.retain(); // append doesn't retain itself
     }
@@ -5318,17 +5460,17 @@ public abstract class TreeBuilder<T> imp
         // ]NOCPP]
         // This method can't be called for custom elements
         T currentNode = stack[currentPtr].node;
         T elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode);
         appendElement(elt, currentNode);
         if (ElementName.TEMPLATE == elementName) {
             elt = getDocumentFragmentForTemplate(elt);
         }
-        StackNode<T> node = new StackNode<T>(elementName, elt
+        StackNode<T> node = createStackNode(elementName, elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
     private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
@@ -5345,17 +5487,17 @@ public abstract class TreeBuilder<T> imp
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes);
         } else {
             elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, current.node);
             appendElement(elt, current.node);
         }
-        StackNode<T> node = new StackNode<T>(elementName, elt, popName
+        StackNode<T> node = createStackNode(elementName, elt, popName
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
     private void appendToCurrentNodeAndPushElementMayFosterMathML(
@@ -5379,17 +5521,17 @@ public abstract class TreeBuilder<T> imp
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes);
         } else {
             elt  = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, current.node);
             appendElement(elt, current.node);
         }
-        StackNode<T> node = new StackNode<T>(elementName, elt, popName,
+        StackNode<T> node = createStackNode(elementName, elt, popName,
                 markAsHtmlIntegrationPoint
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
@@ -5428,17 +5570,17 @@ public abstract class TreeBuilder<T> imp
         StackNode<T> current = stack[currentPtr];
         if (current.isFosterParenting()) {
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes);
         } else {
             elt = createElement("http://www.w3.org/2000/svg", popName, attributes, current.node);
             appendElement(elt, current.node);
         }
-        StackNode<T> node = new StackNode<T>(elementName, popName, elt
+        StackNode<T> node = createStackNode(elementName, popName, elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
     private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
@@ -5455,17 +5597,17 @@ public abstract class TreeBuilder<T> imp
             fatal();
             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(),
                     attributes, formOwner);
         } else {
             elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(),
                     attributes, formOwner, current.node);
             appendElement(elt, current.node);
         }
-        StackNode<T> node = new StackNode<T>(elementName, elt
+        StackNode<T> node = createStackNode(elementName, elt
                 // [NOCPP[
                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
         // ]NOCPP]
         );
         push(node);
     }
 
     private void appendVoidElementToCurrentMayFoster(
@@ -5938,39 +6080,41 @@ public abstract class TreeBuilder<T> imp
      * @throws SAXException
      */
     @SuppressWarnings("unchecked") public TreeBuilderState<T> newSnapshot()
             throws SAXException {
         StackNode<T>[] listCopy = new StackNode[listPtr + 1];
         for (int i = 0; i < listCopy.length; i++) {
             StackNode<T> node = listOfActiveFormattingElements[i];
             if (node != null) {
-                StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
+                StackNode<T> newNode = new StackNode<T>(-1);
+                newNode.setValues(node.getFlags(), node.ns,
                         node.name, node.node, node.popName,
                         node.attributes.cloneAttributes(null)
                         // [NOCPP[
                         , node.getLocator()
-                // ]NOCPP]
+                        // ]NOCPP]
                 );
                 listCopy[i] = newNode;
             } else {
                 listCopy[i] = null;
             }
         }
         StackNode<T>[] stackCopy = new StackNode[currentPtr + 1];
         for (int i = 0; i < stackCopy.length; i++) {
             StackNode<T> node = stack[i];
             int listIndex = findInListOfActiveFormattingElements(node);
             if (listIndex == -1) {
-                StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
+                StackNode<T> newNode = new StackNode<T>(-1);
+                newNode.setValues(node.getFlags(), node.ns,
                         node.name, node.node, node.popName,
                         null
                         // [NOCPP[
                         , node.getLocator()
-                // ]NOCPP]
+                        // ]NOCPP]
                 );
                 stackCopy[i] = newNode;
             } else {
                 stackCopy[i] = listCopy[listIndex];
                 stackCopy[i].retain();
             }
         }
         int[] templateModeStackCopy = new int[templateModePtr + 1];
@@ -6035,58 +6179,58 @@ public abstract class TreeBuilder<T> imp
         int stackLen = snapshot.getStackLength();
         StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
         int listLen = snapshot.getListOfActiveFormattingElementsLength();
         int[] templateModeStackCopy = snapshot.getTemplateModeStack();
         int templateModeStackLen = snapshot.getTemplateModeStackLength();
 
         for (int i = 0; i <= listPtr; i++) {
             if (listOfActiveFormattingElements[i] != null) {
-                listOfActiveFormattingElements[i].release();
+                listOfActiveFormattingElements[i].release(this);
             }
         }
         if (listOfActiveFormattingElements.length < listLen) {
             listOfActiveFormattingElements = new StackNode[listLen];
         }
         listPtr = listLen - 1;
 
         for (int i = 0; i <= currentPtr; i++) {
-            stack[i].release();
+            stack[i].release(this);
         }
         if (stack.length < stackLen) {
             stack = new StackNode[stackLen];
         }
         currentPtr = stackLen - 1;
 
         if (templateModeStack.length < templateModeStackLen) {
             templateModeStack = new int[templateModeStackLen];
         }
         templateModePtr = templateModeStackLen - 1;
 
         for (int i = 0; i < listLen; i++) {
             StackNode<T> node = listCopy[i];
             if (node != null) {
-                StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
+                StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
                         Portability.newLocalFromLocal(node.name, interner), node.node,
                         Portability.newLocalFromLocal(node.popName, interner),
                         node.attributes.cloneAttributes(null)
                         // [NOCPP[
                         , node.getLocator()
                 // ]NOCPP]
                 );
                 listOfActiveFormattingElements[i] = newNode;
             } else {
                 listOfActiveFormattingElements[i] = null;
             }
         }
         for (int i = 0; i < stackLen; i++) {
             StackNode<T> node = stackCopy[i];
             int listIndex = findInArray(node, listCopy);
             if (listIndex == -1) {
-                StackNode<T> newNode = new StackNode<T>(node.getFlags(), node.ns,
+                StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
                         Portability.newLocalFromLocal(node.name, interner), node.node,
                         Portability.newLocalFromLocal(node.popName, interner),
                         null
                         // [NOCPP[
                         , node.getLocator()
                 // ]NOCPP]
                 );
                 stack[i] = newNode;
--- a/parser/html/nsHtml5ElementName.cpp
+++ b/parser/html/nsHtml5ElementName.cpp
@@ -636,17 +636,17 @@ nsHtml5ElementName::initializeStatics()
     nsGkAtoms::mpath, nsGkAtoms::mpath, nsHtml5TreeBuilder::OTHER);
   ELT_PATH = new nsHtml5ElementName(
     nsGkAtoms::path, nsGkAtoms::path, nsHtml5TreeBuilder::OTHER);
   ELT_TH = new nsHtml5ElementName(nsGkAtoms::th,
                                   nsGkAtoms::th,
                                   nsHtml5TreeBuilder::TD_OR_TH | SPECIAL |
                                     SCOPING | OPTIONAL_END_TAG);
   ELT_SWITCH = new nsHtml5ElementName(
-    nsGkAtoms::_switch, nsGkAtoms::_switch, nsHtml5TreeBuilder::OTHER);
+    nsGkAtoms::svgSwitch, nsGkAtoms::svgSwitch, nsHtml5TreeBuilder::OTHER);
   ELT_TEXTPATH = new nsHtml5ElementName(
     nsGkAtoms::textpath, nsGkAtoms::textPath, nsHtml5TreeBuilder::OTHER);
   ELT_LI =
     new nsHtml5ElementName(nsGkAtoms::li,
                            nsGkAtoms::li,
                            nsHtml5TreeBuilder::LI | SPECIAL | OPTIONAL_END_TAG);
   ELT_MI = new nsHtml5ElementName(nsGkAtoms::mi_,
                                   nsGkAtoms::mi_,
--- a/parser/html/nsHtml5StackNode.cpp
+++ b/parser/html/nsHtml5StackNode.cpp
@@ -80,101 +80,119 @@ nsHtml5StackNode::isFosterParenting()
 }
 
 bool 
 nsHtml5StackNode::isHtmlIntegrationPoint()
 {
   return (flags & nsHtml5ElementName::HTML_INTEGRATION_POINT);
 }
 
-
-nsHtml5StackNode::nsHtml5StackNode(int32_t flags, int32_t ns, nsIAtom* name, nsIContentHandle* node, nsIAtom* popName, nsHtml5HtmlAttributes* attributes)
-  : flags(flags),
-    name(name),
-    popName(popName),
-    ns(ns),
-    node(node),
-    attributes(attributes),
-    refcount(1)
+nsHtml5StackNode::nsHtml5StackNode(int32_t idxInTreeBuilder)
+  : idxInTreeBuilder(idxInTreeBuilder)
+  , refcount(0)
 {
   MOZ_COUNT_CTOR(nsHtml5StackNode);
 }
 
-nsHtml5StackNode::nsHtml5StackNode(nsHtml5ElementName* elementName,
-                                   nsIContentHandle* node)
-  : flags(elementName->getFlags())
-  , name(elementName->getName())
-  , popName(elementName->getName())
-  , ns(kNameSpaceID_XHTML)
-  , node(node)
-  , attributes(nullptr)
-  , refcount(1)
+void
+nsHtml5StackNode::setValues(int32_t flags,
+                            int32_t ns,
+                            nsIAtom* name,
+                            nsIContentHandle* node,
+                            nsIAtom* popName,
+                            nsHtml5HtmlAttributes* attributes)
 {
-  MOZ_COUNT_CTOR(nsHtml5StackNode);
+  MOZ_ASSERT(isUnused());
+  this->flags = flags;
+  this->name = name;
+  this->popName = popName;
+  this->ns = ns;
+  this->node = node;
+  this->attributes = attributes;
+  this->refcount = 1;
+}
+
+void
+nsHtml5StackNode::setValues(nsHtml5ElementName* elementName,
+                            nsIContentHandle* node)
+{
+  MOZ_ASSERT(isUnused());
+  this->flags = elementName->getFlags();
+  this->name = elementName->getName();
+  this->popName = elementName->getName();
+  this->ns = kNameSpaceID_XHTML;
+  this->node = node;
+  this->attributes = nullptr;
+  this->refcount = 1;
   MOZ_ASSERT(elementName->isInterned(),
              "Don't use this constructor for custom elements.");
 }
 
-nsHtml5StackNode::nsHtml5StackNode(nsHtml5ElementName* elementName,
-                                   nsIContentHandle* node,
-                                   nsHtml5HtmlAttributes* attributes)
-  : flags(elementName->getFlags())
-  , name(elementName->getName())
-  , popName(elementName->getName())
-  , ns(kNameSpaceID_XHTML)
-  , node(node)
-  , attributes(attributes)
-  , refcount(1)
+void
+nsHtml5StackNode::setValues(nsHtml5ElementName* elementName,
+                            nsIContentHandle* node,
+                            nsHtml5HtmlAttributes* attributes)
 {
-  MOZ_COUNT_CTOR(nsHtml5StackNode);
+  MOZ_ASSERT(isUnused());
+  this->flags = elementName->getFlags();
+  this->name = elementName->getName();
+  this->popName = elementName->getName();
+  this->ns = kNameSpaceID_XHTML;
+  this->node = node;
+  this->attributes = attributes;
+  this->refcount = 1;
   MOZ_ASSERT(elementName->isInterned(),
              "Don't use this constructor for custom elements.");
 }
 
-nsHtml5StackNode::nsHtml5StackNode(nsHtml5ElementName* elementName,
-                                   nsIContentHandle* node,
-                                   nsIAtom* popName)
-  : flags(elementName->getFlags())
-  , name(elementName->getName())
-  , popName(popName)
-  , ns(kNameSpaceID_XHTML)
-  , node(node)
-  , attributes(nullptr)
-  , refcount(1)
+void
+nsHtml5StackNode::setValues(nsHtml5ElementName* elementName,
+                            nsIContentHandle* node,
+                            nsIAtom* popName)
 {
-  MOZ_COUNT_CTOR(nsHtml5StackNode);
+  MOZ_ASSERT(isUnused());
+  this->flags = elementName->getFlags();
+  this->name = elementName->getName();
+  this->popName = popName;
+  this->ns = kNameSpaceID_XHTML;
+  this->node = node;
+  this->attributes = nullptr;
+  this->refcount = 1;
 }
 
-nsHtml5StackNode::nsHtml5StackNode(nsHtml5ElementName* elementName,
-                                   nsIAtom* popName,
-                                   nsIContentHandle* node)
-  : flags(prepareSvgFlags(elementName->getFlags()))
-  , name(elementName->getName())
-  , popName(popName)
-  , ns(kNameSpaceID_SVG)
-  , node(node)
-  , attributes(nullptr)
-  , refcount(1)
+void
+nsHtml5StackNode::setValues(nsHtml5ElementName* elementName,
+                            nsIAtom* popName,
+                            nsIContentHandle* node)
 {
-  MOZ_COUNT_CTOR(nsHtml5StackNode);
+  MOZ_ASSERT(isUnused());
+  this->flags = prepareSvgFlags(elementName->getFlags());
+  this->name = elementName->getName();
+  this->popName = popName;
+  this->ns = kNameSpaceID_SVG;
+  this->node = node;
+  this->attributes = nullptr;
+  this->refcount = 1;
 }
 
-nsHtml5StackNode::nsHtml5StackNode(nsHtml5ElementName* elementName,
-                                   nsIContentHandle* node,
-                                   nsIAtom* popName,
-                                   bool markAsIntegrationPoint)
-  : flags(prepareMathFlags(elementName->getFlags(), markAsIntegrationPoint))
-  , name(elementName->getName())
-  , popName(popName)
-  , ns(kNameSpaceID_MathML)
-  , node(node)
-  , attributes(nullptr)
-  , refcount(1)
+void
+nsHtml5StackNode::setValues(nsHtml5ElementName* elementName,
+                            nsIContentHandle* node,
+                            nsIAtom* popName,
+                            bool markAsIntegrationPoint)
 {
-  MOZ_COUNT_CTOR(nsHtml5StackNode);
+  MOZ_ASSERT(isUnused());
+  this->flags =
+    prepareMathFlags(elementName->getFlags(), markAsIntegrationPoint);
+  this->name = elementName->getName();
+  this->popName = popName;
+  this->ns = kNameSpaceID_MathML;
+  this->node = node;
+  this->attributes = nullptr;
+  this->refcount = 1;
 }
 
 int32_t 
 nsHtml5StackNode::prepareSvgFlags(int32_t flags)
 {
   flags &=
     ~(nsHtml5ElementName::FOSTER_PARENTING | nsHtml5ElementName::SCOPING |
       nsHtml5ElementName::SPECIAL | nsHtml5ElementName::OPTIONAL_END_TAG);
@@ -199,40 +217,52 @@ nsHtml5StackNode::prepareMathFlags(int32
   }
   return flags;
 }
 
 
 nsHtml5StackNode::~nsHtml5StackNode()
 {
   MOZ_COUNT_DTOR(nsHtml5StackNode);
-  delete attributes;
 }
 
 void 
 nsHtml5StackNode::dropAttributes()
 {
   attributes = nullptr;
 }
 
 void 
 nsHtml5StackNode::retain()
 {
   refcount++;
 }
 
-void 
-nsHtml5StackNode::release()
+void
+nsHtml5StackNode::release(nsHtml5TreeBuilder* owningTreeBuilder)
 {
   refcount--;
+  MOZ_ASSERT(refcount >= 0);
   if (!refcount) {
-    delete this;
+    delete attributes;
+    if (idxInTreeBuilder >= 0) {
+      owningTreeBuilder->notifyUnusedStackNode(idxInTreeBuilder);
+    } else {
+      MOZ_ASSERT(!owningTreeBuilder);
+      delete this;
+    }
   }
 }
 
+bool
+nsHtml5StackNode::isUnused()
+{
+  return !refcount;
+}
+
 void
 nsHtml5StackNode::initializeStatics()
 {
 }
 
 void
 nsHtml5StackNode::releaseStatics()
 {
--- a/parser/html/nsHtml5StackNode.h
+++ b/parser/html/nsHtml5StackNode.h
@@ -55,16 +55,17 @@ class nsHtml5MetaScanner;
 class nsHtml5UTF16Buffer;
 class nsHtml5StateSnapshot;
 class nsHtml5Portability;
 
 
 class nsHtml5StackNode
 {
   public:
+    int32_t idxInTreeBuilder;
     int32_t flags;
     nsIAtom* name;
     nsIAtom* popName;
     int32_t ns;
     nsIContentHandle* node;
     nsHtml5HtmlAttributes* attributes;
   private:
     int32_t refcount;
@@ -74,28 +75,45 @@ class nsHtml5StackNode
       return flags;
     }
 
     int32_t getGroup();
     bool isScoping();
     bool isSpecial();
     bool isFosterParenting();
     bool isHtmlIntegrationPoint();
-    nsHtml5StackNode(int32_t flags, int32_t ns, nsIAtom* name, nsIContentHandle* node, nsIAtom* popName, nsHtml5HtmlAttributes* attributes);
-    nsHtml5StackNode(nsHtml5ElementName* elementName, nsIContentHandle* node);
-    nsHtml5StackNode(nsHtml5ElementName* elementName, nsIContentHandle* node, nsHtml5HtmlAttributes* attributes);
-    nsHtml5StackNode(nsHtml5ElementName* elementName, nsIContentHandle* node, nsIAtom* popName);
-    nsHtml5StackNode(nsHtml5ElementName* elementName, nsIAtom* popName, nsIContentHandle* node);
-    nsHtml5StackNode(nsHtml5ElementName* elementName, nsIContentHandle* node, nsIAtom* popName, bool markAsIntegrationPoint);
+    explicit nsHtml5StackNode(int32_t idxInTreeBuilder);
+    void setValues(int32_t flags,
+                   int32_t ns,
+                   nsIAtom* name,
+                   nsIContentHandle* node,
+                   nsIAtom* popName,
+                   nsHtml5HtmlAttributes* attributes);
+    void setValues(nsHtml5ElementName* elementName, nsIContentHandle* node);
+    void setValues(nsHtml5ElementName* elementName,
+                   nsIContentHandle* node,
+                   nsHtml5HtmlAttributes* attributes);
+    void setValues(nsHtml5ElementName* elementName,
+                   nsIContentHandle* node,
+                   nsIAtom* popName);
+    void setValues(nsHtml5ElementName* elementName,
+                   nsIAtom* popName,
+                   nsIContentHandle* node);
+    void setValues(nsHtml5ElementName* elementName,
+                   nsIContentHandle* node,
+                   nsIAtom* popName,
+                   bool markAsIntegrationPoint);
+
   private:
     static int32_t prepareSvgFlags(int32_t flags);
     static int32_t prepareMathFlags(int32_t flags, bool markAsIntegrationPoint);
   public:
     ~nsHtml5StackNode();
     void dropAttributes();
     void retain();
-    void release();
+    void release(nsHtml5TreeBuilder* owningTreeBuilder);
+    bool isUnused();
     static void initializeStatics();
     static void releaseStatics();
 };
 
 #endif
 
--- a/parser/html/nsHtml5StateSnapshot.cpp
+++ b/parser/html/nsHtml5StateSnapshot.cpp
@@ -155,21 +155,21 @@ nsHtml5StateSnapshot::getTemplateModeSta
   return templateModeStack.length;
 }
 
 
 nsHtml5StateSnapshot::~nsHtml5StateSnapshot()
 {
   MOZ_COUNT_DTOR(nsHtml5StateSnapshot);
   for (int32_t i = 0; i < stack.length; i++) {
-    stack[i]->release();
+    stack[i]->release(nullptr);
   }
   for (int32_t i = 0; i < listOfActiveFormattingElements.length; i++) {
     if (listOfActiveFormattingElements[i]) {
-      listOfActiveFormattingElements[i]->release();
+      listOfActiveFormattingElements[i]->release(nullptr);
     }
   }
 }
 
 void
 nsHtml5StateSnapshot::initializeStatics()
 {
 }
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -69,22 +69,25 @@
 
 char16_t nsHtml5TreeBuilder::REPLACEMENT_CHARACTER[] = { 0xfffd };
 static const char* const QUIRKY_PUBLIC_IDS_DATA[] = { "+//silmaril//dtd html pro v0r11 19970101//", "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", "-//as//dtd html 3.0 aswedit + extensions//", "-//ietf//dtd html 2.0 level 1//", "-//ietf//dtd html 2.0 level 2//", "-//ietf//dtd html 2.0 strict level 1//", "-//ietf//dtd html 2.0 strict level 2//", "-//ietf//dtd html 2.0 strict//", "-//ietf//dtd html 2.0//", "-//ietf//dtd html 2.1e//", "-//ietf//dtd html 3.0//", "-//ietf//dtd html 3.2 final//", "-//ietf//dtd html 3.2//", "-//ietf//dtd html 3//", "-//ietf//dtd html level 0//", "-//ietf//dtd html level 1//", "-//ietf//dtd html level 2//", "-//ietf//dtd html level 3//", "-//ietf//dtd html strict level 0//", "-//ietf//dtd html strict level 1//", "-//ietf//dtd html strict level 2//", "-//ietf//dtd html strict level 3//", "-//ietf//dtd html strict//", "-//ietf//dtd html//", "-//metrius//dtd metrius presentational//", "-//microsoft//dtd internet explorer 2.0 html strict//", "-//microsoft//dtd internet explorer 2.0 html//", "-//microsoft//dtd internet explorer 2.0 tables//", "-//microsoft//dtd internet explorer 3.0 html strict//", "-//microsoft//dtd internet explorer 3.0 html//", "-//microsoft//dtd internet explorer 3.0 tables//", "-//netscape comm. corp.//dtd html//", "-//netscape comm. corp.//dtd strict html//", "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", "-//spyglass//dtd html 2.0 extended//", "-//sq//dtd html 2.0 hotmetal + extensions//", "-//sun microsystems corp.//dtd hotjava html//", "-//sun microsystems corp.//dtd hotjava strict html//", "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", "-//w3c//dtd html 4.0 transitional//", "-//w3c//dtd html experimental 19960712//", "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", "-//webtechs//dtd mozilla html//" };
 staticJArray<const char*,int32_t> nsHtml5TreeBuilder::QUIRKY_PUBLIC_IDS = { QUIRKY_PUBLIC_IDS_DATA, MOZ_ARRAY_LENGTH(QUIRKY_PUBLIC_IDS_DATA) };
 void 
 nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self)
 {
   tokenizer = self;
+  stackNodes = jArray<nsHtml5StackNode*, int32_t>::newJArray(64);
   stack = jArray<nsHtml5StackNode*,int32_t>::newJArray(64);
   templateModeStack = jArray<int32_t,int32_t>::newJArray(64);
   listOfActiveFormattingElements = jArray<nsHtml5StackNode*,int32_t>::newJArray(64);
   needToDropLF = false;
   originalMode = INITIAL;
   templateModePtr = -1;
+  stackNodesIdx = 0;
+  numStackNodes = 0;
   currentPtr = -1;
   listPtr = -1;
   formPointer = nullptr;
   headPointer = nullptr;
   deepTreeSurrogateParent = nullptr;
   start(fragment);
   charBufferLen = 0;
   charBuffer = nullptr;
@@ -98,40 +101,41 @@ nsHtml5TreeBuilder::startTokenization(ns
     }
     if (contextNamespace == kNameSpaceID_SVG) {
       nsHtml5ElementName* elementName = nsHtml5ElementName::ELT_SVG;
       if (nsGkAtoms::title == contextName || nsGkAtoms::desc == contextName ||
           nsGkAtoms::foreignObject == contextName) {
         elementName = nsHtml5ElementName::ELT_FOREIGNOBJECT;
       }
       nsHtml5StackNode* node =
-        new nsHtml5StackNode(elementName, elementName->getCamelCaseName(), elt);
+        createStackNode(elementName, elementName->getCamelCaseName(), elt);
       currentPtr++;
       stack[currentPtr] = node;
       tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::DATA,
                                               contextName);
       mode = FRAMESET_OK;
     } else if (contextNamespace == kNameSpaceID_MathML) {
       nsHtml5ElementName* elementName = nsHtml5ElementName::ELT_MATH;
       if (nsGkAtoms::mi_ == contextName || nsGkAtoms::mo_ == contextName ||
           nsGkAtoms::mn_ == contextName || nsGkAtoms::ms_ == contextName ||
           nsGkAtoms::mtext_ == contextName) {
         elementName = nsHtml5ElementName::ELT_MTEXT;
       } else if (nsGkAtoms::annotation_xml_ == contextName) {
         elementName = nsHtml5ElementName::ELT_ANNOTATION_XML;
       }
       nsHtml5StackNode* node =
-        new nsHtml5StackNode(elementName, elt, elementName->getName(), false);
+        createStackNode(elementName, elt, elementName->getName(), false);
       currentPtr++;
       stack[currentPtr] = node;
       tokenizer->setStateAndEndTagExpectation(nsHtml5Tokenizer::DATA,
                                               contextName);
       mode = FRAMESET_OK;
     } else {
-      nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_HTML, elt);
+      nsHtml5StackNode* node =
+        createStackNode(nsHtml5ElementName::ELT_HTML, elt);
       currentPtr++;
       stack[currentPtr] = node;
       if (nsGkAtoms::_template == contextName) {
         pushTemplateMode(IN_TEMPLATE);
       }
       resetTheInsertionMode();
       formPointer = getFormPointerForContext(contextNode);
       if (nsGkAtoms::title == contextName ||
@@ -162,17 +166,17 @@ nsHtml5TreeBuilder::startTokenization(ns
   } else {
     mode = INITIAL;
     if (tokenizer->isViewingXmlSource()) {
       nsIContentHandle* elt = createElement(kNameSpaceID_SVG,
                                             nsGkAtoms::svg,
                                             tokenizer->emptyAttributes(),
                                             nullptr);
       nsHtml5StackNode* node =
-        new nsHtml5StackNode(nsHtml5ElementName::ELT_SVG, nsGkAtoms::svg, elt);
+        createStackNode(nsHtml5ElementName::ELT_SVG, nsGkAtoms::svg, elt);
       currentPtr++;
       stack[currentPtr] = node;
     }
   }
 }
 
 void
 nsHtml5TreeBuilder::doctype(nsIAtom* name,
@@ -603,30 +607,39 @@ void
 nsHtml5TreeBuilder::endTokenization()
 {
   formPointer = nullptr;
   headPointer = nullptr;
   deepTreeSurrogateParent = nullptr;
   templateModeStack = nullptr;
   if (stack) {
     while (currentPtr > -1) {
-      stack[currentPtr]->release();
+      stack[currentPtr]->release(this);
       currentPtr--;
     }
     stack = nullptr;
   }
   if (listOfActiveFormattingElements) {
     while (listPtr > -1) {
       if (listOfActiveFormattingElements[listPtr]) {
-        listOfActiveFormattingElements[listPtr]->release();
+        listOfActiveFormattingElements[listPtr]->release(this);
       }
       listPtr--;
     }
     listOfActiveFormattingElements = nullptr;
   }
+  if (stackNodes) {
+    for (int32_t i = 0; i < numStackNodes; i++) {
+      MOZ_ASSERT(stackNodes[i]->isUnused());
+      delete stackNodes[i];
+    }
+    numStackNodes = 0;
+    stackNodesIdx = 0;
+    stackNodes = nullptr;
+  }
   charBuffer = nullptr;
   end();
 }
 
 void 
 nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes, bool selfClosing)
 {
   flushCharacters();
@@ -1176,17 +1189,17 @@ nsHtml5TreeBuilder::startTag(nsHtml5Elem
                 nsHtml5StackNode* activeA = listOfActiveFormattingElements[activeAPos];
                 activeA->retain();
                 adoptionAgencyEndTag(nsGkAtoms::a);
                 removeFromStack(activeA);
                 activeAPos = findInListOfActiveFormattingElements(activeA);
                 if (activeAPos != -1) {
                   removeFromListOfActiveFormattingElements(activeAPos);
                 }
-                activeA->release();
+                activeA->release(this);
               }
               reconstructTheActiveFormattingElements();
               appendToCurrentNodeAndPushFormattingElementMayFoster(elementName, attributes);
               attributes = nullptr;
               NS_HTML5_BREAK(starttagloop);
             }
             case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
             case FONT: {
@@ -3586,29 +3599,29 @@ nsHtml5TreeBuilder::append(nsHtml5StackN
 void 
 nsHtml5TreeBuilder::clearTheListOfActiveFormattingElementsUpToTheLastMarker()
 {
   while (listPtr > -1) {
     if (!listOfActiveFormattingElements[listPtr]) {
       --listPtr;
       return;
     }
-    listOfActiveFormattingElements[listPtr]->release();
+    listOfActiveFormattingElements[listPtr]->release(this);
     --listPtr;
   }
 }
 
 void 
 nsHtml5TreeBuilder::removeFromStack(int32_t pos)
 {
   if (currentPtr == pos) {
     pop();
   } else {
 
-    stack[pos]->release();
+    stack[pos]->release(this);
     nsHtml5ArrayCopy::arraycopy(stack, pos + 1, pos, currentPtr - pos);
     MOZ_ASSERT(debugOnlyClearLastStackSlot());
     currentPtr--;
   }
 }
 
 void 
 nsHtml5TreeBuilder::removeFromStack(nsHtml5StackNode* node)
@@ -3619,27 +3632,27 @@ nsHtml5TreeBuilder::removeFromStack(nsHt
     int32_t pos = currentPtr - 1;
     while (pos >= 0 && stack[pos] != node) {
       pos--;
     }
     if (pos == -1) {
       return;
     }
 
-    node->release();
+    node->release(this);
     nsHtml5ArrayCopy::arraycopy(stack, pos + 1, pos, currentPtr - pos);
     currentPtr--;
   }
 }
 
 void 
 nsHtml5TreeBuilder::removeFromListOfActiveFormattingElements(int32_t pos)
 {
   MOZ_ASSERT(!!listOfActiveFormattingElements[pos]);
-  listOfActiveFormattingElements[pos]->release();
+  listOfActiveFormattingElements[pos]->release(this);
   if (pos == listPtr) {
     MOZ_ASSERT(debugOnlyClearLastListSlot());
     listPtr--;
     return;
   }
   MOZ_ASSERT(pos < listPtr);
   nsHtml5ArrayCopy::arraycopy(listOfActiveFormattingElements, pos + 1, pos, listPtr - pos);
   MOZ_ASSERT(debugOnlyClearLastListSlot());
@@ -3741,38 +3754,49 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag
         continue;
       }
       if (nodePos == furthestBlockPos) {
         bookmark = nodeListPos + 1;
       }
       MOZ_ASSERT(node == listOfActiveFormattingElements[nodeListPos]);
       MOZ_ASSERT(node == stack[nodePos]);
       nsIContentHandle* clone = createElement(kNameSpaceID_XHTML, node->name, node->attributes->cloneAttributes(nullptr), commonAncestor->node);
-      nsHtml5StackNode* newNode = new nsHtml5StackNode(node->getFlags(), node->ns, node->name, clone, node->popName, node->attributes);
+      nsHtml5StackNode* newNode = createStackNode(node->getFlags(),
+                                                  node->ns,
+                                                  node->name,
+                                                  clone,
+                                                  node->popName,
+                                                  node->attributes);
       node->dropAttributes();
       stack[nodePos] = newNode;
       newNode->retain();
       listOfActiveFormattingElements[nodeListPos] = newNode;
-      node->release();
-      node->release();
+      node->release(this);
+      node->release(this);
       node = newNode;
       detachFromParent(lastNode->node);
       appendElement(lastNode->node, node->node);
       lastNode = node;
     }
     if (commonAncestor->isFosterParenting()) {
 
       detachFromParent(lastNode->node);
       insertIntoFosterParent(lastNode->node);
     } else {
       detachFromParent(lastNode->node);
       appendElement(lastNode->node, commonAncestor->node);
     }
     nsIContentHandle* clone = createElement(kNameSpaceID_XHTML, formattingElt->name, formattingElt->attributes->cloneAttributes(nullptr), furthestBlock->node);
-    nsHtml5StackNode* formattingClone = new nsHtml5StackNode(formattingElt->getFlags(), formattingElt->ns, formattingElt->name, clone, formattingElt->popName, formattingElt->attributes);
+    nsHtml5StackNode* formattingClone =
+      createStackNode(formattingElt->getFlags(),
+                      formattingElt->ns,
+                      formattingElt->name,
+                      clone,
+                      formattingElt->popName,
+                      formattingElt->attributes);
     formattingElt->dropAttributes();
     appendChildrenToNewParent(furthestBlock->node, clone);
     appendElement(clone, furthestBlock->node);
     removeFromListOfActiveFormattingElements(formattingEltListPos);
     insertIntoListOfActiveFormattingElements(formattingClone, bookmark);
     MOZ_ASSERT(formattingEltStackPos < furthestBlockPos);
     removeFromStack(formattingEltStackPos);
     insertIntoStack(formattingClone, furthestBlockPos);
@@ -3893,17 +3917,17 @@ nsHtml5TreeBuilder::addAttributesToHtml(
 }
 
 void 
 nsHtml5TreeBuilder::pushHeadPointerOntoStack()
 {
   MOZ_ASSERT(!!headPointer);
   MOZ_ASSERT(mode == AFTER_HEAD);
 
-  silentPush(new nsHtml5StackNode(nsHtml5ElementName::ELT_HEAD, headPointer));
+  silentPush(createStackNode(nsHtml5ElementName::ELT_HEAD, headPointer));
 }
 
 void 
 nsHtml5TreeBuilder::reconstructTheActiveFormattingElements()
 {
   if (listPtr == -1) {
     return;
   }
@@ -3930,26 +3954,125 @@ nsHtml5TreeBuilder::reconstructTheActive
     nsHtml5StackNode* currentNode = stack[currentPtr];
     nsIContentHandle* clone;
     if (currentNode->isFosterParenting()) {
       clone = createAndInsertFosterParentedElement(kNameSpaceID_XHTML, entry->name, entry->attributes->cloneAttributes(nullptr));
     } else {
       clone = createElement(kNameSpaceID_XHTML, entry->name, entry->attributes->cloneAttributes(nullptr), currentNode->node);
       appendElement(clone, currentNode->node);
     }
-    nsHtml5StackNode* entryClone = new nsHtml5StackNode(entry->getFlags(), entry->ns, entry->name, clone, entry->popName, entry->attributes);
+    nsHtml5StackNode* entryClone = createStackNode(entry->getFlags(),
+                                                   entry->ns,
+                                                   entry->name,
+                                                   clone,
+                                                   entry->popName,
+                                                   entry->attributes);
     entry->dropAttributes();
     push(entryClone);
     listOfActiveFormattingElements[entryPos] = entryClone;
-    entry->release();
+    entry->release(this);
     entryClone->retain();
   }
 }
 
-void 
+void
+nsHtml5TreeBuilder::notifyUnusedStackNode(int32_t idxInStackNodes)
+{
+  if (idxInStackNodes < stackNodesIdx) {
+    stackNodesIdx = idxInStackNodes;
+  }
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::getUnusedStackNode()
+{
+  while (stackNodesIdx < numStackNodes) {
+    if (stackNodes[stackNodesIdx]->isUnused()) {
+      return stackNodes[stackNodesIdx++];
+    }
+    stackNodesIdx++;
+  }
+  if (stackNodesIdx < stackNodes.length) {
+    stackNodes[stackNodesIdx] = new nsHtml5StackNode(stackNodesIdx);
+    numStackNodes++;
+    return stackNodes[stackNodesIdx++];
+  }
+  jArray<nsHtml5StackNode*, int32_t> newStack =
+    jArray<nsHtml5StackNode*, int32_t>::newJArray(stackNodes.length + 64);
+  nsHtml5ArrayCopy::arraycopy(stackNodes, newStack, stackNodes.length);
+  stackNodes = newStack;
+  stackNodes[stackNodesIdx] = new nsHtml5StackNode(stackNodesIdx);
+  numStackNodes++;
+  return stackNodes[stackNodesIdx++];
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::createStackNode(int32_t flags,
+                                    int32_t ns,
+                                    nsIAtom* name,
+                                    nsIContentHandle* node,
+                                    nsIAtom* popName,
+                                    nsHtml5HtmlAttributes* attributes)
+{
+  nsHtml5StackNode* instance = getUnusedStackNode();
+  instance->setValues(flags, ns, name, node, popName, attributes);
+  return instance;
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::createStackNode(nsHtml5ElementName* elementName,
+                                    nsIContentHandle* node)
+{
+  nsHtml5StackNode* instance = getUnusedStackNode();
+  instance->setValues(elementName, node);
+  return instance;
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::createStackNode(nsHtml5ElementName* elementName,
+                                    nsIContentHandle* node,
+                                    nsHtml5HtmlAttributes* attributes)
+{
+  nsHtml5StackNode* instance = getUnusedStackNode();
+  instance->setValues(elementName, node, attributes);
+  return instance;
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::createStackNode(nsHtml5ElementName* elementName,
+                                    nsIContentHandle* node,
+                                    nsIAtom* popName)
+{
+  nsHtml5StackNode* instance = getUnusedStackNode();
+  instance->setValues(elementName, node, popName);
+  return instance;
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::createStackNode(nsHtml5ElementName* elementName,
+                                    nsIAtom* popName,
+                                    nsIContentHandle* node)
+{
+  nsHtml5StackNode* instance = getUnusedStackNode();
+  instance->setValues(elementName, popName, node);
+  return instance;
+}
+
+nsHtml5StackNode*
+nsHtml5TreeBuilder::createStackNode(nsHtml5ElementName* elementName,
+                                    nsIContentHandle* node,
+                                    nsIAtom* popName,
+                                    bool markAsIntegrationPoint)
+{
+  nsHtml5StackNode* instance = getUnusedStackNode();
+  instance->setValues(elementName, node, popName, markAsIntegrationPoint);
+  return instance;
+}
+
+void
 nsHtml5TreeBuilder::insertIntoFosterParent(nsIContentHandle* child)
 {
   int32_t tablePos = findLastOrRoot(nsHtml5TreeBuilder::TABLE);
   int32_t templatePos = findLastOrRoot(nsHtml5TreeBuilder::TEMPLATE);
   if (templatePos >= tablePos) {
     appendElement(child, stack[templatePos]->node);
     return;
   }
@@ -3996,44 +4119,44 @@ nsHtml5TreeBuilder::popTemplateMode()
 
 void 
 nsHtml5TreeBuilder::pop()
 {
   nsHtml5StackNode* node = stack[currentPtr];
   MOZ_ASSERT(debugOnlyClearLastStackSlot());
   currentPtr--;
   elementPopped(node->ns, node->popName, node->node);
-  node->release();
+  node->release(this);
 }
 
 void 
 nsHtml5TreeBuilder::silentPop()
 {
   nsHtml5StackNode* node = stack[currentPtr];
   MOZ_ASSERT(debugOnlyClearLastStackSlot());
   currentPtr--;
-  node->release();
+  node->release(this);
 }
 
 void 
 nsHtml5TreeBuilder::popOnEof()
 {
   nsHtml5StackNode* node = stack[currentPtr];
   MOZ_ASSERT(debugOnlyClearLastStackSlot());
   currentPtr--;
   markMalformedIfScript(node->node);
   elementPopped(node->ns, node->popName, node->node);
-  node->release();
+  node->release(this);
 }
 
 void 
 nsHtml5TreeBuilder::appendHtmlElementToDocumentAndPush(nsHtml5HtmlAttributes* attributes)
 {
   nsIContentHandle* elt = createHtmlElementSetAsRoot(attributes);
-  nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_HTML, elt);
+  nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_HTML, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendHtmlElementToDocumentAndPush()
 {
   appendHtmlElementToDocumentAndPush(tokenizer->emptyAttributes());
 }
@@ -4041,17 +4164,17 @@ nsHtml5TreeBuilder::appendHtmlElementToD
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushHeadElement(nsHtml5HtmlAttributes* attributes)
 {
   nsIContentHandle* currentNode = stack[currentPtr]->node;
   nsIContentHandle* elt =
     createElement(kNameSpaceID_XHTML, nsGkAtoms::head, attributes, currentNode);
   appendElement(elt, currentNode);
   headPointer = elt;
-  nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_HEAD, elt);
+  nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_HEAD, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushBodyElement(nsHtml5HtmlAttributes* attributes)
 {
   appendToCurrentNodeAndPushElement(nsHtml5ElementName::ELT_BODY, attributes);
 }
@@ -4074,17 +4197,17 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
   } else {
     elt = createElement(
       kNameSpaceID_XHTML, nsGkAtoms::form, attributes, current->node);
     appendElement(elt, current->node);
   }
   if (!isTemplateContents()) {
     formPointer = elt;
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_FORM, elt);
+  nsHtml5StackNode* node = createStackNode(nsHtml5ElementName::ELT_FORM, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
   nsHtml5HtmlAttributes* clone = attributes->cloneAttributes(nullptr);
   nsIContentHandle* elt;
@@ -4093,50 +4216,50 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
 
     elt = createAndInsertFosterParentedElement(
       kNameSpaceID_XHTML, elementName->getName(), attributes);
   } else {
     elt = createElement(
       kNameSpaceID_XHTML, elementName->getName(), attributes, current->node);
     appendElement(elt, current->node);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, clone);
+  nsHtml5StackNode* node = createStackNode(elementName, elt, clone);
   push(node);
   append(node);
   node->retain();
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushElement(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
   nsIContentHandle* currentNode = stack[currentPtr]->node;
   nsIContentHandle* elt = createElement(
     kNameSpaceID_XHTML, elementName->getName(), attributes, currentNode);
   appendElement(elt, currentNode);
   if (nsHtml5ElementName::ELT_TEMPLATE == elementName) {
     elt = getDocumentFragmentForTemplate(elt);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt);
+  nsHtml5StackNode* node = createStackNode(elementName, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFoster(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
   nsIAtom* popName = elementName->getName();
   nsIContentHandle* elt;
   nsHtml5StackNode* current = stack[currentPtr];
   if (current->isFosterParenting()) {
 
     elt = createAndInsertFosterParentedElement(kNameSpaceID_XHTML, popName, attributes);
   } else {
     elt = createElement(kNameSpaceID_XHTML, popName, attributes, current->node);
     appendElement(elt, current->node);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, popName);
+  nsHtml5StackNode* node = createStackNode(elementName, elt, popName);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFosterMathML(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
 {
   nsIAtom* popName = elementName->getName();
   bool markAsHtmlIntegrationPoint = false;
@@ -4147,17 +4270,18 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
   nsHtml5StackNode* current = stack[currentPtr];
   if (current->isFosterParenting()) {
 
     elt = createAndInsertFosterParentedElement(kNameSpaceID_MathML, popName, attributes);
   } else {
     elt = createElement(kNameSpaceID_MathML, popName, attributes, current->node);
     appendElement(elt, current->node);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, popName, markAsHtmlIntegrationPoint);
+  nsHtml5StackNode* node =
+    createStackNode(elementName, elt, popName, markAsHtmlIntegrationPoint);
   push(node);
 }
 
 bool 
 nsHtml5TreeBuilder::annotationXmlEncodingPermitsHtml(nsHtml5HtmlAttributes* attributes)
 {
   nsHtml5String encoding =
     attributes->getValue(nsHtml5AttributeName::ATTR_ENCODING);
@@ -4175,17 +4299,17 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
   nsHtml5StackNode* current = stack[currentPtr];
   if (current->isFosterParenting()) {
 
     elt = createAndInsertFosterParentedElement(kNameSpaceID_SVG, popName, attributes);
   } else {
     elt = createElement(kNameSpaceID_SVG, popName, attributes, current->node);
     appendElement(elt, current->node);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, popName, elt);
+  nsHtml5StackNode* node = createStackNode(elementName, popName, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendToCurrentNodeAndPushElementMayFoster(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes, nsIContentHandle* form)
 {
   nsIContentHandle* elt;
   nsIContentHandle* formOwner = !form || fragment || isTemplateContents() ? nullptr : form;
@@ -4197,17 +4321,17 @@ nsHtml5TreeBuilder::appendToCurrentNodeA
   } else {
     elt = createElement(kNameSpaceID_XHTML,
                         elementName->getName(),
                         attributes,
                         formOwner,
                         current->node);
     appendElement(elt, current->node);
   }
-  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt);
+  nsHtml5StackNode* node = createStackNode(elementName, elt);
   push(node);
 }
 
 void 
 nsHtml5TreeBuilder::appendVoidElementToCurrentMayFoster(nsIAtom* name, nsHtml5HtmlAttributes* attributes, nsIContentHandle* form)
 {
   nsIContentHandle* elt;
   nsIContentHandle* formOwner = !form || fragment || isTemplateContents() ? nullptr : form;
@@ -4403,28 +4527,40 @@ nsHtml5TreeBuilder::charBufferContainsNo
 
 nsAHtml5TreeBuilderState* 
 nsHtml5TreeBuilder::newSnapshot()
 {
   jArray<nsHtml5StackNode*,int32_t> listCopy = jArray<nsHtml5StackNode*,int32_t>::newJArray(listPtr + 1);
   for (int32_t i = 0; i < listCopy.length; i++) {
     nsHtml5StackNode* node = listOfActiveFormattingElements[i];
     if (node) {
-      nsHtml5StackNode* newNode = new nsHtml5StackNode(node->getFlags(), node->ns, node->name, node->node, node->popName, node->attributes->cloneAttributes(nullptr));
+      nsHtml5StackNode* newNode = new nsHtml5StackNode(-1);
+      newNode->setValues(node->getFlags(),
+                         node->ns,
+                         node->name,
+                         node->node,
+                         node->popName,
+                         node->attributes->cloneAttributes(nullptr));
       listCopy[i] = newNode;
     } else {
       listCopy[i] = nullptr;
     }
   }
   jArray<nsHtml5StackNode*,int32_t> stackCopy = jArray<nsHtml5StackNode*,int32_t>::newJArray(currentPtr + 1);
   for (int32_t i = 0; i < stackCopy.length; i++) {
     nsHtml5StackNode* node = stack[i];
     int32_t listIndex = findInListOfActiveFormattingElements(node);
     if (listIndex == -1) {
-      nsHtml5StackNode* newNode = new nsHtml5StackNode(node->getFlags(), node->ns, node->name, node->node, node->popName, nullptr);
+      nsHtml5StackNode* newNode = new nsHtml5StackNode(-1);
+      newNode->setValues(node->getFlags(),
+                         node->ns,
+                         node->name,
+                         node->node,
+                         node->popName,
+                         nullptr);
       stackCopy[i] = newNode;
     } else {
       stackCopy[i] = listCopy[listIndex];
       stackCopy[i]->retain();
     }
   }
   jArray<int32_t,int32_t> templateModeStackCopy = jArray<int32_t,int32_t>::newJArray(templateModePtr + 1);
   nsHtml5ArrayCopy::arraycopy(templateModeStack, templateModeStackCopy, templateModeStackCopy.length);
@@ -4472,48 +4608,60 @@ nsHtml5TreeBuilder::loadState(nsAHtml5Tr
   jArray<nsHtml5StackNode*,int32_t> stackCopy = snapshot->getStack();
   int32_t stackLen = snapshot->getStackLength();
   jArray<nsHtml5StackNode*,int32_t> listCopy = snapshot->getListOfActiveFormattingElements();
   int32_t listLen = snapshot->getListOfActiveFormattingElementsLength();
   jArray<int32_t,int32_t> templateModeStackCopy = snapshot->getTemplateModeStack();
   int32_t templateModeStackLen = snapshot->getTemplateModeStackLength();
   for (int32_t i = 0; i <= listPtr; i++) {
     if (listOfActiveFormattingElements[i]) {
-      listOfActiveFormattingElements[i]->release();
+      listOfActiveFormattingElements[i]->release(this);
     }
   }
   if (listOfActiveFormattingElements.length < listLen) {
     listOfActiveFormattingElements = jArray<nsHtml5StackNode*,int32_t>::newJArray(listLen);
   }
   listPtr = listLen - 1;
   for (int32_t i = 0; i <= currentPtr; i++) {
-    stack[i]->release();
+    stack[i]->release(this);
   }
   if (stack.length < stackLen) {
     stack = jArray<nsHtml5StackNode*,int32_t>::newJArray(stackLen);
   }
   currentPtr = stackLen - 1;
   if (templateModeStack.length < templateModeStackLen) {
     templateModeStack = jArray<int32_t,int32_t>::newJArray(templateModeStackLen);
   }
   templateModePtr = templateModeStackLen - 1;
   for (int32_t i = 0; i < listLen; i++) {
     nsHtml5StackNode* node = listCopy[i];
     if (node) {
-      nsHtml5StackNode* newNode = new nsHtml5StackNode(node->getFlags(), node->ns, nsHtml5Portability::newLocalFromLocal(node->name, interner), node->node, nsHtml5Portability::newLocalFromLocal(node->popName, interner), node->attributes->cloneAttributes(nullptr));
+      nsHtml5StackNode* newNode = createStackNode(
+        node->getFlags(),
+        node->ns,
+        nsHtml5Portability::newLocalFromLocal(node->name, interner),
+        node->node,
+        nsHtml5Portability::newLocalFromLocal(node->popName, interner),
+        node->attributes->cloneAttributes(nullptr));
       listOfActiveFormattingElements[i] = newNode;
     } else {
       listOfActiveFormattingElements[i] = nullptr;
     }
   }
   for (int32_t i = 0; i < stackLen; i++) {
     nsHtml5StackNode* node = stackCopy[i];
     int32_t listIndex = findInArray(node, listCopy);
     if (listIndex == -1) {
-      nsHtml5StackNode* newNode = new nsHtml5StackNode(node->getFlags(), node->ns, nsHtml5Portability::newLocalFromLocal(node->name, interner), node->node, nsHtml5Portability::newLocalFromLocal(node->popName, interner), nullptr);
+      nsHtml5StackNode* newNode = createStackNode(
+        node->getFlags(),
+        node->ns,
+        nsHtml5Portability::newLocalFromLocal(node->name, interner),
+        node->node,
+        nsHtml5Portability::newLocalFromLocal(node->popName, interner),
+        nullptr);
       stack[i] = newNode;
     } else {
       stack[i] = listOfActiveFormattingElements[listIndex];
       stack[i]->retain();
     }
   }
   nsHtml5ArrayCopy::arraycopy(templateModeStackCopy, templateModeStack, templateModeStackLen);
   formPointer = snapshot->getFormPointer();
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -296,16 +296,19 @@ class nsHtml5TreeBuilder : public nsAHtm
     bool scriptingEnabled;
     bool needToDropLF;
     bool fragment;
     nsIAtom* contextName;
     int32_t contextNamespace;
     nsIContentHandle* contextNode;
     autoJArray<int32_t,int32_t> templateModeStack;
     int32_t templateModePtr;
+    autoJArray<nsHtml5StackNode*, int32_t> stackNodes;
+    int32_t stackNodesIdx;
+    int32_t numStackNodes;
     autoJArray<nsHtml5StackNode*,int32_t> stack;
     int32_t currentPtr;
     autoJArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements;
     int32_t listPtr;
     nsIContentHandle* formPointer;
     nsIContentHandle* headPointer;
     nsIContentHandle* deepTreeSurrogateParent;
   protected:
@@ -396,16 +399,43 @@ class nsHtml5TreeBuilder : public nsAHtm
     int32_t findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker(nsIAtom* name);
     void maybeForgetEarlierDuplicateFormattingElement(nsIAtom* name, nsHtml5HtmlAttributes* attributes);
     int32_t findLastOrRoot(nsIAtom* name);
     int32_t findLastOrRoot(int32_t group);
     bool addAttributesToBody(nsHtml5HtmlAttributes* attributes);
     void addAttributesToHtml(nsHtml5HtmlAttributes* attributes);
     void pushHeadPointerOntoStack();
     void reconstructTheActiveFormattingElements();
+
+  public:
+    void notifyUnusedStackNode(int32_t idxInStackNodes);
+
+  private:
+    nsHtml5StackNode* getUnusedStackNode();
+    nsHtml5StackNode* createStackNode(int32_t flags,
+                                      int32_t ns,
+                                      nsIAtom* name,
+                                      nsIContentHandle* node,
+                                      nsIAtom* popName,
+                                      nsHtml5HtmlAttributes* attributes);
+    nsHtml5StackNode* createStackNode(nsHtml5ElementName* elementName,
+                                      nsIContentHandle* node);
+    nsHtml5StackNode* createStackNode(nsHtml5ElementName* elementName,
+                                      nsIContentHandle* node,
+                                      nsHtml5HtmlAttributes* attributes);
+    nsHtml5StackNode* createStackNode(nsHtml5ElementName* elementName,
+                                      nsIContentHandle* node,
+                                      nsIAtom* popName);
+    nsHtml5StackNode* createStackNode(nsHtml5ElementName* elementName,
+                                      nsIAtom* popName,
+                                      nsIContentHandle* node);
+    nsHtml5StackNode* createStackNode(nsHtml5ElementName* elementName,
+                                      nsIContentHandle* node,
+                                      nsIAtom* popName,
+                                      bool markAsIntegrationPoint);
     void insertIntoFosterParent(nsIContentHandle* child);
     nsIContentHandle* createAndInsertFosterParentedElement(int32_t ns, nsIAtom* name, nsHtml5HtmlAttributes* attributes);
     nsIContentHandle* createAndInsertFosterParentedElement(int32_t ns, nsIAtom* name, nsHtml5HtmlAttributes* attributes, nsIContentHandle* form);
     bool isInStack(nsHtml5StackNode* node);
     void popTemplateMode();
     void pop();
     void silentPop();
     void popOnEof();