| <!DOCTYPE html> |
| <script src = '../../../resources/testharness.js'></script> |
| <script src = '../../../resources/testharnessreport.js'></script> |
| <section id ="testSection"> |
| <test-element id="testElement">test</test-element> |
| </section> |
| <div id="stylesDiv"> |
| </div> |
| |
| <style id="universal"> |
| *, div { |
| color: red; |
| background-color: green; |
| } |
| </style> |
| |
| <style id="classIdTag"> |
| .test, #testId { |
| color: red; |
| background-color: red !important; |
| } |
| </style> |
| |
| <style id="compoundClass"> |
| .test1.test2, test-element-compound.test3 { |
| color: red; background-color: green; |
| } |
| </style> |
| |
| <style id="complex"> |
| * > .testA, * .testB, * ~ .testC { |
| color: red; |
| } |
| </style> |
| |
| <style id="cascade"> |
| * { |
| color: red; |
| background-color: red !important; |
| } |
| </style> |
| |
| <style id="stylesColor"> |
| test-multiple { |
| color: green; |
| } |
| </style> |
| |
| <style id="stylesBackground"> |
| test-multiple { |
| background-color: green; |
| } |
| </style> |
| |
| <style id="stylesBothGreen"> |
| * { |
| color: green; |
| background-color: green; |
| } |
| </style> |
| |
| <style id="stylesMultiSpec"> |
| multi-spec { |
| color: red; |
| } |
| </style> |
| |
| <script> |
| `use strict`; |
| const universalStyleSheet = universal.sheet; |
| const classIdTagStyleSheet = classIdTag.sheet; |
| const compoundClassStyleSheet = compoundClass.sheet; |
| const complexStyleSheet = complex.sheet; |
| const cascadeStyleSheet = cascade.sheet; |
| const stylesColorSheet = stylesColor.sheet; |
| const stylesBackgroundSheet = stylesBackground.sheet; |
| const stylesBothGreenSheet = stylesBothGreen.sheet; |
| const stylesMultiSpecSheet = stylesMultiSpec.sheet; |
| |
| // If these style elements aren't removed, they will be collected into global rule set automatically. |
| // To test that default style is added to global rule set properly, these style elements need to be removed here. |
| universal.remove(); |
| classIdTag.remove(); |
| compoundClass.remove(); |
| complex.remove(); |
| cascade.remove(); |
| stylesBothGreen.remove(); |
| |
| const redValue = "rgb(255, 0, 0)"; |
| const greenValue = "rgb(0, 128, 0)"; |
| const colorNotStyledValue = "rgb(0, 0, 0)"; |
| const backgroundColorNotStyledValue = "rgba(0, 0, 0, 0)"; |
| |
| test (() => { |
| window.customElements.define("test-element", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [universalStyleSheet] }); |
| assert_equals(getComputedStyle(testElement).color, redValue, "Color should follow default style"); |
| assert_equals(getComputedStyle(testElement).backgroundColor, greenValue, "Background color should follow default style"); |
| let testDiv = document.createElement("div"); |
| testElement.appendChild(testDiv); |
| assert_equals(getComputedStyle(testDiv).color, redValue, "Color should inherit default style"); |
| assert_equals(getComputedStyle(testDiv).backgroundColor, backgroundColorNotStyledValue, "Background color should not inherit default style"); |
| }, "Upgraded custom element has style from definition"); |
| |
| test (() => { |
| window.customElements.define("test-element-createlement", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [universalStyleSheet] }); |
| const el = document.createElement("test-element-createlement"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, redValue, "Color should follow default style"); |
| assert_equals(getComputedStyle(el).backgroundColor, greenValue, "Background color should follow default style"); |
| let testDiv = document.createElement("div"); |
| el.appendChild(testDiv); |
| assert_equals(getComputedStyle(testDiv).color, redValue, "Color should inherit default style"); |
| assert_equals(getComputedStyle(testDiv).backgroundColor, backgroundColorNotStyledValue, "Background color should not inherit default style"); |
| }, "Custom Element created with createElement has style from definition"); |
| |
| test (() => { |
| window.customElements.define("test-element-invalidation", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [classIdTagStyleSheet] }); |
| const el = document.createElement("test-element-invalidation"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Color should not follow default style before class added"); |
| el.classList.add("test"); |
| assert_equals(getComputedStyle(el).color, redValue , "Color should follow default style after class added"); |
| el.classList.remove("test"); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Color should not follow default style after class removed"); |
| el.setAttribute("id", "testId"); |
| assert_equals(getComputedStyle(el).color, redValue , "Color should follow default style after ID added"); |
| el.removeAttribute("id"); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Color should not follow default style after id removed"); |
| }, "Style invalidation should work"); |
| |
| test (() => { |
| window.customElements.define("test-element-author", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [classIdTagStyleSheet] }); |
| const el = document.createElement("test-element-author"); |
| testSection.appendChild(el); |
| el.classList.add("test"); |
| assert_equals(getComputedStyle(el).color, redValue , "Color should follow default style after class added"); |
| assert_equals(getComputedStyle(el).backgroundColor, redValue, "Background color should follow default style after class added"); |
| let styleEl = document.createElement("style"); |
| styleEl.innerHTML = ".test { color: green; background-color: green !important; }"; |
| testSection.appendChild(styleEl); |
| assert_equals(getComputedStyle(el).color, greenValue , "Default style should lose to normal author style"); |
| assert_equals(getComputedStyle(el).backgroundColor, redValue, "!important declaration in default style should win over !important declaration in author style"); |
| // Remove the style element after each test. Otherwise it styles elements unexpectedly later on. |
| testSection.removeChild(styleEl); |
| }, "Normal default style should lose to normal author style, !important declaration in default style should win over important declaration in author style"); |
| |
| test (() => { |
| window.customElements.define("test-element-important", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [classIdTagStyleSheet] }); |
| const el = document.createElement("test-element-important"); |
| testSection.appendChild(el); |
| el.classList.add("test"); |
| assert_equals(getComputedStyle(el).color, redValue , "Color should follow default style after class added"); |
| assert_equals(getComputedStyle(el).backgroundColor, redValue, "Background color should follow default style after class added"); |
| let styleImportant = document.createElement("style"); |
| styleImportant.innerHTML = ".test { color: green !important; background-color: green; }"; |
| testSection.appendChild(styleImportant); |
| assert_equals(getComputedStyle(el).color, greenValue , "Default style is an author style, normal declaration should lose to !important declaration in author style"); |
| assert_equals(getComputedStyle(el).backgroundColor, redValue, "!important declaration in default style should win over normal author style"); |
| testSection.removeChild(styleImportant); |
| }, "Default style should be treated as an author style"); |
| |
| test (() => { |
| window.customElements.define("test-element-order", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [cascadeStyleSheet] }); |
| const el = document.createElement("test-element-order"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, redValue, "Color should follow default style"); |
| assert_equals(getComputedStyle(el).backgroundColor, redValue, "Background color should follow default style"); |
| let shadowRoot = el.attachShadow({mode:"open"}) |
| let styleShadow = document.createElement("style"); |
| shadowRoot.appendChild(styleShadow); |
| styleShadow.innerHTML = ":host { color: green; background-color: green !important; }"; |
| assert_equals(getComputedStyle(el).color, greenValue , "Normal default style should lose to normal styles in shadow tree under the custom element"); |
| assert_equals(getComputedStyle(el).backgroundColor, greenValue, "!important declaration in default style should lose to !important declaration in styles in shadow tree under the custom element"); |
| }, "Default style in custom element with a shadow root should act like the first style in the shadow tree and have the same cascading order as styles in the shadow tree"); |
| |
| test (() => { |
| window.customElements.define("test-element-compound", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [compoundClassStyleSheet] }); |
| const el = document.createElement("test-element-compound"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Custom element should not be styled"); |
| assert_equals(getComputedStyle(el).backgroundColor, backgroundColorNotStyledValue, "Custom element should not be styled"); |
| el.classList.add("test1"); |
| el.classList.add("test2"); |
| assert_equals(getComputedStyle(el).color, redValue, "Custom element matching compound selector should follow default style"); |
| assert_equals(getComputedStyle(el).backgroundColor, greenValue, "Custom element matching compound selector should follow default style"); |
| el.classList.remove("test2"); |
| el.classList.add("test3"); |
| assert_equals(getComputedStyle(el).color, redValue, "Custom element matching compound selector with tagname equal to custom element name should follow default style"); |
| assert_equals(getComputedStyle(el).backgroundColor, greenValue, "Custom element matching compound selector with tagname equal to custom element should follow default style"); |
| el.classList.remove("test1"); |
| el.classList.remove("test3"); |
| let testDiv = document.createElement("div"); |
| el.appendChild(testDiv); |
| testDiv.classList.add("test1"); |
| assert_equals(getComputedStyle(testDiv).color, colorNotStyledValue, "Child element not matching compound selector should not be styled"); |
| assert_equals(getComputedStyle(testDiv).backgroundColor, backgroundColorNotStyledValue, "Child element not matching compound selector should not be styled"); |
| testDiv.classList.add("test2"); |
| assert_equals(getComputedStyle(testDiv).color, colorNotStyledValue, "Child element matching compound selector should not be styled"); |
| assert_equals(getComputedStyle(testDiv).backgroundColor, backgroundColorNotStyledValue, "Child element matching compound selector should not be styled"); |
| }, "Compound selectors should work"); |
| |
| test (() => { |
| window.customElements.define("test-element-complex", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [complexStyleSheet] }); |
| const el = document.createElement("test-element-complex"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Custom element should not be styled"); |
| assert_equals(getComputedStyle(el).backgroundColor, backgroundColorNotStyledValue, "Custom element should not be styled"); |
| let testDiv = document.createElement("div"); |
| el.appendChild(testDiv); |
| testDiv.classList.add("testA"); |
| assert_equals(getComputedStyle(testDiv).color, colorNotStyledValue, "Child element matching complex selector should not be styled"); |
| assert_equals(getComputedStyle(testDiv).backgroundColor, backgroundColorNotStyledValue, "Child element matching complex selector should not be styled"); |
| testDiv.classList.add("testB"); |
| assert_equals(getComputedStyle(testDiv).color, colorNotStyledValue, "Child element matching complex selector should not be styled"); |
| assert_equals(getComputedStyle(testDiv).backgroundColor, backgroundColorNotStyledValue, "Child element matching complex selector should not be styled"); |
| let testSpan = document.createElement("span"); |
| testSection.appendChild(testSpan); |
| assert_equals(getComputedStyle(testSpan).color, colorNotStyledValue, "Sibling element matching complex selector should not be styled"); |
| assert_equals(getComputedStyle(testSpan).backgroundColor, backgroundColorNotStyledValue, "Sibling element matching complex selector should not be styled"); |
| }, "Complex selectors should be ignored"); |
| |
| test (() => { |
| window.customElements.define("test-element-shadow", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [universalStyleSheet] }); |
| const el = document.createElement("test-element-shadow"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, redValue, "Color of the custom element should follow defaultStyle"); |
| assert_equals(getComputedStyle(el).backgroundColor, greenValue, "Background color of the custom element should follow default style"); |
| let childDiv = document.createElement("div"); |
| el.appendChild(childDiv); |
| assert_equals(getComputedStyle(childDiv).color, redValue, "Color of the child element should inherit defaultStyle"); |
| assert_equals(getComputedStyle(childDiv).backgroundColor, backgroundColorNotStyledValue, "Background color of the child element should not inherit default style"); |
| let shadowRoot = el.attachShadow({mode:'open'}) |
| let shadowDiv = document.createElement("div"); |
| shadowRoot.appendChild(shadowDiv); |
| assert_equals(getComputedStyle(shadowDiv).color, redValue, "Color of elements in shadow tree should inherit defaultStyle"); |
| assert_equals(getComputedStyle(shadowDiv).backgroundColor, backgroundColorNotStyledValue, "Background Color of elements in shadow tree should not inherit defaultStyle"); |
| assert_equals(getComputedStyle(childDiv).color, colorNotStyledValue, "Color of the child element should not inherit defaultStyle"); |
| assert_equals(getComputedStyle(childDiv).backgroundColor, backgroundColorNotStyledValue, "Background color of the child element should not inherit default style"); |
| }, "Default style should not style shadow tree under a custom element"); |
| |
| test(() => { |
| customElements.define("test-multiple", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [stylesColorSheet, stylesBackgroundSheet]} ); |
| const el = document.createElement("test-multiple"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, greenValue, "Color of the custom element should follow defaultStyle"); |
| assert_equals(getComputedStyle(el).backgroundColor, greenValue, "Background color of the custom element should follow default style"); |
| stylesBackgroundSheet.deleteRule(0); |
| assert_equals(getComputedStyle(el).color, greenValue, "Color of the custom element should still follow defaultStyle"); |
| assert_equals(getComputedStyle(el).backgroundColor, backgroundColorNotStyledValue, "Background color of the custom element should follow update of default style"); |
| stylesBackgroundSheet.insertRule("test-multiple { color: red; }"); |
| assert_equals(getComputedStyle(el).color, redValue, "Order of stylesheet is preserved, same specifity rule means following the last one."); |
| }, "Multiple default style should work." ); |
| |
| test(() => { |
| customElements.define("same-spec", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [stylesBothGreenSheet, universalStyleSheet]}); |
| const el = document.createElement("same-spec"); |
| testSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, redValue, "Color of the custom element should follow last rule when specificity is the same"); |
| customElements.define("multi-spec", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [stylesBothGreenSheet, stylesMultiSpecSheet]}); |
| const anotherEl = document.createElement("multi-spec"); |
| testSection.appendChild(anotherEl); |
| assert_equals(getComputedStyle(anotherEl).color, redValue, "Color of the custom element should follow rule with higher specificity."); |
| }, "Multiple stylesheets specificity."); |
| |
| </script> |
| |
| <section id="secondTestSection"> |
| <test-element id="secondTest">test</test-element> |
| <div id="divForStyle"></div> |
| <div id ="divForShadowElement"></div> |
| </section> |
| |
| <script> |
| `use strict`; |
| |
| // In order to have an instance of non-constructed stylesheet with non-null OwnerNode, |
| // we need to create a style element and keep it part of the DOM tree. |
| // This style has to be under a shadow tree so that the style from this style element will |
| // not be applied to elements outside. |
| const shadowRootNonConstructed = divForStyle.attachShadow({mode:'open'}); |
| nonConstructedStyle = document.createElement("style"); |
| shadowRootNonConstructed.appendChild(nonConstructedStyle); |
| nonConstructedStyle.sheet.insertRule("* { color: red; }", 0); |
| const nonConstructedStyleSheet = nonConstructedStyle.sheet; |
| const shadowRoot = divForShadowElement.attachShadow({mode:'open'}); |
| |
| test (() => { |
| let testElementLast = document.getElementById("secondTest"); |
| assert_equals(getComputedStyle(testElementLast).color, redValue, "Color should follow default style"); |
| assert_equals(getComputedStyle(testElementLast).backgroundColor, greenValue, "Background color should follow default style"); |
| }, "Custom element constructed from HTML parser should work"); |
| |
| test (() => { |
| window.customElements.define("el-nc", class SomeClass extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [nonConstructedStyleSheet] }); |
| const el = document.createElement("el-nc"); |
| secondTestSection.appendChild(el); |
| let shadowEl = document.createElement("el-nc"); |
| shadowRoot.appendChild(shadowEl); |
| assert_equals(getComputedStyle(el).color, redValue, "Color should follow defaultStyle"); |
| assert_equals(getComputedStyle(shadowEl).color, redValue, "Color of custom element under shadow tree should follow defaultStyle"); |
| |
| nonConstructedStyleSheet.deleteRule(0); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Rule deletion in non-constructed stylesheet should be reflected"); |
| assert_equals(getComputedStyle(shadowEl).color, colorNotStyledValue, "Rule deletion in non-constructed stylesheet should be reflected in custom elements under shadow tree"); |
| |
| nonConstructedStyleSheet.insertRule("* { color: green; }"); |
| assert_equals(getComputedStyle(el).color, greenValue, "Rule addition in non-constructed stylesheet should be reflected"); |
| assert_equals(getComputedStyle(shadowEl).color, greenValue, "Rule addition in non-constructed stylesheet should be reflected in custom elements under shadow tree"); |
| }, "CSSOM changes to non-constructed stylesheet should affect default style"); |
| |
| test (() => { |
| const constructedStyleSheet = new CSSStyleSheet(); |
| const redStyleText = "* { color: red; }"; |
| constructedStyleSheet.insertRule(redStyleText); |
| window.customElements.define("element-constructed", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [constructedStyleSheet] }); |
| const el = document.createElement("element-constructed"); |
| secondTestSection.appendChild(el); |
| let shadowEl = document.createElement("element-constructed"); |
| shadowRoot.appendChild(shadowEl); |
| assert_equals(getComputedStyle(el).color, redValue, "Color should follow defaultStyle"); |
| assert_equals(getComputedStyle(shadowEl).color, redValue, "Color of custom elements under shadow tree should follow defaultStyle"); |
| constructedStyleSheet.deleteRule(0); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Rule deletion in constructed stylesheet should be reflected"); |
| assert_equals(getComputedStyle(shadowEl).color, colorNotStyledValue, "Rule deletion in constructed stylesheet should be reflected in custom elements under shadow tree") |
| constructedStyleSheet.insertRule("* { color: green; }"); |
| assert_equals(getComputedStyle(el).color, greenValue, "Rule addition in constructed stylesheet should be reflected"); |
| assert_equals(getComputedStyle(shadowEl).color, greenValue, "Rule addition in constructed stylesheet should be reflected in custom elements under shadow tree"); |
| }, "CSSOM changes to constructed stylesheet should affect default style"); |
| |
| </script> |
| |
| <section id="thirdTestSection"> |
| </section> |
| |
| <script> |
| const linkSR = thirdTestSection.attachShadow({ mode: "open" }); |
| linkSR.innerHTML = '<link rel="stylesheet" type="text/css" href="define-with-default-style.css" id="linkedStyle">'; |
| const linkStyle = linkSR.querySelector("#linkedStyle"); |
| |
| async_test(t => { |
| linkStyle.addEventListener("load", t.step_func_done(() => { |
| const linkedStyleSheet = linkStyle.sheet; |
| window.customElements.define("element-link", class extends HTMLElement { |
| constructor() { |
| super(); |
| } |
| }, { styles: [linkedStyleSheet] }); |
| |
| const el = document.createElement("element-link"); |
| thirdTestSection.appendChild(el); |
| assert_equals(getComputedStyle(el).color, redValue, "Color should follow defaultStyle"); |
| linkedStyleSheet.deleteRule(0); |
| assert_equals(getComputedStyle(el).color, colorNotStyledValue, "Rule deletion in non-constructed stylesheet after the style being removed will be reflected in custom elements under shadow tree"); |
| linkedStyleSheet.insertRule("* { color: green; }"); |
| assert_equals(getComputedStyle(el).color, greenValue, "Rule addition in non-constructed stylesheet after the style being removed will be reflected in custom elements under shadow tree"); |
| })); |
| }, "CSSOM changes to non-constructed stylesheet from link element affect default style after link element is removed"); |
| |
| </script> |