forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add serializable shadow roots and getHTML()
https://bugs.webkit.org/show_bug.cgi?id=271343 rdar://125513986 Reviewed by Ryosuke Niwa. This implements whatwg/html#10139 as amended by whatwg/html#10260. This also changes the way a shadow root is serialized to avoid creating a template element. The API is guarded by DeclarativeShadowRootsSerializerAPIsEnabled just in case there is a need to disable it without removing the code. Thanks to Chris Dumez for his help with Vector<RefPtr<ShadowRoot>>. WPT tests are synchronized up to and including this commit: web-platform-tests/wpt@dd2e51d This also adjusts the copyright lines to match the commit log for the relevant files. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html: * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml-ordering-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml-ordering.html: Added. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml.html: Added. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml.tentative-expected.txt: Removed. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml.tentative.html: Removed. * LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/w3c-import.log: * LayoutTests/tests-options.json: * Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml: * Source/WebCore/CMakeLists.txt: * Source/WebCore/DerivedSources-input.xcfilelist: * Source/WebCore/DerivedSources-output.xcfilelist: * Source/WebCore/DerivedSources.make: * Source/WebCore/Headers.cmake: * Source/WebCore/Sources.txt: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/dom/Element.cpp: (WebCore::Element::getHTML const): * Source/WebCore/dom/Element.h: * Source/WebCore/dom/GetHTMLOptions.h: Added. * Source/WebCore/dom/GetHTMLOptions.idl: Added. * Source/WebCore/dom/InnerHTML.idl: * Source/WebCore/dom/ShadowRoot.cpp: (WebCore::ShadowRoot::ShadowRoot): (WebCore::ShadowRoot::getHTML const): (WebCore::ShadowRoot::cloneNodeInternal): * Source/WebCore/dom/ShadowRoot.h: * Source/WebCore/dom/ShadowRoot.idl: * Source/WebCore/dom/ShadowRootInit.h: * Source/WebCore/dom/ShadowRootInit.idl: * Source/WebCore/editing/MarkupAccumulator.cpp: (WebCore::MarkupAccumulator::MarkupAccumulator): (WebCore::MarkupAccumulator::shouldIncludeShadowRoots const): (WebCore::MarkupAccumulator::includeShadowRoot const): (WebCore::MarkupAccumulator::serializeNodesWithNamespaces): (WebCore::MarkupAccumulator::replacementElement): * Source/WebCore/editing/MarkupAccumulator.h: (WebCore::MarkupAccumulator::MarkupAccumulator): * Source/WebCore/editing/markup.cpp: (WebCore::serializeFragment): * Source/WebCore/editing/markup.h: (WebCore::serializeFragment): * Source/WebCore/html/HTMLAttributeNames.in: * Source/WebCore/html/HTMLTemplateElement.cpp: (WebCore::HTMLTemplateElement::attachAsDeclarativeShadowRootIfNeeded): * Source/WebCore/html/HTMLTemplateElement.idl: * Source/WebCore/html/parser/HTMLConstructionSite.cpp: (WebCore::HTMLConstructionSite::insertHTMLTemplateElement): * Source/WebCore/loader/archive/cf/LegacyWebArchive.cpp: (WebCore::LegacyWebArchive::create): Canonical link: https://commits.webkit.org/277374@main
- Loading branch information
Showing
37 changed files
with
4,152 additions
and
2,038 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
...w3c/web-platform-tests/shadow-dom/declarative/declarative-shadow-dom-repeats-expected.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
|
||
PASS Repeated declarative shadow roots keep only the first | ||
PASS Calling attachShadow() on declarative shadow root must match type | ||
PASS Calling attachShadow() on declarative shadow root must match mode | ||
PASS Calling attachShadow() on declarative shadow root must match all parameters | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3,642 changes: 3,642 additions & 0 deletions
3,642
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml-expected.txt
Large diffs are not rendered by default.
Oops, something went wrong.
5 changes: 5 additions & 0 deletions
5
...ests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml-ordering-expected.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
|
||
PASS template position | ||
PASS attribute position | ||
PASS both template and attribute position | ||
|
69 changes: 69 additions & 0 deletions
69
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml-ordering.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<!DOCTYPE html> | ||
<title>getHTML ordering behavior</title> | ||
<link rel='author' href='mailto:masonf@chromium.org'> | ||
<link rel='help' href='https://github.com/whatwg/html/pull/10139'> | ||
<script src='/resources/testharness.js'></script> | ||
<script src='/resources/testharnessreport.js'></script> | ||
|
||
|
||
<div id=tests> | ||
<div data-name="base"> | ||
<template shadowrootmode=open shadowrootdelegatesfocus shadowrootserializable shadowrootclonable> | ||
<slot></slot> | ||
</template> | ||
<span class=content>Content 1</span> | ||
<span class=content>Content 2</span> | ||
</div> | ||
|
||
<div data-name="template position"> | ||
<span class=content>Content 1</span> | ||
<template shadowrootmode=open shadowrootdelegatesfocus shadowrootserializable shadowrootclonable> | ||
<slot></slot> | ||
</template> | ||
<span class=content>Content 2</span> | ||
</div> | ||
|
||
<div data-name="attribute position"> | ||
<template shadowrootclonable shadowrootserializable shadowrootdelegatesfocus shadowrootmode=open> | ||
<slot></slot> | ||
</template> | ||
<span class=content>Content 1</span> | ||
<span class=content>Content 2</span> | ||
</div> | ||
|
||
<div data-name="both template and attribute position"> | ||
<span class=content>Content 1</span> | ||
<span class=content>Content 2</span> | ||
<template shadowrootclonable shadowrootserializable shadowrootdelegatesfocus shadowrootmode=open> | ||
<slot></slot> | ||
</template> | ||
</div> | ||
</div> | ||
|
||
<script> | ||
function removeWhitespaceNodes(el) { | ||
el.shadowRoot && removeWhitespaceNodes(el.shadowRoot); | ||
var iter = document.createNodeIterator(el, NodeFilter.SHOW_TEXT, | ||
(node) => (node.data.replace(/\s/g,'').length === 0) ? | ||
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT); | ||
let node; | ||
while (node = iter.nextNode()) { | ||
node.remove(); | ||
} | ||
return el; | ||
} | ||
const serialize = (host) => host.getHTML({shadowRoots: [host.shadowRoot]}); | ||
|
||
const testCases = Array.from(document.querySelectorAll('#tests>div')); | ||
assert_true(testCases.length > 1); | ||
const baseHost = removeWhitespaceNodes(testCases[0]); | ||
const correctSerialization = serialize(baseHost); | ||
baseHost.remove(); | ||
for(let i=1;i<testCases.length;++i) { | ||
const thisHost = removeWhitespaceNodes(testCases[i]); | ||
test(t => { | ||
assert_equals(serialize(thisHost),correctSerialization,'Serialization should be identical'); | ||
thisHost.remove(); | ||
},thisHost.dataset.name); | ||
} | ||
</script> |
142 changes: 142 additions & 0 deletions
142
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/declarative/gethtml.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<!DOCTYPE html> | ||
<title>getHTML behavior</title> | ||
<meta name="timeout" content="long"> | ||
<link rel='author' href='mailto:masonf@chromium.org'> | ||
<link rel='help' href='https://github.com/whatwg/html/issues/8867'> | ||
<script src='/resources/testharness.js'></script> | ||
<script src='/resources/testharnessreport.js'></script> | ||
<script src='../../html/resources/common.js'></script> | ||
|
||
<body> | ||
|
||
<script> | ||
function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, | ||
lightDOMContent, declarativeShadowDom, mode, delegatesFocus, | ||
serializable, clonable) { | ||
const t = test(t => { | ||
// Create and attach element | ||
let wrapper; | ||
if (runGetHTMLOnShadowRoot) { | ||
// This ensures we're testing both Element.getHTML() and ShadowRoot.getHTML(). | ||
const host = document.createElement('div'); | ||
t.add_cleanup(function() { host.remove(); }); | ||
document.body.appendChild(host); | ||
wrapper = host.attachShadow({mode: 'open'}); | ||
} else { | ||
wrapper = document.createElement('div'); | ||
t.add_cleanup(function() { wrapper.remove(); }); | ||
document.body.appendChild(wrapper); | ||
} | ||
|
||
let shadowRoot; | ||
let initDict = {mode: mode, delegatesFocus: delegatesFocus, clonable}; | ||
let expectedSerializable = null; | ||
switch (serializable) { | ||
case undefined: expectedSerializable = false; break; | ||
case "true": initDict.serializable = expectedSerializable = true; break; | ||
case "false": initDict.serializable = expectedSerializable = false; break; | ||
default: throw new Error(`Invalid serializable ${serializable}`); | ||
} | ||
const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : ''; | ||
const serializableAttr = expectedSerializable ? ' shadowrootserializable=""' : ''; | ||
const clonableAttr = clonable ? ' shadowrootclonable=""' : ''; | ||
|
||
if (allowsShadowDom && declarativeShadowDom) { | ||
const html = `<${elementType}>${lightDOMContent}<template ` + | ||
`shadowrootmode=${mode}${delegatesAttr}${serializableAttr}` + | ||
`${clonableAttr}>`; | ||
wrapper.setHTMLUnsafe(html); | ||
// Get hold of the declarative shadow root in a way that works when its mode is "closed" | ||
shadowRoot = wrapper.firstElementChild.attachShadow(initDict); | ||
} else { | ||
// Imperative shadow dom | ||
const element = document.createElement(elementType); | ||
wrapper.appendChild(element); | ||
const temp = document.createElement('div'); | ||
temp.innerHTML = lightDOMContent; | ||
element.append(...temp.childNodes); | ||
if (allowsShadowDom) { | ||
shadowRoot = element.attachShadow(initDict); | ||
} | ||
} | ||
assert_true(!allowsShadowDom || !!shadowRoot); | ||
|
||
if (allowsShadowDom) { | ||
const correctShadowHtml = `<template shadowrootmode="${mode}"` + | ||
`${delegatesAttr}${serializableAttr}${clonableAttr}><slot></slot>` + | ||
`</template>`; | ||
const correctHtml = `<${elementType}>${correctShadowHtml}` + | ||
`${lightDOMContent}</${elementType}>`; | ||
assert_equals(shadowRoot.mode,mode); | ||
assert_equals(shadowRoot.delegatesFocus,delegatesFocus); | ||
assert_equals(shadowRoot.serializable,expectedSerializable); | ||
assert_equals(shadowRoot.clonable,clonable); | ||
shadowRoot.appendChild(document.createElement('slot')); | ||
const emptyElement = `<${elementType}>${lightDOMContent}</${elementType}>`; | ||
if (expectedSerializable) { | ||
assert_equals(wrapper.getHTML({serializableShadowRoots: true}), | ||
correctHtml); | ||
assert_equals(wrapper.firstElementChild.getHTML({ | ||
serializableShadowRoots: true}), | ||
`${correctShadowHtml}${lightDOMContent}`); | ||
} else { | ||
assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement); | ||
} | ||
// If we provide the shadow root, serialize it, regardless of serializableShadowRoots. | ||
assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: | ||
[shadowRoot]}),correctHtml); | ||
assert_equals(wrapper.getHTML({serializableShadowRoots: false, shadowRoots: | ||
[shadowRoot]}),correctHtml); | ||
assert_equals(wrapper.getHTML({shadowRoots: [shadowRoot]}),correctHtml); | ||
} else { | ||
// For non-shadow hosts, getHTML() should also match .innerHTML | ||
assert_equals(wrapper.getHTML({serializableShadowRoots: true}),wrapper.innerHTML); | ||
} | ||
|
||
// Either way, make sure getHTML({serializableShadowRoots: false}) matches .innerHTML | ||
assert_equals(wrapper.getHTML({serializableShadowRoots: false}),wrapper.innerHTML, | ||
'getHTML() with serializableShadowRoots false should return the same as .innerHTML'); | ||
// ...and that the default for serializableShadowRoots is false. | ||
assert_equals(wrapper.getHTML(),wrapper.innerHTML, | ||
'The default for serializableShadowRoots should be false'); | ||
|
||
}, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on ` + | ||
`<${elementType}>${lightDOMContent}${allowsShadowDom ? | ||
`, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, ` + | ||
`mode=${mode}, delegatesFocus=${delegatesFocus}, ` + | ||
`serializable=${serializable}, clonable=${clonable}.` : ''}`); | ||
} | ||
|
||
function runAllTests() { | ||
const allElements = [...HTML5_ELEMENTS, 'htmlunknown']; | ||
const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS.filter(el => el != 'body'); | ||
for (const elementName of allElements) { | ||
const allowsShadowDom = safelisted.includes(elementName); | ||
for (const runGetHTMLOnShadowRoot of [false, true]) { | ||
for (const lightDOMContent of ['','<span>light</span>']) { | ||
if (allowsShadowDom) { | ||
for (const declarativeShadowDom of [false, true]) { | ||
for (const delegatesFocus of [false, true]) { | ||
for (const clonable of [false, true]) { | ||
for (const mode of ['open', 'closed']) { | ||
for (const serializable of [undefined, 'false', 'true']) { | ||
testElementType(true, elementName, runGetHTMLOnShadowRoot, | ||
lightDOMContent, declarativeShadowDom, mode, | ||
delegatesFocus, serializable, clonable); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} else { | ||
testElementType(false, elementName, runGetHTMLOnShadowRoot, | ||
lightDOMContent); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
runAllTests(); | ||
|
||
</script> |
Oops, something went wrong.