gethtml.html (6627B)
1 <!DOCTYPE html> 2 <title>getHTML behavior</title> 3 <meta name="timeout" content="long"> 4 <link rel='author' href='mailto:masonf@chromium.org'> 5 <link rel='help' href='https://github.com/whatwg/html/issues/8867'> 6 <script src='/resources/testharness.js'></script> 7 <script src='/resources/testharnessreport.js'></script> 8 <script src='../../html/resources/common.js'></script> 9 10 <body> 11 12 <script> 13 function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, 14 lightDOMContent, declarativeShadowDom, mode, delegatesFocus, 15 serializable, clonable, emptyShadowTree) { 16 const t = test(t => { 17 // Create and attach element 18 let wrapper; 19 if (runGetHTMLOnShadowRoot) { 20 // This ensures we're testing both Element.getHTML() and ShadowRoot.getHTML(). 21 const host = document.createElement('div'); 22 t.add_cleanup(function() { host.remove(); }); 23 document.body.appendChild(host); 24 wrapper = host.attachShadow({mode: 'open'}); 25 } else { 26 wrapper = document.createElement('div'); 27 t.add_cleanup(function() { wrapper.remove(); }); 28 document.body.appendChild(wrapper); 29 } 30 31 let shadowRoot; 32 let initDict = {mode: mode, delegatesFocus: delegatesFocus, clonable}; 33 let expectedSerializable = null; 34 switch (serializable) { 35 case undefined: expectedSerializable = false; break; 36 case "true": initDict.serializable = expectedSerializable = true; break; 37 case "false": initDict.serializable = expectedSerializable = false; break; 38 default: throw new Error(`Invalid serializable ${serializable}`); 39 } 40 const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : ''; 41 const serializableAttr = expectedSerializable ? ' shadowrootserializable=""' : ''; 42 const clonableAttr = clonable ? ' shadowrootclonable=""' : ''; 43 44 if (allowsShadowDom && declarativeShadowDom) { 45 const html = `<${elementType}>${lightDOMContent}<template ` + 46 `shadowrootmode=${mode}${delegatesAttr}${serializableAttr}` + 47 `${clonableAttr}>`; 48 wrapper.setHTMLUnsafe(html); 49 // Get hold of the declarative shadow root in a way that works when its mode is "closed" 50 shadowRoot = wrapper.firstElementChild.attachShadow(initDict); 51 } else { 52 // Imperative shadow dom 53 const element = document.createElement(elementType); 54 wrapper.appendChild(element); 55 const temp = document.createElement('div'); 56 temp.innerHTML = lightDOMContent; 57 element.append(...temp.childNodes); 58 if (allowsShadowDom) { 59 shadowRoot = element.attachShadow(initDict); 60 } 61 } 62 assert_true(!allowsShadowDom || !!shadowRoot); 63 64 if (allowsShadowDom) { 65 const correctShadowHtml = `<template shadowrootmode="${mode}"` + 66 `${delegatesAttr}${serializableAttr}${clonableAttr}>` + 67 (emptyShadowTree ? `` : `<slot></slot>`) + `</template>`; 68 const correctHtml = `<${elementType}>${correctShadowHtml}` + 69 `${lightDOMContent}</${elementType}>`; 70 assert_equals(shadowRoot.mode,mode); 71 assert_equals(shadowRoot.delegatesFocus,delegatesFocus); 72 assert_equals(shadowRoot.serializable,expectedSerializable); 73 assert_equals(shadowRoot.clonable,clonable); 74 if (!emptyShadowTree) { 75 shadowRoot.appendChild(document.createElement('slot')); 76 } 77 const emptyElement = `<${elementType}>${lightDOMContent}</${elementType}>`; 78 if (expectedSerializable) { 79 assert_equals(wrapper.getHTML({serializableShadowRoots: true}), 80 correctHtml); 81 assert_equals(wrapper.firstElementChild.getHTML({ 82 serializableShadowRoots: true}), 83 `${correctShadowHtml}${lightDOMContent}`); 84 } else { 85 assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement); 86 } 87 // If we provide the shadow root, serialize it, regardless of serializableShadowRoots. 88 assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: 89 [shadowRoot]}),correctHtml); 90 assert_equals(wrapper.getHTML({serializableShadowRoots: false, shadowRoots: 91 [shadowRoot]}),correctHtml); 92 assert_equals(wrapper.getHTML({shadowRoots: [shadowRoot]}),correctHtml); 93 } else { 94 // For non-shadow hosts, getHTML() should also match .innerHTML 95 assert_equals(wrapper.getHTML({serializableShadowRoots: true}),wrapper.innerHTML); 96 } 97 98 // Either way, make sure getHTML({serializableShadowRoots: false}) matches .innerHTML 99 assert_equals(wrapper.getHTML({serializableShadowRoots: false}),wrapper.innerHTML, 100 'getHTML() with serializableShadowRoots false should return the same as .innerHTML'); 101 // ...and that the default for serializableShadowRoots is false. 102 assert_equals(wrapper.getHTML(),wrapper.innerHTML, 103 'The default for serializableShadowRoots should be false'); 104 105 }, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on ` + 106 `<${elementType}>${lightDOMContent}${allowsShadowDom ? 107 `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, ` + 108 `mode=${mode}, delegatesFocus=${delegatesFocus}, ` + 109 `serializable=${serializable}, clonable=${clonable},` : ''}` + 110 ` with${emptyShadowTree ? `out` : ``} shadow tree contents.`); 111 } 112 113 function runAllTests() { 114 const allElements = [...HTML5_ELEMENTS, 'htmlunknown']; 115 const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS.filter(el => el != 'body'); 116 for (const elementName of allElements) { 117 const allowsShadowDom = safelisted.includes(elementName); 118 for (const runGetHTMLOnShadowRoot of [false, true]) { 119 for (const lightDOMContent of ['','<span>light</span>']) { 120 if (allowsShadowDom) { 121 for (const declarativeShadowDom of [false, true]) { 122 for (const delegatesFocus of [false, true]) { 123 for (const clonable of [false, true]) { 124 for (const mode of ['open', 'closed']) { 125 for (const serializable of [undefined, 'false', 'true']) { 126 for (const emptyShadowTree of [false, true]) { 127 testElementType(true, elementName, runGetHTMLOnShadowRoot, 128 lightDOMContent, declarativeShadowDom, mode, 129 delegatesFocus, serializable, clonable, 130 emptyShadowTree); 131 } 132 } 133 } 134 } 135 } 136 } 137 } else { 138 testElementType(false, elementName, runGetHTMLOnShadowRoot, 139 lightDOMContent); 140 } 141 } 142 } 143 } 144 } 145 146 runAllTests(); 147 148 </script>