browser_computed_custom_properties.js (11054B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test that custom properties are displayed in the computed view. 7 8 const TEST_URI = ` 9 <style type="text/css"> 10 @property --registered-color { 11 syntax: '<color>'; 12 inherits: true; 13 initial-value: rgb(0, 100, 200); 14 } 15 16 @property --registered-color-secondary { 17 syntax: '<color>'; 18 inherits: true; 19 initial-value: rgb(200, 100, 00); 20 } 21 22 @property --registered-length { 23 syntax: '<length>'; 24 inherits: false; 25 initial-value: 10px; 26 } 27 28 /* This property should not be used. It shares the same suffix than previous property 29 names to assert our check isn't too loose */ 30 @property --registered { 31 syntax: '<length>'; 32 inherits: false; 33 initial-value: 10px; 34 } 35 36 body { 37 --global-custom-property: red; 38 /* invalid at computed value time */ 39 --registered-color-secondary: 1em; 40 } 41 42 main { 43 --registered-color-secondary: rgb(3, 7, 11); 44 } 45 46 h1 { 47 color: var(--global-custom-property); 48 } 49 50 #match-1 { 51 --global-custom-property: blue; 52 --custom-property-1: lime; 53 /* invalid at computed value time */ 54 --registered-color-secondary: 10px; 55 } 56 #match-2 { 57 --global-custom-property: gold; 58 --custom-property-2: cyan; 59 --custom-property-empty: ; 60 --registered-color-secondary: rgb(13, 17, 23); 61 outline: var(--registered-length) solid var( /* color */ --registered-color ); 62 } 63 </style> 64 <main> 65 <h1 id="match-1">Hello</h1> 66 <h1 id="match-2">World</h1> 67 <main> 68 `; 69 70 add_task(async function () { 71 await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); 72 const { inspector, view } = await openComputedView(); 73 74 await assertComputedPropertiesForNode(inspector, view, "body", [ 75 { 76 name: "--global-custom-property", 77 value: "red", 78 matchedRules: [ 79 { 80 selector: "body", 81 value: "red", 82 invalidAtComputedValueTime: false, 83 }, 84 ], 85 }, 86 { 87 name: "--registered-color-secondary", 88 value: "rgb(200, 100, 0)", 89 invalidAtComputedValueTime: false, 90 syntax: "<color>", 91 matchedRules: [ 92 { 93 selector: "body", 94 value: "1em", 95 invalidAtComputedValueTime: true, 96 }, 97 { 98 selector: "initial-value", 99 value: "rgb(200, 100, 00)", 100 invalidAtComputedValueTime: false, 101 }, 102 ], 103 }, 104 ]); 105 106 await assertComputedPropertiesForNode(inspector, view, "main", [ 107 { 108 name: "--global-custom-property", 109 value: "red", 110 matchedRules: [ 111 { 112 selector: "body", 113 value: "red", 114 invalidAtComputedValueTime: false, 115 }, 116 ], 117 }, 118 { 119 name: "--registered-color-secondary", 120 value: "rgb(3, 7, 11)", 121 syntax: "<color>", 122 matchedRules: [ 123 { 124 selector: "main", 125 value: "rgb(3, 7, 11)", 126 invalidAtComputedValueTime: false, 127 }, 128 { 129 selector: "body", 130 value: "1em", 131 invalidAtComputedValueTime: true, 132 }, 133 { 134 selector: "initial-value", 135 value: "rgb(200, 100, 00)", 136 invalidAtComputedValueTime: false, 137 }, 138 ], 139 }, 140 ]); 141 142 await assertComputedPropertiesForNode(inspector, view, "#match-1", [ 143 { 144 name: "color", 145 value: "rgb(0, 0, 255)", 146 }, 147 { 148 name: "--custom-property-1", 149 value: "lime", 150 }, 151 { 152 name: "--global-custom-property", 153 value: "blue", 154 }, 155 { 156 name: "--registered-color-secondary", 157 // value inherited from `main`, as the one set in #match-1 is invalid at comptued value time 158 value: "rgb(3, 7, 11)", 159 syntax: "<color>", 160 matchedRules: [ 161 { 162 selector: "#match-1", 163 value: "10px", 164 invalidAtComputedValueTime: true, 165 }, 166 { 167 selector: "main", 168 value: "rgb(3, 7, 11)", 169 invalidAtComputedValueTime: false, 170 }, 171 { 172 selector: "body", 173 value: "1em", 174 invalidAtComputedValueTime: true, 175 }, 176 { 177 selector: "initial-value", 178 value: "rgb(200, 100, 00)", 179 invalidAtComputedValueTime: false, 180 }, 181 ], 182 }, 183 ]); 184 185 await assertComputedPropertiesForNode(inspector, view, "#match-2", [ 186 { 187 name: "color", 188 value: "rgb(255, 215, 0)", 189 }, 190 { 191 name: "outline-color", 192 value: "rgb(0, 100, 200)", 193 }, 194 { 195 name: "outline-style", 196 value: "solid", 197 }, 198 { 199 name: "outline-width", 200 value: "10px", 201 }, 202 { 203 name: "--custom-property-2", 204 value: "cyan", 205 }, 206 { 207 name: "--custom-property-empty", 208 value: "<empty>", 209 }, 210 { 211 name: "--global-custom-property", 212 value: "gold", 213 }, 214 { 215 name: "--registered-color", 216 value: "rgb(0, 100, 200)", 217 matchedRules: [ 218 { 219 selector: "initial-value", 220 value: "rgb(0, 100, 200)", 221 invalidAtComputedValueTime: false, 222 }, 223 ], 224 }, 225 { 226 name: "--registered-color-secondary", 227 value: "rgb(13, 17, 23)", 228 syntax: "<color>", 229 matchedRules: [ 230 { 231 selector: "#match-2", 232 value: "rgb(13, 17, 23)", 233 invalidAtComputedValueTime: false, 234 }, 235 { 236 selector: "main", 237 value: "rgb(3, 7, 11)", 238 invalidAtComputedValueTime: false, 239 }, 240 { 241 selector: "body", 242 value: "1em", 243 invalidAtComputedValueTime: true, 244 }, 245 { 246 selector: "initial-value", 247 value: "rgb(200, 100, 00)", 248 invalidAtComputedValueTime: false, 249 }, 250 ], 251 }, 252 { 253 name: "--registered-length", 254 value: "10px", 255 matchedRules: [ 256 { 257 selector: "initial-value", 258 value: "10px", 259 invalidAtComputedValueTime: false, 260 }, 261 ], 262 }, 263 ]); 264 265 info( 266 "Checking matched selectors for shorthand property defined in longhand with CSS variable" 267 ); 268 const container = await getComputedViewMatchedRules(view, "outline-color"); 269 Assert.deepEqual( 270 [...container.querySelectorAll("p")].map(matchEl => 271 [...matchEl.querySelectorAll("div")].map(el => el.textContent) 272 ), 273 [ 274 [ 275 "#match-2", 276 277 // FIXME: At the moment, we only have an empty string value, which is the result of 278 // `CSSStyleDeclaration.getPropertyValue`. 279 // See Bug 2003264. 280 "", 281 ], 282 ], 283 "Got the expected matched selectors" 284 ); 285 286 await assertComputedPropertiesForNode(inspector, view, "html", []); 287 }); 288 289 async function assertComputedPropertiesForNode( 290 inspector, 291 view, 292 selector, 293 expected 294 ) { 295 const onRefreshed = inspector.once("computed-view-refreshed"); 296 await selectNode(selector, inspector); 297 await onRefreshed; 298 299 const computedItems = getComputedViewProperties(view); 300 is( 301 computedItems.length, 302 expected.length, 303 `Computed view has the expected number of items for "${selector}"` 304 ); 305 for (let i = 0; i < computedItems.length; i++) { 306 const expectedData = expected[i]; 307 const computedEl = computedItems[i]; 308 const nameSpan = computedEl.querySelector(".computed-property-name"); 309 const propertyName = nameSpan.firstChild.textContent; 310 const valueSpan = computedEl.querySelector(".computed-property-value"); 311 const iacvtIcon = computedEl.querySelector( 312 ".computed-property-value-container .invalid-at-computed-value-time-warning:not([hidden])" 313 ); 314 315 is( 316 propertyName, 317 expectedData.name, 318 `computed item #${i} for "${selector}" is the expected one` 319 ); 320 is( 321 valueSpan.textContent, 322 expectedData.value, 323 `computed item #${i} for "${selector}" has expected value` 324 ); 325 if (expectedData.invalidAtComputedValueTime) { 326 ok( 327 !!iacvtIcon, 328 `computed item #${i} for "${selector}" has the invalid-at-computed-value-time icon` 329 ); 330 is( 331 iacvtIcon.getAttribute("title"), 332 `Property value does not match expected "${expectedData.syntax}" syntax`, 333 `iacvt icon on computed item #${i} for "${selector}" has expected title` 334 ); 335 } else { 336 is( 337 iacvtIcon, 338 null, 339 `computed item #${i} for "${selector}" does not have the invalid-at-computed-value-time icon` 340 ); 341 } 342 343 if (expectedData.matchedRules) { 344 info(`Check matched rules for computed item #${i} for "${selector}"`); 345 const matchedRulesContainerEl = await getComputedViewMatchedRules( 346 view, 347 propertyName 348 ); 349 const matchedRulesEls = matchedRulesContainerEl.querySelectorAll("p"); 350 is( 351 matchedRulesEls.length, 352 expectedData.matchedRules.length, 353 `computed item #${i} for "${selector}" have the expected number of matched rules` 354 ); 355 356 for (let j = 0; j < matchedRulesEls.length; j++) { 357 const expectedMatchRuleData = expectedData.matchedRules[j]; 358 const matchedRuleEl = matchedRulesEls[j]; 359 const matchedRuleSelector = 360 matchedRuleEl.querySelector(".fix-get-selection").textContent; 361 const matchedRuleValue = matchedRuleEl.querySelector( 362 ".computed-other-property-value" 363 ).textContent; 364 const matchedRuleIacvtIcon = matchedRuleEl.querySelector( 365 ".invalid-at-computed-value-time-warning" 366 ); 367 is( 368 matchedRuleSelector, 369 expectedMatchRuleData.selector, 370 `Got expected selector for matched rule #${j} of computed item #${i} for "${selector}"` 371 ); 372 is( 373 matchedRuleValue, 374 expectedMatchRuleData.value, 375 `Got expected value for matched rule #${j} of computed item #${i} for "${selector}"` 376 ); 377 378 if (expectedMatchRuleData.invalidAtComputedValueTime) { 379 ok( 380 !!matchedRuleIacvtIcon, 381 `matched rule #${j} of computed item #${i} for "${selector}" has the invalid-at-computed-value-time icon` 382 ); 383 is( 384 matchedRuleIacvtIcon.getAttribute("title"), 385 `Property value does not match expected "${expectedData.syntax}" syntax`, 386 `iacvt icon on computed item #${i} for "${expectedMatchRuleData.selector}" has expected title` 387 ); 388 } else { 389 is( 390 matchedRuleIacvtIcon, 391 null, 392 `matched rule #${j} of computed item #${i} for "${selector}" does not have the invalid-at-computed-value-time icon` 393 ); 394 } 395 } 396 397 // Close the match rules to avoid issues in next iteration 398 const onMatchedRulesCollapsed = inspector.once( 399 "computed-view-property-collapsed" 400 ); 401 computedEl.querySelector(".computed-expandable").click(); 402 await onMatchedRulesCollapsed; 403 } 404 } 405 }