Skip to content

Commit

Permalink
Add getHTML() and modify getInnerHTML() accordingly [2/N]
Browse files Browse the repository at this point in the history
This CL adds a new getHTML() method, and modifies the existing
getInnerHTML() method accordingly. The new method follows the
discussion and conclusions here:
  whatwg/html#8867 (comment)

Essentially, that is:
 1. Provide a boolean option to getHTML() that says "please serialize
    opted-in shadow roots". The default for this option is false.
 2. Provide an option to getHTML() containing a sequence of shadowRoots
    that should be serialized, independent of whether they opted in via
    the flag above.

This work falls under these two chromestatus entries:
  https://chromestatus.com/guide/editall/5081733588582400
  https://chromestatus.com/guide/editall/5102952270528512
and these two blink-dev threads:
  https://groups.google.com/a/chromium.org/g/blink-dev/c/PE4VwMjLVTo
  https://groups.google.com/a/chromium.org/g/blink-dev/c/it0X7BOimKw

Bug: 1519972, 1517959
Change-Id: I5181a0702a12d550b4dab64c0c306ea2ccb25fa3
  • Loading branch information
Mason Freed authored and chromium-wpt-export-bot committed Jan 20, 2024
1 parent 9b4d3ba commit 1603ab6
Showing 1 changed file with 101 additions and 0 deletions.
101 changes: 101 additions & 0 deletions shadow-dom/declarative/gethtml.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE html>
<title>getHTML </title>
<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, mode, delegatesFocus, serializable) {
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);
}
const element = document.createElement(elementType);
wrapper.appendChild(element);

const isOpen = mode === 'open';
if (allowsShadowDom) {
const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : '';
const correctShadowHtml = `<template shadowrootmode="${mode}"${delegatesAttr}><slot></slot></template>`;
const correctHtml = `<${elementType}>${correctShadowHtml}</${elementType}>`;
let initDict = {mode: mode, delegatesFocus: delegatesFocus};
let expectedSerializable = null;
switch (serializable) {
case "none": expectedSerializable = false; break;
case "true": initDict.serializable = expectedSerializable = true; break;
case "false": initDict.serializable = expectedSerializable = false; break;
default: throw new Error("Invalid");
}
const shadowRoot = element.attachShadow(initDict);
assert_equals(shadowRoot.mode,mode);
assert_equals(shadowRoot.delegatesFocus,delegatesFocus);
assert_equals(shadowRoot.serializable,expectedSerializable);
shadowRoot.appendChild(document.createElement('slot'));
const emptyElement = `<${elementType}></${elementType}>`;
if (isOpen) {
if (expectedSerializable) {
assert_equals(wrapper.getHTML({includeShadowRoots: true}), correctHtml);
} else {
assert_equals(wrapper.getHTML({includeShadowRoots: true}), emptyElement);
}
} else {
// Closed shadow roots should not be returned unless shadowRoots specifically contains the shadow root:
assert_equals(wrapper.getHTML({includeShadowRoots: true}), emptyElement);
assert_equals(wrapper.getHTML({includeShadowRoots: true, shadowRoots: []}), emptyElement);
}
// If we provide the shadow root, serialize it, regardless of includeShadowRoots.
assert_equals(wrapper.getHTML({includeShadowRoots: true, shadowRoots: [shadowRoot]}),correctHtml);
assert_equals(wrapper.getHTML({shadowRoots: [shadowRoot]}),correctHtml);
// This should always throw - includeShadowRoots false, but we've provided roots.
assert_throws_dom("NotSupportedError",() => wrapper.getHTML({includeShadowRoots: false, shadowRoots: [shadowRoot]}));
} else {
// For non-shadow hosts, getHTML() should also match .innerHTML
assert_equals(wrapper.getHTML({includeShadowRoots: true}),wrapper.innerHTML);
}

// Either way, make sure getHTML({includeShadowRoots: false}) matches .innerHTML
assert_equals(wrapper.getHTML({includeShadowRoots: false}),wrapper.innerHTML,'getHTML() with includeShadowRoots false should return the same as .innerHTML');
// ...and that the default for includeShadowRoots is false.
assert_equals(wrapper.getHTML(),wrapper.innerHTML,'The default for includeShadowRoots should be false');

}, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on <${elementType}>${allowsShadowDom ? `, with mode=${mode}, delegatesFocus=${delegatesFocus}, serializable=${serializable}.` : ''}`);
}

function runAllTests() {
const allElements = [...HTML5_ELEMENTS, 'htmlunknown'];
const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS;
for (const elementName of allElements) {
const allowsShadowDom = safelisted.includes(elementName);
for (const runGetHTMLOnShadowRoot of [false, true]) {
if (allowsShadowDom) {
for (const delegatesFocus of [false, true]) {
for (const mode of ['open', 'closed']) {
for (const serializable of ['none', 'false', 'true']) {
testElementType(true, elementName, runGetHTMLOnShadowRoot, mode, delegatesFocus, serializable);
}
}
}
} else {
testElementType(false, elementName, runGetHTMLOnShadowRoot);
}
}
}
}

runAllTests();

</script>

0 comments on commit 1603ab6

Please sign in to comment.