Bug 888820 - Save template insertion mode stack information in TreeBuilderState. r=hsivonen, a=abillings
authorWilliam Chen <wchen@mozilla.com>
Mon, 12 Aug 2013 14:46:12 -0700
changeset 153741 2ae4fd73f6758eb0ba09cbf5b7f85165c5b6f488
parent 153740 0eadb85d3eecb510f28866b63626c612266546aa
child 153742 141f5c87fbf7db2468c3b31cc585af343301d438
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen, abillings
bugs888820
milestone25.0a2
Bug 888820 - Save template insertion mode stack information in TreeBuilderState. r=hsivonen, a=abillings
parser/html/javasrc/StateSnapshot.java
parser/html/javasrc/TreeBuilder.java
parser/html/nsAHtml5TreeBuilderState.h
parser/html/nsHtml5StateSnapshot.cpp
parser/html/nsHtml5StateSnapshot.h
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeBuilder.h
--- a/parser/html/javasrc/StateSnapshot.java
+++ b/parser/html/javasrc/StateSnapshot.java
@@ -26,16 +26,18 @@ import nu.validator.htmlparser.annotatio
 
 
 public class StateSnapshot<T> implements TreeBuilderState<T> {
 
     private final @Auto StackNode<T>[] stack;
 
     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;
 
@@ -45,27 +47,33 @@ public class StateSnapshot<T> implements
 
     private final boolean needToDropLF;
 
     private final boolean quirks;
 
     /**
      * @param stack
      * @param listOfActiveFormattingElements
+     * @param templateModeStack
      * @param formPointer
-     * @param quirks 
-     * @param needToDropLF 
-     * @param foreignFlag 
-     * @param originalMode 
-     * @param mode 
+     * @param headPointer
+     * @param deepTreeSurrogateParent
+     * @param mode
+     * @param originalMode
+     * @param framesetOk
+     * @param needToDropLF
+     * @param quirks
      */
     StateSnapshot(StackNode<T>[] stack,
-            StackNode<T>[] listOfActiveFormattingElements, T formPointer, T headPointer, T deepTreeSurrogateParent, int mode, int originalMode, boolean framesetOk, boolean needToDropLF, boolean quirks) {
+            StackNode<T>[] listOfActiveFormattingElements, int[] templateModeStack, T formPointer,
+            T headPointer, T deepTreeSurrogateParent, 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;
@@ -74,16 +82,23 @@ public class StateSnapshot<T> implements
     /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getStack()
      */
     public StackNode<T>[] getStack() {
         return stack;
     }
 
     /**
+     * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStack()
+     */
+    public int[] getTemplateModeStack() {
+        return templateModeStack;
+    }
+
+    /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElements()
      */
     public StackNode<T>[] getListOfActiveFormattingElements() {
         return listOfActiveFormattingElements;
     }
 
     /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getFormPointer()
@@ -164,16 +179,23 @@ public class StateSnapshot<T> implements
 
     /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getStackLength()
      */
     public int getStackLength() {
         return stack.length;
     }
 
+    /**
+     * @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();
         }
         for (int i = 0; i < listOfActiveFormattingElements.length; i++) {
             if (listOfActiveFormattingElements[i] != null) {
                 listOfActiveFormattingElements[i].release();                
             }
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -4304,16 +4304,17 @@ public abstract class TreeBuilder<T> imp
                     name = contextName;
                     ns = contextNamespace;
                 } else {
                     mode = framesetOk ? FRAMESET_OK : IN_BODY; // XXX from Hixie's email
                     return;
                 }
             }
             if ("template" == name) {
+                assert templateModePtr >= 0;
                 mode = templateModeStack[templateModePtr];
                 return;
             } else if ("select" == name) {
                 // TODO: Fragment case. Add error reporting.
                 mode = IN_SELECT;
                 return;
             } else if ("td" == name || "th" == name) {
                 mode = IN_CELL;
@@ -5681,27 +5682,35 @@ public abstract class TreeBuilder<T> imp
                 // ]NOCPP]
                 );
                 stackCopy[i] = newNode;
             } else {
                 stackCopy[i] = listCopy[listIndex];
                 stackCopy[i].retain();
             }
         }
-        return new StateSnapshot<T>(stackCopy, listCopy, formPointer, headPointer, deepTreeSurrogateParent, mode, originalMode, framesetOk, needToDropLF, quirks);
+        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,
+                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
@@ -5720,26 +5729,33 @@ public abstract class TreeBuilder<T> imp
                               // strict
             }
         }
         for (int i = stackLen - 1; i >= 0; i--) {
             if (stackCopy[i].node != stack[i].node) {
                 return false;
             }
         }
+        for (int i = templateModeStackLen - 1; i >=0; i--) {
+            if (templateModeStackCopy[i] != templateModeStack[i]) {
+                return false;
+            }
+        }
         return true;
     }
 
     @SuppressWarnings("unchecked") public void loadState(
             TreeBuilderState<T> snapshot, Interner interner)
             throws SAXException {
         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();
 
         for (int i = 0; i <= listPtr; i++) {
             if (listOfActiveFormattingElements[i] != null) {
                 listOfActiveFormattingElements[i].release();
             }
         }
         if (listOfActiveFormattingElements.length < listLen) {
             listOfActiveFormattingElements = new StackNode[listLen];
@@ -5749,16 +5765,21 @@ public abstract class TreeBuilder<T> imp
         for (int i = 0; i <= currentPtr; i++) {
             stack[i].release();
         }
         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,
                         Portability.newLocalFromLocal(node.name, interner), node.node,
                         Portability.newLocalFromLocal(node.popName, interner),
                         node.attributes.cloneAttributes(null)
                         // [NOCPP[
@@ -5783,16 +5804,17 @@ public abstract class TreeBuilder<T> imp
                 // ]NOCPP]
                 );
                 stack[i] = newNode;
             } 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();
@@ -5842,16 +5864,23 @@ public abstract class TreeBuilder<T> imp
     /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getStack()
      */
     public StackNode<T>[] getStack() {
         return stack;
     }
 
     /**
+     * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStack()
+     */
+    public int[] getTemplateModeStack() {
+        return templateModeStack;
+    }
+
+    /**
      * Returns the mode.
      *
      * @return the mode
      */
     public int getMode() {
         return mode;
     }
 
@@ -5901,16 +5930,23 @@ public abstract class TreeBuilder<T> imp
     /**
      * @see nu.validator.htmlparser.impl.TreeBuilderState#getStackLength()
      */
     public int getStackLength() {
         return currentPtr + 1;
     }
 
     /**
+     * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStackLength()
+     */
+    public int getTemplateModeStackLength() {
+        return templateModePtr + 1;
+    }
+
+    /**
      * 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/parser/html/nsAHtml5TreeBuilderState.h
+++ b/parser/html/nsAHtml5TreeBuilderState.h
@@ -11,21 +11,25 @@
  * http://hg.mozilla.org/projects/htmlparser/file/tip/src/nu/validator/htmlparser/impl/StateSnapshot.java
  */
 class nsAHtml5TreeBuilderState {
   public:
   
     virtual jArray<nsHtml5StackNode*,int32_t> getStack() = 0;
     
     virtual jArray<nsHtml5StackNode*,int32_t> getListOfActiveFormattingElements() = 0;
-    
+
+    virtual jArray<int32_t,int32_t> getTemplateModeStack() = 0;
+
     virtual int32_t getStackLength() = 0;
 
     virtual int32_t getListOfActiveFormattingElementsLength() = 0;
 
+    virtual int32_t getTemplateModeStackLength() = 0;
+
     virtual nsIContent** getFormPointer() = 0;
     
     virtual nsIContent** getHeadPointer() = 0;
 
     virtual nsIContent** getDeepTreeSurrogateParent() = 0;
 
     virtual int32_t getMode() = 0;
 
--- a/parser/html/nsHtml5StateSnapshot.cpp
+++ b/parser/html/nsHtml5StateSnapshot.cpp
@@ -49,19 +49,20 @@
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsHtml5Portability.h"
 
 #include "nsHtml5StateSnapshot.h"
 
 
-nsHtml5StateSnapshot::nsHtml5StateSnapshot(jArray<nsHtml5StackNode*,int32_t> stack, jArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements, nsIContent** formPointer, nsIContent** headPointer, nsIContent** deepTreeSurrogateParent, int32_t mode, int32_t originalMode, bool framesetOk, bool needToDropLF, bool quirks)
+nsHtml5StateSnapshot::nsHtml5StateSnapshot(jArray<nsHtml5StackNode*,int32_t> stack, jArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements, jArray<int32_t,int32_t> templateModeStack, nsIContent** formPointer, nsIContent** headPointer, nsIContent** deepTreeSurrogateParent, int32_t mode, int32_t originalMode, bool framesetOk, bool needToDropLF, bool quirks)
   : stack(stack),
     listOfActiveFormattingElements(listOfActiveFormattingElements),
+    templateModeStack(templateModeStack),
     formPointer(formPointer),
     headPointer(headPointer),
     deepTreeSurrogateParent(deepTreeSurrogateParent),
     mode(mode),
     originalMode(originalMode),
     framesetOk(framesetOk),
     needToDropLF(needToDropLF),
     quirks(quirks)
@@ -70,16 +71,22 @@ nsHtml5StateSnapshot::nsHtml5StateSnapsh
 }
 
 jArray<nsHtml5StackNode*,int32_t> 
 nsHtml5StateSnapshot::getStack()
 {
   return stack;
 }
 
+jArray<int32_t,int32_t> 
+nsHtml5StateSnapshot::getTemplateModeStack()
+{
+  return templateModeStack;
+}
+
 jArray<nsHtml5StackNode*,int32_t> 
 nsHtml5StateSnapshot::getListOfActiveFormattingElements()
 {
   return listOfActiveFormattingElements;
 }
 
 nsIContent** 
 nsHtml5StateSnapshot::getFormPointer()
@@ -136,16 +143,22 @@ nsHtml5StateSnapshot::getListOfActiveFor
 }
 
 int32_t 
 nsHtml5StateSnapshot::getStackLength()
 {
   return stack.length;
 }
 
+int32_t 
+nsHtml5StateSnapshot::getTemplateModeStackLength()
+{
+  return templateModeStack.length;
+}
+
 
 nsHtml5StateSnapshot::~nsHtml5StateSnapshot()
 {
   MOZ_COUNT_DTOR(nsHtml5StateSnapshot);
   for (int32_t i = 0; i < stack.length; i++) {
     stack[i]->release();
   }
   for (int32_t i = 0; i < listOfActiveFormattingElements.length; i++) {
--- a/parser/html/nsHtml5StateSnapshot.h
+++ b/parser/html/nsHtml5StateSnapshot.h
@@ -54,38 +54,41 @@ class nsHtml5UTF16Buffer;
 class nsHtml5Portability;
 
 
 class nsHtml5StateSnapshot : public nsAHtml5TreeBuilderState
 {
   private:
     autoJArray<nsHtml5StackNode*,int32_t> stack;
     autoJArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements;
+    autoJArray<int32_t,int32_t> templateModeStack;
     nsIContent** formPointer;
     nsIContent** headPointer;
     nsIContent** deepTreeSurrogateParent;
     int32_t mode;
     int32_t originalMode;
     bool framesetOk;
     bool needToDropLF;
     bool quirks;
   public:
-    nsHtml5StateSnapshot(jArray<nsHtml5StackNode*,int32_t> stack, jArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements, nsIContent** formPointer, nsIContent** headPointer, nsIContent** deepTreeSurrogateParent, int32_t mode, int32_t originalMode, bool framesetOk, bool needToDropLF, bool quirks);
+    nsHtml5StateSnapshot(jArray<nsHtml5StackNode*,int32_t> stack, jArray<nsHtml5StackNode*,int32_t> listOfActiveFormattingElements, jArray<int32_t,int32_t> templateModeStack, nsIContent** formPointer, nsIContent** headPointer, nsIContent** deepTreeSurrogateParent, int32_t mode, int32_t originalMode, bool framesetOk, bool needToDropLF, bool quirks);
     jArray<nsHtml5StackNode*,int32_t> getStack();
+    jArray<int32_t,int32_t> getTemplateModeStack();
     jArray<nsHtml5StackNode*,int32_t> getListOfActiveFormattingElements();
     nsIContent** getFormPointer();
     nsIContent** getHeadPointer();
     nsIContent** getDeepTreeSurrogateParent();
     int32_t getMode();
     int32_t getOriginalMode();
     bool isFramesetOk();
     bool isNeedToDropLF();
     bool isQuirks();
     int32_t getListOfActiveFormattingElementsLength();
     int32_t getStackLength();
+    int32_t getTemplateModeStackLength();
     ~nsHtml5StateSnapshot();
     static void initializeStatics();
     static void releaseStatics();
 };
 
 
 
 #endif
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -3239,16 +3239,17 @@ nsHtml5TreeBuilder::resetTheInsertionMod
         name = contextName;
         ns = contextNamespace;
       } else {
         mode = framesetOk ? NS_HTML5TREE_BUILDER_FRAMESET_OK : NS_HTML5TREE_BUILDER_IN_BODY;
         return;
       }
     }
     if (nsHtml5Atoms::template_ == name) {
+      MOZ_ASSERT(templateModePtr >= 0);
       mode = templateModeStack[templateModePtr];
       return;
     } else if (nsHtml5Atoms::select == name) {
       mode = NS_HTML5TREE_BUILDER_IN_SELECT;
       return;
     } else if (nsHtml5Atoms::td == name || nsHtml5Atoms::th == name) {
       mode = NS_HTML5TREE_BUILDER_IN_CELL;
       return;
@@ -4146,27 +4147,31 @@ nsHtml5TreeBuilder::newSnapshot()
     if (listIndex == -1) {
       nsHtml5StackNode* newNode = new nsHtml5StackNode(node->getFlags(), node->ns, node->name, node->node, node->popName, nullptr);
       stackCopy[i] = newNode;
     } else {
       stackCopy[i] = listCopy[listIndex];
       stackCopy[i]->retain();
     }
   }
-  return new nsHtml5StateSnapshot(stackCopy, listCopy, formPointer, headPointer, deepTreeSurrogateParent, mode, originalMode, framesetOk, needToDropLF, quirks);
+  jArray<int32_t,int32_t> templateModeStackCopy = jArray<int32_t,int32_t>::newJArray(templateModePtr + 1);
+  nsHtml5ArrayCopy::arraycopy(templateModeStack, templateModeStackCopy, templateModeStackCopy.length);
+  return new nsHtml5StateSnapshot(stackCopy, listCopy, templateModeStackCopy, formPointer, headPointer, deepTreeSurrogateParent, mode, originalMode, framesetOk, needToDropLF, quirks);
 }
 
 bool 
 nsHtml5TreeBuilder::snapshotMatches(nsAHtml5TreeBuilderState* snapshot)
 {
   jArray<nsHtml5StackNode*,int32_t> stackCopy = snapshot->getStack();
   int32_t stackLen = snapshot->getStackLength();
   jArray<nsHtml5StackNode*,int32_t> listCopy = snapshot->getListOfActiveFormattingElements();
   int32_t listLen = snapshot->getListOfActiveFormattingElementsLength();
-  if (stackLen != currentPtr + 1 || listLen != listPtr + 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()) {
+  jArray<int32_t,int32_t> templateModeStackCopy = snapshot->getTemplateModeStack();
+  int32_t 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()) {
     return false;
   }
   for (int32_t i = listLen - 1; i >= 0; i--) {
     if (!listCopy[i] && !listOfActiveFormattingElements[i]) {
       continue;
     } else if (!listCopy[i] || !listOfActiveFormattingElements[i]) {
       return false;
     }
@@ -4174,42 +4179,53 @@ nsHtml5TreeBuilder::snapshotMatches(nsAH
       return false;
     }
   }
   for (int32_t i = stackLen - 1; i >= 0; i--) {
     if (stackCopy[i]->node != stack[i]->node) {
       return false;
     }
   }
+  for (int32_t i = templateModeStackLen - 1; i >= 0; i--) {
+    if (templateModeStackCopy[i] != templateModeStack[i]) {
+      return false;
+    }
+  }
   return true;
 }
 
 void 
 nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot, nsHtml5AtomTable* interner)
 {
   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();
     }
   }
   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();
   }
   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));
       listOfActiveFormattingElements[i] = newNode;
     } else {
       listOfActiveFormattingElements[i] = nullptr;
     }
@@ -4220,16 +4236,17 @@ nsHtml5TreeBuilder::loadState(nsAHtml5Tr
     if (listIndex == -1) {
       nsHtml5StackNode* newNode = new nsHtml5StackNode(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();
   headPointer = snapshot->getHeadPointer();
   deepTreeSurrogateParent = snapshot->getDeepTreeSurrogateParent();
   mode = snapshot->getMode();
   originalMode = snapshot->getOriginalMode();
   framesetOk = snapshot->isFramesetOk();
   needToDropLF = snapshot->isNeedToDropLF();
   quirks = snapshot->isQuirks();
@@ -4271,16 +4288,22 @@ nsHtml5TreeBuilder::getListOfActiveForma
 }
 
 jArray<nsHtml5StackNode*,int32_t> 
 nsHtml5TreeBuilder::getStack()
 {
   return stack;
 }
 
+jArray<int32_t,int32_t> 
+nsHtml5TreeBuilder::getTemplateModeStack()
+{
+  return templateModeStack;
+}
+
 int32_t 
 nsHtml5TreeBuilder::getMode()
 {
   return mode;
 }
 
 int32_t 
 nsHtml5TreeBuilder::getOriginalMode()
@@ -4313,16 +4336,22 @@ nsHtml5TreeBuilder::getListOfActiveForma
 }
 
 int32_t 
 nsHtml5TreeBuilder::getStackLength()
 {
   return currentPtr + 1;
 }
 
+int32_t 
+nsHtml5TreeBuilder::getTemplateModeStackLength()
+{
+  return templateModePtr + 1;
+}
+
 void
 nsHtml5TreeBuilder::initializeStatics()
 {
 }
 
 void
 nsHtml5TreeBuilder::releaseStatics()
 {
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -247,23 +247,25 @@ class nsHtml5TreeBuilder : public nsAHtm
   private:
     int32_t findInArray(nsHtml5StackNode* node, jArray<nsHtml5StackNode*,int32_t> arr);
   public:
     nsIContent** getFormPointer();
     nsIContent** getHeadPointer();
     nsIContent** getDeepTreeSurrogateParent();
     jArray<nsHtml5StackNode*,int32_t> getListOfActiveFormattingElements();
     jArray<nsHtml5StackNode*,int32_t> getStack();
+    jArray<int32_t,int32_t> getTemplateModeStack();
     int32_t getMode();
     int32_t getOriginalMode();
     bool isFramesetOk();
     bool isNeedToDropLF();
     bool isQuirks();
     int32_t getListOfActiveFormattingElementsLength();
     int32_t getStackLength();
+    int32_t getTemplateModeStackLength();
     static void initializeStatics();
     static void releaseStatics();
 
 #include "nsHtml5TreeBuilderHSupplement.h"
 };
 
 #define NS_HTML5TREE_BUILDER_OTHER 0
 #define NS_HTML5TREE_BUILDER_A 1