test_getMatchingCSSRules.html (7255B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test for bug 1359217</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 <iframe id="test"></iframe> 10 <pre id="log"> 11 <script> 12 /** 13 * This test checks that getMatchingCSSRules returns correct style set in 14 * various cases. To avoid effects from UA sheets, most of the tests use 15 * an element with "unknowntagname". 16 */ 17 18 const InspectorUtils = SpecialPowers.InspectorUtils; 19 20 let iframe = document.getElementById("test"); 21 22 SimpleTest.waitForExplicitFinish(); 23 24 function getStyleRules(elem) { 25 return InspectorUtils.getMatchingCSSRules(elem); 26 } 27 28 function getDeclarationWithOrigin(target, origin) { 29 for (let rule of getStyleRules(target)) { 30 if (rule.declarationOrigin == origin) { 31 return rule; 32 } 33 } 34 return null; 35 } 36 37 function checkDeclaration(doc, selector, origin, props) { 38 info(`Checking ${origin} declarations for element "${selector}"`); 39 ok(!!props, "Should pass some properties to check"); 40 41 const target = doc.querySelector(selector); 42 let decl = getDeclarationWithOrigin(target, origin); 43 ok(!!decl, `Should find a ${origin} declaration for "${selector}"`); 44 45 const propsEntries = Object.entries(props); 46 is(decl.style.length, propsEntries.length, `Got expected number of ${origin} declarations for "${selector}"`); 47 for (let i = 0; i < decl.style.length; i++) { 48 const [name, value] = propsEntries[i] || []; 49 is(decl.style[i], name, `Should see "${name}" property on ${origin} declaration for "${selector}"`); 50 is(decl.style[name], value, `"${name}" has expected "${value}" value on ${origin} declaration for "${selector}"`); 51 } 52 let modificationThrew = false; 53 try { 54 presHintDecl.style.width = "0px"; 55 } catch (e) { 56 modificationThrew = true; 57 } 58 ok(modificationThrew, "Should not be mutable"); 59 } 60 61 // This will check that value of z-index property declarations in the 62 // rules from getMatchingCSSRules matches the given content. 63 function checkRules(doc, rulesContent, queryStr = "unknowntagname") { 64 let elem = doc.querySelector(queryStr); 65 let rules = getStyleRules(elem); 66 is(rules.length, rulesContent.length, "Rule length should match"); 67 if (rules.length != rulesContent.length) { 68 return; 69 } 70 for (let i = 0; i < rules.length; i++) { 71 let style = rules[i].style; 72 let expectation = rulesContent[i].toString(); 73 is(style.length, 1, "Should contain only one declaration"); 74 is(style.zIndex, expectation, "Should match expectation"); 75 } 76 } 77 78 const tests = [ 79 { 80 title: "Add new stylesheet", 81 async run(doc) { 82 checkRules(doc, [1]); 83 let link = doc.createElement("link"); 84 link.rel = "stylesheet"; 85 link.href = "getMatchingCSSRules-2.css"; 86 let load = new Promise(resolve => { link.onload = resolve; }); 87 doc.head.appendChild(link); 88 await load; 89 checkRules(doc, [1, 2]); 90 }, 91 }, 92 { 93 title: "Remove stylesheet", 94 async run(doc) { 95 checkRules(doc, [1]); 96 doc.querySelector("link").remove(); 97 checkRules(doc, []); 98 }, 99 }, 100 { 101 title: "Enable stylesheet", 102 async run(doc) { 103 // Set disabled flag before we invoke the utils. 104 let link = doc.querySelector("link"); 105 link.sheet.disabled = true; 106 checkRules(doc, []); 107 link.sheet.disabled = false; 108 checkRules(doc, [1]); 109 }, 110 }, 111 { 112 title: "Disable stylesheet", 113 async run(doc) { 114 checkRules(doc, [1]); 115 doc.querySelector("link").sheet.disabled = true; 116 checkRules(doc, []); 117 }, 118 }, 119 { 120 title: "Change stylesheet set", 121 base: "alternate", 122 async run(doc) { 123 checkRules(doc, []); 124 doc.selectedStyleSheetSet = "x"; 125 checkRules(doc, [1]); 126 doc.selectedStyleSheetSet = ""; 127 checkRules(doc, []); 128 }, 129 }, 130 { 131 title: "Add and remove rules", 132 async run(doc) { 133 checkRules(doc, [1]); 134 135 let sheet = doc.querySelector("link").sheet; 136 info("Inserting style rule"); 137 sheet.insertRule("unknowntagname { z-index: 3; }", 1); 138 checkRules(doc, [1, 3]); 139 140 info("Removing style rule"); 141 sheet.deleteRule(0); 142 checkRules(doc, [3]); 143 144 info("Inserting media rule"); 145 sheet.insertRule("@media all { unknowntagname { z-index: 4; } }", 1); 146 checkRules(doc, [3, 4]); 147 148 info("Inserting supports rule"); 149 sheet.insertRule( 150 "@supports (z-index: 0) { unknowntagname { z-index: 5; } }", 1); 151 checkRules(doc, [3, 5, 4]); 152 153 info("Inserting import rule"); 154 sheet.insertRule("@import url(getMatchingCSSRules-2.css);", 0); 155 // There is no notification we can get when the associated style 156 // sheet gets loaded, so we have to query it. 157 while (true) { 158 try { 159 sheet.cssRules[0].styleSheet.cssRules; 160 break; 161 } catch (e) { 162 if (e.name == "InvalidAccessError") { 163 await new Promise(resolve => requestAnimationFrame(resolve)); 164 } else { 165 throw e; 166 } 167 } 168 } 169 checkRules(doc, [2, 3, 5, 4]); 170 171 info("Removing supports rule"); 172 sheet.deleteRule(2); 173 checkRules(doc, [2, 3, 4]); 174 175 info("Removing media rule"); 176 sheet.deleteRule(2); 177 checkRules(doc, [2, 3]); 178 179 info("Removing import rule"); 180 sheet.deleteRule(0); 181 checkRules(doc, [3]); 182 }, 183 }, 184 { 185 title: "Check UA sheets", 186 async run(doc) { 187 doc.querySelector("link").remove(); 188 checkRules(doc, []); 189 let elem = doc.querySelector("unknowntagname"); 190 elem.setAttribute("dir", ""); 191 let seenUnicodeBidi = false; 192 for (let rule of getStyleRules(elem)) { 193 if (rule.style.unicodeBidi == "isolate") { 194 seenUnicodeBidi = true; 195 break; 196 } 197 } 198 ok(seenUnicodeBidi, "Should have unicode-bidi " + 199 "declaration from UA stylesheet html.css"); 200 }, 201 }, 202 { 203 title: "Check adopted sheets", 204 async run(doc, win) { 205 checkRules(doc, [1]); 206 let sheet = new win.CSSStyleSheet(); 207 sheet.replaceSync(`unknowntagname { z-index: 5 }`); 208 doc.adoptedStyleSheets.push(sheet); 209 checkRules(doc, [1, 5]); 210 }, 211 }, 212 { 213 title: "Check pres hints", 214 async run(doc, win) { 215 checkDeclaration(doc, "img", "pres-hints", { width: "10px", height: "15px", "aspect-ratio": "auto 10 / 15" }); 216 checkDeclaration(doc, "table", "pres-hints", {color: "-moz-inherit-from-body-quirk"}); 217 checkDeclaration(doc, ".anim", "animations", {"z-index": "1"}); 218 checkDeclaration(doc, "[style]", "style-attribute", {"z-index": "1"}); 219 // TODO: transitions/SMIL are a bit more annoying to test. 220 }, 221 } 222 ]; 223 224 add_task(async function runTests() { 225 for (let i = 0; i < tests.length; i++) { 226 let test = tests[i]; 227 info(`Test ${i}: ${test.title}`); 228 iframe.src = "about:blank"; 229 if (!test.base) { 230 test.base = "default"; 231 } 232 iframe.src = `file_getMatchingCSSRules-${test.base}.html`; 233 await new Promise(resolve => { iframe.onload = resolve; }); 234 try { 235 await test.run(iframe.contentDocument, iframe.contentWindow); 236 } catch (e) { 237 ok(false, "JavaScript error: " + e); 238 } 239 } 240 }); 241 </script> 242 </pre> 243 </body> 244 </html>