Backed out changeset 41fd454b843a (bug 1870812) for causing failures at browser_resources_css_registered_properties.js. CLOSED TREE

This commit is contained in:
Butkovits Atila 2024-01-18 03:16:03 +02:00
parent 7631053400
commit 161bb7e6fd
7 changed files with 0 additions and 651 deletions

View File

@ -1,278 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
TYPES: { CSS_REGISTERED_PROPERTIES },
} = require("resource://devtools/server/actors/resources/index.js");
/**
* @typedef InspectorCSSPropertyDefinition (see InspectorUtils.webidl)
* @type {object}
* @property {string} name
* @property {string} syntax
* @property {boolean} inherits
* @property {string} initialValue
* @property {boolean} fromJS - true if property was registered via CSS.registerProperty
*/
class CSSRegisteredPropertiesWatcher {
#abortController;
#onAvailable;
#onUpdated;
#onDestroyed;
#registeredPropertiesCache = new Map();
#styleSheetsManager;
#targetActor;
/**
* Start watching for all registered CSS properties (@property/CSS.registerProperty)
* related to a given Target Actor.
*
* @param TargetActor targetActor
* The target actor from which we should observe css changes.
* @param Object options
* Dictionary object with following attributes:
* - onAvailable: mandatory function
* - onUpdated: mandatory function
* - onDestroyed: mandatory function
* This will be called for each resource.
*/
async watch(targetActor, { onAvailable, onUpdated, onDestroyed }) {
this.#targetActor = targetActor;
this.#onAvailable = onAvailable;
this.#onUpdated = onUpdated;
this.#onDestroyed = onDestroyed;
// Notify about existing properties
const registeredProperties = this.#getRegisteredProperties();
for (const registeredProperty of registeredProperties) {
this.#registeredPropertiesCache.set(
registeredProperty.name,
registeredProperty
);
}
this.#notifyResourcesAvailable(registeredProperties);
// Add event listener on stylesheets additions, updates and removals, as registered
// properties could be impacted.
this.#abortController = new AbortController();
const { signal } = this.#abortController;
this.#styleSheetsManager = targetActor.getStyleSheetsManager();
this.#styleSheetsManager.on(
"applicable-stylesheet-added",
this.#refreshCacheAndNotify,
{ signal }
);
this.#styleSheetsManager.on(
"stylesheet-updated",
this.#refreshCacheAndNotify,
{ signal }
);
this.#styleSheetsManager.on(
"applicable-stylesheet-removed",
this.#refreshCacheAndNotify,
{ signal }
);
// startWatching will emit applicable-stylesheet-added for already existing stylesheet
await this.#styleSheetsManager.startWatching();
// Also listen for new properties being registered via CSS.registerProperty
this.#targetActor.chromeEventHandler.addEventListener(
"csscustompropertyregistered",
this.#onCssCustomPropertyRegistered,
{ capture: true, signal }
);
}
/**
* Get all the registered properties for the target actor document.
*
* @returns Array<InspectorCSSPropertyDefinition>
*/
#getRegisteredProperties() {
return InspectorUtils.getCSSRegisteredProperties(
this.#targetActor.window.document
);
}
/**
* Compute a resourceId from a given property definition
*
* @param {InspectorCSSPropertyDefinition} propertyDefinition
* @returns string
*/
#getRegisteredPropertyResourceId(propertyDefinition) {
return `${this.#targetActor.actorID}:css-registered-property:${
propertyDefinition.name
}`;
}
/**
* Called when a stylesheet is added, removed or modified.
* This will retrieve the registered properties at this very moment, and notify
* about new, updated and removed registered properties.
*/
#refreshCacheAndNotify = async () => {
const registeredProperties = this.#getRegisteredProperties();
const existingPropertiesNames = new Set(
this.#registeredPropertiesCache.keys()
);
const added = [];
const updated = [];
const removed = [];
for (const registeredProperty of registeredProperties) {
// If the property isn't in the cache already, this is a new one.
if (!this.#registeredPropertiesCache.has(registeredProperty.name)) {
added.push(registeredProperty);
this.#registeredPropertiesCache.set(
registeredProperty.name,
registeredProperty
);
continue;
}
// Removing existing property from the Set so we can then later get the properties
// that don't exist anymore.
existingPropertiesNames.delete(registeredProperty.name);
// The property already existed, so we need to check if its definition was modified
const cachedRegisteredProperty = this.#registeredPropertiesCache.get(
registeredProperty.name
);
const resourceUpdates = {};
let wasUpdated = false;
if (registeredProperty.syntax !== cachedRegisteredProperty.syntax) {
resourceUpdates.syntax = registeredProperty.syntax;
wasUpdated = true;
}
if (registeredProperty.inherits !== cachedRegisteredProperty.inherits) {
resourceUpdates.inherits = registeredProperty.inherits;
wasUpdated = true;
}
if (
registeredProperty.initialValue !==
cachedRegisteredProperty.initialValue
) {
resourceUpdates.initialValue = registeredProperty.initialValue;
wasUpdated = true;
}
if (wasUpdated === true) {
updated.push({
registeredProperty,
resourceUpdates,
});
this.#registeredPropertiesCache.set(
registeredProperty.name,
registeredProperty
);
}
}
// If there are items left in the Set, it means they weren't processed in the for loop
// before, meaning they don't exist anymore.
for (const registeredPropertyName of existingPropertiesNames) {
removed.push(this.#registeredPropertiesCache.get(registeredPropertyName));
this.#registeredPropertiesCache.delete(registeredPropertyName);
}
this.#notifyResourcesAvailable(added);
this.#notifyResourcesUpdated(updated);
this.#notifyResourcesDestroyed(removed);
};
/**
* csscustompropertyregistered event listener callback (fired when a property
* is registered via CSS.registerProperty).
*
* @param {CSSCustomPropertyRegisteredEvent} event
*/
#onCssCustomPropertyRegistered = event => {
// Ignore event if property was registered from a global different from the target global.
if (
this.#targetActor.ignoreSubFrames &&
event.target.ownerGlobal !== this.#targetActor.window
) {
return;
}
const registeredProperty = event.propertyDefinition;
this.#registeredPropertiesCache.set(
registeredProperty.name,
registeredProperty
);
this.#notifyResourcesAvailable([registeredProperty]);
};
/**
* @param {Array<InspectorCSSPropertyDefinition>} registeredProperties
*/
#notifyResourcesAvailable = registeredProperties => {
if (!registeredProperties.length) {
return;
}
for (const registeredProperty of registeredProperties) {
registeredProperty.resourceId =
this.#getRegisteredPropertyResourceId(registeredProperty);
registeredProperty.resourceType = CSS_REGISTERED_PROPERTIES;
}
this.#onAvailable(registeredProperties);
};
/**
* @param {Array<Object>} updates: Array of update object, which have the following properties:
* - {InspectorCSSPropertyDefinition} registeredProperty: The property definition
* of the updated property
* - {Object} resourceUpdates: An object containing all the fields that are
* modified for the registered property.
*/
#notifyResourcesUpdated = updates => {
if (!updates.length) {
return;
}
for (const update of updates) {
update.resourceId = this.#getRegisteredPropertyResourceId(
update.registeredProperty
);
update.resourceType = CSS_REGISTERED_PROPERTIES;
// We don't need to send the property definition
delete update.registeredProperty;
}
this.#onUpdated(updates);
};
/**
* @param {Array<InspectorCSSPropertyDefinition>} registeredProperties
*/
#notifyResourcesDestroyed = registeredProperties => {
if (!registeredProperties.length) {
return;
}
this.#onDestroyed(
registeredProperties.map(registeredProperty => ({
resourceType: CSS_REGISTERED_PROPERTIES,
resourceId: this.#getRegisteredPropertyResourceId(registeredProperty),
}))
);
};
destroy() {
this.#abortController.abort();
}
}
module.exports = CSSRegisteredPropertiesWatcher;

View File

@ -10,7 +10,6 @@ const TYPES = {
CONSOLE_MESSAGE: "console-message",
CSS_CHANGE: "css-change",
CSS_MESSAGE: "css-message",
CSS_REGISTERED_PROPERTIES: "css-registered-properties",
DOCUMENT_EVENT: "document-event",
ERROR_MESSAGE: "error-message",
LAST_PRIVATE_CONTEXT_EXIT: "last-private-context-exit",
@ -68,9 +67,6 @@ const FrameTargetResources = augmentResourceDictionary({
[TYPES.CSS_MESSAGE]: {
path: "devtools/server/actors/resources/css-messages",
},
[TYPES.CSS_REGISTERED_PROPERTIES]: {
path: "devtools/server/actors/resources/css-registered-properties",
},
[TYPES.DOCUMENT_EVENT]: {
path: "devtools/server/actors/resources/document-event",
},

View File

@ -13,7 +13,6 @@ DevToolsModules(
"console-messages.js",
"css-changes.js",
"css-messages.js",
"css-registered-properties.js",
"document-event.js",
"error-messages.js",
"extensions-backgroundscript-status.js",

View File

@ -185,7 +185,6 @@ function getWatcherSupportedResources(type) {
[Resources.TYPES.CONSOLE_MESSAGE]: true,
[Resources.TYPES.CSS_CHANGE]: isTabOrWebExtensionToolbox,
[Resources.TYPES.CSS_MESSAGE]: true,
[Resources.TYPES.CSS_REGISTERED_PROPERTIES]: true,
[Resources.TYPES.DOCUMENT_EVENT]: true,
[Resources.TYPES.CACHE_STORAGE]: true,
[Resources.TYPES.COOKIE]: true,

View File

@ -1198,7 +1198,6 @@ ResourceCommand.TYPES = ResourceCommand.prototype.TYPES = {
CONSOLE_MESSAGE: "console-message",
CSS_CHANGE: "css-change",
CSS_MESSAGE: "css-message",
CSS_REGISTERED_PROPERTIES: "css-registered-properties",
ERROR_MESSAGE: "error-message",
PLATFORM_MESSAGE: "platform-message",
DOCUMENT_EVENT: "document-event",

View File

@ -54,8 +54,6 @@ support-files = [
["browser_resources_css_messages.js"]
["browser_resources_css_registered_properties.js"]
["browser_resources_document_events.js"]
skip-if = [
"win10_2004", # Bug 1723573

View File

@ -1,364 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the ResourceCommand API around CSS_REGISTERED_PROPERTIES.
const ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
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>
`)}`;
const TEST_URL = `https://example.org/document-builder.sjs?html=
<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, b) =>
a.isTopLevel ? -1 : 1
);
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 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(
"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"
);
}