[HTMLTemplateElement] Disallow cycles within template content
authoradamk@chromium.org <adamk@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Jan 2013 21:16:59 +0000 (21:16 +0000)
committeradamk@chromium.org <adamk@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Jan 2013 21:16:59 +0000 (21:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=105066

Reviewed by Ojan Vafai.

Source/WebCore:

Cycles in <template> content aren't quite as bad as cycles in normal
DOM trees, but they can easily cause crashes, e.g. in cloneNode and
innerHTML.

Shadow DOM has an analagous issue, and this patch tackles that problem
at the same time by creating a new method, Node::containsIncludingHostElements.

In order to disallow cycles, the HTMLTemplateElement.content
DocumentFragment needs a pointer to its host. The approach here
creates a new subclass with a host pointer and a new virtual method
to DocumentFragment to identify the subclass.

To avoid unnecessary virtual function calls, also changed how
Document::templateContentsOwnerDocument works to allow fast inlined
access and avoid lazy creation when not needed.

Tests: fast/dom/HTMLTemplateElement/cycles-in-shadow.html
       fast/dom/HTMLTemplateElement/cycles.html
       fast/dom/shadow/shadow-hierarchy-exception.html

* GNUmakefile.list.am:
* Target.pri:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* dom/ContainerNode.cpp:
(WebCore::isInTemplateContent):
(WebCore::containsConsideringHostElements):
(WebCore::checkAcceptChild):
* dom/Document.cpp:
(WebCore::Document::ensureTemplateContentsOwnerDocument): Renamed to make clear that it lazily creates the Document. Updated all existing callers to call this method.
* dom/Document.h:
(Document):
(WebCore::Document::templateContentsOwnerDocument): Fast, inlined accessor for use in checkAcceptChild().
* dom/DocumentFragment.h:
(WebCore::DocumentFragment::isTemplateContent):
* dom/Node.cpp:
(WebCore::Node::containsIncludingShadowDOM): made const, simplified
(WebCore::Node::containsIncludingHostElements): Specialized version of Node::contains that knows how to jump over template content boundaries.
* dom/Node.h:
(Node):
* dom/TemplateContentDocumentFragment.h: Added.
(TemplateContentDocumentFragment): Subclass of DocumentFragment which stores its host template element.
(WebCore::TemplateContentDocumentFragment::create):
(WebCore::TemplateContentDocumentFragment::host):
(WebCore::TemplateContentDocumentFragment::TemplateContentDocumentFragment):
* editing/markup.cpp:
(WebCore::createFragmentForInnerOuterHTML):
* html/HTMLTemplateElement.cpp:
(WebCore::HTMLTemplateElement::content): Construct the new subclass.

LayoutTests:

* fast/dom/HTMLTemplateElement/cycles-expected.txt: Added.
* fast/dom/HTMLTemplateElement/cycles-in-shadow-expected.txt: Added.
* fast/dom/HTMLTemplateElement/cycles-in-shadow.html: Added.
* fast/dom/HTMLTemplateElement/cycles.html: Added.
* fast/dom/shadow/shadow-hierarchy-exception-expected.txt: Added.
* fast/dom/shadow/shadow-hierarchy-exception.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@138730 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/HTMLTemplateElement/cycles-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/HTMLTemplateElement/cycles-in-shadow-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/HTMLTemplateElement/cycles-in-shadow.html [new file with mode: 0644]
LayoutTests/fast/dom/HTMLTemplateElement/cycles.html [new file with mode: 0644]
LayoutTests/fast/dom/shadow/shadow-hierarchy-exception-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/shadow/shadow-hierarchy-exception.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Target.pri
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/ContainerNode.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/DocumentFragment.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/TemplateContentDocumentFragment.h [new file with mode: 0644]
Source/WebCore/editing/markup.cpp
Source/WebCore/html/HTMLTemplateElement.cpp

index ab1963e13cb368d0ed1c86a377f0f4f26a969df5..9937be0144a146af6ae6b8bfe194694aca68f42b 100644 (file)
@@ -1,3 +1,17 @@
+2013-01-03  Adam Klein  <adamk@chromium.org>
+
+        [HTMLTemplateElement] Disallow cycles within template content
+        https://bugs.webkit.org/show_bug.cgi?id=105066
+
+        Reviewed by Ojan Vafai.
+
+        * fast/dom/HTMLTemplateElement/cycles-expected.txt: Added.
+        * fast/dom/HTMLTemplateElement/cycles-in-shadow-expected.txt: Added.
+        * fast/dom/HTMLTemplateElement/cycles-in-shadow.html: Added.
+        * fast/dom/HTMLTemplateElement/cycles.html: Added.
+        * fast/dom/shadow/shadow-hierarchy-exception-expected.txt: Added.
+        * fast/dom/shadow/shadow-hierarchy-exception.html: Added.
+
 2013-01-03  Alexis Menard  <alexis@webkit.org>
 
         Querying transition-timing-function value on the computed style does not return keywords when it should.
 2013-01-03  Alexis Menard  <alexis@webkit.org>
 
         Querying transition-timing-function value on the computed style does not return keywords when it should.
diff --git a/LayoutTests/fast/dom/HTMLTemplateElement/cycles-expected.txt b/LayoutTests/fast/dom/HTMLTemplateElement/cycles-expected.txt
new file mode 100644 (file)
index 0000000..390f75f
--- /dev/null
@@ -0,0 +1,14 @@
+Test that cycles are not allowed in template content
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS template.content.appendChild(template) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS template.content.appendChild(outerDiv) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS innerDiv.appendChild(template) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS innerDiv.appendChild(outerDiv) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS innerTemplate.appendChild(outerDiv) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/HTMLTemplateElement/cycles-in-shadow-expected.txt b/LayoutTests/fast/dom/HTMLTemplateElement/cycles-in-shadow-expected.txt
new file mode 100644 (file)
index 0000000..4535054
--- /dev/null
@@ -0,0 +1,10 @@
+Test that cycle detection traverses over both templates and shadow roots
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS shadowDiv.appendChild(outerDiv) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/HTMLTemplateElement/cycles-in-shadow.html b/LayoutTests/fast/dom/HTMLTemplateElement/cycles-in-shadow.html
new file mode 100644 (file)
index 0000000..fc2e63e
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<body>
+<script src="../../js/resources/js-test-pre.js"></script>
+<div id=container>
+  <template><div></div></template>
+</div>
+<script>
+description('Test that cycle detection traverses over both templates and shadow roots');
+var outerDiv = document.getElementById('container');
+var template = document.querySelector('template');
+var div = template.content.firstChild;
+var shadowRoot = div.webkitCreateShadowRoot();
+var shadowDiv = shadowRoot.appendChild(document.createElement('div'));
+shouldThrow('shadowDiv.appendChild(outerDiv)');
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
diff --git a/LayoutTests/fast/dom/HTMLTemplateElement/cycles.html b/LayoutTests/fast/dom/HTMLTemplateElement/cycles.html
new file mode 100644 (file)
index 0000000..ef26b42
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script>
+description('Test that cycles are not allowed in template content');
+var template = document.createElement('template');
+shouldThrow('template.content.appendChild(template)');
+var outerDiv = document.createElement('div');
+outerDiv.appendChild(template);
+shouldThrow('template.content.appendChild(outerDiv)');
+var innerDiv = template.content.appendChild(document.createElement('div'));
+shouldThrow('innerDiv.appendChild(template)');
+shouldThrow('innerDiv.appendChild(outerDiv)');
+var innerTemplate = innerDiv.appendChild(document.createElement('template'));
+shouldThrow('innerTemplate.appendChild(outerDiv)');
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
diff --git a/LayoutTests/fast/dom/shadow/shadow-hierarchy-exception-expected.txt b/LayoutTests/fast/dom/shadow/shadow-hierarchy-exception-expected.txt
new file mode 100644 (file)
index 0000000..28f5690
--- /dev/null
@@ -0,0 +1,10 @@
+Appending an ancestor to a shadow tree should throw an exception
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS shadowDiv.appendChild(container) threw exception Error: HierarchyRequestError: DOM Exception 3.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/shadow/shadow-hierarchy-exception.html b/LayoutTests/fast/dom/shadow/shadow-hierarchy-exception.html
new file mode 100644 (file)
index 0000000..b9c877a
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="../../js/resources/js-test-pre.js"></script>
+<div id=container>
+  <div>
+      <span></span>
+  </div>
+</div>
+<script>
+description('Appending an ancestor to a shadow tree should throw an exception');
+var container = document.getElementById('container');
+var span = container.querySelector('span');
+var shadow = span.webkitCreateShadowRoot();
+var shadowDiv = shadow.appendChild(document.createElement('div'));
+shouldThrow('shadowDiv.appendChild(container)');
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
index 35073d8b60547805cd7f2682b9579d0c9e16b9f4..d0620e7d0ca79167339c9d2a9919d24bcaecb93b 100644 (file)
@@ -1,3 +1,60 @@
+2013-01-03  Adam Klein  <adamk@chromium.org>
+
+        [HTMLTemplateElement] Disallow cycles within template content
+        https://bugs.webkit.org/show_bug.cgi?id=105066
+
+        Reviewed by Ojan Vafai.
+
+        Cycles in <template> content aren't quite as bad as cycles in normal
+        DOM trees, but they can easily cause crashes, e.g. in cloneNode and
+        innerHTML.
+
+        Shadow DOM has an analagous issue, and this patch tackles that problem
+        at the same time by creating a new method, Node::containsIncludingHostElements.
+
+        In order to disallow cycles, the HTMLTemplateElement.content
+        DocumentFragment needs a pointer to its host. The approach here
+        creates a new subclass with a host pointer and a new virtual method
+        to DocumentFragment to identify the subclass.
+
+        To avoid unnecessary virtual function calls, also changed how
+        Document::templateContentsOwnerDocument works to allow fast inlined
+        access and avoid lazy creation when not needed.
+
+        Tests: fast/dom/HTMLTemplateElement/cycles-in-shadow.html
+               fast/dom/HTMLTemplateElement/cycles.html
+               fast/dom/shadow/shadow-hierarchy-exception.html
+
+        * GNUmakefile.list.am:
+        * Target.pri:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/ContainerNode.cpp:
+        (WebCore::isInTemplateContent):
+        (WebCore::containsConsideringHostElements):
+        (WebCore::checkAcceptChild):
+        * dom/Document.cpp:
+        (WebCore::Document::ensureTemplateContentsOwnerDocument): Renamed to make clear that it lazily creates the Document. Updated all existing callers to call this method.
+        * dom/Document.h:
+        (Document):
+        (WebCore::Document::templateContentsOwnerDocument): Fast, inlined accessor for use in checkAcceptChild().
+        * dom/DocumentFragment.h:
+        (WebCore::DocumentFragment::isTemplateContent):
+        * dom/Node.cpp:
+        (WebCore::Node::containsIncludingShadowDOM): made const, simplified
+        (WebCore::Node::containsIncludingHostElements): Specialized version of Node::contains that knows how to jump over template content boundaries.
+        * dom/Node.h:
+        (Node):
+        * dom/TemplateContentDocumentFragment.h: Added.
+        (TemplateContentDocumentFragment): Subclass of DocumentFragment which stores its host template element.
+        (WebCore::TemplateContentDocumentFragment::create):
+        (WebCore::TemplateContentDocumentFragment::host):
+        (WebCore::TemplateContentDocumentFragment::TemplateContentDocumentFragment):
+        * editing/markup.cpp:
+        (WebCore::createFragmentForInnerOuterHTML):
+        * html/HTMLTemplateElement.cpp:
+        (WebCore::HTMLTemplateElement::content): Construct the new subclass.
+
 2013-01-02  Jon Lee  <jonlee@apple.com>
 
         Revert auto-start plugins to snapshotted plugins after a period of inactivity
 2013-01-02  Jon Lee  <jonlee@apple.com>
 
         Revert auto-start plugins to snapshotted plugins after a period of inactivity
index b6c874b22a26d2c77ffeb7b924d451ce2f31c883..b413abb6353feedfd0dbe1189370d431739c1055 100644 (file)
@@ -2961,6 +2961,7 @@ webcore_sources += \
        Source/WebCore/dom/StyleElement.h \
        Source/WebCore/dom/TagNodeList.cpp \
        Source/WebCore/dom/TagNodeList.h \
        Source/WebCore/dom/StyleElement.h \
        Source/WebCore/dom/TagNodeList.cpp \
        Source/WebCore/dom/TagNodeList.h \
+       Source/WebCore/dom/TemplateContentDocumentFragment.h \
        Source/WebCore/dom/Text.cpp \
        Source/WebCore/dom/TextEvent.cpp \
        Source/WebCore/dom/TextEvent.h \
        Source/WebCore/dom/Text.cpp \
        Source/WebCore/dom/TextEvent.cpp \
        Source/WebCore/dom/TextEvent.h \
index dd43c1f7fd160db4b6cde068ce8047ec9363e89a..cf310edf2ea6cc14e85895facdf0c5411e9ff1bc 100644 (file)
@@ -1645,6 +1645,7 @@ HEADERS += \
     dom/StyledElement.h \
     dom/StyleElement.h \
     dom/TagNodeList.h \
     dom/StyledElement.h \
     dom/StyleElement.h \
     dom/TagNodeList.h \
+    dom/TemplateContentDocumentFragment.h \
     dom/TextEvent.h \
     dom/TextEventInputType.h \
     dom/Text.h \
     dom/TextEvent.h \
     dom/TextEventInputType.h \
     dom/Text.h \
index c16308dbb7f3ca3243825df49b441d71c0d5164a..3c3a321523e9e5b60c9cde6c8ad6239b968969a9 100755 (executable)
                                RelativePath="..\dom\TagNodeList.h"
                                >
                        </File>
                                RelativePath="..\dom\TagNodeList.h"
                                >
                        </File>
+                       <File
+                               RelativePath="..\dom\TemplateContentDocumentFragment.h"
+                               >
+                       </File>
                        <File
                                RelativePath="..\dom\Text.cpp"
                                >
                        <File
                                RelativePath="..\dom\Text.cpp"
                                >
index 9b092895649159e402de82384ac71fcc93c1a473..05c753540c7bd05870aa54ef1c16af111b132121 100644 (file)
                C5D4AA7A116BAFB60069CA93 /* GlyphMetricsMap.h in Headers */ = {isa = PBXBuildFile; fileRef = C5D4AA78116BAFB60069CA93 /* GlyphMetricsMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C5E9B67710697E1300C7BB1A /* StorageEventDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C5E9B67610697E1300C7BB1A /* StorageEventDispatcher.cpp */; };
                C5EBDD84105EDDEC0056816F /* StorageEventDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EBDD81105EDDEC0056816F /* StorageEventDispatcher.h */; };
                C5D4AA7A116BAFB60069CA93 /* GlyphMetricsMap.h in Headers */ = {isa = PBXBuildFile; fileRef = C5D4AA78116BAFB60069CA93 /* GlyphMetricsMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C5E9B67710697E1300C7BB1A /* StorageEventDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C5E9B67610697E1300C7BB1A /* StorageEventDispatcher.cpp */; };
                C5EBDD84105EDDEC0056816F /* StorageEventDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EBDD81105EDDEC0056816F /* StorageEventDispatcher.h */; };
+               C65046A9167BFB5500CC2A4D /* TemplateContentDocumentFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = C65046A8167BFB5500CC2A4D /* TemplateContentDocumentFragment.h */; };
                C6A703325C9D0B6CDCBC4D77 /* JSEventTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6A703325C9D0B6CDCBC4D78 /* JSEventTarget.cpp */; };
                C6B31B2E14F841FB0089F23F /* ScrollbarThemeClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C691614714F6EBA70046375C /* ScrollbarThemeClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C6D74AD509AA282E000B0A52 /* ModifySelectionListLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = C6D74AD309AA282E000B0A52 /* ModifySelectionListLevel.h */; };
                C6A703325C9D0B6CDCBC4D77 /* JSEventTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6A703325C9D0B6CDCBC4D78 /* JSEventTarget.cpp */; };
                C6B31B2E14F841FB0089F23F /* ScrollbarThemeClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C691614714F6EBA70046375C /* ScrollbarThemeClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                C6D74AD509AA282E000B0A52 /* ModifySelectionListLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = C6D74AD309AA282E000B0A52 /* ModifySelectionListLevel.h */; };
                C5EBDD81105EDDEC0056816F /* StorageEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageEventDispatcher.h; sourceTree = "<group>"; };
                C5F765B414E1D414006C899B /* PasteboardStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasteboardStrategy.h; sourceTree = "<group>"; };
                C5F765BA14E1ECF4006C899B /* PlatformPasteboardMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformPasteboardMac.mm; sourceTree = "<group>"; };
                C5EBDD81105EDDEC0056816F /* StorageEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageEventDispatcher.h; sourceTree = "<group>"; };
                C5F765B414E1D414006C899B /* PasteboardStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasteboardStrategy.h; sourceTree = "<group>"; };
                C5F765BA14E1ECF4006C899B /* PlatformPasteboardMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformPasteboardMac.mm; sourceTree = "<group>"; };
+               C65046A8167BFB5500CC2A4D /* TemplateContentDocumentFragment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateContentDocumentFragment.h; sourceTree = "<group>"; };
                C691614714F6EBA70046375C /* ScrollbarThemeClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollbarThemeClient.h; sourceTree = "<group>"; };
                C6A703325C9D0B6CDCBC4D78 /* JSEventTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSEventTarget.cpp; sourceTree = "<group>"; };
                C6D74AD309AA282E000B0A52 /* ModifySelectionListLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModifySelectionListLevel.h; sourceTree = "<group>"; };
                C691614714F6EBA70046375C /* ScrollbarThemeClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollbarThemeClient.h; sourceTree = "<group>"; };
                C6A703325C9D0B6CDCBC4D78 /* JSEventTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSEventTarget.cpp; sourceTree = "<group>"; };
                C6D74AD309AA282E000B0A52 /* ModifySelectionListLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModifySelectionListLevel.h; sourceTree = "<group>"; };
                                AA4C3A750B2B1679002334A2 /* StyleElement.h */,
                                BCE3BEC00D222B1D007E06E4 /* TagNodeList.cpp */,
                                BCE3BEC10D222B1D007E06E4 /* TagNodeList.h */,
                                AA4C3A750B2B1679002334A2 /* StyleElement.h */,
                                BCE3BEC00D222B1D007E06E4 /* TagNodeList.cpp */,
                                BCE3BEC10D222B1D007E06E4 /* TagNodeList.h */,
+                               C65046A8167BFB5500CC2A4D /* TemplateContentDocumentFragment.h */,
                                6550B69B099DF0270090D781 /* Text.cpp */,
                                6550B69C099DF0270090D781 /* Text.h */,
                                93EEC1F609C2877700C515D1 /* Text.idl */,
                                6550B69B099DF0270090D781 /* Text.cpp */,
                                6550B69C099DF0270090D781 /* Text.h */,
                                93EEC1F609C2877700C515D1 /* Text.idl */,
                                A8CFF0510A154F09000A4234 /* TableLayout.h in Headers */,
                                BCE3BEC30D222B1D007E06E4 /* TagNodeList.h in Headers */,
                                F55B3DD61251F12D003EF269 /* TelephoneInputType.h in Headers */,
                                A8CFF0510A154F09000A4234 /* TableLayout.h in Headers */,
                                BCE3BEC30D222B1D007E06E4 /* TagNodeList.h in Headers */,
                                F55B3DD61251F12D003EF269 /* TelephoneInputType.h in Headers */,
+                               C65046A9167BFB5500CC2A4D /* TemplateContentDocumentFragment.h in Headers */,
                                6550B6A6099DF0270090D781 /* Text.h in Headers */,
                                93309E17099E64920056E581 /* TextAffinity.h in Headers */,
                                CE7B2DB51586ABAD0098B3FA /* TextAlternativeWithRange.h in Headers */,
                                6550B6A6099DF0270090D781 /* Text.h in Headers */,
                                93309E17099E64920056E581 /* TextAffinity.h in Headers */,
                                CE7B2DB51586ABAD0098B3FA /* TextAlternativeWithRange.h in Headers */,
index 75fd704123176d90f12dd94854c3ba969508bf47..58780016aa0cf67e9ed806b21421a308a03e3974 100644 (file)
@@ -47,6 +47,7 @@
 #include "RenderTheme.h"
 #include "RenderWidget.h"
 #include "RootInlineBox.h"
 #include "RenderTheme.h"
 #include "RenderWidget.h"
 #include "RootInlineBox.h"
+#include "TemplateContentDocumentFragment.h"
 #include <wtf/CurrentTime.h>
 #include <wtf/Vector.h>
 
 #include <wtf/CurrentTime.h>
 #include <wtf/Vector.h>
 
@@ -137,6 +138,24 @@ static inline bool isChildTypeAllowed(ContainerNode* newParent, Node* child)
     return true;
 }
 
     return true;
 }
 
+static inline bool isInTemplateContent(const Node* node)
+{
+#if ENABLE(TEMPLATE_ELEMENT)
+    Document* document = node->document();
+    return document && document == document->templateContentsOwnerDocument();
+#else
+    UNUSED(node);
+    return false;
+#endif
+}
+
+static inline bool containsConsideringHostElements(const Node* newChild, const Node* newParent)
+{
+    return (newParent->isInShadowTree() || isInTemplateContent(newParent))
+        ? newChild->containsIncludingHostElements(newParent)
+        : newChild->contains(newParent);
+}
+
 static inline ExceptionCode checkAcceptChild(ContainerNode* newParent, Node* newChild, Node* oldChild)
 {
     // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null
 static inline ExceptionCode checkAcceptChild(ContainerNode* newParent, Node* newChild, Node* oldChild)
 {
     // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null
@@ -148,7 +167,7 @@ static inline ExceptionCode checkAcceptChild(ContainerNode* newParent, Node* new
         ASSERT(!newParent->isReadOnlyNode());
         ASSERT(!newParent->isDocumentTypeNode());
         ASSERT(isChildTypeAllowed(newParent, newChild));
         ASSERT(!newParent->isReadOnlyNode());
         ASSERT(!newParent->isDocumentTypeNode());
         ASSERT(isChildTypeAllowed(newParent, newChild));
-        if (newChild->contains(newParent))
+        if (containsConsideringHostElements(newChild, newParent))
             return HIERARCHY_REQUEST_ERR;
         return 0;
     }
             return HIERARCHY_REQUEST_ERR;
         return 0;
     }
@@ -162,7 +181,7 @@ static inline ExceptionCode checkAcceptChild(ContainerNode* newParent, Node* new
         return NO_MODIFICATION_ALLOWED_ERR;
     if (newChild->inDocument() && newChild->isDocumentTypeNode())
         return HIERARCHY_REQUEST_ERR;
         return NO_MODIFICATION_ALLOWED_ERR;
     if (newChild->inDocument() && newChild->isDocumentTypeNode())
         return HIERARCHY_REQUEST_ERR;
-    if (newChild->contains(newParent))
+    if (containsConsideringHostElements(newChild, newParent))
         return HIERARCHY_REQUEST_ERR;
 
     if (oldChild && newParent->isDocumentNode()) {
         return HIERARCHY_REQUEST_ERR;
 
     if (oldChild && newParent->isDocumentNode()) {
index 3a9d746104963004dff1d4ee3ce3e367da6e510b..305458a906a63b175d4eae0708525e3c63660102 100644 (file)
@@ -5945,18 +5945,15 @@ Locale& Document::getCachedLocale(const AtomicString& locale)
 }
 
 #if ENABLE(TEMPLATE_ELEMENT)
 }
 
 #if ENABLE(TEMPLATE_ELEMENT)
-Document* Document::templateContentsOwnerDocument()
+Document* Document::ensureTemplateContentsOwnerDocument()
 {
 {
-    // If DOCUMENT does not have a browsing context, Let TEMPLATE CONTENTS OWNER be DOCUMENT and abort these steps.
-    if (!m_frame)
-        return this;
+    if (const Document* document = templateContentsOwnerDocument())
+        return const_cast<Document*>(document);
 
 
-    if (!m_templateContentsOwnerDocument) {
-        if (isHTMLDocument())
-            m_templateContentsOwnerDocument = HTMLDocument::create(0, blankURL());
-        else
-            m_templateContentsOwnerDocument = Document::create(0, blankURL());
-    }
+    if (isHTMLDocument())
+        m_templateContentsOwnerDocument = HTMLDocument::create(0, blankURL());
+    else
+        m_templateContentsOwnerDocument = Document::create(0, blankURL());
 
     return m_templateContentsOwnerDocument.get();
 }
 
     return m_templateContentsOwnerDocument.get();
 }
index e39b2ddbb61f5f81a0b7421f81d946122efe901d..752b3cd167f1df4dbec015e3d59c495e3070f27f 100644 (file)
@@ -1192,7 +1192,8 @@ public:
 #endif
 
 #if ENABLE(TEMPLATE_ELEMENT)
 #endif
 
 #if ENABLE(TEMPLATE_ELEMENT)
-    Document* templateContentsOwnerDocument();
+    const Document* templateContentsOwnerDocument() const;
+    Document* ensureTemplateContentsOwnerDocument();
 #endif
 
     virtual void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0);
 #endif
 
     virtual void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0);
@@ -1570,6 +1571,17 @@ inline void Document::notifyRemovePendingSheetIfNeeded()
         didRemoveAllPendingStylesheet();
 }
 
         didRemoveAllPendingStylesheet();
 }
 
+#if ENABLE(TEMPLATE_ELEMENT)
+inline const Document* Document::templateContentsOwnerDocument() const
+{
+    // If DOCUMENT does not have a browsing context, Let TEMPLATE CONTENTS OWNER be DOCUMENT and abort these steps.
+    if (!m_frame)
+        return this;
+
+    return m_templateContentsOwnerDocument.get();
+}
+#endif
+
 // Put these methods here, because they require the Document definition, but we really want to inline them.
 
 inline bool Node::isDocumentNode() const
 // Put these methods here, because they require the Document definition, but we really want to inline them.
 
 inline bool Node::isDocumentNode() const
index 3af5f1b28faa1bea83f7750fdd1e7fff37ea34d3..1aca4ccf8ab0a6f4602cb4a4eef40fa2e59ccf6d 100644 (file)
@@ -37,6 +37,7 @@ public:
     bool parseXML(const String&, Element* contextElement, FragmentScriptingPermission = AllowScriptingContent);
     
     virtual bool canContainRangeEndPoint() const { return true; }
     bool parseXML(const String&, Element* contextElement, FragmentScriptingPermission = AllowScriptingContent);
     
     virtual bool canContainRangeEndPoint() const { return true; }
+    virtual bool isTemplateContent() const { return false; }
 
 protected:
     DocumentFragment(Document*, ConstructionType = CreateContainer);
 
 protected:
     DocumentFragment(Document*, ConstructionType = CreateContainer);
index ae53feeb7f125e1dd94462ba5ac83eac989c5c2e..6ac43fd1e6d61c03e20471148d882c53a66cd49c 100644 (file)
@@ -46,6 +46,7 @@
 #include "DOMImplementation.h"
 #include "DOMSettableTokenList.h"
 #include "Document.h"
 #include "DOMImplementation.h"
 #include "DOMSettableTokenList.h"
 #include "Document.h"
+#include "DocumentFragment.h"
 #include "DocumentType.h"
 #include "Element.h"
 #include "ElementRareData.h"
 #include "DocumentType.h"
 #include "Element.h"
 #include "ElementRareData.h"
@@ -96,6 +97,7 @@
 #include "StorageEvent.h"
 #include "StyleResolver.h"
 #include "TagNodeList.h"
 #include "StorageEvent.h"
 #include "StyleResolver.h"
 #include "TagNodeList.h"
+#include "TemplateContentDocumentFragment.h"
 #include "Text.h"
 #include "TextEvent.h"
 #include "TreeScopeAdopter.h"
 #include "Text.h"
 #include "TextEvent.h"
 #include "TreeScopeAdopter.h"
@@ -1055,17 +1057,32 @@ bool Node::contains(const Node* node) const
     return this == node || node->isDescendantOf(this);
 }
 
     return this == node || node->isDescendantOf(this);
 }
 
-bool Node::containsIncludingShadowDOM(Node* node)
+bool Node::containsIncludingShadowDOM(const Node* node) const
 {
 {
-    if (!node)
-        return false;
-    for (Node* n = node; n; n = n->parentOrHostNode()) {
-        if (n == this)
+    for (; node; node = node->parentOrHostNode()) {
+        if (node == this)
             return true;
     }
     return false;
 }
 
             return true;
     }
     return false;
 }
 
+bool Node::containsIncludingHostElements(const Node* node) const
+{
+#if ENABLE(TEMPLATE_ELEMENT)
+    while (node) {
+        if (node == this)
+            return true;
+        if (node->isDocumentFragment() && static_cast<const DocumentFragment*>(node)->isTemplateContent())
+            node = static_cast<const TemplateContentDocumentFragment*>(node)->host();
+        else
+            node = node->parentOrHostNode();
+    }
+    return false;
+#else
+    return containsIncludingShadowDOM(node);
+#endif
+}
+
 void Node::attach()
 {
     ASSERT(!attached());
 void Node::attach()
 {
     ASSERT(!attached());
index d0e41f01d3878ef0f3355cdaae0bff0343501f11..38b0fdd64538e12011c80694a82fbd203612ec3c 100644 (file)
@@ -277,6 +277,7 @@ public:
     Node* nonBoundaryShadowTreeRootNode();
 
     // Node's parent, shadow tree host.
     Node* nonBoundaryShadowTreeRootNode();
 
     // Node's parent, shadow tree host.
+    // FIXME: These methods should be renamed parentOrShadowHost*
     ContainerNode* parentOrHostNode() const;
     Element* parentOrHostElement() const;
     void setParentOrHostNode(ContainerNode*);
     ContainerNode* parentOrHostNode() const;
     Element* parentOrHostElement() const;
     void setParentOrHostNode(ContainerNode*);
@@ -488,7 +489,8 @@ public:
     void checkSetPrefix(const AtomicString& prefix, ExceptionCode&);
     bool isDescendantOf(const Node*) const;
     bool contains(const Node*) const;
     void checkSetPrefix(const AtomicString& prefix, ExceptionCode&);
     bool isDescendantOf(const Node*) const;
     bool contains(const Node*) const;
-    bool containsIncludingShadowDOM(Node*);
+    bool containsIncludingShadowDOM(const Node*) const;
+    bool containsIncludingHostElements(const Node*) const;
 
     // Used to determine whether range offsets use characters or node indices.
     virtual bool offsetInCharacters() const;
 
     // Used to determine whether range offsets use characters or node indices.
     virtual bool offsetInCharacters() const;
diff --git a/Source/WebCore/dom/TemplateContentDocumentFragment.h b/Source/WebCore/dom/TemplateContentDocumentFragment.h
new file mode 100644 (file)
index 0000000..0a900be
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TemplateContentDocumentFragment_h
+#define TemplateContentDocumentFragment_h
+
+#if ENABLE(TEMPLATE_ELEMENT)
+
+#include "DocumentFragment.h"
+
+namespace WebCore {
+
+class TemplateContentDocumentFragment : public DocumentFragment {
+public:
+    static PassRefPtr<TemplateContentDocumentFragment> create(Document* document, const Element* host)
+    {
+        return adoptRef(new TemplateContentDocumentFragment(document, host));
+    }
+
+    const Element* host() const { return m_host; }
+
+private:
+    TemplateContentDocumentFragment(Document* document, const Element* host)
+        : DocumentFragment(document, CreateDocumentFragment)
+        , m_host(host)
+    {
+    }
+
+    virtual bool isTemplateContent() const OVERRIDE { return true; }
+
+    const Element* m_host;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(TEMPLATE_ELEMENT)
+
+#endif // TemplateContentDocumentFragment_h
index 63abb5f541dd80240527b66644377e867e1e4014..0082c63bcd559be392fff84150e28627af8606dd 100644 (file)
@@ -996,7 +996,7 @@ PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& marku
     Document* document = contextElement->document();
 #if ENABLE(TEMPLATE_ELEMENT)
     if (contextElement->hasTagName(templateTag))
     Document* document = contextElement->document();
 #if ENABLE(TEMPLATE_ELEMENT)
     if (contextElement->hasTagName(templateTag))
-        document = document->templateContentsOwnerDocument();
+        document = document->ensureTemplateContentsOwnerDocument();
 #endif
     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
 
 #endif
     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
 
index ea071322453c262b54029fcf92bf2de0e65c5446..662bf1d1fd9e1959611d37d63b8545b2c9d62946 100644 (file)
@@ -37,6 +37,7 @@
 #include "DOMImplementation.h"
 #include "DocumentFragment.h"
 #include "HTMLDocument.h"
 #include "DOMImplementation.h"
 #include "DocumentFragment.h"
 #include "HTMLDocument.h"
+#include "TemplateContentDocumentFragment.h"
 #include "markup.h"
 
 namespace WebCore {
 #include "markup.h"
 
 namespace WebCore {
@@ -62,7 +63,7 @@ PassRefPtr<HTMLTemplateElement> HTMLTemplateElement::create(const QualifiedName&
 DocumentFragment* HTMLTemplateElement::content() const
 {
     if (!m_content)
 DocumentFragment* HTMLTemplateElement::content() const
 {
     if (!m_content)
-        m_content = DocumentFragment::create(ownerDocument()->templateContentsOwnerDocument());
+        m_content = TemplateContentDocumentFragment::create(document()->ensureTemplateContentsOwnerDocument(), this);
 
     return m_content.get();
 }
 
     return m_content.get();
 }