browser_resources_css_registered_properties.js (10632B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test the ResourceCommand API around CSS_REGISTERED_PROPERTIES. 7 8 const ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js"); 9 10 const IFRAME_URL = `https://example.org/document-builder.sjs?html=${encodeURIComponent(` 11 <style> 12 @property --css-a { 13 syntax: "<color>"; 14 inherits: true; 15 initial-value: gold; 16 } 17 </style> 18 <script> 19 CSS.registerProperty({ 20 name: "--js-a", 21 syntax: "<length>", 22 inherits: true, 23 initialValue: "20px" 24 }); 25 </script> 26 <h1>iframe</h1> 27 `)}`; 28 29 const TEST_URL = `https://example.org/document-builder.sjs?html= 30 <head> 31 <style> 32 @property --css-a { 33 syntax: "*"; 34 inherits: false; 35 } 36 37 @property --css-b { 38 syntax: "<color>"; 39 inherits: true; 40 initial-value: tomato; 41 } 42 </style> 43 <script> 44 CSS.registerProperty({ 45 name: "--js-a", 46 syntax: "*", 47 inherits: false, 48 }); 49 CSS.registerProperty({ 50 name: "--js-b", 51 syntax: "<length>", 52 inherits: true, 53 initialValue: "10px" 54 }); 55 </script> 56 </head> 57 <h1>CSS_REGISTERED_PROPERTIES</h1> 58 <iframe src="${encodeURIComponent(IFRAME_URL)}"></iframe>`; 59 60 add_task(async function () { 61 await pushPref("layout.css.properties-and-values.enabled", true); 62 const tab = await addTab(TEST_URL); 63 64 const { client, resourceCommand, targetCommand } = 65 await initResourceCommand(tab); 66 67 // Wait for targets 68 await targetCommand.startListening(); 69 const targets = []; 70 const onAvailable = ({ targetFront }) => targets.push(targetFront); 71 await targetCommand.watchTargets({ 72 types: [targetCommand.TYPES.FRAME], 73 onAvailable, 74 }); 75 await waitFor(() => targets.length === 2); 76 const [topLevelTarget, iframeTarget] = targets.sort((a, _) => 77 a.isTopLevel ? -1 : 1 78 ); 79 80 // Watching for new stylesheets shouldn't be 81 const stylesheets = []; 82 await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], { 83 onAvailable: resources => stylesheets.push(...resources), 84 ignoreExistingResources: true, 85 }); 86 87 info("Check that we get existing registered properties"); 88 const availableResources = []; 89 const updatedResources = []; 90 const destroyedResources = []; 91 await resourceCommand.watchResources( 92 [resourceCommand.TYPES.CSS_REGISTERED_PROPERTIES], 93 { 94 onAvailable: resources => availableResources.push(...resources), 95 onUpdated: resources => updatedResources.push(...resources), 96 onDestroyed: resources => destroyedResources.push(...resources), 97 } 98 ); 99 100 is( 101 availableResources.length, 102 6, 103 "The 6 existing registered properties where retrieved" 104 ); 105 106 // Sort resources so we get them alphabetically ordered by their name, with the ones for 107 // the top level target displayed first. 108 availableResources.sort((a, b) => { 109 if (a.targetFront !== b.targetFront) { 110 return a.targetFront.isTopLevel ? -1 : 1; 111 } 112 return a.name < b.name ? -1 : 1; 113 }); 114 115 assertResource(availableResources[0], { 116 name: "--css-a", 117 syntax: "*", 118 inherits: false, 119 initialValue: null, 120 fromJS: false, 121 targetFront: topLevelTarget, 122 }); 123 assertResource(availableResources[1], { 124 name: "--css-b", 125 syntax: "<color>", 126 inherits: true, 127 initialValue: "tomato", 128 fromJS: false, 129 targetFront: topLevelTarget, 130 }); 131 assertResource(availableResources[2], { 132 name: "--js-a", 133 syntax: "*", 134 inherits: false, 135 initialValue: null, 136 fromJS: true, 137 targetFront: topLevelTarget, 138 }); 139 assertResource(availableResources[3], { 140 name: "--js-b", 141 syntax: "<length>", 142 inherits: true, 143 initialValue: "10px", 144 fromJS: true, 145 targetFront: topLevelTarget, 146 }); 147 assertResource(availableResources[4], { 148 name: "--css-a", 149 syntax: "<color>", 150 inherits: true, 151 initialValue: "gold", 152 fromJS: false, 153 targetFront: iframeTarget, 154 }); 155 assertResource(availableResources[5], { 156 name: "--js-a", 157 syntax: "<length>", 158 inherits: true, 159 initialValue: "20px", 160 fromJS: true, 161 targetFront: iframeTarget, 162 }); 163 164 info("Check that we didn't get notified about existing stylesheets"); 165 // wait a bit so we'd have the time to be notified about stylesheet resources 166 await wait(500); 167 is( 168 stylesheets.length, 169 0, 170 "Watching for registered properties does not notify about existing stylesheets resources" 171 ); 172 173 info("Check that we get properties from new stylesheets"); 174 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { 175 const s = content.document.createElement("style"); 176 s.textContent = ` 177 @property --css-c { 178 syntax: "<custom-ident>"; 179 inherits: true; 180 initial-value: custom; 181 } 182 183 @property --css-d { 184 syntax: "big | bigger"; 185 inherits: true; 186 initial-value: big; 187 } 188 `; 189 content.document.head.append(s); 190 }); 191 192 info("Wait for registered properties to be available"); 193 await waitFor(() => availableResources.length === 8); 194 ok(true, "Got notified about 2 new registered properties"); 195 assertResource(availableResources[6], { 196 name: "--css-c", 197 syntax: "<custom-ident>", 198 inherits: true, 199 initialValue: "custom", 200 fromJS: false, 201 targetFront: topLevelTarget, 202 }); 203 assertResource(availableResources[7], { 204 name: "--css-d", 205 syntax: "big | bigger", 206 inherits: true, 207 initialValue: "big", 208 fromJS: false, 209 targetFront: topLevelTarget, 210 }); 211 212 info("Wait to be notified about the new stylesheet"); 213 await waitFor(() => stylesheets.length === 1); 214 ok(true, "we do get notified about stylesheets"); 215 216 info( 217 "Check that we get notified about properties registered via CSS.registerProperty" 218 ); 219 await SpecialPowers.spawn(tab.linkedBrowser, [], () => { 220 content.CSS.registerProperty({ 221 name: "--js-c", 222 syntax: "*", 223 inherits: false, 224 initialValue: 42, 225 }); 226 content.CSS.registerProperty({ 227 name: "--js-d", 228 syntax: "<color>#", 229 inherits: true, 230 initialValue: "blue,cyan", 231 }); 232 }); 233 234 await waitFor(() => availableResources.length === 10); 235 ok(true, "Got notified about 2 new registered properties"); 236 assertResource(availableResources[8], { 237 name: "--js-c", 238 syntax: "*", 239 inherits: false, 240 initialValue: "42", 241 fromJS: true, 242 targetFront: topLevelTarget, 243 }); 244 assertResource(availableResources[9], { 245 name: "--js-d", 246 syntax: "<color>#", 247 inherits: true, 248 initialValue: "blue,cyan", 249 fromJS: true, 250 targetFront: topLevelTarget, 251 }); 252 253 info( 254 "Check that we get notified about properties registered via CSS.registerProperty in iframe" 255 ); 256 const iframeBrowsingContext = await SpecialPowers.spawn( 257 tab.linkedBrowser, 258 [], 259 () => content.document.querySelector("iframe").browsingContext 260 ); 261 262 await SpecialPowers.spawn(iframeBrowsingContext, [], () => { 263 content.CSS.registerProperty({ 264 name: "--js-iframe", 265 syntax: "<color>#", 266 inherits: true, 267 initialValue: "red,salmon", 268 }); 269 }); 270 271 await waitFor(() => availableResources.length === 11); 272 ok(true, "Got notified about 2 new registered properties"); 273 assertResource(availableResources[10], { 274 name: "--js-iframe", 275 syntax: "<color>#", 276 inherits: true, 277 initialValue: "red,salmon", 278 fromJS: true, 279 targetFront: iframeTarget, 280 }); 281 282 info( 283 "Check that we get notified about destroyed properties when removing stylesheet" 284 ); 285 // sanity check 286 is(destroyedResources.length, 0, "No destroyed resources yet"); 287 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { 288 content.document.querySelector("style").remove(); 289 }); 290 await waitFor(() => destroyedResources.length == 2); 291 ok(true, "We got notified about destroyed resources"); 292 destroyedResources.sort((a, b) => a < b); 293 is( 294 destroyedResources[0].resourceType, 295 ResourceCommand.TYPES.CSS_REGISTERED_PROPERTIES, 296 "resource type is correct" 297 ); 298 is( 299 destroyedResources[0].resourceId, 300 `${topLevelTarget.actorID}:css-registered-property:--css-a`, 301 "expected css property was destroyed" 302 ); 303 is( 304 destroyedResources[1].resourceType, 305 ResourceCommand.TYPES.CSS_REGISTERED_PROPERTIES, 306 "resource type is correct" 307 ); 308 is( 309 destroyedResources[1].resourceId, 310 `${topLevelTarget.actorID}:css-registered-property:--css-b`, 311 "expected css property was destroyed" 312 ); 313 314 info( 315 "Check that we get notified about updated properties when modifying stylesheet" 316 ); 317 is(updatedResources.length, 0, "No updated resources yet"); 318 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { 319 content.document.querySelector("style").textContent = ` 320 /* not updated */ 321 @property --css-c { 322 syntax: "<custom-ident>"; 323 inherits: true; 324 initial-value: custom; 325 } 326 327 @property --css-d { 328 syntax: "big | bigger"; 329 inherits: true; 330 /* only change initial value (was big) */ 331 initial-value: bigger; 332 } 333 334 /* add a new property */ 335 @property --css-e { 336 syntax: "<color>"; 337 inherits: false; 338 initial-value: green; 339 } 340 `; 341 }); 342 await waitFor(() => updatedResources.length === 1); 343 ok(true, "One property was updated"); 344 assertResource(updatedResources[0].resource, { 345 name: "--css-d", 346 syntax: "big | bigger", 347 inherits: true, 348 initialValue: "bigger", 349 fromJS: false, 350 targetFront: topLevelTarget, 351 }); 352 353 await waitFor(() => availableResources.length === 12); 354 ok(true, "We got notified about the new property"); 355 assertResource(availableResources.at(-1), { 356 name: "--css-e", 357 syntax: "<color>", 358 inherits: false, 359 initialValue: "green", 360 fromJS: false, 361 targetFront: topLevelTarget, 362 }); 363 364 await client.close(); 365 }); 366 367 async function assertResource(resource, expected) { 368 is( 369 resource.resourceType, 370 ResourceCommand.TYPES.CSS_REGISTERED_PROPERTIES, 371 "Resource type is correct" 372 ); 373 is(resource.name, expected.name, "name is correct"); 374 is(resource.syntax, expected.syntax, "syntax is correct"); 375 is(resource.inherits, expected.inherits, "inherits is correct"); 376 is(resource.initialValue, expected.initialValue, "initialValue is correct"); 377 is(resource.fromJS, expected.fromJS, "fromJS is correct"); 378 is( 379 resource.targetFront, 380 expected.targetFront, 381 "resource is associated with expected target" 382 ); 383 }