Bug 1011831 - Support <template> element in XHTML parser. r=hsivonen
authorWilliam Chen <wchen@mozilla.com>
Wed, 04 Jun 2014 15:21:23 -0700
changeset 207025 cb20c1b5311841f729bd01566bae6c9f090c07b9
parent 207024 948a658c002ba13c6f647b99dbb8bb8a5a017b20
child 207026 c9a08c041519433f45dddc0054537fc4f10278c9
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1011831
milestone32.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 1011831 - Support <template> element in XHTML parser. r=hsivonen
content/base/src/FragmentOrElement.cpp
content/base/src/nsDocumentEncoder.cpp
content/xml/document/src/nsXMLContentSink.cpp
dom/tests/mochitest/webcomponents/mochitest.ini
dom/tests/mochitest/webcomponents/test_template_xhtml.html
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -2592,19 +2592,20 @@ Serialize(FragmentOrElement* aRoot, bool
 
       if ((next = current->GetNextSibling())) {
         current = next;
         break;
       }
 
       current = current->GetParentNode();
 
-      // Template case, if we are in a template's content, then the parent
-      // should be the host template element.
-      if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+      // Handle template element. If the parent is a template's content,
+      // then adjust the parent to be the template element.
+      if (current != aRoot &&
+          current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
         DocumentFragment* frag = static_cast<DocumentFragment*>(current);
         nsIContent* fragHost = frag->GetHost();
         if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
           current = fragHost;
         }
       }
 
       if (aDescendentsOnly && current == aRoot) {
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -517,30 +517,41 @@ nsDocumentEncoder::SerializeToStringRecu
 }
 
 nsresult
 nsDocumentEncoder::SerializeToStringIterative(nsINode* aNode,
                                               nsAString& aStr)
 {
   nsresult rv;
 
-  nsINode* node = aNode->GetFirstChild();
+  nsINode* node = nsNodeUtils::GetFirstChildOfTemplateOrNode(aNode);
   while (node) {
     nsINode* current = node;
     rv = SerializeNodeStart(current, 0, -1, aStr, current);
     NS_ENSURE_SUCCESS(rv, rv);
-    node = current->GetFirstChild();
+    node = nsNodeUtils::GetFirstChildOfTemplateOrNode(current);
     while (!node && current && current != aNode) {
       rv = SerializeNodeEnd(current, aStr);
       NS_ENSURE_SUCCESS(rv, rv);
       // Check if we have siblings.
       node = current->GetNextSibling();
       if (!node) {
         // Perhaps parent node has siblings.
         current = current->GetParentNode();
+
+        // Handle template element. If the parent is a template's content,
+        // then adjust the parent to be the template element.
+        if (current && current != aNode &&
+            current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+          DocumentFragment* frag = static_cast<DocumentFragment*>(current);
+          nsIContent* host = frag->GetHost();
+          if (host && host->IsHTML(nsGkAtoms::_template)) {
+            current = host;
+          }
+        }
       }
     }
   }
 
   return NS_OK;
 }
 
 bool 
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -55,16 +55,17 @@
 #include "nsIHTMLDocument.h"
 #include "mozAutoDocUpdate.h"
 #include "nsMimeTypes.h"
 #include "nsHtml5SVGLoadDispatcher.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/Comment.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 
 using namespace mozilla::dom;
 
 // XXX Open Issues:
 // 1) what's not allowed - We need to figure out which HTML tags
 //    (prefixed with a HTML namespace qualifier) are explicitly not
 //    allowed (if any).
@@ -843,17 +844,27 @@ nsXMLContentSink::GetCurrentStackNode()
 
 nsresult
 nsXMLContentSink::PushContent(nsIContent *aContent)
 {
   NS_PRECONDITION(aContent, "Null content being pushed!");
   StackNode *sn = mContentStack.AppendElement();
   NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY);
 
-  sn->mContent = aContent;
+  nsIContent* contentToPush = aContent;
+
+  // When an XML parser would append a node to a template element, it
+  // must instead append it to the template element's template contents.
+  if (contentToPush->IsHTML(nsGkAtoms::_template)) {
+    HTMLTemplateElement* templateElement =
+      static_cast<HTMLTemplateElement*>(contentToPush);
+    contentToPush = templateElement->Content();
+  }
+
+  sn->mContent = contentToPush;
   sn->mNumFlushed = 0;
   return NS_OK;
 }
 
 void
 nsXMLContentSink::PopContent()
 {
   int32_t count = mContentStack.Length();
@@ -1071,18 +1082,23 @@ nsXMLContentSink::HandleEndElement(const
   NS_ASSERTION(content, "failed to pop content");
 #ifdef DEBUG
   // Check that we're closing the right thing
   nsCOMPtr<nsIAtom> debugNameSpacePrefix, debugTagAtom;
   int32_t debugNameSpaceID;
   nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix),
                                  getter_AddRefs(debugTagAtom),
                                  &debugNameSpaceID);
-  NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID),
-               "Wrong element being closed");
+  // Check if we are closing a template element because template
+  // elements do not get pushed on the stack, the template
+  // element content is pushed instead.
+  bool isTemplateElement = debugTagAtom == nsGkAtoms::_template &&
+                           debugNameSpaceID == kNameSpaceID_XHTML;
+  NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID) ||
+               isTemplateElement, "Wrong element being closed");
 #endif  
 
   result = CloseElement(content);
 
   if (mCurrentHead == content) {
     mCurrentHead = nullptr;
   }
   
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -12,14 +12,15 @@ support-files =
 [test_dynamic_content_element_matching.html]
 [test_document_register.html]
 [test_document_register_base_queue.html]
 [test_document_register_lifecycle.html]
 [test_document_register_parser.html]
 [test_document_register_stack.html]
 [test_document_shared_registry.html]
 [test_template.html]
+[test_template_xhtml.html]
 [test_shadowroot.html]
 [test_shadowroot_inert_element.html]
 [test_shadowroot_style.html]
 [test_shadowroot_style_multiple_shadow.html]
 [test_shadowroot_style_order.html]
 [test_style_fallback_content.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_template_xhtml.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1011831
+-->
+<head>
+  <title>Test for template element</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1011831">Bug 1011831</a>
+<script>
+var docSrc =
+  '<!DOCTYPE html>' +
+  '<html xmlns="http://www.w3.org/1999/xhtml">' +
+    '<body>' +
+      '<template id="t">Content<span>Content</span></template>' +
+      '<div id="container"><template>One</template><div>Two</div></div>' +
+      '<template id="t2"></template>' +
+    '</body>' +
+  '</html>';
+
+var doc = (new DOMParser()).parseFromString(docSrc, 'application/xhtml+xml');
+
+var t = doc.getElementById("t");
+is(t.childNodes.length, 0, "Template should have no children.");
+is(t.content.childNodes.length, 2, "Template content should have two children, text node and a span.");
+
+// Test serialization of template element.
+is(t.innerHTML, 'Content<span xmlns="http://www.w3.org/1999/xhtml">Content</span>', "Template contents should be serialized.");
+is(t.outerHTML, '<template xmlns="http://www.w3.org/1999/xhtml" id="t">Content<span>Content</span></template>', "Template contents should be serialized.");
+
+var c = doc.getElementById("container");
+is(c.innerHTML, '<template xmlns="http://www.w3.org/1999/xhtml">One</template><div xmlns="http://www.w3.org/1999/xhtml">Two</div>', "Template contents should be serialized.");
+is(c.outerHTML, '<div xmlns="http://www.w3.org/1999/xhtml" id="container"><template>One</template><div>Two</div></div>', "Template contents should be serialized.");
+
+// Test setting innerHTML on template element.
+var t2 = doc.getElementById("t2");
+t2.innerHTML = 'Three<span>Four</span>';
+is(t2.childNodes.length, 0, "Setting innerHTML should append children into template content.");
+is(t2.content.childNodes.length, 2, "Setting innerHTML should append children into template content.");
+
+</script>
+</body>
+</html>