declarative-shadow-dom-opt-in.html (6670B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>Declarative Shadow DOM</title> 4 <link rel="author" href="mailto:masonf@chromium.org"> 5 <link rel="help" href="https://github.com/whatwg/dom/issues/831"> 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 <style> 12 * { white-space: pre; } 13 iframe { display:none; } 14 </style> 15 <div id=log></div> 16 17 <div id=mainpage style="display:none"> 18 <div class=wrapper> 19 <div class=host> 20 <template shadowrootmode=open> 21 <span class=content>Content</span> 22 </template> 23 </div> 24 </div> 25 </div> 26 27 <script> 28 const content = ` 29 <html><body> 30 <div class=wrapper> 31 <div class=host> 32 <template shadowrootmode=open> 33 <span class=content>Content</span> 34 </template> 35 </div> 36 </div> 37 </body></html> 38 `; 39 40 function assert_dsd(el,shouldHaveShadow) { 41 const wrapper = el.querySelector('.wrapper'); 42 assert_true(!!wrapper,'Unable to find wrapper element'); 43 const host = wrapper.querySelector('.host'); 44 assert_true(!!host,'Unable to find host element'); 45 if (shouldHaveShadow) { 46 assert_true(!!host.shadowRoot, 'Shadow root NOT FOUND.'); 47 assert_true(!!host.shadowRoot.querySelector('.content'),'Unable to locate content'); 48 } else { 49 assert_true(!host.shadowRoot, 'Shadow root FOUND - none should be present.'); 50 const tmpl = host.querySelector('template'); 51 assert_true(!!tmpl, 'The template should be left as a <template> element'); 52 assert_equals(tmpl.getAttribute('shadowrootmode'),'open','The shadowrootmode attribute should still be present'); 53 assert_true(!!tmpl.content.querySelector('.content'),'Unable to locate content'); 54 } 55 } 56 57 test(() => { 58 const div = document.getElementById('mainpage'); 59 assert_dsd(div,true); 60 }, 'Non-fragment parsing needs no opt-in'); 61 62 const noChildElements = ['iframe','noscript','script','select','style','template','textarea','title','colgroup']; 63 const elements = HTML5_ELEMENTS.filter(el => !noChildElements.includes(el)); 64 for (let elementName of elements) { 65 var t = test(function() { 66 const el1 = document.createElement(elementName); 67 el1.innerHTML = content; 68 assert_dsd(el1,false); 69 70 const templateContent = `<template id=tmpl>${content}</template>`; 71 const el2 = document.createElement('div'); 72 el2.innerHTML = templateContent; 73 assert_dsd(el2.querySelector('#tmpl').content,false); 74 }, `innerHTML on a <${elementName}>`); 75 } 76 77 test(() => { 78 const temp = document.createElement('template'); 79 temp.innerHTML = content; 80 assert_dsd(temp.content,false, 'innerHTML should not allow declarative shadow content'); 81 }, 'innerHTML on template'); 82 83 test(() => { 84 const templateContent = `<template id=tmpl>${content}</template>`; 85 const temp = document.createElement('template'); 86 temp.innerHTML = templateContent; 87 assert_dsd(temp.content.querySelector('#tmpl').content,false); 88 }, 'innerHTML on template, with nested template content'); 89 90 test(() => { 91 const div = document.createElement('div'); 92 const shadow = div.attachShadow({mode: 'open'}); 93 shadow.innerHTML = content; 94 assert_dsd(shadow,false); 95 }, 'innerHTML on shadowRoot'); 96 97 test(() => { 98 const parser = new DOMParser(); 99 let fragment = parser.parseFromString(content, 'text/html'); 100 assert_dsd(fragment.body,false); 101 fragment = parser.parseFromString(content, 'text/html', {includeShadowRoots: false}); 102 assert_dsd(fragment.body,false); 103 fragment = parser.parseFromString(content, 'text/html', {includeShadowRoots: true}); 104 assert_dsd(fragment.body,false); 105 }, 'DOMParser (includeShadowRoots is historical)'); 106 107 test(() => { 108 const doc = document.implementation.createHTMLDocument(''); 109 doc.body.innerHTML = content; 110 assert_dsd(doc.body,false); 111 }, 'createHTMLDocument with innerHTML - not supported'); 112 113 test(() => { 114 const doc = document.implementation.createHTMLDocument(''); 115 let range = doc.createRange(); 116 range.selectNode(doc.body); 117 let documentFragment = range.createContextualFragment(content); 118 assert_dsd(documentFragment,false); 119 }, 'createContextualFragment - not supported'); 120 121 async_test((t) => { 122 let client = new XMLHttpRequest(); 123 client.addEventListener('load', t.step_func_done(() => { 124 assert_true(client.status == 200 && client.responseXML != null); 125 assert_dsd(client.responseXML.body,false); 126 t.done(); 127 })); 128 client.open("GET", `data:text/html,${content}`); 129 client.responseType = 'document'; 130 client.send(); 131 }, 'XMLHttpRequest - not supported'); 132 133 test(() => { 134 const div = document.createElement('div'); 135 div.insertAdjacentHTML('afterbegin',content); 136 assert_dsd(div,false); 137 }, 'insertAdjacentHTML on element - not supported'); 138 139 test(() => { 140 const id = 'doc-write-1'; 141 document.write(`<div id=${id} style="display:none">${content}</div>`); 142 assert_dsd(document.getElementById(id),true); 143 }, 'document.write allowed from synchronous script loaded from main document'); 144 145 test(() => { 146 const id = 'doc-write-2'; 147 const doc = document.implementation.createHTMLDocument(''); 148 doc.write(`<div id=${id}>${content}</div>`); 149 assert_dsd(doc.getElementById(id),false); 150 }, 'document.write disallowed on fresh document'); 151 152 153 async_test((t) => { 154 const iframe = document.createElement('iframe'); 155 iframe.style.display = "none"; 156 iframe.sandbox = "allow-same-origin"; 157 document.body.appendChild(iframe); 158 iframe.addEventListener('load', t.step_func_done(() => { 159 assert_dsd(iframe.contentDocument.body,true); 160 t.done(); 161 })); 162 iframe.srcdoc = content; 163 }, 'iframe'); 164 165 async_test((t) => { 166 const iframe = document.createElement('iframe'); 167 iframe.style.display = "none"; 168 document.body.appendChild(iframe); 169 iframe.addEventListener('load', t.step_func_done(() => { 170 assert_dsd(iframe.contentDocument.body,true); 171 t.done(); 172 })); 173 iframe.srcdoc = content; 174 }, 'iframe, no sandbox'); 175 176 function getHandler(t, name, shouldHaveShadow) { 177 return (e) => { 178 t.step(() => { 179 if (e.data.name == name) { 180 assert_false(e.data.error,e.data.msg); 181 assert_true(e.data.hasShadow == shouldHaveShadow); 182 t.done(); 183 } 184 }); 185 }; 186 } 187 async_test((t) => { 188 window.addEventListener('message', getHandler(t, 'iframe-sandbox', true)); 189 }, 'sandboxed iframe allows declarative Shadow DOM'); 190 191 async_test((t) => { 192 window.addEventListener('message', getHandler(t,'iframe-no-sandbox', true)); 193 }, 'iframe with no sandbox allows declarative Shadow DOM'); 194 195 </script> 196 197 <iframe name="iframe-sandbox" sandbox="allow-scripts" src="support/declarative-child-frame.html" ></iframe> 198 <iframe name="iframe-no-sandbox" src="support/declarative-child-frame.html"></iframe>