declarative-shadow-dom-attachment.html (4984B)
1 <!DOCTYPE html> 2 <title>Declarative Shadow DOM Element Attachment</title> 3 <link rel='author' href='mailto:masonf@chromium.org'> 4 <link rel='help' href='https://github.com/whatwg/dom/issues/831'> 5 <script src='/resources/testharness.js'></script> 6 <script src='/resources/testharnessreport.js'></script> 7 <script src='../../html/resources/common.js'></script> 8 9 <script> 10 const shadowContent = '<span>Shadow tree</span><slot></slot>'; 11 function getDeclarativeContent(mode, delegatesFocus) { 12 const delegatesFocusText = delegatesFocus ? ' shadowrootdelegatesfocus' : ''; 13 return `<template shadowrootmode=${mode}${delegatesFocusText}>${shadowContent}</template>`; 14 } 15 16 const lightDomTextContent = 'Light DOM'; 17 function addDeclarativeShadowRoot(elementType, mode, delegatesFocus) { 18 const declarativeString = `<${elementType} id=theelement>${getDeclarativeContent(mode, delegatesFocus)} 19 <span class='lightdom'>${lightDomTextContent}</span></${elementType}>`; 20 const wrapper = document.createElement('div'); 21 wrapper.setHTMLUnsafe(declarativeString); // Should never throw 22 const element = wrapper.querySelector('#theelement'); 23 return {wrapper, element}; 24 } 25 26 function testElementType(allowed, nochildren, elementType, mode, delegatesFocus) { 27 test((t) => { 28 const nodes = addDeclarativeShadowRoot(elementType, mode, delegatesFocus); 29 if (allowed) { 30 const element = nodes.element; 31 assert_true(!!element, 'Unable to locate the element'); 32 // Just one light DOM child, and no leftover template. 33 assert_true(!nodes.wrapper.querySelector('template')); 34 assert_equals(element.children.length, 1); 35 assert_equals(element.children[0].textContent, lightDomTextContent); 36 let originalShadowRoot = null; 37 if (mode === 'open') { 38 assert_true(!!element.shadowRoot, 'Shadow root should be present'); 39 assert_equals(element.shadowRoot.innerHTML, shadowContent, 'Correct shadow content'); 40 assert_equals(element.shadowRoot.delegatesFocus,delegatesFocus,'Correct delegatesFocus') 41 originalShadowRoot = element.shadowRoot; 42 } 43 44 const oppositeMode = (mode === 'open') ? 'closed' : 'open'; 45 assert_throws_dom('NotSupportedError', () => { 46 element.attachShadow({mode: oppositeMode}); 47 }, 'Calling attachShadow with a declarative shadow fails if the mode doesn\'t match'); 48 49 // Now, call attachShadow() and make sure we get back the same (original) shadowRoot, but empty. 50 const newShadow = element.attachShadow({mode: mode, delegatesFocus: delegatesFocus}); 51 if (mode === 'open') { 52 assert_equals(element.shadowRoot, originalShadowRoot, 'The same shadow root should be returned'); 53 assert_equals(element.shadowRoot.innerHTML, '', 'Empty shadow content'); 54 assert_equals(element.shadowRoot.mode, mode, 'Original shadow mode'); 55 } 56 57 assert_throws_dom('NotSupportedError', () => { 58 element.attachShadow({mode: mode}); 59 }, 'Calling attachShadow a second time on an element with a declarative shadow fails (same mode)'); 60 61 assert_throws_dom('NotSupportedError', () => { 62 element.attachShadow({mode: oppositeMode}); 63 }, 'Calling attachShadow a second time on an element with a declarative shadow fails (opposite mode)'); 64 } else { 65 if (!nochildren) { 66 // Invalid elements should retain a <template> element child with a shadowrootmode attribute. 67 const template = nodes.wrapper.querySelector('template[shadowrootmode]'); 68 assert_true(!!template); 69 assert_equals(template.getAttribute('shadowrootmode'), mode, `Template with shadowrootmode=${mode} should be left over`); 70 const span = nodes.wrapper.querySelector('span.lightdom'); 71 assert_true(!!span); 72 assert_equals(span.textContent, lightDomTextContent); 73 if (nodes.element) { 74 // For some tags (e.g. <html>) there won't be an element inside wrapper. 75 assert_true(!nodes.element.shadowRoot, 'Shadow root should not be present'); 76 } 77 } 78 } 79 }, `Declarative Shadow DOM as a child of <${elementType}>, with mode=${mode}, delegatesFocus=${delegatesFocus}. Should be ${allowed ? 'safelisted' : 'disallowed'}.`); 80 } 81 82 function runAllTests() { 83 const noCheck = ['body', 'template']; 84 const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS.filter(el => !noCheck.includes(el)); 85 const disallowed = HTML5_SHADOW_DISALLOWED_ELEMENTS.filter(el => !noCheck.includes(el)); 86 const noChildElements = ['iframe','noscript','script','select','style','textarea','title']; 87 for (let delegatesFocus of [false, true]) { 88 for (let mode of ['open', 'closed', 'invalid']) { 89 for (let elementName of safelisted) { 90 testElementType(mode !== 'invalid', false, elementName, mode, delegatesFocus); 91 } 92 for (let elementName of disallowed) { 93 testElementType(false, noChildElements.includes(elementName), elementName, mode, delegatesFocus); 94 } 95 } 96 } 97 } 98 99 runAllTests(); 100 101 </script>