Bug 818976 - Part 2: Implement template element interface. r=mrbkap,bz
authorWilliam Chen <wchen@mozilla.com>
Tue, 26 Mar 2013 00:15:23 -0700
changeset 126208 f704985a8246
parent 126207 a9862de183ff
child 126209 833a9f1cce27
push id24477
push userryanvm@gmail.com
push dateTue, 26 Mar 2013 14:07:34 +0000
treeherdermozilla-central@8d09f003e087 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, bz
bugs818976
milestone22.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 818976 - Part 2: Implement template element interface. r=mrbkap,bz
content/base/public/nsContentUtils.h
content/base/public/nsIDocument.h
content/base/src/DocumentFragment.cpp
content/base/src/DocumentFragment.h
content/base/src/Element.cpp
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsDocumentEncoder.cpp
content/base/src/nsINode.cpp
content/base/src/nsNodeUtils.cpp
content/base/src/nsNodeUtils.h
content/html/content/src/HTMLTemplateElement.cpp
content/html/content/src/HTMLTemplateElement.h
content/html/content/src/Makefile.in
content/html/content/src/nsGenericHTMLElement.h
content/html/content/test/test_bug389797.html
dom/bindings/Bindings.conf
dom/tests/browser/browser_bug396843.js
dom/tests/mochitest/bugs/test_bug396843.html
dom/tests/mochitest/webcomponents/Makefile.in
dom/tests/mochitest/webcomponents/test_template.html
dom/webidl/HTMLTemplateElement.webidl
dom/webidl/WebIDL.mk
editor/libeditor/html/nsHTMLEditUtils.cpp
layout/style/html.css
parser/htmlparser/public/nsHTMLTagList.h
parser/htmlparser/src/nsElementTable.cpp
parser/htmlparser/src/nsHTMLTags.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -205,16 +205,25 @@ public:
    * @return true if aPossibleDescendant is a descendant of
    *         aPossibleAncestor (or is aPossibleAncestor).  false
    *         otherwise.
    */
   static bool ContentIsDescendantOf(const nsINode* aPossibleDescendant,
                                       const nsINode* aPossibleAncestor);
 
   /**
+   * Similar to ContentIsDescendantOf, except will treat an HTMLTemplateElement
+   * or ShadowRoot as an ancestor of things in the corresponding DocumentFragment.
+   * See the concept of "host-including inclusive ancestor" in the DOM
+   * specification.
+   */
+  static bool ContentIsHostIncludingDescendantOf(
+    const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
+
+  /**
    * Similar to ContentIsDescendantOf except it crosses document boundaries.
    */
   static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
                                               nsINode* aPossibleAncestor);
 
   /*
    * This method fills the |aArray| with all ancestor nodes of |aNode|
    * including |aNode| at the zero index.
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1626,16 +1626,22 @@ public:
            InternalAllowXULXBL();
   }
 
   void ForceEnableXULXBL() {
     mAllowXULXBL = eTriTrue;
   }
 
   /**
+   * Returns the template content owner document that owns the content of
+   * HTMLTemplateElement.
+   */
+  virtual nsIDocument* GetTemplateContentsOwner() = 0;
+
+  /**
    * true when this document is a static clone of a normal document.
    * For example print preview and printing use static documents.
    */
   bool IsStaticDocument() { return mIsStaticDocument; }
 
   /**
    * Clones the document and subdocuments and stylesheet etc.
    * @param aCloneContainer The container for the clone document.
--- a/content/base/src/DocumentFragment.cpp
+++ b/content/base/src/DocumentFragment.cpp
@@ -50,17 +50,17 @@ NS_NewDocumentFragment(nsNodeInfoManager
   nsRefPtr<DocumentFragment> it = new DocumentFragment(nodeInfo.forget());
   return it.forget();
 }
 
 namespace mozilla {
 namespace dom {
 
 DocumentFragment::DocumentFragment(already_AddRefed<nsINodeInfo> aNodeInfo)
-  : FragmentOrElement(aNodeInfo)
+  : FragmentOrElement(aNodeInfo), mHost(nullptr)
 {
   NS_ABORT_IF_FALSE(mNodeInfo->NodeType() ==
                     nsIDOMNode::DOCUMENT_FRAGMENT_NODE &&
                     mNodeInfo->Equals(nsGkAtoms::documentFragmentNodeName,
                                       kNameSpaceID_None),
                     "Bad NodeType in aNodeInfo");
 
   SetIsDOMBinding();
--- a/content/base/src/DocumentFragment.h
+++ b/content/base/src/DocumentFragment.h
@@ -15,16 +15,17 @@ class nsIAtom;
 class nsAString;
 class nsIDocument;
 class nsIContent;
 
 namespace mozilla {
 namespace dom {
 
 class Element;
+class HTMLTemplateElement;
 
 class DocumentFragment : public FragmentOrElement,
                          public nsIDOMDocumentFragment
 {
 public:
   using FragmentOrElement::GetFirstChild;
 
   // nsISupports
@@ -96,22 +97,33 @@ public:
     return;
   }
 
   virtual Element* GetNameSpaceElement()
   {
     return nullptr;
   }
 
+  HTMLTemplateElement* GetHost() const
+  {
+    return mHost;
+  }
+
+  void SetHost(HTMLTemplateElement* aHost)
+  {
+    mHost = aHost;
+  }
+
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const;
 #endif
 
 protected:
   nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+  mozilla::dom::HTMLTemplateElement* mHost; // Weak
 };
 
 } // namespace dom
 } // namespace mozilla
 
 
 #endif // mozilla_dom_DocumentFragment_h__
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -96,16 +96,17 @@
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
@@ -3101,31 +3102,34 @@ IsVoidTag(Element* aElement)
     }
   }
   return false;
 }
 
 static bool
 Serialize(Element* aRoot, bool aDescendentsOnly, nsAString& aOut)
 {
-  nsINode* current = aDescendentsOnly ? aRoot->GetFirstChild() : aRoot;
+  nsINode* current = aDescendentsOnly ?
+    nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
+
   if (!current) {
     return true;
   }
 
   StringBuilder builder;
   nsIContent* next;
   while (true) {
     bool isVoid = false;
     switch (current->NodeType()) {
       case nsIDOMNode::ELEMENT_NODE: {
         Element* elem = current->AsElement();
         StartElement(elem, builder);
         isVoid = IsVoidTag(elem);
-        if (!isVoid && (next = current->GetFirstChild())) {
+        if (!isVoid &&
+            (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
           current = next;
           continue;
         }
         break;
       }
 
       case nsIDOMNode::TEXT_NODE:
       case nsIDOMNode::CDATA_SECTION_NODE: {
@@ -3181,16 +3185,27 @@ Serialize(Element* aRoot, bool aDescende
       }
 
       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) {
+        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
+        HTMLTemplateElement* fragHost = frag->GetHost();
+        if (fragHost) {
+          current = fragHost;
+        }
+      }
+
       if (aDescendentsOnly && current == aRoot) {
         return builder.ToString(aOut);
       }
     }
   }
 }
 
 nsresult
@@ -3287,61 +3302,70 @@ void
 Element::GetInnerHTML(nsAString& aInnerHTML, ErrorResult& aError)
 {
   aError = GetMarkup(false, aInnerHTML);
 }
 
 void
 Element::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
 {
-  nsIDocument* doc = OwnerDoc();
+  FragmentOrElement* target = this;
+  // Handle template case.
+  if (nsNodeUtils::IsTemplateElement(target)) {
+    DocumentFragment* frag =
+      static_cast<HTMLTemplateElement*>(target)->Content();
+    MOZ_ASSERT(frag);
+    target = frag;
+  }
+
+  nsIDocument* doc = target->OwnerDoc();
 
   // Batch possible DOMSubtreeModified events.
   mozAutoSubtreeModified subtree(doc, nullptr);
 
-  FireNodeRemovedForChildren();
+  target->FireNodeRemovedForChildren();
 
   // Needed when innerHTML is used in combination with contenteditable
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
 
   // Remove childnodes.
-  uint32_t childCount = GetChildCount();
-  nsAutoMutationBatch mb(this, true, false);
+  uint32_t childCount = target->GetChildCount();
+  nsAutoMutationBatch mb(target, true, false);
   for (uint32_t i = 0; i < childCount; ++i) {
-    RemoveChildAt(0, true);
+    target->RemoveChildAt(0, true);
   }
   mb.RemovalDone();
 
   nsAutoScriptLoaderDisabler sld(doc);
 
   if (doc->IsHTML()) {
-    int32_t oldChildCount = GetChildCount();
+    int32_t oldChildCount = target->GetChildCount();
     aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
-                                               this,
+                                               target,
                                                Tag(),
                                                GetNameSpaceID(),
                                                doc->GetCompatibilityMode() ==
                                                  eCompatibility_NavQuirks,
                                                true);
     mb.NodesAdded();
     // HTML5 parser has notified, but not fired mutation events.
-    FireMutationEventsForDirectParsing(doc, this, oldChildCount);
+    FireMutationEventsForDirectParsing(doc, target, oldChildCount);
   } else {
     nsCOMPtr<nsIDOMDocumentFragment> df;
-    aError = nsContentUtils::CreateContextualFragment(this, aInnerHTML,
+    aError = nsContentUtils::CreateContextualFragment(target, aInnerHTML,
                                                       true,
                                                       getter_AddRefs(df));
     nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
     if (!aError.Failed()) {
       // Suppress assertion about node removal mutation events that can't have
       // listeners anyway, because no one has had the chance to register mutation
       // listeners on the fragment that comes from the parser.
       nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
-      static_cast<nsINode*>(this)->AppendChild(*fragment, aError);
+      static_cast<nsINode*>(target)->AppendChild(*fragment, aError);
       mb.NodesAdded();
     }
   }
 }
 
 void
 Element::GetOuterHTML(nsAString& aOuterHTML, ErrorResult& aError)
 {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -25,16 +25,17 @@
 #include "Layers.h"
 #include "MediaDecoder.h"
 #include "mozAutoDocUpdate.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Base64.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/TextDecoderBase.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Selection.h"
 #include "mozilla/Util.h"
 #include "nsAString.h"
 #include "nsAttrName.h"
 #include "nsAttrValue.h"
@@ -1849,16 +1850,37 @@ nsContentUtils::ContentIsDescendantOf(co
     if (aPossibleDescendant == aPossibleAncestor)
       return true;
     aPossibleDescendant = aPossibleDescendant->GetParentNode();
   } while (aPossibleDescendant);
 
   return false;
 }
 
+bool
+nsContentUtils::ContentIsHostIncludingDescendantOf(
+  const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
+{
+  NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
+  NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
+
+  do {
+    if (aPossibleDescendant == aPossibleAncestor)
+      return true;
+    if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+      aPossibleDescendant =
+        static_cast<const DocumentFragment*>(aPossibleDescendant)->GetHost();
+    } else {
+      aPossibleDescendant = aPossibleDescendant->GetParentNode();
+    }
+  } while (aPossibleDescendant);
+
+  return false;
+}
+
 // static
 bool
 nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
                                               nsINode* aPossibleAncestor)
 {
   NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
   NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1703,16 +1703,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCatalogSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
 
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
@@ -1779,16 +1780,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
 
   tmp->mParentDocument = nullptr;
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
 
   if (tmp->mBoxObjectTable) {
    tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
@@ -8531,16 +8533,47 @@ nsDocument::UnsuppressEventHandlingAndFi
 }
 
 nsISupports*
 nsDocument::GetCurrentContentSink()
 {
   return mParser ? mParser->GetContentSink() : nullptr;
 }
 
+nsIDocument*
+nsDocument::GetTemplateContentsOwner()
+{
+  if (!mTemplateContentsOwner) {
+    bool hasHadScriptObject = true;
+    nsIScriptGlobalObject* scriptObject =
+      GetScriptHandlingObject(hasHadScriptObject);
+    NS_ENSURE_TRUE(scriptObject || !hasHadScriptObject, nullptr);
+
+    nsCOMPtr<nsIDOMDocument> domDocument;
+    nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
+                                    EmptyString(), // aNamespaceURI
+                                    EmptyString(), // aQualifiedName
+                                    nullptr, // aDoctype
+                                    nsIDocument::GetDocumentURI(),
+                                    nsIDocument::GetDocBaseURI(),
+                                    NodePrincipal(),
+                                    true, // aLoadedAsData
+                                    scriptObject, // aEventObject
+                                    DocumentFlavorHTML);
+    NS_ENSURE_SUCCESS(rv, nullptr);
+
+    mTemplateContentsOwner = do_QueryInterface(domDocument);
+    NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
+
+    mTemplateContentsOwner->SetScriptHandlingObject(scriptObject);
+  }
+
+  return mTemplateContentsOwner;
+}
+
 void
 nsDocument::RegisterHostObjectUri(const nsACString& aUri)
 {
   mHostObjectURIs.AppendElement(aUri);
 }
 
 void
 nsDocument::UnregisterHostObjectUri(const nsACString& aUri)
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -859,16 +859,18 @@ public:
 
   virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents);
   
   void DecreaseEventSuppression() {
     --mEventsSuppressed;
     MaybeRescheduleAnimationFrameNotifications();
   }
 
+  virtual nsIDocument* GetTemplateContentsOwner();
+
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
                                                                    nsIDocument)
 
   void DoNotifyPossibleTitleChange();
 
   nsExternalResourceMap& ExternalResourceMap()
   {
     return mExternalResourceMap;
@@ -1286,16 +1288,20 @@ protected:
   uint8_t mXMLDeclarationBits;
 
   nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject> *mBoxObjectTable;
 
   // The channel that got passed to StartDocumentLoad(), if any
   nsCOMPtr<nsIChannel> mChannel;
   nsRefPtr<nsHTMLCSSStyleSheet> mStyleAttrStyleSheet;
 
+  // A document "without a browsing context" that owns the content of
+  // HTMLTemplateElement.
+  nsCOMPtr<nsIDocument> mTemplateContentsOwner;
+
   // Our update nesting level
   uint32_t mUpdateNestLevel;
 
   // The application cache that this document is associated with, if
   // any.  This can change during the lifetime of the document.
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
 
   nsCOMPtr<nsIContent> mFirstBaseNodeWithHref;
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -37,16 +37,17 @@
 #include "nsIParserService.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozilla/Selection.h"
 #include "nsISelectionPrivate.h"
 #include "nsITransferable.h" // for kUnicodeMime
 #include "nsContentUtils.h"
+#include "nsNodeUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Element.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
@@ -490,17 +491,18 @@ nsDocumentEncoder::SerializeToStringRecu
 
   if (!aDontSerializeRoot) {
     rv = SerializeNodeStart(maybeFixedNode, 0, -1, aStr, aNode);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsINode* node = serializeClonedChildren ? maybeFixedNode : aNode;
 
-  for (nsINode* child = node->GetFirstChild(); child;
+  for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node);
+       child;
        child = child->GetNextSibling()) {
     rv = SerializeToStringRecursive(child, aStr, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!aDontSerializeRoot) {
     rv = SerializeNodeEnd(node, aStr);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -1415,18 +1415,20 @@ bool IsAllowedAsChild(nsIContent* aNewCh
              "Nodes that are not documents, document fragments or elements "
              "can't be parents!");
 
   // A common case is that aNewChild has no kids, in which case
   // aParent can't be a descendant of aNewChild unless they're
   // actually equal to each other.  Fast-path that case, since aParent
   // could be pretty deep in the DOM tree.
   if (aNewChild == aParent ||
-      (aNewChild->GetFirstChild() &&
-       nsContentUtils::ContentIsDescendantOf(aParent, aNewChild))) {
+      ((aNewChild->GetFirstChild() ||
+        aNewChild->Tag() == nsGkAtoms::_template) &&
+       nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
+                                                          aNewChild))) {
     return false;
   }
 
   // The allowed child nodes differ for documents and elements
   switch (aNewChild->NodeType()) {
   case nsIDOMNode::COMMENT_NODE :
   case nsIDOMNode::PROCESSING_INSTRUCTION_NODE :
     // OK in both cases
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -26,16 +26,17 @@
 #include "nsGenericHTMLElement.h"
 #ifdef MOZ_MEDIA
 #include "mozilla/dom/HTMLMediaElement.h"
 #endif // MOZ_MEDIA
 #include "nsWrapperCacheInlines.h"
 #include "nsObjectLoadingContent.h"
 #include "nsDOMMutationObserver.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
 
 using namespace mozilla::dom;
 
 // This macro expects the ownerDocument of content_ to be in scope as
 // |nsIDocument* doc|
 #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_)      \
   PR_BEGIN_MACRO                                                  \
   bool needsEnterLeave = doc->MayHaveDOMMutationObservers();      \
@@ -557,16 +558,39 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
       nsCOMPtr<nsINode> child;
       rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
                          aCx, aNewScope, aNodesWithProperties, clone,
                          getter_AddRefs(child));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
+  // Cloning template element.
+  if (aDeep && aClone && IsTemplateElement(aNode)) {
+    DocumentFragment* origContent =
+      static_cast<HTMLTemplateElement*>(aNode)->Content();
+    DocumentFragment* cloneContent =
+      static_cast<HTMLTemplateElement*>(clone.get())->Content();
+
+    // Clone the children into the clone's template content owner
+    // document's nodeinfo manager.
+    nsNodeInfoManager* ownerNodeInfoManager =
+      cloneContent->mNodeInfo->NodeInfoManager();
+
+    for (nsIContent* cloneChild = origContent->GetFirstChild();
+         cloneChild;
+         cloneChild = cloneChild->GetNextSibling()) {
+      nsCOMPtr<nsINode> child;
+      rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
+                         aCx, aNewScope, aNodesWithProperties, cloneContent,
+                         getter_AddRefs(child));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
   // XXX setting document on some nodes not in a document so XBL will bind
   // and chrome won't break. Make XBL bind to document-less nodes!
   // XXXbz Once this is fixed, fix up the asserts in all implementations of
   // BindToTree to assert what they would like to assert, and fix the
   // ChangeDocumentFor() call in nsXULElement::BindToTree as well.  Also,
   // remove the UnbindFromTree call in ~nsXULElement, and add back in the
   // precondition in nsXULElement::UnbindFromTree and remove the line in
   // nsXULElement.h that makes nsNodeUtils a friend of nsXULElement.
@@ -604,8 +628,27 @@ nsNodeUtils::UnlinkUserData(nsINode *aNo
   NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed.");
 
   // Strong reference to the document so that deleting properties can't
   // delete the document.
   nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
   document->PropertyTable(DOM_USER_DATA)->DeleteAllPropertiesFor(aNode);
   document->PropertyTable(DOM_USER_DATA_HANDLER)->DeleteAllPropertiesFor(aNode);
 }
+
+bool
+nsNodeUtils::IsTemplateElement(const nsINode *aNode)
+{
+  return aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::_template);
+}
+
+nsIContent*
+nsNodeUtils::GetFirstChildOfTemplateOrNode(nsINode* aNode)
+{
+  if (nsNodeUtils::IsTemplateElement(aNode)) {
+    DocumentFragment* frag =
+      static_cast<HTMLTemplateElement*>(aNode)->Content();
+    return frag->GetFirstChild();
+  }
+
+  return aNode->GetFirstChild();
+}
+
--- a/content/base/src/nsNodeUtils.h
+++ b/content/base/src/nsNodeUtils.h
@@ -243,16 +243,32 @@ public:
 
   /**
    * Release the UserData and UserDataHandlers for aNode.
    *
    * @param aNode the node to release the UserData and UserDataHandlers for
    */
   static void UnlinkUserData(nsINode *aNode);
 
+  /**
+   * Returns a true if the node is a HTMLTemplate element.
+   *
+   * @param aNode a node to test for HTMLTemplate elementness.
+   */
+  static bool IsTemplateElement(const nsINode *aNode);
+
+  /**
+   * Returns the first child of a node or the first child of
+   * a template element's content if the provided node is a
+   * template element.
+   *
+   * @param aNode A node from which to retrieve the first child.
+   */
+  static nsIContent* GetFirstChildOfTemplateOrNode(nsINode* aNode);
+
 private:
   /**
    * Walks aNode, its attributes and, if aDeep is true, its descendant nodes.
    * If aClone is true the nodes will be cloned. If aNewNodeInfoManager is
    * not null, it is used to create new nodeinfos for the nodes. Also reparents
    * the XPConnect wrappers for the nodes in aNewScope if aCx is not null.
    * aNodesWithProperties will be filled with all the nodes that have
    * properties.
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLTemplateElement.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/HTMLTemplateElementBinding.h"
+
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsIAtom.h"
+#include "nsRuleData.h"
+
+using namespace mozilla::dom;
+
+nsGenericHTMLElement*
+NS_NewHTMLTemplateElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                          FromParser aFromParser)
+{
+  HTMLTemplateElement* it = new HTMLTemplateElement(aNodeInfo);
+  nsresult rv = it->Init();
+  if (NS_FAILED(rv)) {
+    delete it;
+    return nullptr;
+  }
+
+  return it;
+}
+
+namespace mozilla {
+namespace dom {
+
+HTMLTemplateElement::HTMLTemplateElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo)
+{
+  SetIsDOMBinding();
+}
+
+nsresult
+HTMLTemplateElement::Init()
+{
+  nsIDocument* doc = OwnerDoc();
+  nsIDocument* contentsOwner = doc;
+
+  // Used to test if the document "has a browsing context".
+  nsCOMPtr<nsISupports> container = doc->GetContainer();
+  if (container) {
+    // GetTemplateContentsOwner lazily creates a document.
+    contentsOwner = doc->GetTemplateContentsOwner();
+    NS_ENSURE_TRUE(contentsOwner, NS_ERROR_UNEXPECTED);
+  }
+
+  ErrorResult rv;
+  mContent = contentsOwner->CreateDocumentFragment(rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
+  mContent->SetHost(this);
+
+  return NS_OK;
+}
+
+HTMLTemplateElement::~HTMLTemplateElement()
+{
+  if (mContent) {
+    mContent->SetHost(nullptr);
+  }
+}
+
+NS_IMPL_ADDREF_INHERITED(HTMLTemplateElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLTemplateElement, Element)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(HTMLTemplateElement,
+                                     nsGenericHTMLElement,
+                                     mContent)
+
+// QueryInterface implementation for HTMLTemplateElement
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLTemplateElement)
+  NS_HTML_CONTENT_INTERFACE_TABLE0(HTMLTemplateElement)
+  NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(HTMLTemplateElement,
+                                               nsGenericHTMLElement)
+NS_HTML_CONTENT_INTERFACE_MAP_END
+
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(HTMLTemplateElement)
+
+JSObject*
+HTMLTemplateElement::WrapNode(JSContext *aCx, JSObject *aScope)
+{
+  return HTMLTemplateElementBinding::Wrap(aCx, aScope, this);
+}
+
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLTemplateElement.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_HTMLTemplateElement_h
+#define mozilla_dom_HTMLTemplateElement_h
+
+#include "nsIDOMHTMLElement.h"
+#include "nsGenericHTMLElement.h"
+#include "mozilla/dom/DocumentFragment.h"
+
+namespace mozilla {
+namespace dom {
+
+class HTMLTemplateElement : public nsGenericHTMLElement,
+                            public nsIDOMHTMLElement
+{
+public:
+  HTMLTemplateElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual ~HTMLTemplateElement();
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMNode
+  NS_FORWARD_NSIDOMNODE_TO_NSINODE
+
+  // nsIDOMElement
+  NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
+
+  // nsIDOMHTMLElement
+  NS_FORWARD_NSIDOMHTMLELEMENT_TO_GENERIC
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLTemplateElement,
+                                           nsGenericHTMLElement)
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  virtual nsIDOMNode* AsDOMNode() { return this; }
+
+  nsresult Init();
+
+  DocumentFragment* Content()
+  {
+    return mContent;
+  }
+
+protected:
+  virtual JSObject* WrapNode(JSContext *aCx, JSObject *aScope) MOZ_OVERRIDE;
+
+  nsRefPtr<DocumentFragment> mContent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HTMLTemplateElement_h
+
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -68,16 +68,17 @@ EXPORTS_mozilla/dom = \
 		HTMLSpanElement.h \
 		HTMLStyleElement.h \
 		HTMLTableCaptionElement.h \
 		HTMLTableCellElement.h \
 		HTMLTableColElement.h \
 		HTMLTableElement.h \
 		HTMLTableRowElement.h \
 		HTMLTableSectionElement.h \
+		HTMLTemplateElement.h \
 		HTMLTextAreaElement.h \
 		HTMLTimeElement.h \
 		HTMLTitleElement.h \
 		HTMLUnknownElement.h \
 		TimeRanges.h \
 		UndoManager.h \
 		ValidityState.h \
 		$(NULL)
@@ -136,16 +137,17 @@ CPPSRCS		= \
 		HTMLSpanElement.cpp \
 		HTMLStyleElement.cpp \
 		HTMLTableElement.cpp \
 		HTMLTableCaptionElement.cpp \
 		HTMLTableCellElement.cpp \
 		HTMLTableColElement.cpp \
 		HTMLTableRowElement.cpp \
 		HTMLTableSectionElement.cpp \
+		HTMLTemplateElement.cpp \
 		HTMLTextAreaElement.cpp \
 		HTMLTimeElement.cpp \
 		HTMLTitleElement.cpp \
 		HTMLUnknownElement.cpp \
 		ValidityState.cpp \
 		nsIConstraintValidation.cpp \
 		nsRadioVisitor.cpp \
 		nsDOMStringMap.cpp \
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1949,16 +1949,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Span)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Style)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TableCaption)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TableCell)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TableCol)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Table)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TableRow)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TableSection)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Tbody)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(Template)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TextArea)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Tfoot)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Thead)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Time)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Title)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Unknown)
 #if defined(MOZ_MEDIA)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Video)
--- a/content/html/content/test/test_bug389797.html
+++ b/content/html/content/test/test_bug389797.html
@@ -209,16 +209,17 @@ HTML_TAG("sub", "");
 HTML_TAG("sup", "");
 HTML_TAG("table", "Table");
 HTML_TAG("tbody", "TableSection");
 HTML_TAG("td", "TableCell");
 HTML_TAG("textarea", "TextArea", [], [ "nsIDOMNSEditableElement" ]);
 HTML_TAG("tfoot", "TableSection");
 HTML_TAG("th", "TableCell");
 HTML_TAG("thead", "TableSection");
+HTML_TAG("template", "Template");
 HTML_TAG("time", "Time");
 HTML_TAG("title", "Title");
 HTML_TAG("tr", "TableRow");
 HTML_TAG("tt", "");
 HTML_TAG("u", "");
 HTML_TAG("ul", "UList");
 HTML_TAG("var", "");
 HTML_TAG("wbr", "");
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -451,16 +451,22 @@ DOMInterfaces = {
 },
 
 'HTMLTableSectionElement': {
     'resultNotAddRefed': [
         'rows'
     ]
 },
 
+'HTMLTemplateElement': {
+    'resultNotAddRefed': [
+        'content'
+    ]
+},
+
 'HTMLTextAreaElement': {
     'resultNotAddRefed': [
         'form', 'controllers', 'editor'
     ],
     'binaryNames': {
         'textLength': 'getTextLength'
     }
 },
--- a/dom/tests/browser/browser_bug396843.js
+++ b/dom/tests/browser/browser_bug396843.js
@@ -106,16 +106,17 @@ function test() {
         HTML_TAG("sup", "Span")
         HTML_TAG("table", "Table")
         HTML_TAG("tbody", "TableSection")
         HTML_TAG("td", "TableCell")
         HTML_TAG("textarea", "TextArea")
         HTML_TAG("tfoot", "TableSection")
         HTML_TAG("th", "TableCell")
         HTML_TAG("thead", "TableSection")
+        HTML_TAG("template", "Template")
         HTML_TAG("title", "Title")
         HTML_TAG("tr", "TableRow")
         HTML_TAG("tt", "Span")
         HTML_TAG("u", "Span")
         HTML_TAG("ul", "SharedList")
         HTML_TAG("var", "Span")
         HTML_TAG("wbr", "Shared")
         HTML_TAG("xmp", "Span")
--- a/dom/tests/mochitest/bugs/test_bug396843.html
+++ b/dom/tests/mochitest/bugs/test_bug396843.html
@@ -124,16 +124,17 @@ HTML_TAG("sub", "Span")
 HTML_TAG("sup", "Span")
 HTML_TAG("table", "Table")
 HTML_TAG("tbody", "TableSection")
 HTML_TAG("td", "TableCell")
 HTML_TAG("textarea", "TextArea")
 HTML_TAG("tfoot", "TableSection")
 HTML_TAG("th", "TableCell")
 HTML_TAG("thead", "TableSection")
+HTML_TAG("template", "Template")
 HTML_TAG("title", "Title")
 HTML_TAG("tr", "TableRow")
 HTML_TAG("tt", "Span")
 HTML_TAG("u", "Span")
 HTML_TAG("ul", "SharedList")
 HTML_TAG("var", "Span")
 HTML_TAG("wbr", "Shared")
 HTML_TAG("xmp", "Span")
--- a/dom/tests/mochitest/webcomponents/Makefile.in
+++ b/dom/tests/mochitest/webcomponents/Makefile.in
@@ -8,11 +8,12 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES	= \
 		test_document_register.html \
 		test_document_register_lifecycle.html \
+		test_template.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_template.html
@@ -0,0 +1,145 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=818976
+-->
+<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" />
+  <script>
+    function shouldNotCall() {
+      ok(false, "Template contents should be inert.");
+    }
+  </script>
+  <template>
+    <script>
+      shouldNotCall();
+    </script>
+  </template>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=818976">Bug 818976</a>
+<template id="grabme"><div id="insidetemplate"></div></template>
+<template id="justtemplate"></template>
+<template id="first">Hi<template>Bye</template></template>
+<div><template id="second"><span></span></template></div>
+<template id="cloneme"><span>I want a clone</span><span>me too</span></template>
+<template id="cycleone"></template>
+<template id="cycletwo"><template></template></template>
+<template id="cyclethree"></template>
+<template id="cyclefour"><template></template></template>
+<template id="appendtome"></template>
+<template id="insertinme"></template>
+<template>
+  <script>
+    shouldNotCall();
+  </script>
+</template>
+<div id="fillme"></div>
+<script>
+var templateEl = document.getElementById("grabme");
+ok(templateEl, "template element should be in document.");
+is(window.getComputedStyle(templateEl).display, "none", "Template element should not be visible.");
+ok(!document.getElementById("insidetemplate"), "Template content should not be in document.");
+is(templateEl.childNodes.length, 0, "Template element should have no children.");
+is(templateEl.content.childNodes.length, 1, "Template content should have 1 child <div>.");
+
+// Make sure that template is owned by different document.
+ok(templateEl.content.ownerDocument != templateEl.ownerDocument, "Template should be in a different document because the current document has a browsing context.");
+var otherTemplateEl = document.getElementById("first");
+is(templateEl.content.ownerDocument, otherTemplateEl.content.ownerDocument, "Template contents within the same document should be owned by the same template contents owner.");
+
+// Tests for XMLSerializer
+templateEl = document.getElementById("justtemplate");
+var serializer = new XMLSerializer();
+is(serializer.serializeToString(templateEl), '<template xmlns="http://www.w3.org/1999/xhtml" id="justtemplate"></template>', "XMLSerializer should serialize template element.");
+
+templateEl = document.getElementById("first");
+is(serializer.serializeToString(templateEl), '<template xmlns="http://www.w3.org/1999/xhtml" id="first">Hi<template>Bye</template></template>', "XMLSerializer should serialize template content.");
+
+// Tests for innerHTML.
+is(templateEl.innerHTML, 'Hi<template>Bye</template>', "innerHTML should serialize content.");
+// Tests for outerHTML, not specified but should do something reasonable.
+is(templateEl.outerHTML, '<template id="first">Hi<template>Bye</template></template>', "outerHTML should serialize content.");
+
+templateEl.innerHTML = "Hello";
+is(templateEl.innerHTML, "Hello", "innerHTML of template should be set to 'Hello'");
+is(templateEl.childNodes.length, 0, "Template element should have no children.");
+is(templateEl.content.childNodes.length, 1, "Template content should have 'Hello' as child.");
+
+// Test for innerHTML on parent of template element.
+var templateParent = document.getElementById("second").parentNode;
+is(templateParent.innerHTML, '<template id="second"><span></span></template>', "InnerHTML on parent of template element should serialize template and template content.");
+
+templateEl.innerHTML = '<template id="inner">Hello</template>';
+ok(templateEl.content.childNodes[0] instanceof HTMLTemplateElement, "Template content should have <template> as child.");
+is(templateEl.content.childNodes[0].childNodes.length, 0, "Parsed temlate element should have no children.");
+is(templateEl.content.childNodes[0].content.childNodes.length, 1, "Parsed temlate element should have 'Hello' in content.");
+
+// Test cloning.
+templateEl = document.getElementById("cloneme");
+var nonDeepClone = templateEl.cloneNode(false);
+is(nonDeepClone.childNodes.length, 0, "There should be no children on the clone.");
+is(nonDeepClone.content.childNodes.length, 0, "Content should not be cloned.");
+var deepClone = templateEl.cloneNode(true);
+is(deepClone.childNodes.length, 0, "There should be no children on the clone.");
+is(deepClone.content.childNodes.length, 2, "The content should be cloned.");
+
+// Append content into a node.
+var parentEl = document.getElementById("fillme");
+parentEl.appendChild(templateEl.content);
+is(parentEl.childNodes.length, 2, "Parent should be appended with cloned content.");
+
+// Test exceptions thrown for cycles.
+templateEl = document.getElementById("cycleone");
+try {
+  templateEl.content.appendChild(templateEl);
+  ok(false, "Exception should be thrown when creating cycles in template content.");
+} catch (ex) {
+  ok(true, "Exception should be thrown when creating cycles in template content.");
+}
+
+templateEl = document.getElementById("cycletwo");
+try {
+  // Append template to template content within the template content.
+  templateEl.content.childNodes[0].content.appendChild(templateEl);
+  ok(false, "Exception should be thrown when creating cycles in template content.");
+} catch (ex) {
+  ok(true, "Exception should be thrown when creating cycles in template content.");
+}
+
+templateEl = document.getElementById("cyclethree");
+try {
+  templateEl.appendChild(templateEl);
+  ok(false, "Exception should be thrown when creating cycles in hierarchy.");
+} catch (ex) {
+  ok(true, "Exception should be thrown when creating cycles in hierarchy.");
+}
+
+templateEl = document.getElementById("cyclefour");
+try {
+  templateEl.content.childNodes[0].appendChild(templateEl);
+  ok(false, "Exception should be thrown when creating cycles in hierarchy.");
+} catch (ex) {
+  ok(true, "Exception should be thrown when creating cycles in hierarchy.");
+}
+
+templateEl = document.getElementById("insertinme");
+var sentinel = document.createElement("div");
+try {
+  templateEl.content.appendChild(sentinel);
+  templateEl.content.insertBefore(templateEl, sentinel);
+  ok(false, "Exception should be thrown when creating cycles in hierarchy.");
+} catch (ex) {
+  ok(true, "Exception should be thrown when creating cycles in hierarchy.");
+}
+
+// Appending normal stuff into content should work.
+templateEl = document.getElementById("appendtome");
+templateEl.content.appendChild(document.createElement("div"));
+is(templateEl.content.childNodes.length, 1, "Template should have div element appended as child");
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLTemplateElement.webidl
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface HTMLTemplateElement : HTMLElement {
+    readonly attribute DocumentFragment content;
+};
+
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -120,16 +120,17 @@ webidl_files = \
   HTMLSpanElement.webidl \
   HTMLStyleElement.webidl \
   HTMLTableCaptionElement.webidl \
   HTMLTableCellElement.webidl \
   HTMLTableColElement.webidl \
   HTMLTableElement.webidl \
   HTMLTableRowElement.webidl \
   HTMLTableSectionElement.webidl \
+  HTMLTemplateElement.webidl \
   HTMLTextAreaElement.webidl \
   HTMLTimeElement.webidl \
   HTMLTitleElement.webidl \
   HTMLUListElement.webidl \
   HTMLVideoElement.webidl \
   IDBVersionChangeEvent.webidl \
   ImageData.webidl \
   InspectorUtils.webidl \
--- a/editor/libeditor/html/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/html/nsHTMLEditUtils.cpp
@@ -746,16 +746,17 @@ static const nsElementInfo kElements[eHT
   ELEM(sup, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(table, true, false, GROUP_BLOCK, GROUP_TABLE_CONTENT),
   ELEM(tbody, true, false, GROUP_TABLE_CONTENT, GROUP_TBODY_CONTENT),
   ELEM(td, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
   ELEM(textarea, true, false, GROUP_FORMCONTROL, GROUP_LEAF),
   ELEM(tfoot, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
   ELEM(th, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
   ELEM(thead, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
+  ELEM(template, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(time, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(title, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
   ELEM(tr, true, false, GROUP_TBODY_CONTENT, GROUP_TR_CONTENT),
   ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   // XXX Can contain self and ol because editor does sublists illegally.
   ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL,
        GROUP_LI | GROUP_OL_UL),
--- a/layout/style/html.css
+++ b/layout/style/html.css
@@ -684,17 +684,17 @@ canvas {
    themselves. */
 :-moz-focusring:not(input):not(button):not(select):not(textarea):not(iframe):not(frame):not(body):not(html) {
   /* Don't specify the outline-color, we should always use initial value. */
    outline: 1px dotted;
 }
 
 /* hidden elements */
 base, basefont, datalist, head, meta, script, style, title,
-noembed, param {
+noembed, param, template {
    display: none;
 }
 
 area {
   /* Don't give it frames other than its imageframe */
   display: none ! important;
 }
 
--- a/parser/htmlparser/public/nsHTMLTagList.h
+++ b/parser/htmlparser/public/nsHTMLTagList.h
@@ -149,16 +149,17 @@ HTML_HTMLELEMENT_TAG(sub)
 HTML_HTMLELEMENT_TAG(sup)
 HTML_TAG(table, Table)
 HTML_TAG(tbody, TableSection)
 HTML_TAG(td, TableCell)
 HTML_TAG(textarea, TextArea)
 HTML_TAG(tfoot, TableSection)
 HTML_TAG(th, TableCell)
 HTML_TAG(thead, TableSection)
+HTML_TAG(template, Template)
 HTML_TAG(time, Time)
 HTML_TAG(title, Title)
 HTML_TAG(tr, TableRow)
 HTML_HTMLELEMENT_TAG(tt)
 HTML_HTMLELEMENT_TAG(u)
 HTML_TAG(ul, SharedList)
 HTML_HTMLELEMENT_TAG(var)
 #if defined(MOZ_MEDIA)
--- a/parser/htmlparser/src/nsElementTable.cpp
+++ b/parser/htmlparser/src/nsElementTable.cpp
@@ -1206,16 +1206,25 @@ const nsHTMLElement gHTMLElements[] = {
     /*req-parent excl-parent*/          eHTMLTag_table,eHTMLTag_unknown,  //fix bug 54840...
     /*rootnodes,endrootnodes*/          &gInTable,&gInTable,  
     /*autoclose starttags and endtags*/ &gTBodyAutoClose,0,0,0,
     /*parent,incl,exclgroups*/          kNone, kNone, kSelf,
     /*special props, prop-range*/       (kNoPropagate|kBadContentWatch|kNoStyleLeaksIn|kNoStyleLeaksOut), kNoPropRange,
     /*special parents,kids*/            &gInTable,&gTableElemKids,
   },
   {
+    /*tag*/                             eHTMLTag_template,
+    /*req-parent excl-parent*/          eHTMLTag_unknown,eHTMLTag_unknown,
+    /*rootnodes,endrootnodes*/          &gRootTags,&gRootTags,
+    /*autoclose starttags and endtags*/ 0,0,0,0,
+    /*parent,incl,exclgroups*/          kNone, kNone, kNone,
+    /*special props, prop-range*/       0,kDefaultPropRange,
+    /*special parents,kids*/            0,0,
+  },
+  {
     /*tag*/                             eHTMLTag_time,
     /*req-parent excl-parent*/          eHTMLTag_unknown,eHTMLTag_unknown,
     /*rootnodes,endrootnodes*/          &gRootTags,&gRootTags,
     /*autoclose starttags and endtags*/ 0,0,0,0,
     /*parent,incl,exclgroups*/          kPhrase, (kSelf|kInlineEntity), kNone,
     /*special props, prop-range*/       0, kDefaultPropRange,
     /*special parents,kids*/            0,0,
   },
--- a/parser/htmlparser/src/nsHTMLTags.cpp
+++ b/parser/htmlparser/src/nsHTMLTags.cpp
@@ -252,16 +252,18 @@ static const PRUnichar sHTMLTagUnicodeNa
 static const PRUnichar sHTMLTagUnicodeName_textarea[] =
   {'t', 'e', 'x', 't', 'a', 'r', 'e', 'a', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_tfoot[] =
   {'t', 'f', 'o', 'o', 't', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_th[] =
   {'t', 'h', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_thead[] =
   {'t', 'h', 'e', 'a', 'd', '\0'};
+static const PRUnichar sHTMLTagUnicodeName_template[] =
+  {'t', 'e', 'm', 'p', 'l', 'a', 't', 'e', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_time[] =
   {'t', 'i', 'm', 'e', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_title[] =
   {'t', 'i', 't', 'l', 'e', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_tr[] =
   {'t', 'r', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_tt[] =
   {'t', 't', '\0'};