CSSStyleSheet-constructable.html (37727B)
1 <!DOCTYPE html> 2 <title>CSSStyleSheet constructor and adoptedStyleSheets</title> 3 <link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org"> 4 <link rel="help" href="https://wicg.github.io/construct-stylesheets/"> 5 <script src = '/resources/testharness.js'></script> 6 <script src = '/resources/testharnessreport.js'></script> 7 8 <section id="firstSection"> 9 <div> 10 <span class="green"></span> 11 <span class="red"></span> 12 <span class="blue"></span> 13 <span class="white"></span> 14 <span class="yellow"></span> 15 </div> 16 </section> 17 <section id="shadowHost"></section> 18 <section id="thirdSection"></section> 19 20 <script> 21 'use strict'; 22 const greenStyleText = ".green { color: green; }"; 23 const redStyleTexts = [".red { color: red; }", ".red + span + span { color: red; }"]; 24 const blueStyleTexts = [".blue { color: blue; }", ".blue + span + span { color: blue; }"]; 25 const whiteStyleText = "* { color: white; }"; 26 const yellowStyleText = ".yellow { color: yellow; }"; 27 28 const firstDiv = document.querySelector('#firstSection > div'); 29 const secondDiv = firstDiv.cloneNode(true); 30 const shadowHost = document.querySelector('#shadowHost'); 31 const shadowRoot = shadowHost.attachShadow({mode: 'open'}); 32 shadowRoot.appendChild(secondDiv); 33 34 const greenSpan = firstDiv.children[0]; 35 const redSpan = firstDiv.children[1]; 36 const blueSpan = firstDiv.children[2]; 37 const whiteSpan = firstDiv.children[3]; 38 const yellowSpan = firstDiv.children[4]; 39 const greenShadowSpan = secondDiv.children[0]; 40 const redShadowSpan = secondDiv.children[1]; 41 const blueShadowSpan = secondDiv.children[2]; 42 const whiteShadowSpan = secondDiv.children[3]; 43 const yellowShadowSpan = secondDiv.children[4]; 44 45 test(() => { 46 assert_equals(document.adoptedStyleSheets.length, 0); 47 }, "document.adoptedStyleSheets should initially have length 0."); 48 49 test(() => { 50 const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"}); 51 assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string"); 52 assert_equals(sheet.ownerNode, null); 53 assert_equals(sheet.ownerRule, null); 54 assert_equals(sheet.media.length, 2); 55 assert_equals(sheet.media.item(0), "screen"); 56 assert_equals(sheet.media.item(1), "print"); 57 assert_true(sheet.disabled); 58 assert_equals(sheet.cssRules.length, 0); 59 60 sheet.insertRule(redStyleTexts[0]); 61 assert_equals(sheet.cssRules.length, 1); 62 assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); 63 64 sheet.insertRule(redStyleTexts[1]); 65 assert_equals(sheet.cssRules.length, 2); 66 assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); 67 68 const sheet2 = new CSSStyleSheet({}); 69 assert_equals(sheet2.title, null, "The title attribute must return the title or null if title is the empty string"); 70 assert_equals(sheet2.ownerNode, null); 71 assert_equals(sheet2.ownerRule, null); 72 assert_equals(sheet2.media.length, 0); 73 assert_false(sheet2.disabled); 74 assert_equals(sheet2.cssRules.length, 0); 75 76 sheet2.insertRule(redStyleTexts[1]); 77 assert_equals(sheet2.cssRules.length, 1); 78 assert_equals(sheet2.cssRules[0].cssText, redStyleTexts[1]); 79 80 sheet2.deleteRule(0); 81 assert_equals(sheet2.cssRules.length, 0); 82 83 const sheet3 = new CSSStyleSheet(); 84 assert_equals(sheet3.title, null, "The title attribute must return the title or null if title is the empty string"); 85 assert_equals(sheet3.ownerNode, null); 86 assert_equals(sheet3.ownerRule, null); 87 assert_equals(sheet3.media.length, 0); 88 assert_false(sheet3.disabled); 89 assert_equals(sheet3.cssRules.length, 0); 90 91 sheet3.insertRule(redStyleTexts[1]); 92 assert_equals(sheet3.cssRules.length, 1); 93 assert_equals(sheet3.cssRules[0].cssText, redStyleTexts[1]); 94 95 sheet3.deleteRule(0); 96 assert_equals(sheet3.cssRules.length, 0); 97 }, 'new CSSStyleSheet produces empty CSSStyleSheet'); 98 99 test(() => { 100 const sheet = new CSSStyleSheet({title: "something"}); 101 assert_equals(sheet.title, null, "title and alternate are not supported by the constructor. https://github.com/WICG/construct-stylesheets/issues/105"); 102 }, "title can be set in the CSSStyleSheet constructor"); 103 104 promise_test(() => { 105 const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"}); 106 const promise_sheet = sheet.replace(redStyleTexts[0]); 107 return promise_sheet.then(function(sheet) { 108 assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string"); 109 assert_equals(sheet.ownerNode, null); 110 assert_equals(sheet.ownerRule, null); 111 assert_equals(sheet.media.length, 2); 112 assert_equals(sheet.media.item(0), "screen"); 113 assert_equals(sheet.media.item(1), "print"); 114 assert_true(sheet.disabled); 115 assert_equals(sheet.cssRules.length, 1); 116 assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); 117 118 sheet.insertRule(redStyleTexts[1]); 119 assert_equals(sheet.cssRules.length, 2); 120 assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); 121 }); 122 }, 'CSSStyleSheet.replace produces Promise<CSSStyleSheet>'); 123 124 function createAllSheetsPromise() { 125 const greenSheet = new CSSStyleSheet(); 126 const redSheet = new CSSStyleSheet({media: "screen, print"}); 127 const blueSheet = new CSSStyleSheet({disabled: true}); 128 const whiteSheet = new CSSStyleSheet({disabled: true}); 129 const yellowSheet = new CSSStyleSheet({disabled: false}); 130 131 const greenPromise = greenSheet.replace(greenStyleText); 132 const redPromise = redSheet.replace(redStyleTexts[0] + redStyleTexts[1]); 133 const bluePromise = blueSheet.replace(blueStyleTexts[0] + blueStyleTexts[1]); 134 const whitePromise = whiteSheet.replace(whiteStyleText); 135 const yellowPromise = yellowSheet.replace(yellowStyleText); 136 return [greenPromise, redPromise, bluePromise, whitePromise, yellowPromise]; 137 } 138 139 promise_test(() => { 140 return Promise.all(createAllSheetsPromise()).then(values => { 141 const greenStyleSheet = values[0]; 142 const redStyleSheet = values[1]; 143 const blueStyleSheet = values[2]; 144 const whiteStyleSheet = values[3]; 145 const yellowStyleSheet = values[4]; 146 147 // Lists of style sheets can be created, assigned and read. 148 149 // disabled stylesheets aren't applied 150 document.adoptedStyleSheets = [whiteStyleSheet]; 151 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 152 assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); 153 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 154 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 155 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 156 157 // disable dsheets don't block other styles from applying 158 document.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet]; 159 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)"); 160 assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); 161 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 162 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 163 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 164 165 document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet]; 166 167 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 168 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 169 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 170 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); 171 assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)"); 172 173 document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet]; 174 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)"); 175 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 176 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 177 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); 178 assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)"); 179 document.adoptedStyleSheets = []; 180 }); 181 }, 'Constructed style sheets can be applied on document'); 182 183 promise_test(() => { 184 return Promise.all(createAllSheetsPromise()).then(values => { 185 const greenStyleSheet = values[0]; 186 const redStyleSheet = values[1]; 187 const blueStyleSheet = values[2]; 188 const whiteStyleSheet = values[3]; 189 const yellowStyleSheet = values[4]; 190 shadowRoot.adoptedStyleSheets = [whiteStyleSheet]; 191 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); 192 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)"); 193 assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); 194 assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); 195 assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); 196 197 shadowRoot.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet]; 198 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)"); 199 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)"); 200 assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); 201 assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); 202 assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); 203 204 shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet]; 205 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); 206 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); 207 assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); 208 assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); 209 assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)"); 210 211 shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet]; 212 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)"); 213 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); 214 assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); 215 assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); 216 assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)"); 217 }); 218 }, 'Constructed style sheets can be applied on shadow root'); 219 220 promise_test(() => { 221 return Promise.all(createAllSheetsPromise()).then(values => { 222 const greenStyleSheet = values[0]; 223 const redStyleSheet = values[1]; 224 shadowRoot.adoptedStyleSheets = [greenStyleSheet]; 225 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies connected"); 226 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)", "Style applies when connected"); 227 let hostParent = shadowHost.parentNode; 228 hostParent.removeChild(shadowHost); 229 assert_equals(getComputedStyle(greenShadowSpan).color, "", "Style doesn't apply when detached"); 230 assert_equals(getComputedStyle(redShadowSpan).color, "", "Style doesn't apply when detached"); 231 shadowRoot.adoptedStyleSheets = [redStyleSheet, greenStyleSheet]; 232 hostParent.appendChild(shadowHost); 233 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies after reattach"); 234 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)", "Style applies after reattach"); 235 }); 236 }, 'Re-attaching shadow host with adopted stylesheets work'); 237 238 test(() => { 239 const sheet = new CSSStyleSheet(); 240 sheet.replaceSync(":host { color: red; }"); 241 const host = document.createElement("div"); 242 let sr = host.attachShadow({mode: "open"}); 243 sr.adoptedStyleSheets = [sheet]; 244 document.body.appendChild(host); 245 assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies when connected"); 246 sheet.replaceSync(":host { color: blue; }"); 247 assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Style update applies when connected"); 248 }, 'Attaching a shadow root that already has adopted stylesheets work'); 249 250 test(() => { 251 const sheet = new CSSStyleSheet(); 252 sheet.replaceSync(":host([red]) { color: red; } :host(.blue) { color: blue; }"); 253 const host = document.createElement("div"); 254 host.toggleAttribute("red"); 255 document.body.appendChild(host); 256 assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "No style applies yet"); 257 258 let sr = host.attachShadow({mode: "open"}); 259 sr.adoptedStyleSheets = [sheet]; 260 261 assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after adding style"); 262 document.body.removeChild(host); 263 document.body.appendChild(host); 264 assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after reattachment"); 265 host.toggleAttribute("red"); 266 assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "Attribute updates to the element after reattachment apply"); 267 host.classList.toggle("blue"); 268 assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Class updates to the element after reattachment apply"); 269 270 }, "Re-attaching shadow host and updating attributes work"); 271 272 promise_test(() => { 273 const plainSheet = new CSSStyleSheet(); 274 const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); 275 return redStyleSheetPromise.then(function(redStyleSheet) { 276 document.adoptedStyleSheets = [redStyleSheet]; 277 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 278 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 279 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 280 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 281 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 282 283 redStyleSheet.insertRule(redStyleTexts[1]); 284 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 285 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 286 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 287 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); 288 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 289 290 redStyleSheet.deleteRule(1); 291 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 292 assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); 293 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 294 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); 295 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 296 297 redStyleSheet.cssRules[0].style.color = "white"; 298 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 299 assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); 300 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 301 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 255, 255)"); 302 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 303 }); 304 }, 'Changes to constructed stylesheets through CSSOM is reflected'); 305 306 promise_test(() => { 307 const plainSheet = new CSSStyleSheet(); 308 const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); 309 return redStyleSheetPromise.then(function(redStyleSheet) { 310 document.adoptedStyleSheets = [redStyleSheet]; 311 shadowRoot.adoptedStyleSheets = [redStyleSheet]; 312 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 313 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 314 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 315 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 316 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 317 318 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); 319 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); 320 assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); 321 assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); 322 assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); 323 324 shadowRoot.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); 325 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 326 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 327 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 328 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); 329 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 330 331 assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); 332 assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); 333 assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); 334 assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); 335 assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); 336 document.adoptedStyleSheets = []; 337 }); 338 }, 'Constructed stylesheet can be used and modified in multiple TreeScopes'); 339 340 promise_test(() => { 341 const iframe = document.createElement("iframe"); 342 document.body.appendChild(iframe); 343 const thirdDiv = firstDiv.cloneNode(true); 344 iframe.contentDocument.body.appendChild(thirdDiv); 345 const greenIframeSpan = thirdDiv.children[0]; 346 const redIframeSpan = thirdDiv.children[1]; 347 const blueIframeSpan = thirdDiv.children[2]; 348 const whiteIframeSpan = thirdDiv.children[3]; 349 const yellowIframeSpan = thirdDiv.children[4]; 350 351 const plainSheet = new CSSStyleSheet(); 352 const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); 353 return redStyleSheetPromise.then(function(redStyleSheet) { 354 assert_throws_dom( 355 'NotAllowedError', 356 iframe.contentWindow.DOMException, 357 () => { iframe.contentDocument.adoptedStyleSheets = [redStyleSheet]; } 358 ); 359 assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); 360 assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)"); 361 assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); 362 assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); 363 assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); 364 365 document.adoptedStyleSheets = [redStyleSheet]; 366 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 367 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 368 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 369 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 370 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 371 372 document.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); 373 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 374 assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); 375 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 376 assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); 377 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 378 }); 379 }, 'Stylesheets constructed on the main Document cannot be used in iframes'); 380 381 promise_test(async () => { 382 const iframe = document.createElement("iframe"); 383 const iframeLoaded = new Promise(resolve => iframe.addEventListener("load", resolve)); 384 document.body.appendChild(iframe); 385 await iframeLoaded; 386 const thirdDiv = firstDiv.cloneNode(true); 387 iframe.contentDocument.body.appendChild(thirdDiv); 388 const greenIframeSpan = thirdDiv.children[0]; 389 const redIframeSpan = thirdDiv.children[1]; 390 const blueIframeSpan = thirdDiv.children[2]; 391 const whiteIframeSpan = thirdDiv.children[3]; 392 const yellowIframeSpan = thirdDiv.children[4]; 393 394 // Make sure both the main Document and the iframe are not styled 395 const emptyStyleSheet = new CSSStyleSheet(); 396 document.adoptedStyleSheets = [emptyStyleSheet]; 397 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 398 assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); 399 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 400 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 401 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 402 403 assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); 404 assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)"); 405 assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); 406 assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); 407 assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); 408 409 const iframePlainSheet = new iframe.contentWindow.CSSStyleSheet(); 410 const iframeRedStyleSheetPromise = iframePlainSheet.replace(redStyleTexts[0]); 411 return iframeRedStyleSheetPromise.then(function(iframeRedStyleSheet) { 412 assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeRedStyleSheet]; }); 413 assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); 414 assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); 415 assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); 416 assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); 417 assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); 418 419 iframe.contentDocument.adoptedStyleSheets = [iframeRedStyleSheet]; 420 assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); 421 assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)"); 422 assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); 423 assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); 424 assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); 425 426 iframe.contentDocument.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); 427 assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); 428 assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)"); 429 assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); 430 assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(255, 0, 0)"); 431 assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); 432 }); 433 }, 'Stylesheet constructed on iframe cannot be used in the main Document'); 434 </script> 435 436 <div id="divNonConstructed" class="nonConstructed"> 437 </div> 438 439 <script> 440 `use strict`; 441 const shadowRootNonConstructed = divNonConstructed.attachShadow({mode:'open'}) 442 nonConstructedStyle = document.createElement("style"); 443 shadowRootNonConstructed.appendChild(nonConstructedStyle); 444 nonConstructedStyle.sheet.insertRule(".nonConstructed { color: red; }", 0); 445 const nonConstructedStyleSheet = nonConstructedStyle.sheet; 446 447 test(() => { 448 assert_equals(getComputedStyle(divNonConstructed).color, "rgb(0, 0, 0)"); 449 assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [nonConstructedStyleSheet]; }); 450 }, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet is in the same document tree as the AdoptedStyleSheets'); 451 452 test(() => { 453 const iframe = document.createElement("iframe"); 454 document.body.appendChild(iframe); 455 iframeDiv = iframe.contentDocument.createElement("div"); 456 iframeDiv.classList.add("nonConstructed"); 457 iframe.contentDocument.body.appendChild(iframeDiv); 458 459 assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); 460 assert_throws_dom('NotAllowedError', iframe.contentWindow.DOMException, () => { 461 iframe.contentDocument.adoptedStyleSheets = [nonConstructedStyleSheet]; 462 }); 463 assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); 464 465 iframeStyle = iframe.contentDocument.createElement("style"); 466 iframe.contentDocument.body.appendChild(iframeStyle); 467 iframeStyle.sheet.insertRule(".nonConstructedSpan { color: red; }"); 468 const iframeStyleSheet = iframeStyle.sheet; 469 nonConstructedSpan = document.createElement("span"); 470 nonConstructedSpan.classList.add(".nonConstructedSpan"); 471 divNonConstructed.appendChild(nonConstructedSpan); 472 473 assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); 474 assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeStyleSheet]; }); 475 assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); 476 }, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet and the AdoptedStyleSheets are in different document trees'); 477 478 function attachShadowDiv(host) { 479 const shadowRoot = host.attachShadow({mode: 'open'}); 480 const shadowDiv = document.createElement("div"); 481 shadowRoot.appendChild(shadowDiv); 482 return shadowDiv; 483 } 484 485 test(() => { 486 const sheet = new CSSStyleSheet(); 487 assert_equals(sheet.cssRules.length, 0); 488 489 sheet.replaceSync(redStyleTexts[0]) 490 assert_equals(sheet.cssRules.length, 1); 491 assert_equals(redStyleTexts[0], sheet.cssRules[0].cssText); 492 493 sheet.replaceSync(redStyleTexts[1]); 494 assert_equals(sheet.cssRules.length, 1); 495 assert_equals(redStyleTexts[1], sheet.cssRules[0].cssText); 496 }, 'CSSStyleSheet.replaceSync replaces stylesheet text synchronously'); 497 498 test(() => { 499 // Attach a div inside a shadow root with the class ".red". 500 const span = document.createElement("span"); 501 thirdSection.appendChild(span); 502 const shadowDiv = attachShadowDiv(span); 503 shadowDiv.classList.add("red"); 504 // Create empty stylesheet. 505 const sheet = new CSSStyleSheet(); 506 span.shadowRoot.adoptedStyleSheets = [sheet]; 507 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 508 // Replace the stylesheet text that will color it red. 509 sheet.replaceSync(redStyleTexts[0]); 510 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); 511 assert_equals(sheet.cssRules.length, 1); 512 assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); 513 sheet.insertRule(redStyleTexts[1]); 514 assert_equals(sheet.cssRules.length, 2); 515 assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); 516 }, 'CSSStyleSheet.replaceSync correctly updates the style of its adopters synchronously'); 517 518 test(() => { 519 // Attach a div inside a shadow root with the class ".red". 520 const span = document.createElement("span"); 521 thirdSection.appendChild(span); 522 const shadowDiv = attachShadowDiv(span); 523 shadowDiv.classList.add("target"); 524 525 // Create empty stylesheet. 526 const sheet = new CSSStyleSheet(); 527 span.shadowRoot.adoptedStyleSheets = [sheet]; 528 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 529 530 // Replace the stylesheet text that will color it red. 531 sheet.replaceSync(".target { color: red; }"); 532 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); 533 534 // Create a style element that will set colors to white. 535 const style = document.createElement("style"); 536 style.textContent = ".target { color: white; }"; 537 span.shadowRoot.appendChild(style) 538 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles"); 539 540 span.shadowRoot.adoptedStyleSheets = []; 541 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect"); 542 543 span.shadowRoot.adoptedStyleSheets = [sheet]; 544 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style"); 545 546 sheet.disabled = true; 547 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect"); 548 549 sheet.disabled = false; 550 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again"); 551 }, 'Adopted sheets are ordered after non-adopted sheets in the shadow root') 552 553 test(() => { 554 // Attach a div inside a shadow root with the class ".red". 555 const span = document.createElement("span"); 556 thirdSection.appendChild(span); 557 span.classList.add("target"); 558 559 // Create empty stylesheet. 560 const sheet = new CSSStyleSheet(); 561 document.adoptedStyleSheets = [sheet]; 562 assert_equals(getComputedStyle(span).color, "rgb(0, 0, 0)"); 563 564 // Replace the stylesheet text that will color it red. 565 sheet.replaceSync(".target { color: red; }"); 566 assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)"); 567 568 // Create a style element that will set colors to white. 569 const style = document.createElement("style"); 570 style.textContent = ".target { color: white; }"; 571 span.appendChild(style) 572 assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles"); 573 574 document.adoptedStyleSheets = []; 575 assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect"); 576 577 document.adoptedStyleSheets = [sheet]; 578 assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style"); 579 580 sheet.disabled = true; 581 assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect"); 582 583 sheet.disabled = false; 584 assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again") 585 }, 'Adopted sheets are ordered after non-adopted sheets in the document') 586 587 const import_text = '@import url("support/constructable-import.css");'; 588 589 test(() => { 590 assert_throws_dom("SyntaxError", () => { (new CSSStyleSheet).insertRule(import_text) }); 591 }, 'Inserting an @import rule through insertRule on a constructed stylesheet throws an exception'); 592 593 promise_test(t => { 594 const importUrl = "support/constructable-import.css"; 595 const sheet = new CSSStyleSheet(); 596 597 sheet.replaceSync(`@import url("${importUrl}");`); 598 599 const timeAfterReplaceSync = performance.now(); 600 let link = document.createElement("link"); 601 link.rel = "stylesheet"; 602 link.href = importUrl; 603 604 return new Promise(resolve => { 605 link.addEventListener("error", t.unreached_func("Load shouldn't fail")); 606 link.addEventListener("load", t.step_func(() => { 607 let entries = window.performance.getEntriesByType('resource').filter(entry => entry.name.includes(importUrl)); 608 assert_equals(entries.length, 1, "There should be only one entry for the import URL"); 609 assert_greater_than_equal(entries[0].startTime, timeAfterReplaceSync, "The entry's start time should be after replaceSync threw"); 610 link.remove(); 611 resolve(); 612 })); 613 document.body.appendChild(link); 614 }); 615 }, "CSSStyleSheet.replaceSync should not trigger any loads from @import rules") 616 617 promise_test(() => { 618 const span = document.createElement("span"); 619 thirdSection.appendChild(span); 620 const shadowDiv = attachShadowDiv(span); 621 const sheet = new CSSStyleSheet(); 622 span.shadowRoot.adoptedStyleSheets = [sheet]; 623 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 624 // Replace and assert that the imported rule is NOT applied. 625 const sheet_promise = sheet.replace(import_text); 626 return sheet_promise.then((sheet) => { 627 // replace() ignores @import rules: 628 assert_equals(sheet.cssRules.length, 0); 629 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 630 }).catch((reason) => { 631 assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); 632 }); 633 }, 'CSSStyleSheet.replace allows, but ignores, import rule inside'); 634 635 promise_test(() => { 636 const span = document.createElement("span"); 637 thirdSection.appendChild(span); 638 const shadowDiv = attachShadowDiv(span); 639 const targetSpan = document.createElement("span"); 640 targetSpan.classList.add("target"); 641 shadowDiv.appendChild(targetSpan); 642 const sheet = new CSSStyleSheet(); 643 span.shadowRoot.adoptedStyleSheets = [sheet]; 644 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 645 // Replace and assert that the imported rule is NOT applied, but regular rule does apply. 646 const sheet_promise = sheet.replace(import_text + ".target { color: blue; }"); 647 return sheet_promise.then((sheet) => { 648 assert_equals(sheet.cssRules.length, 1); 649 // @import not applied: 650 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 651 // regular rule applied: 652 assert_equals(getComputedStyle(targetSpan).color, "rgb(0, 0, 255)"); 653 }).catch((reason) => { 654 assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); 655 }); 656 }, 'CSSStyleSheet.replace ignores @import rule but still loads other rules'); 657 658 test(() => { 659 const span = document.createElement("span"); 660 thirdSection.appendChild(span); 661 const shadowDiv = attachShadowDiv(span); 662 const sheet = new CSSStyleSheet(); 663 span.shadowRoot.adoptedStyleSheets = [sheet]; 664 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 665 // Replace and assert that the imported rule is NOT applied. 666 try { 667 sheet.replaceSync(import_text); 668 // replaceSync() ignores @import rules: 669 assert_equals(sheet.cssRules.length, 0); 670 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 671 } catch(reason) { 672 assert_unreached(`replaceSync threw an exception (${reason}) when it shouldn't have`); 673 } 674 }, 'CSSStyleSheet.replaceSync allows, but ignores, import rule inside'); 675 676 promise_test(() => { 677 const sheet = new CSSStyleSheet(); 678 const sheet_promise = sheet.replace("@import url('not-there.css');"); 679 680 return sheet_promise.then((sheet) => { 681 // No exception here 682 }).catch((reason) => { 683 assert_unreached("Promise was rejected"); 684 }); 685 }, 'CSSStyleSheet.replace does not reject on failed imports'); 686 687 test(() => { 688 const span = document.createElement("span"); 689 thirdSection.appendChild(span); 690 const shadowDiv = attachShadowDiv(span); 691 const sheet = new CSSStyleSheet(); 692 span.shadowRoot.adoptedStyleSheets = [sheet]; 693 694 const newSpan = span.cloneNode(true); 695 assert_equals(newSpan.shadowRoot, null); 696 }, 'Cloning a shadow host will not clone shadow root, and also adoptedStyleSheets'); 697 698 test(() => { 699 const span = document.createElement("span"); 700 thirdSection.appendChild(span); 701 const shadowDiv = attachShadowDiv(span); 702 const sheet = new CSSStyleSheet(); 703 span.shadowRoot.adoptedStyleSheets = [sheet]; 704 705 const iframe = document.createElement("iframe"); 706 document.body.appendChild(iframe); 707 const newSpan = iframe.contentDocument.importNode(span, true); 708 iframe.contentDocument.body.appendChild(newSpan); 709 assert_equals(newSpan.shadowRoot, null); 710 }, 'Importing a shadow host will not copy shadow root, and also adoptedStyleSheets'); 711 712 test(() => { 713 const span = document.createElement("span"); 714 thirdSection.appendChild(span); 715 const shadowDiv = attachShadowDiv(span); 716 const sheet = new CSSStyleSheet(); 717 sheet.replaceSync("* { color: red; }"); 718 span.shadowRoot.adoptedStyleSheets = [sheet]; 719 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); 720 721 document.adoptNode(span); 722 assert_equals(span.shadowRoot.adoptedStyleSheets.length, 1); 723 assert_equals(span.shadowRoot.adoptedStyleSheets[0], sheet); 724 725 const iframe = document.createElement("iframe"); 726 document.body.appendChild(iframe); 727 iframe.contentDocument.adoptNode(span); 728 iframe.contentDocument.body.appendChild(span); 729 assert_not_equals(span.shadowRoot, null); 730 assert_equals(span.shadowRoot.adoptedStyleSheets.length, 0); 731 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 732 }, 'Adopting a shadow host will empty adoptedStyleSheets if adopting to a different document'); 733 734 test(() => { 735 const span = document.createElement("span"); 736 const div = document.createElement("div"); 737 thirdSection.appendChild(span); 738 span.appendChild(div); 739 const shadowDiv = attachShadowDiv(div); 740 const sheet = new CSSStyleSheet(); 741 sheet.replaceSync("* { color: red; }"); 742 div.shadowRoot.adoptedStyleSheets = [sheet]; 743 assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); 744 745 document.adoptNode(span); 746 assert_equals(div.shadowRoot.adoptedStyleSheets.length, 1); 747 assert_equals(div.shadowRoot.adoptedStyleSheets[0], sheet); 748 749 const iframe = document.createElement("iframe"); 750 document.body.appendChild(iframe); 751 iframe.contentDocument.adoptNode(span); 752 iframe.contentDocument.body.appendChild(span); 753 assert_not_equals(div.shadowRoot, null); 754 assert_equals(div.shadowRoot.adoptedStyleSheets.length, 0); 755 assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); 756 }, `Adopting a shadow host's ancestor will empty adoptedStyleSheets if adopting to a different document`); 757 758 test(() => { 759 const host = document.createElement("div"); 760 const root = host.attachShadow({mode: "open"}); 761 root.adoptedStyleSheets = [new CSSStyleSheet()]; 762 document.body.offsetTop; 763 }, 'Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.'); 764 765 test(() => { 766 const host = document.createElement("div"); 767 thirdSection.appendChild(host); 768 const root = host.attachShadow({mode: "open"}); 769 const sheet = new CSSStyleSheet(); 770 root.adoptedStyleSheets = [sheet]; 771 host.remove(); 772 sheet.replaceSync(''); 773 }, 'Modifying an adopted stylesheet on a disconnected shadow root should not crash.'); 774 775 function currentLocation() { 776 const sections = location.href.split("/") 777 sections.pop(); 778 return sections.join("/"); 779 } 780 781 test(() => { 782 const span = document.createElement("span"); 783 thirdSection.appendChild(span); 784 const shadowDiv = attachShadowDiv(span); 785 786 const fileName = "example.png" 787 const fullPath = `${currentLocation()}/${fileName}` 788 789 const sheet = new CSSStyleSheet(); 790 span.shadowRoot.adoptedStyleSheets = [sheet]; 791 792 sheet.replaceSync(`* { background-image: url("${fileName}"); }`); 793 const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage; 794 795 sheet.replaceSync(`* { background-image: url("${fullPath}"); }`); 796 const styleFromFull = getComputedStyle(shadowDiv).backgroundImage; 797 798 assert_equals(styleFromRelative, styleFromFull); 799 }, "Constructing a sheet with the default base URL uses the constructor document's base URL for CSS rules"); 800 801 </script>