diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html')
-rw-r--r-- | testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html new file mode 100644 index 0000000000..f84e4ea9af --- /dev/null +++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html @@ -0,0 +1,801 @@ +<!DOCTYPE html> +<title>CSSStyleSheet constructor and adoptedStyleSheets</title> +<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org"> +<link rel="help" href="https://wicg.github.io/construct-stylesheets/"> +<script src = '/resources/testharness.js'></script> +<script src = '/resources/testharnessreport.js'></script> + +<section id="firstSection"> + <div> + <span class="green"></span> + <span class="red"></span> + <span class="blue"></span> + <span class="white"></span> + <span class="yellow"></span> + </div> +</section> +<section id="shadowHost"></section> +<section id="thirdSection"></section> + +<script> +'use strict'; +const greenStyleText = ".green { color: green; }"; +const redStyleTexts = [".red { color: red; }", ".red + span + span { color: red; }"]; +const blueStyleTexts = [".blue { color: blue; }", ".blue + span + span { color: blue; }"]; +const whiteStyleText = "* { color: white; }"; +const yellowStyleText = ".yellow { color: yellow; }"; + +const firstDiv = document.querySelector('#firstSection > div'); +const secondDiv = firstDiv.cloneNode(true); +const shadowHost = document.querySelector('#shadowHost'); +const shadowRoot = shadowHost.attachShadow({mode: 'open'}); +shadowRoot.appendChild(secondDiv); + +const greenSpan = firstDiv.children[0]; +const redSpan = firstDiv.children[1]; +const blueSpan = firstDiv.children[2]; +const whiteSpan = firstDiv.children[3]; +const yellowSpan = firstDiv.children[4]; +const greenShadowSpan = secondDiv.children[0]; +const redShadowSpan = secondDiv.children[1]; +const blueShadowSpan = secondDiv.children[2]; +const whiteShadowSpan = secondDiv.children[3]; +const yellowShadowSpan = secondDiv.children[4]; + +test(() => { + assert_equals(document.adoptedStyleSheets.length, 0); +}, "document.adoptedStyleSheets should initially have length 0."); + +test(() => { + const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"}); + assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet.ownerNode, null); + assert_equals(sheet.ownerRule, null); + assert_equals(sheet.media.length, 2); + assert_equals(sheet.media.item(0), "screen"); + assert_equals(sheet.media.item(1), "print"); + assert_true(sheet.disabled); + assert_equals(sheet.cssRules.length, 0); + + sheet.insertRule(redStyleTexts[0]); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); + + sheet.insertRule(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); + + const sheet2 = new CSSStyleSheet({}); + assert_equals(sheet2.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet2.ownerNode, null); + assert_equals(sheet2.ownerRule, null); + assert_equals(sheet2.media.length, 0); + assert_false(sheet2.disabled); + assert_equals(sheet2.cssRules.length, 0); + + sheet2.insertRule(redStyleTexts[1]); + assert_equals(sheet2.cssRules.length, 1); + assert_equals(sheet2.cssRules[0].cssText, redStyleTexts[1]); + + sheet2.deleteRule(0); + assert_equals(sheet2.cssRules.length, 0); + + const sheet3 = new CSSStyleSheet(); + assert_equals(sheet3.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet3.ownerNode, null); + assert_equals(sheet3.ownerRule, null); + assert_equals(sheet3.media.length, 0); + assert_false(sheet3.disabled); + assert_equals(sheet3.cssRules.length, 0); + + sheet3.insertRule(redStyleTexts[1]); + assert_equals(sheet3.cssRules.length, 1); + assert_equals(sheet3.cssRules[0].cssText, redStyleTexts[1]); + + sheet3.deleteRule(0); + assert_equals(sheet3.cssRules.length, 0); +}, 'new CSSStyleSheet produces empty CSSStyleSheet'); + +test(() => { + const sheet = new CSSStyleSheet({title: "something"}); + assert_equals(sheet.title, null, "title and alternate are not supported by the constructor. https://github.com/WICG/construct-stylesheets/issues/105"); +}, "title can be set in the CSSStyleSheet constructor"); + +promise_test(() => { + const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"}); + const promise_sheet = sheet.replace(redStyleTexts[0]); + return promise_sheet.then(function(sheet) { + assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string"); + assert_equals(sheet.ownerNode, null); + assert_equals(sheet.ownerRule, null); + assert_equals(sheet.media.length, 2); + assert_equals(sheet.media.item(0), "screen"); + assert_equals(sheet.media.item(1), "print"); + assert_true(sheet.disabled); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); + + sheet.insertRule(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); + }); +}, 'CSSStyleSheet.replace produces Promise<CSSStyleSheet>'); + +function createAllSheetsPromise() { + const greenSheet = new CSSStyleSheet(); + const redSheet = new CSSStyleSheet({media: "screen, print"}); + const blueSheet = new CSSStyleSheet({disabled: true}); + const whiteSheet = new CSSStyleSheet({disabled: true}); + const yellowSheet = new CSSStyleSheet({disabled: false}); + + const greenPromise = greenSheet.replace(greenStyleText); + const redPromise = redSheet.replace(redStyleTexts[0] + redStyleTexts[1]); + const bluePromise = blueSheet.replace(blueStyleTexts[0] + blueStyleTexts[1]); + const whitePromise = whiteSheet.replace(whiteStyleText); + const yellowPromise = yellowSheet.replace(yellowStyleText); + return [greenPromise, redPromise, bluePromise, whitePromise, yellowPromise]; +} + +promise_test(() => { + return Promise.all(createAllSheetsPromise()).then(values => { + const greenStyleSheet = values[0]; + const redStyleSheet = values[1]; + const blueStyleSheet = values[2]; + const whiteStyleSheet = values[3]; + const yellowStyleSheet = values[4]; + + // Lists of style sheets can be created, assigned and read. + + // disabled stylesheets aren't applied + document.adoptedStyleSheets = [whiteStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + // disable dsheets don't block other styles from applying + document.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet]; + + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)"); + + document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)"); + document.adoptedStyleSheets = []; + }); +}, 'Constructed style sheets can be applied on document'); + +promise_test(() => { + return Promise.all(createAllSheetsPromise()).then(values => { + const greenStyleSheet = values[0]; + const redStyleSheet = values[1]; + const blueStyleSheet = values[2]; + const whiteStyleSheet = values[3]; + const yellowStyleSheet = values[4]; + shadowRoot.adoptedStyleSheets = [whiteStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + + shadowRoot.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + + shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)"); + + shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)"); + }); +}, 'Constructed style sheets can be applied on shadow root'); + +promise_test(() => { + return Promise.all(createAllSheetsPromise()).then(values => { + const greenStyleSheet = values[0]; + const redStyleSheet = values[1]; + shadowRoot.adoptedStyleSheets = [greenStyleSheet]; + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies connected"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)", "Style applies when connected"); + let hostParent = shadowHost.parentNode; + hostParent.removeChild(shadowHost); + assert_equals(getComputedStyle(greenShadowSpan).color, "", "Style doesn't apply when detached"); + assert_equals(getComputedStyle(redShadowSpan).color, "", "Style doesn't apply when detached"); + shadowRoot.adoptedStyleSheets = [redStyleSheet, greenStyleSheet]; + hostParent.appendChild(shadowHost); + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies after reattach"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)", "Style applies after reattach"); + }); +}, 'Re-attaching shadow host with adopted stylesheets work'); + +test(() => { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(":host { color: red; }"); + const host = document.createElement("div"); + let sr = host.attachShadow({mode: "open"}); + sr.adoptedStyleSheets = [sheet]; + document.body.appendChild(host); + assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies when connected"); + sheet.replaceSync(":host { color: blue; }"); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Style update applies when connected"); +}, 'Attaching a shadow root that already has adopted stylesheets work'); + +test(() => { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(":host([red]) { color: red; } :host(.blue) { color: blue; }"); + const host = document.createElement("div"); + host.toggleAttribute("red"); + document.body.appendChild(host); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "No style applies yet"); + + let sr = host.attachShadow({mode: "open"}); + sr.adoptedStyleSheets = [sheet]; + + assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after adding style"); + document.body.removeChild(host); + document.body.appendChild(host); + assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after reattachment"); + host.toggleAttribute("red"); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "Attribute updates to the element after reattachment apply"); + host.classList.toggle("blue"); + assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Class updates to the element after reattachment apply"); + +}, "Re-attaching shadow host and updating attributes work"); + +promise_test(() => { + const plainSheet = new CSSStyleSheet(); + const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); + return redStyleSheetPromise.then(function(redStyleSheet) { + document.adoptedStyleSheets = [redStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + redStyleSheet.insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + redStyleSheet.deleteRule(1); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + redStyleSheet.cssRules[0].style.color = "white"; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 255, 255)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); +}); +}, 'Changes to constructed stylesheets through CSSOM is reflected'); + +promise_test(() => { + const plainSheet = new CSSStyleSheet(); + const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); + return redStyleSheetPromise.then(function(redStyleSheet) { + document.adoptedStyleSheets = [redStyleSheet]; + shadowRoot.adoptedStyleSheets = [redStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + + shadowRoot.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)"); + document.adoptedStyleSheets = []; + }); +}, 'Constructed stylesheet can be used and modified in multiple TreeScopes'); + +promise_test(() => { + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + const thirdDiv = firstDiv.cloneNode(true); + iframe.contentDocument.body.appendChild(thirdDiv); + const greenIframeSpan = thirdDiv.children[0]; + const redIframeSpan = thirdDiv.children[1]; + const blueIframeSpan = thirdDiv.children[2]; + const whiteIframeSpan = thirdDiv.children[3]; + const yellowIframeSpan = thirdDiv.children[4]; + + const plainSheet = new CSSStyleSheet(); + const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]); + return redStyleSheetPromise.then(function(redStyleSheet) { + assert_throws_dom( + 'NotAllowedError', + iframe.contentWindow.DOMException, + () => { iframe.contentDocument.adoptedStyleSheets = [redStyleSheet]; } + ); + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + + document.adoptedStyleSheets = [redStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + document.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + }); +}, 'Stylesheets constructed on the main Document cannot be used in iframes'); + +promise_test(async () => { + const iframe = document.createElement("iframe"); + const iframeLoaded = new Promise(resolve => iframe.addEventListener("load", resolve)); + document.body.appendChild(iframe); + await iframeLoaded; + const thirdDiv = firstDiv.cloneNode(true); + iframe.contentDocument.body.appendChild(thirdDiv); + const greenIframeSpan = thirdDiv.children[0]; + const redIframeSpan = thirdDiv.children[1]; + const blueIframeSpan = thirdDiv.children[2]; + const whiteIframeSpan = thirdDiv.children[3]; + const yellowIframeSpan = thirdDiv.children[4]; + + // Make sure both the main Document and the iframe are not styled + const emptyStyleSheet = new CSSStyleSheet(); + document.adoptedStyleSheets = [emptyStyleSheet]; + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + + const iframePlainSheet = new iframe.contentWindow.CSSStyleSheet(); + const iframeRedStyleSheetPromise = iframePlainSheet.replace(redStyleTexts[0]); + return iframeRedStyleSheetPromise.then(function(iframeRedStyleSheet) { + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeRedStyleSheet]; }); + assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)"); + + iframe.contentDocument.adoptedStyleSheets = [iframeRedStyleSheet]; + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + + iframe.contentDocument.adoptedStyleSheets[0].insertRule(redStyleTexts[1]); + assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)"); + assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(255, 0, 0)"); + assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)"); + }); +}, 'Stylesheet constructed on iframe cannot be used in the main Document'); +</script> + +<div id="divNonConstructed" class="nonConstructed"> +</div> + +<script> +`use strict`; +const shadowRootNonConstructed = divNonConstructed.attachShadow({mode:'open'}) +nonConstructedStyle = document.createElement("style"); +shadowRootNonConstructed.appendChild(nonConstructedStyle); +nonConstructedStyle.sheet.insertRule(".nonConstructed { color: red; }", 0); +const nonConstructedStyleSheet = nonConstructedStyle.sheet; + +test(() => { + assert_equals(getComputedStyle(divNonConstructed).color, "rgb(0, 0, 0)"); + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [nonConstructedStyleSheet]; }); +}, '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'); + +test(() => { + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframeDiv = iframe.contentDocument.createElement("div"); + iframeDiv.classList.add("nonConstructed"); + iframe.contentDocument.body.appendChild(iframeDiv); + + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); + assert_throws_dom('NotAllowedError', iframe.contentWindow.DOMException, () => { + iframe.contentDocument.adoptedStyleSheets = [nonConstructedStyleSheet]; + }); + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); + + iframeStyle = iframe.contentDocument.createElement("style"); + iframe.contentDocument.body.appendChild(iframeStyle); + iframeStyle.sheet.insertRule(".nonConstructedSpan { color: red; }"); + const iframeStyleSheet = iframeStyle.sheet; + nonConstructedSpan = document.createElement("span"); + nonConstructedSpan.classList.add(".nonConstructedSpan"); + divNonConstructed.appendChild(nonConstructedSpan); + + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); + assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeStyleSheet]; }); + assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)"); +}, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet and the AdoptedStyleSheets are in different document trees'); + +function attachShadowDiv(host) { + const shadowRoot = host.attachShadow({mode: 'open'}); + const shadowDiv = document.createElement("div"); + shadowRoot.appendChild(shadowDiv); + return shadowDiv; +} + +test(() => { + const sheet = new CSSStyleSheet(); + assert_equals(sheet.cssRules.length, 0); + + sheet.replaceSync(redStyleTexts[0]) + assert_equals(sheet.cssRules.length, 1); + assert_equals(redStyleTexts[0], sheet.cssRules[0].cssText); + + sheet.replaceSync(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 1); + assert_equals(redStyleTexts[1], sheet.cssRules[0].cssText); +}, 'CSSStyleSheet.replaceSync replaces stylesheet text synchronously'); + +test(() => { + // Attach a div inside a shadow root with the class ".red". + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + shadowDiv.classList.add("red"); + // Create empty stylesheet. + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace the stylesheet text that will color it red. + sheet.replaceSync(redStyleTexts[0]); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + assert_equals(sheet.cssRules.length, 1); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]); + sheet.insertRule(redStyleTexts[1]); + assert_equals(sheet.cssRules.length, 2); + assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); +}, 'CSSStyleSheet.replaceSync correctly updates the style of its adopters synchronously'); + +test(() => { + // Attach a div inside a shadow root with the class ".red". + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + shadowDiv.classList.add("target"); + + // Create empty stylesheet. + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + + // Replace the stylesheet text that will color it red. + sheet.replaceSync(".target { color: red; }"); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + + // Create a style element that will set colors to white. + const style = document.createElement("style"); + style.textContent = ".target { color: white; }"; + span.shadowRoot.appendChild(style) + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles"); + + span.shadowRoot.adoptedStyleSheets = []; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect"); + + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style"); + + sheet.disabled = true; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect"); + + sheet.disabled = false; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again"); +}, 'Adopted sheets are ordered after non-adopted sheets in the shadow root') + +test(() => { + // Attach a div inside a shadow root with the class ".red". + const span = document.createElement("span"); + thirdSection.appendChild(span); + span.classList.add("target"); + + // Create empty stylesheet. + const sheet = new CSSStyleSheet(); + document.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(span).color, "rgb(0, 0, 0)"); + + // Replace the stylesheet text that will color it red. + sheet.replaceSync(".target { color: red; }"); + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)"); + + // Create a style element that will set colors to white. + const style = document.createElement("style"); + style.textContent = ".target { color: white; }"; + span.appendChild(style) + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles"); + + document.adoptedStyleSheets = []; + assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect"); + + document.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style"); + + sheet.disabled = true; + assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect"); + + sheet.disabled = false; + assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again") +}, 'Adopted sheets are ordered after non-adopted sheets in the document') + +const import_text = '@import url("support/constructable-import.css");'; + +test(() => { + assert_throws_dom("SyntaxError", () => { (new CSSStyleSheet).insertRule(import_text) }); +}, 'Inserting an @import rule through insertRule on a constructed stylesheet throws an exception'); + +promise_test(t => { + const importUrl = "support/constructable-import.css"; + const sheet = new CSSStyleSheet(); + + sheet.replaceSync(`@import url("${importUrl}");`); + + const timeAfterReplaceSync = performance.now(); + let link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = importUrl; + + return new Promise(resolve => { + link.addEventListener("error", t.unreached_func("Load shouldn't fail")); + link.addEventListener("load", t.step_func(() => { + let entries = window.performance.getEntriesByType('resource').filter(entry => entry.name.includes(importUrl)); + assert_equals(entries.length, 1, "There should be only one entry for the import URL"); + assert_greater_than_equal(entries[0].startTime, timeAfterReplaceSync, "The entry's start time should be after replaceSync threw"); + link.remove(); + resolve(); + })); + document.body.appendChild(link); + }); +}, "CSSStyleSheet.replaceSync should not trigger any loads from @import rules") + +promise_test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is NOT applied. + const sheet_promise = sheet.replace(import_text); + return sheet_promise.then((sheet) => { + // replace() ignores @import rules: + assert_equals(sheet.cssRules.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + }).catch((reason) => { + assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); + }); +}, 'CSSStyleSheet.replace allows, but ignores, import rule inside'); + +promise_test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const targetSpan = document.createElement("span"); + targetSpan.classList.add("target"); + shadowDiv.appendChild(targetSpan); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is NOT applied, but regular rule does apply. + const sheet_promise = sheet.replace(import_text + ".target { color: blue; }"); + return sheet_promise.then((sheet) => { + assert_equals(sheet.cssRules.length, 1); + // @import not applied: + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // regular rule applied: + assert_equals(getComputedStyle(targetSpan).color, "rgb(0, 0, 255)"); + }).catch((reason) => { + assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`); + }); +}, 'CSSStyleSheet.replace ignores @import rule but still loads other rules'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + // Replace and assert that the imported rule is NOT applied. + try { + sheet.replaceSync(import_text); + // replaceSync() ignores @import rules: + assert_equals(sheet.cssRules.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); + } catch(reason) { + assert_unreached(`replaceSync threw an exception (${reason}) when it shouldn't have`); + } +}, 'CSSStyleSheet.replaceSync allows, but ignores, import rule inside'); + +promise_test(() => { + const sheet = new CSSStyleSheet(); + const sheet_promise = sheet.replace("@import url('not-there.css');"); + + return sheet_promise.then((sheet) => { + // No exception here + }).catch((reason) => { + assert_unreached("Promise was rejected"); + }); +}, 'CSSStyleSheet.replace does not reject on failed imports'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + const newSpan = span.cloneNode(true); + assert_equals(newSpan.shadowRoot, null); +}, 'Cloning a shadow host will not clone shadow root, and also adoptedStyleSheets'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + const newSpan = iframe.contentDocument.importNode(span, true); + iframe.contentDocument.body.appendChild(newSpan); + assert_equals(newSpan.shadowRoot, null); +}, 'Importing a shadow host will not copy shadow root, and also adoptedStyleSheets'); + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + const sheet = new CSSStyleSheet(); + sheet.replaceSync("* { color: red; }"); + span.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + + document.adoptNode(span); + assert_equals(span.shadowRoot.adoptedStyleSheets.length, 1); + assert_equals(span.shadowRoot.adoptedStyleSheets[0], sheet); + + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.contentDocument.adoptNode(span); + iframe.contentDocument.body.appendChild(span); + assert_not_equals(span.shadowRoot, null); + assert_equals(span.shadowRoot.adoptedStyleSheets.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); +}, 'Adopting a shadow host will empty adoptedStyleSheets if adopting to a different document'); + +test(() => { + const span = document.createElement("span"); + const div = document.createElement("div"); + thirdSection.appendChild(span); + span.appendChild(div); + const shadowDiv = attachShadowDiv(div); + const sheet = new CSSStyleSheet(); + sheet.replaceSync("* { color: red; }"); + div.shadowRoot.adoptedStyleSheets = [sheet]; + assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)"); + + document.adoptNode(span); + assert_equals(div.shadowRoot.adoptedStyleSheets.length, 1); + assert_equals(div.shadowRoot.adoptedStyleSheets[0], sheet); + + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.contentDocument.adoptNode(span); + iframe.contentDocument.body.appendChild(span); + assert_not_equals(div.shadowRoot, null); + assert_equals(div.shadowRoot.adoptedStyleSheets.length, 0); + assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)"); +}, `Adopting a shadow host's ancestor will empty adoptedStyleSheets if adopting to a different document`); + +test(() => { + const host = document.createElement("div"); + const root = host.attachShadow({mode: "open"}); + root.adoptedStyleSheets = [new CSSStyleSheet()]; + document.body.offsetTop; +}, 'Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.'); + +test(() => { + const host = document.createElement("div"); + thirdSection.appendChild(host); + const root = host.attachShadow({mode: "open"}); + const sheet = new CSSStyleSheet(); + root.adoptedStyleSheets = [sheet]; + host.remove(); + sheet.replaceSync(''); +}, 'Modifying an adopted stylesheet on a disconnected shadow root should not crash.'); + +function currentLocation() { + const sections = location.href.split("/") + sections.pop(); + return sections.join("/"); +} + +test(() => { + const span = document.createElement("span"); + thirdSection.appendChild(span); + const shadowDiv = attachShadowDiv(span); + + const fileName = "example.png" + const fullPath = `${currentLocation()}/${fileName}` + + const sheet = new CSSStyleSheet(); + span.shadowRoot.adoptedStyleSheets = [sheet]; + + sheet.replaceSync(`* { background-image: url("${fileName}"); }`); + const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage; + + sheet.replaceSync(`* { background-image: url("${fullPath}"); }`); + const styleFromFull = getComputedStyle(shadowDiv).backgroundImage; + + assert_equals(styleFromRelative, styleFromFull); +}, "Constructing a sheet with the default base URL uses the constructor document's base URL for CSS rules"); + +</script> |