Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"use strict";
// Test the ResourceCommand API around CSS_REGISTERED_PROPERTIES.
const IFRAME_URL = `https://example.org/document-builder.sjs?html=${encodeURIComponent(`
<style>
@property --css-a {
syntax: "<color>";
inherits: true;
initial-value: gold;
}
</style>
<script>
CSS.registerProperty({
name: "--js-a",
syntax: "<length>",
inherits: true,
initialValue: "20px"
});
</script>
<h1>iframe</h1>
`)}`;
<head>
<style>
@property --css-a {
syntax: "*";
inherits: false;
}
@property --css-b {
syntax: "<color>";
inherits: true;
initial-value: tomato;
}
</style>
<script>
CSS.registerProperty({
name: "--js-a",
syntax: "*",
inherits: false,
});
CSS.registerProperty({
name: "--js-b",
syntax: "<length>",
inherits: true,
initialValue: "10px"
});
</script>
</head>
<h1>CSS_REGISTERED_PROPERTIES</h1>
<iframe src="${encodeURIComponent(IFRAME_URL)}"></iframe>`;
add_task(async function () {
await pushPref("layout.css.properties-and-values.enabled", true);
const tab = await addTab(TEST_URL);
const { client, resourceCommand, targetCommand } = await initResourceCommand(
tab
);
// Wait for targets
await targetCommand.startListening();
const targets = [];
const onAvailable = ({ targetFront }) => targets.push(targetFront);
await targetCommand.watchTargets({
types: [targetCommand.TYPES.FRAME],
onAvailable,
});
await waitFor(() => targets.length === 2);
const [topLevelTarget, iframeTarget] = targets.sort((a, _) =>
a.isTopLevel ? -1 : 1
);
// Watching for new stylesheets shouldn't be
const stylesheets = [];
await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
onAvailable: resources => stylesheets.push(...resources),
ignoreExistingResources: true,
});
info("Check that we get existing registered properties");
const availableResources = [];
const updatedResources = [];
const destroyedResources = [];
await resourceCommand.watchResources(
[resourceCommand.TYPES.CSS_REGISTERED_PROPERTIES],
{
onAvailable: resources => availableResources.push(...resources),
onUpdated: resources => updatedResources.push(...resources),
onDestroyed: resources => destroyedResources.push(...resources),
}
);
is(
availableResources.length,
6,
"The 6 existing registered properties where retrieved"
);
// Sort resources so we get them alphabetically ordered by their name, with the ones for
// the top level target displayed first.
availableResources.sort((a, b) => {
if (a.targetFront !== b.targetFront) {
return a.targetFront.isTopLevel ? -1 : 1;
}
return a.name < b.name ? -1 : 1;
});
assertResource(availableResources[0], {
name: "--css-a",
syntax: "*",
inherits: false,
initialValue: null,
fromJS: false,
targetFront: topLevelTarget,
});
assertResource(availableResources[1], {
name: "--css-b",
syntax: "<color>",
inherits: true,
initialValue: "tomato",
fromJS: false,
targetFront: topLevelTarget,
});
assertResource(availableResources[2], {
name: "--js-a",
syntax: "*",
inherits: false,
initialValue: null,
fromJS: true,
targetFront: topLevelTarget,
});
assertResource(availableResources[3], {
name: "--js-b",
syntax: "<length>",
inherits: true,
initialValue: "10px",
fromJS: true,
targetFront: topLevelTarget,
});
assertResource(availableResources[4], {
name: "--css-a",
syntax: "<color>",
inherits: true,
initialValue: "gold",
fromJS: false,
targetFront: iframeTarget,
});
assertResource(availableResources[5], {
name: "--js-a",
syntax: "<length>",
inherits: true,
initialValue: "20px",
fromJS: true,
targetFront: iframeTarget,
});
info("Check that we didn't get notified about existing stylesheets");
// wait a bit so we'd have the time to be notified about stylesheet resources
await wait(500);
is(
stylesheets.length,
0,
"Watching for registered properties does not notify about existing stylesheets resources"
);
info("Check that we get properties from new stylesheets");
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const s = content.document.createElement("style");
s.textContent = `
@property --css-c {
syntax: "<custom-ident>";
inherits: true;
initial-value: custom;
}
@property --css-d {
syntax: "big | bigger";
inherits: true;
initial-value: big;
}
`;
content.document.head.append(s);
});
info("Wait for registered properties to be available");
await waitFor(() => availableResources.length === 8);
ok(true, "Got notified about 2 new registered properties");
assertResource(availableResources[6], {
name: "--css-c",
syntax: "<custom-ident>",
inherits: true,
initialValue: "custom",
fromJS: false,
targetFront: topLevelTarget,
});
assertResource(availableResources[7], {
name: "--css-d",
syntax: "big | bigger",
inherits: true,
initialValue: "big",
fromJS: false,
targetFront: topLevelTarget,
});
info("Wait to be notified about the new stylesheet");
await waitFor(() => stylesheets.length === 1);
ok(true, "we do get notified about stylesheets");
info(
"Check that we get notified about properties registered via CSS.registerProperty"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
content.CSS.registerProperty({
name: "--js-c",
syntax: "*",
inherits: false,
initialValue: 42,
});
content.CSS.registerProperty({
name: "--js-d",
syntax: "<color>#",
inherits: true,
initialValue: "blue,cyan",
});
});
await waitFor(() => availableResources.length === 10);
ok(true, "Got notified about 2 new registered properties");
assertResource(availableResources[8], {
name: "--js-c",
syntax: "*",
inherits: false,
initialValue: "42",
fromJS: true,
targetFront: topLevelTarget,
});
assertResource(availableResources[9], {
name: "--js-d",
syntax: "<color>#",
inherits: true,
initialValue: "blue,cyan",
fromJS: true,
targetFront: topLevelTarget,
});
info(
"Check that we get notified about properties registered via CSS.registerProperty in iframe"
);
const iframeBrowsingContext = await SpecialPowers.spawn(
tab.linkedBrowser,
[],
() => content.document.querySelector("iframe").browsingContext
);
await SpecialPowers.spawn(iframeBrowsingContext, [], () => {
content.CSS.registerProperty({
name: "--js-iframe",
syntax: "<color>#",
inherits: true,
initialValue: "red,salmon",
});
});
await waitFor(() => availableResources.length === 11);
ok(true, "Got notified about 2 new registered properties");
assertResource(availableResources[10], {
name: "--js-iframe",
syntax: "<color>#",
inherits: true,
initialValue: "red,salmon",
fromJS: true,
targetFront: iframeTarget,
});
info(
"Check that we get notified about destroyed properties when removing stylesheet"
);
// sanity check
is(destroyedResources.length, 0, "No destroyed resources yet");
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
content.document.querySelector("style").remove();
});
await waitFor(() => destroyedResources.length == 2);
ok(true, "We got notified about destroyed resources");
destroyedResources.sort((a, b) => a < b);
is(
destroyedResources[0].resourceType,
ResourceCommand.TYPES.CSS_REGISTERED_PROPERTIES,
"resource type is correct"
);
is(
destroyedResources[0].resourceId,
`${topLevelTarget.actorID}:css-registered-property:--css-a`,
"expected css property was destroyed"
);
is(
destroyedResources[1].resourceType,
ResourceCommand.TYPES.CSS_REGISTERED_PROPERTIES,
"resource type is correct"
);
is(
destroyedResources[1].resourceId,
`${topLevelTarget.actorID}:css-registered-property:--css-b`,
"expected css property was destroyed"
);
info(
"Check that we get notified about updated properties when modifying stylesheet"
);
is(updatedResources.length, 0, "No updated resources yet");
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
content.document.querySelector("style").textContent = `
/* not updated */
@property --css-c {
syntax: "<custom-ident>";
inherits: true;
initial-value: custom;
}
@property --css-d {
syntax: "big | bigger";
inherits: true;
/* only change initial value (was big) */
initial-value: bigger;
}
/* add a new property */
@property --css-e {
syntax: "<color>";
inherits: false;
initial-value: green;
}
`;
});
await waitFor(() => updatedResources.length === 1);
ok(true, "One property was updated");
assertResource(updatedResources[0].resource, {
name: "--css-d",
syntax: "big | bigger",
inherits: true,
initialValue: "bigger",
fromJS: false,
targetFront: topLevelTarget,
});
await waitFor(() => availableResources.length === 12);
ok(true, "We got notified about the new property");
assertResource(availableResources.at(-1), {
name: "--css-e",
syntax: "<color>",
inherits: false,
initialValue: "green",
fromJS: false,
targetFront: topLevelTarget,
});
await client.close();
});
async function assertResource(resource, expected) {
is(
resource.resourceType,
ResourceCommand.TYPES.CSS_REGISTERED_PROPERTIES,
"Resource type is correct"
);
is(resource.name, expected.name, "name is correct");
is(resource.syntax, expected.syntax, "syntax is correct");
is(resource.inherits, expected.inherits, "inherits is correct");
is(resource.initialValue, expected.initialValue, "initialValue is correct");
is(resource.fromJS, expected.fromJS, "fromJS is correct");
is(
resource.targetFront,
expected.targetFront,
"resource is associated with expected target"
);
}