Permalink
Browse files

Bug 818976 - Part 2: Implement template element interface. r=mrbkap,bz

  • Loading branch information...
1 parent cf2b829 commit be5cd8ecbbfd0331d97ae9934bfe39805610d7b5 William Chen committed Mar 26, 2013
@@ -209,6 +209,15 @@ class nsContentUtils
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.
*/
@@ -1630,6 +1630,12 @@ class nsIDocument : public nsINode
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.
@@ -55,7 +55,7 @@ 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 &&
@@ -20,6 +20,7 @@ namespace mozilla {
namespace dom {
class Element;
+class HTMLTemplateElement;
class DocumentFragment : public FragmentOrElement,
public nsIDOMDocumentFragment
@@ -101,13 +102,24 @@ class DocumentFragment : public FragmentOrElement,
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
@@ -101,6 +101,7 @@
#include "nsRuleProcessorData.h"
#include "nsAsyncDOMEvent.h"
#include "nsTextNode.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
@@ -3106,7 +3107,9 @@ IsVoidTag(Element* aElement)
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;
}
@@ -3120,7 +3123,8 @@ Serialize(Element* aRoot, bool aDescendentsOnly, nsAString& aOut)
Element* elem = current->AsElement();
StartElement(elem, builder);
isVoid = IsVoidTag(elem);
- if (!isVoid && (next = current->GetFirstChild())) {
+ if (!isVoid &&
+ (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
current = next;
continue;
}
@@ -3186,6 +3190,17 @@ Serialize(Element* aRoot, bool aDescendentsOnly, nsAString& aOut)
}
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);
}
@@ -3292,41 +3307,50 @@ Element::GetInnerHTML(nsAString& aInnerHTML, ErrorResult& aError)
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);
@@ -3336,7 +3360,7 @@ Element::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
// 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();
}
}
@@ -30,6 +30,7 @@
#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"
@@ -1854,6 +1855,27 @@ nsContentUtils::ContentIsDescendantOf(const nsINode* 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,
@@ -1708,6 +1708,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
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)
@@ -1784,6 +1785,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
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;
@@ -8536,6 +8538,37 @@ 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)
{
@@ -864,6 +864,8 @@ class nsDocument : public nsIDocument,
MaybeRescheduleAnimationFrameNotifications();
}
+ virtual nsIDocument* GetTemplateContentsOwner();
+
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
nsIDocument)
@@ -1291,6 +1293,10 @@ class nsDocument : public nsIDocument,
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;
@@ -42,6 +42,7 @@
#include "nsISelectionPrivate.h"
#include "nsITransferable.h" // for kUnicodeMime
#include "nsContentUtils.h"
+#include "nsNodeUtils.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsTArray.h"
@@ -495,7 +496,8 @@ nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
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);
@@ -1420,8 +1420,10 @@ bool IsAllowedAsChild(nsIContent* aNewChild, nsINode* aParent,
// 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;
}
@@ -31,6 +31,7 @@
#include "nsObjectLoadingContent.h"
#include "nsDOMMutationObserver.h"
#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
using namespace mozilla::dom;
@@ -562,6 +563,29 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
}
}
+ // 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
@@ -609,3 +633,22 @@ nsNodeUtils::UnlinkUserData(nsINode *aNode)
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();
+}
+
Oops, something went wrong.

0 comments on commit be5cd8e

Please sign in to comment.