browser_markup_shadowdom.js (8658B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 requestLongerTimeout(2); 7 8 // Test a few static pages using webcomponents and check that they are displayed as 9 // expected in the markup view. 10 11 const TEST_DATA = [ 12 { 13 // Test that expanding a shadow host shows a shadow root node and direct children. 14 // Test that expanding a shadow root shows the shadow dom. 15 // Test that slotted elements are visible in the shadow dom. 16 title: "generic shadow dom test", 17 url: `data:text/html;charset=utf-8, 18 <test-component> 19 <div slot="slot1">slotted-1<div>inner</div></div> 20 <div slot="slot2">slotted-2<div>inner</div></div> 21 <div class="no-slot-class">no-slot-text<div>inner</div></div> 22 </test-component> 23 24 <script> 25 'use strict'; 26 customElements.define('test-component', class extends HTMLElement { 27 constructor() { 28 super(); 29 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 30 shadowRoot.innerHTML = \` 31 <slot name="slot1"></slot> 32 <slot name="slot2"></slot> 33 <slot></slot> 34 \`; 35 } 36 }); 37 </script>`, 38 tree: ` 39 test-component 40 #shadow-root 41 name="slot1" 42 div!slotted 43 name="slot2" 44 div!slotted 45 slot 46 div!slotted 47 slot="slot1" 48 slotted-1 49 inner 50 slot="slot2" 51 slotted-2 52 inner 53 class="no-slot-class" 54 no-slot-text 55 inner`, 56 }, 57 { 58 // Test that components without any direct children still display a shadow root node, 59 // if a shadow root is attached to the host. 60 title: "shadow root without direct children", 61 url: `data:text/html;charset=utf-8, 62 <test-component></test-component> 63 <script> 64 "use strict"; 65 customElements.define("test-component", class extends HTMLElement { 66 constructor() { 67 super(); 68 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 69 shadowRoot.innerHTML = "<slot><div>fallback-content</div></slot>"; 70 } 71 }); 72 </script>`, 73 tree: ` 74 test-component 75 #shadow-root 76 slot 77 fallback-content`, 78 }, 79 { 80 // Test that markup view is correctly displayed for non-trivial shadow DOM nesting. 81 title: "nested components", 82 url: `data:text/html;charset=utf-8, 83 <test-component > 84 <div slot="slot1">slot1-1</div> 85 <third-component slot="slot2"></third-component> 86 </test-component> 87 88 <script> 89 (function() { 90 'use strict'; 91 92 function defineComponent(name, html) { 93 customElements.define(name, class extends HTMLElement { 94 constructor() { 95 super(); 96 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 97 shadowRoot.innerHTML = html; 98 } 99 }); 100 } 101 102 defineComponent('test-component', \` 103 <div id="test-container"> 104 <slot name="slot1"></slot> 105 <slot name="slot2"></slot> 106 <other-component><div slot="other1">other1-content</div></other-component> 107 </div>\`); 108 defineComponent('other-component', 109 '<div id="other-container"><slot id="other1" name="other1"></slot></div>'); 110 defineComponent('third-component', '<div>Third component</div>'); 111 })(); 112 </script>`, 113 tree: ` 114 test-component 115 #shadow-root 116 test-container 117 slot 118 div!slotted 119 slot 120 third-component!slotted 121 other-component 122 #shadow-root 123 div 124 slot 125 div!slotted 126 div 127 div 128 third-component 129 #shadow-root 130 div`, 131 }, 132 { 133 // Test that ::before and ::after pseudo elements are correctly displayed in host 134 // components and in slot elements. 135 title: "pseudo elements", 136 url: `data:text/html;charset=utf-8, 137 <style> 138 test-component::before { content: "before-host" } 139 test-component::after { content: "after-host" } 140 </style> 141 142 <test-component> 143 <div class="light-dom"></div> 144 </test-component> 145 146 <script> 147 "use strict"; 148 customElements.define("test-component", class extends HTMLElement { 149 constructor() { 150 super(); 151 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 152 shadowRoot.innerHTML = \` 153 <style> 154 slot { display: block } /* avoid whitespace nodes */ 155 slot::before { content: "before-slot" } 156 slot::after { content: "after-slot" } 157 </style> 158 <slot>default content</slot> 159 \`; 160 } 161 }); 162 </script>`, 163 tree: ` 164 test-component 165 #shadow-root 166 style 167 slot { display: block } 168 slot 169 ::before 170 div!slotted 171 default content 172 ::after 173 ::before 174 class="light-dom" 175 ::after`, 176 }, 177 { 178 // Test empty web components are still displayed correctly. 179 title: "empty components", 180 url: `data:text/html;charset=utf-8, 181 <test-component></test-component> 182 183 <script> 184 "use strict"; 185 customElements.define("test-component", class extends HTMLElement { 186 constructor() { 187 super(); 188 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 189 shadowRoot.innerHTML = ""; 190 } 191 }); 192 </script>`, 193 tree: ` 194 test-component 195 #shadow-root`, 196 }, 197 { 198 // Test shadow hosts show their shadow root even if they contain just a short text. 199 title: "shadow host with inline-text-child", 200 url: `data:text/html;charset=utf-8, 201 <test-component> 202 <inner-component>short-text-outside</inner-component> 203 </test-component> 204 205 <script> 206 "use strict"; 207 208 customElements.define("test-component", class extends HTMLElement { 209 constructor() { 210 super(); 211 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 212 shadowRoot.innerHTML = "<div><slot></slot></div>"; 213 } 214 }); 215 216 customElements.define("inner-component", class extends HTMLElement { 217 constructor() { 218 super(); 219 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 220 shadowRoot.innerHTML = "short-text-inside"; 221 } 222 }); 223 </script>`, 224 tree: ` 225 test-component 226 #shadow-root 227 div 228 slot 229 inner-component!slotted 230 inner-component 231 #shadow-root 232 short-text-inside 233 short-text-outside`, 234 }, 235 { 236 // Test for Bug 1537877, crash with nested custom elements without slot. 237 title: "nested components without slot", 238 url: `data:text/html;charset=utf-8, 239 <test-component> 240 <inner-component slot="non-existing-slot"></inner-component> 241 </test-component> 242 243 <script> 244 "use strict"; 245 246 customElements.define('test-component', class extends HTMLElement { 247 constructor() { 248 super(); 249 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 250 shadowRoot.innerHTML = '<div>test-component-content</div>' 251 } 252 }); 253 254 customElements.define('inner-component', class extends HTMLElement { 255 constructor() { 256 super(); 257 let shadowRoot = this.attachShadow({mode: "#MODE#"}); 258 shadowRoot.innerHTML = 'inner-component-content' 259 } 260 }); 261 </script>`, 262 tree: ` 263 test-component 264 #shadow-root 265 div 266 inner-component 267 #shadow-root 268 inner-component-content`, 269 }, 270 ]; 271 272 for (const { url, tree, title } of TEST_DATA) { 273 // Test each configuration in both open and closed modes 274 add_task(async function () { 275 info(`Testing: [${title}] in OPEN mode`); 276 const { inspector, tab } = await openInspectorForURL( 277 url.replace(/#MODE#/g, "open") 278 ); 279 await assertMarkupViewAsTree(tree, "test-component", inspector); 280 await removeTab(tab); 281 }); 282 add_task(async function () { 283 info(`Testing: [${title}] in CLOSED mode`); 284 const { inspector, tab } = await openInspectorForURL( 285 url.replace(/#MODE#/g, "closed") 286 ); 287 await assertMarkupViewAsTree(tree, "test-component", inspector); 288 await removeTab(tab); 289 }); 290 }