test_selectormatcheselement_host.html (5510B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test for CSSStyleRule::selectorMatchesElement on :host rules</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 7 </head> 8 <body> 9 <div id="host"></div> 10 <script> 11 /***** Add Shadow DOM *****/ 12 const topLevelHostEl = document.getElementById("host"); 13 const topShadow = topLevelHostEl.attachShadow({ 14 mode: "open" 15 }); 16 const topLevelShadowSectionEl = document.createElement("section"); 17 topLevelShadowSectionEl.id = "top-level-shadow-section"; 18 const nestedHostEl = document.createElement("div"); 19 nestedHostEl.id = "nested-host"; 20 21 topLevelShadowSectionEl.append("top shadow dom section",) 22 topShadow.append(topLevelShadowSectionEl, nestedHostEl); 23 24 const sharedStyleSheet = new CSSStyleSheet(); 25 sharedStyleSheet.replaceSync(` 26 section, [test-rule-shared] { 27 color: var(--a); 28 } 29 :host, [test-rule-shared] { 30 --a: red; 31 }`); 32 33 const topLevelStyleSheet = new CSSStyleSheet(); 34 topLevelStyleSheet.replaceSync(` 35 :where(section), [test-rule] { 36 background-color: var(--b); 37 } 38 :host, [test-rule] { 39 --b: gold; 40 }`); 41 42 topShadow.adoptedStyleSheets = [sharedStyleSheet, topLevelStyleSheet]; 43 44 45 const nestedShadow = nestedHostEl.attachShadow({ 46 mode: "open" 47 }); 48 const nestedShadowSectionEl = document.createElement("section"); 49 nestedShadowSectionEl.textContent = "nested shadow dom section"; 50 nestedShadowSectionEl.id = "nested-shadow-dom-section"; 51 nestedShadow.append(nestedShadowSectionEl); 52 53 const nestedStyleSheet = new CSSStyleSheet(); 54 nestedStyleSheet.replaceSync(` 55 :is(section), [test-rule-nested] { 56 border-color: var(--c); 57 } 58 :host, [test-rule-nested] { 59 --c: lime; 60 }`); 61 nestedShadow.adoptedStyleSheets = [sharedStyleSheet, nestedStyleSheet]; 62 63 /***** Test *****/ 64 add_task(async () => { 65 info("Checking rules on top level shadow section el"); 66 checkRulesAndSelectors(topLevelShadowSectionEl, [{ 67 selectorText: `:where(section), [test-rule]`, 68 selectorMatches: [true, false] 69 },{ 70 selectorText: `section, [test-rule-shared]`, 71 selectorMatches: [true, false] 72 }]); 73 74 info("Checking rules on top level host"); 75 checkRulesAndSelectors(topLevelHostEl, [{ 76 selectorText: `:host, [test-rule-shared]`, 77 selectorMatches: [true, false] 78 },{ 79 selectorText: `:host, [test-rule]`, 80 selectorMatches: [true, false] 81 }]); 82 83 info("Checking rules on nested shadow section el"); 84 checkRulesAndSelectors(nestedShadowSectionEl, [{ 85 selectorText: `section, [test-rule-shared]`, 86 selectorMatches: [true, false] 87 },{ 88 selectorText: `:is(section), [test-rule-nested]`, 89 selectorMatches: [true, false] 90 }]); 91 92 info("Checking rules on nested host"); 93 checkRulesAndSelectors(nestedHostEl, [{ 94 selectorText: `:host, [test-rule-shared]`, 95 selectorMatches: [true, false] 96 },{ 97 selectorText: `:host, [test-rule-nested]`, 98 selectorMatches: [true, false] 99 }]); 100 101 info("Check that non-shared rule selectors don't match for non-matching elements"); 102 const nonSharedTopLevelHostRules = getNonUAMatchingRules(topLevelHostEl) 103 .filter(rule => rule.parentStyleSheet !== sharedStyleSheet); 104 is(nonSharedTopLevelHostRules.length, 1, "top level host only has 1 non shared rule"); 105 is( 106 nonSharedTopLevelHostRules[0].selectorMatchesElement(0, nestedHostEl), 107 false, 108 "non-shared top level host rule does not match nested host" 109 ); 110 111 const nonSharedNestedHostRules = getNonUAMatchingRules(nestedHostEl) 112 .filter(rule => rule.parentStyleSheet !== sharedStyleSheet); 113 is(nonSharedNestedHostRules.length, 1, "nested host only has 1 non shared rule"); 114 is( 115 nonSharedNestedHostRules[0].selectorMatchesElement(0, topLevelHostEl), 116 false, 117 "non-shared nested host rule does not match top level host" 118 ); 119 }); 120 121 function checkRulesAndSelectors (element, expectedData) { 122 const rules = getNonUAMatchingRules(element); 123 is(rules.length, expectedData.length, `Got expected number of rules for #${element.id}`); 124 for (let i = 0; i < expectedData.length; i++) { 125 const rule = rules[i]; 126 const expected = expectedData[i]; 127 is(rule.selectorText, expected.selectorText, `Got expected rule at index ${i} for #${element.id}`); 128 for (let j = 0; j < expected.selectorMatches.length; j++) { 129 const selectorMatch = expected.selectorMatches[j]; 130 is( 131 rule.selectorMatchesElement(j, element), 132 selectorMatch, 133 `"${rule.selectorText}" selector part at index ${j} ${selectorMatch ? "matches": "does not match"} #${element.id}` 134 ); 135 } 136 } 137 } 138 139 function getNonUAMatchingRules(element) { 140 return SpecialPowers.InspectorUtils.getMatchingCSSRules(element) 141 .filter(rule => { 142 let selector = ""; 143 // Accessing selectorText does throw for some rules (these aren't the ones 144 // we're interested in, so it's fine) 145 try { 146 selector = rule.selectorText; 147 } catch (e) {} 148 return selector.includes("[test-rule"); 149 }) 150 } 151 152 </script> 153 </body> 154 </html>