diff --git a/devtools/client/styleeditor/StyleEditorUI.sys.mjs b/devtools/client/styleeditor/StyleEditorUI.sys.mjs index 958482cee4d6..e31bd4bcc17c 100644 --- a/devtools/client/styleeditor/StyleEditorUI.sys.mjs +++ b/devtools/client/styleeditor/StyleEditorUI.sys.mjs @@ -1400,6 +1400,10 @@ export class StyleEditorUI extends EventEmitter { type.append(this.#panelDoc.createTextNode(`@${rule.type}\u00A0`)); if (rule.type == "layer" && rule.layerName) { type.append(this.#panelDoc.createTextNode(`${rule.layerName}\u00A0`)); + } else if (rule.type === "property") { + type.append( + this.#panelDoc.createTextNode(`${rule.propertyName}\u00A0`) + ); } const cond = this.#panelDoc.createElementNS(HTML_NS, "span"); diff --git a/devtools/client/styleeditor/test/browser_styleeditor_at_rules_sidebar.js b/devtools/client/styleeditor/test/browser_styleeditor_at_rules_sidebar.js index a0a9bc93fdc3..d106d6780eb1 100644 --- a/devtools/client/styleeditor/test/browser_styleeditor_at_rules_sidebar.js +++ b/devtools/client/styleeditor/test/browser_styleeditor_at_rules_sidebar.js @@ -39,6 +39,8 @@ waitForExplicitFinish(); add_task(async function () { await pushPref("layout.css.container-queries.enabled", true); + // Enable @property rules + await pushPref("layout.css.properties-and-values.enabled", true); const { ui } = await openStyleEditorForURL(TESTCASE_URI); @@ -88,7 +90,7 @@ async function testInlineMediaEditor(ui, editor) { is(sidebar.hidden, false, "sidebar is showing on editor with @media"); const entries = sidebar.querySelectorAll(".at-rule-label"); - is(entries.length, 6, "6 @media rules displayed in sidebar"); + is(entries.length, 7, "7 at-rules displayed in sidebar"); await testRule({ ui, @@ -123,7 +125,6 @@ async function testInlineMediaEditor(ui, editor) { ui, editor, rule: entries[3], - conditionText: "", line: 16, type: "layer", layerName: "myLayer", @@ -146,6 +147,15 @@ async function testInlineMediaEditor(ui, editor) { line: 21, type: "support", }); + + await testRule({ + ui, + editor, + rule: entries[6], + line: 30, + type: "property", + propertyName: "--my-property", + }); } async function testMediaEditor(ui, editor) { @@ -273,27 +283,35 @@ async function testMediaRuleAdded(ui, editor) { * @param {StyleEditorUI} options.ui * @param {StyleSheetEditor} options.editor: The editor the rule is displayed in * @param {Element} options.rule: The rule element in the media sidebar - * @param {String} options.conditionText: media query condition text + * @param {String} options.conditionText: at-rule condition text (for @media, @container, @support) * @param {Boolean} options.matches: Whether or not the document matches the rule * @param {String} options.layerName: Optional name of the @layer + * @param {String} options.propertyName: Name of the @property if type is "property" * @param {Number} options.line: Line of the rule - * @param {String} options.type: The type of the rule (container, layer, media, support ). + * @param {String} options.type: The type of the rule (container, layer, media, support, property ). * Defaults to "media". */ async function testRule({ ui, editor, rule, - conditionText, + conditionText = "", matches, layerName, + propertyName, line, type = "media", }) { const atTypeEl = rule.querySelector(".at-rule-type"); + let name; + if (type === "layer") { + name = layerName; + } else if (type === "property") { + name = propertyName; + } is( atTypeEl.textContent, - `@${type}\u00A0${layerName ? `${layerName}\u00A0` : ""}`, + `@${type}\u00A0${name ? `${name}\u00A0` : ""}`, "label for at-rule type is correct" ); diff --git a/devtools/client/styleeditor/test/media-rules.html b/devtools/client/styleeditor/test/media-rules.html index 76725bfb5414..1e855aa83bf8 100644 --- a/devtools/client/styleeditor/test/media-rules.html +++ b/devtools/client/styleeditor/test/media-rules.html @@ -38,6 +38,12 @@ } } } + + @property --my-property { + syntax: ""; + inherits: true; + initial-value: #f06; + } diff --git a/devtools/server/actors/utils/stylesheets-manager.js b/devtools/server/actors/utils/stylesheets-manager.js index a9c0705e8db9..30533ecd08af 100644 --- a/devtools/server/actors/utils/stylesheets-manager.js +++ b/devtools/server/actors/utils/stylesheets-manager.js @@ -705,6 +705,13 @@ class StyleSheetsManager extends EventEmitter { line: InspectorUtils.getRelativeRuleLine(rule), column: InspectorUtils.getRuleColumn(rule), }); + } else if (className === "CSSPropertyRule") { + atRules.push({ + type: "property", + propertyName: rule.name, + line: InspectorUtils.getRelativeRuleLine(rule), + column: InspectorUtils.getRuleColumn(rule), + }); } } return { diff --git a/devtools/shared/commands/resource/tests/browser_resources_stylesheets.js b/devtools/shared/commands/resource/tests/browser_resources_stylesheets.js index ec81e8118de6..6c4f2e867a46 100644 --- a/devtools/shared/commands/resource/tests/browser_resources_stylesheets.js +++ b/devtools/shared/commands/resource/tests/browser_resources_stylesheets.js @@ -118,6 +118,8 @@ const ADDITIONAL_FROM_ACTOR_RESOURCE = { }; add_task(async function () { + // Enable @property + await pushPref("layout.css.properties-and-values.enabled", true); await testResourceAvailableDestroyedFeature(); await testResourceUpdateFeature(); await testNestedResourceUpdateFeature(); @@ -496,13 +498,18 @@ async function testNestedResourceUpdateFeature() { } } } + } + @property --my-property { + syntax: ""; + inherits: true; + initial-value: #f06; }`, false ); await waitUntil(() => updates.length === 3); is( updates.at(-1).resource.ruleCount, - 7, + 8, "Resource in update has expected ruleCount" ); @@ -554,6 +561,10 @@ async function testNestedResourceUpdateFeature() { type: "container", conditionText: "root (width > 10px)", }, + { + type: "property", + propertyName: "--my-property", + }, ]; assertAtRules(targetUpdate.resource.atRules, expectedAtRules); @@ -562,7 +573,7 @@ async function testNestedResourceUpdateFeature() { const styleSheetResult = await getStyleSheetResult(tab); is( styleSheetResult.ruleCount, - 7, + 8, "ruleCount of actual stylesheet is updated correctly" ); assertAtRules(styleSheetResult.atRules, expectedAtRules); @@ -620,6 +631,11 @@ async function getStyleSheetResult(tab) { type: "support", conditionText: rule.conditionText, }); + } else if (rule instanceof content.CSSPropertyRule) { + atRules.push({ + type: "property", + propertyName: rule.name, + }); } if (rule.cssRules) { @@ -655,6 +671,8 @@ function assertAtRules(atRules, expectedAtRules) { is(atRule.matches, expected.matches, "matches is correct"); } else if (expected.type === "layer") { is(atRule.layerName, expected.layerName, "layerName is correct"); + } else if (expected.type === "property") { + is(atRule.propertyName, expected.propertyName, "propertyName is correct"); } if (expected.line !== undefined) { diff --git a/layout/inspector/InspectorUtils.cpp b/layout/inspector/InspectorUtils.cpp index 80b93029e542..0b2138e9fc1b 100644 --- a/layout/inspector/InspectorUtils.cpp +++ b/layout/inspector/InspectorUtils.cpp @@ -415,6 +415,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList, case StyleCssRuleType::Media: case StyleCssRuleType::Supports: case StyleCssRuleType::LayerBlock: + case StyleCssRuleType::Property: case StyleCssRuleType::Container: { Unused << aResult.AppendElement(OwningNonNull(*rule), fallible); break; @@ -425,7 +426,6 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList, case StyleCssRuleType::LayerStatement: case StyleCssRuleType::FontFace: case StyleCssRuleType::Page: - case StyleCssRuleType::Property: case StyleCssRuleType::Keyframes: case StyleCssRuleType::Keyframe: case StyleCssRuleType::Margin: