commit 4a691496e764f7d9935454a3126745dad99be714
parent 202ae50fb8ff23ab8e855e2971df577cb7d62975
Author: Nicolas Chevobbe <nchevobbe@mozilla.com>
Date: Tue, 16 Dec 2025 07:05:30 +0000
Bug 2006255 - [devtools] Add chrome-only CSSStyleDeclaration#hasLonghandProperty(propertyName). r=emilio,webidl,firefox-style-system-reviewers,layout-reviewers.
Differential Revision: https://phabricator.services.mozilla.com/D274933
Diffstat:
9 files changed, 185 insertions(+), 1 deletion(-)
diff --git a/dom/webidl/CSSStyleDeclaration.webidl b/dom/webidl/CSSStyleDeclaration.webidl
@@ -30,6 +30,8 @@ interface CSSStyleDeclaration {
undefined setProperty(UTF8String property, [LegacyNullToEmptyString] UTF8String value, optional [LegacyNullToEmptyString] UTF8String priority = "");
[CEReactions, Throws]
UTF8String removeProperty(UTF8String property);
+ [ChromeOnly]
+ boolean hasLonghandProperty(UTF8String property);
readonly attribute CSSRule? parentRule;
};
diff --git a/layout/inspector/InspectorUtils.cpp b/layout/inspector/InspectorUtils.cpp
@@ -289,6 +289,9 @@ class ReadOnlyInspectorDeclaration final : public nsDOMCSSDeclaration {
void GetPropertyValue(NonCustomCSSPropertyId aId, nsACString& aValue) final {
Servo_DeclarationBlock_GetPropertyValueByNonCustomId(mRaw, aId, &aValue);
}
+ bool HasLonghandProperty(const nsACString& aPropName) final {
+ return Servo_DeclarationBlock_HasLonghandProperty(mRaw, &aPropName);
+ }
void IndexedGetter(uint32_t aIndex, bool& aFound,
nsACString& aPropName) final {
aFound = Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aPropName);
diff --git a/layout/inspector/tests/chrome/chrome.toml b/layout/inspector/tests/chrome/chrome.toml
@@ -4,6 +4,8 @@ prefs = [
]
support-files = ["GentiumPlus-R.woff"]
+["test_CSSStyleDeclaration_hasLonghandProperty.html"]
+
["test_CSSStyleRule_getScopeRootFor.html"]
["test_CSSStyleRule_querySelectorAll.html"]
diff --git a/layout/inspector/tests/chrome/test_CSSStyleDeclaration_hasLonghandProperty.html b/layout/inspector/tests/chrome/test_CSSStyleDeclaration_hasLonghandProperty.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1894251
+-->
+<title>Test for CSSStyleDeclaration::hasLonghandProperty</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+
+<div
+ id="target"
+ align="right"
+ style="
+ --in-style-attribute:red;
+ --in-style-attribute-empty: ;
+ color: blue;
+ outline: 1px solid lime;
+ caret-color: var(--in-style-attribute);"
+></div>
+
+<style>
+ #target {
+ --in-rule: hotpink;
+ --in-rule-empty: ;
+ background-color: peachpuff;
+ text-decoration-color: var(--in-rule);
+ padding: 1em;
+ }
+
+ @property --my-color {
+ syntax: "<color>";
+ inherits: false;
+ initial-value: #c0ffee;
+ }
+</style>
+
+<script>
+add_task(async () => {
+ const InspectorUtils = SpecialPowers.InspectorUtils;
+ const el = document.querySelector("#target");
+ // Use InspectorUtils.getMatchingCSSRules so we get InspectorDeclaration instances, like
+ // we do in DevTools.
+ const rules = InspectorUtils.getMatchingCSSRules(el);
+
+ info("Check that hasProperty returns expected values for regular rule CSSStyleProperties")
+ const targetRule = rules.find(r => r.selectorText === "#target");
+ ok(
+ targetRule.style.hasLonghandProperty("background-color"),
+ `hasProperty returned true for "background-color" property on #target rule`
+ );
+ ok(
+ targetRule.style.hasLonghandProperty("text-decoration-color"),
+ `hasProperty returned true for property with a CSS variable value on #target rule`
+ );
+ ok(
+ !targetRule.style.hasLonghandProperty("caret-color"),
+ `hasProperty returned false for non-defined "caret-color" property on #target rule`
+ );
+ ok(
+ !targetRule.style.hasLonghandProperty("my-amazing-property"),
+ `hasProperty returned false for invalid property on #target rule`
+ );
+ ok(
+ targetRule.style.hasLonghandProperty("--in-rule"),
+ `hasProperty returned true for "--in-rule" property on #target rule`
+ );
+ ok(
+ targetRule.style.hasLonghandProperty("--in-rule-empty"),
+ `hasProperty returned true for "--in-rule-empty" property on #target rule`
+ );
+ ok(
+ !targetRule.style.hasLonghandProperty("--my-color"),
+ `hasProperty returned false for registered but unset "--my-color" property on #target rule`
+ );
+ ok(
+ !targetRule.style.hasLonghandProperty("--unknown"),
+ `hasProperty returned false for "--unknown" property on #target rule`
+ );
+ ok(
+ !targetRule.style.hasLonghandProperty("padding"),
+ `hasProperty returned false for shorthand property (padding) on #target rule`
+ );
+ ok(
+ targetRule.style.hasLonghandProperty("padding-top"),
+ `hasProperty returned true for longhand property (padding-top) on #target rule`
+ );
+
+ info("Check that hasProperty returns expected values for style attribute InspectorDeclaration CSSStyleProperties")
+ const styleAttributeInspectorDeclaration = rules.find(r => r.declarationOrigin === "style-attribute");
+ ok(
+ styleAttributeInspectorDeclaration.style.hasLonghandProperty("color"),
+ `hasProperty returned true for "color" property on style attribute`
+ );
+ ok(
+ styleAttributeInspectorDeclaration.style.hasLonghandProperty("caret-color"),
+ `hasProperty returned true for property with a CSS variable value on style attribute`
+ );
+ ok(
+ !styleAttributeInspectorDeclaration.style.hasLonghandProperty("background-color"),
+ `hasProperty returned false for non-defined "background-color" property on style attribute`
+ );
+ ok(
+ styleAttributeInspectorDeclaration.style.hasLonghandProperty("--in-style-attribute"),
+ `hasProperty returned true for "--in-style-attribute" property on style attribute`
+ );
+ ok(
+ styleAttributeInspectorDeclaration.style.hasLonghandProperty("--in-style-attribute-empty"),
+ `hasProperty returned true for "--in-style-attribute-empty" property on style attribute`
+ );
+ ok(
+ !styleAttributeInspectorDeclaration.style.hasLonghandProperty("--my-color"),
+ `hasProperty returned false for registered but unset "--my-color" property on style attribute`
+ );
+ ok(
+ !styleAttributeInspectorDeclaration.style.hasLonghandProperty("--unknown"),
+ `hasProperty returned false for "--unknown" property on style attribute`
+ );
+ ok(
+ !styleAttributeInspectorDeclaration.style.hasLonghandProperty("outline"),
+ `hasProperty returned false for shorthand property (outline) on style attribute`
+ );
+ ok(
+ styleAttributeInspectorDeclaration.style.hasLonghandProperty("outline-color"),
+ `hasProperty returned true for longhand property (outline-color) on style attribute`
+ );
+
+ info("Check that hasProperty returns expected values for pres hints InspectorDeclaration CSSStyleProperties")
+ const presHintsInspectorDeclaration = rules.find(r => r.declarationOrigin === "pres-hints");
+ ok(
+ presHintsInspectorDeclaration.style.hasLonghandProperty("text-align"),
+ `hasProperty returned true for "text-align" property on pres-hints style`
+ );
+ ok(
+ !presHintsInspectorDeclaration.style.hasLonghandProperty("background-color"),
+ `hasProperty returned false for non-defined "background-color" property on pres-hints style`
+ );
+ ok(
+ !presHintsInspectorDeclaration.style.hasLonghandProperty("--my-color"),
+ `hasProperty returned false for registered but unset "--my-color" property on pres-hints style`
+ );
+ ok(
+ !presHintsInspectorDeclaration.style.hasLonghandProperty("--unknown"),
+ `hasProperty returned false for "--unknown" property on pres-hints style`
+ );
+});
+</script>
diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp
@@ -140,6 +140,15 @@ void nsDOMCSSDeclaration::GetPropertyValue(const nsACString& aPropertyName,
}
}
+bool nsDOMCSSDeclaration::HasLonghandProperty(const nsACString& aPropertyName) {
+ if (auto* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr)) {
+ return Servo_DeclarationBlock_HasLonghandProperty(decl->Raw(),
+ &aPropertyName);
+ }
+
+ return false;
+}
+
void nsDOMCSSDeclaration::GetPropertyPriority(const nsACString& aPropertyName,
nsACString& aPriority) {
MOZ_ASSERT(aPriority.IsEmpty());
diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h
@@ -87,6 +87,7 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration {
mozilla::ErrorResult& aRv) override;
void GetPropertyValue(const nsACString& propertyName,
nsACString& _retval) override;
+ bool HasLonghandProperty(const nsACString& propertyName) override;
void RemoveProperty(const nsACString& propertyName, nsACString& _retval,
mozilla::ErrorResult& aRv) override;
void GetPropertyPriority(const nsACString& propertyName,
diff --git a/layout/style/nsICSSDeclaration.h b/layout/style/nsICSSDeclaration.h
@@ -101,6 +101,11 @@ class nsICSSDeclaration : public nsISupports, public nsWrapperCache {
nsACString& aPriority) = 0;
virtual mozilla::css::Rule* GetParentRule() = 0;
+ // [Chrome only]
+ virtual bool HasLonghandProperty(const nsACString& aPropName) {
+ return false;
+ };
+
protected:
bool IsReadOnly();
};
diff --git a/servo/components/style/properties/properties.mako.rs b/servo/components/style/properties/properties.mako.rs
@@ -1258,7 +1258,7 @@ impl CountedUnknownProperty {
impl PropertyId {
/// Returns a given property from the given name, _regardless of whether it
/// is enabled or not_, or Err(()) for unknown properties.
- pub(super) fn parse_unchecked(
+ pub fn parse_unchecked(
property_name: &str,
use_counters: Option<&UseCounters>,
) -> Result<Self, ()> {
diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs
@@ -5881,6 +5881,23 @@ pub extern "C" fn Servo_DeclarationBlock_PropertyIsSet(
}
#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_HasLonghandProperty(
+ declarations: &LockedDeclarationBlock,
+ property: &nsACString,
+) -> bool {
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ let prop_name = property.as_str_unchecked();
+ if let Ok(property_id) = PropertyId::parse_unchecked(prop_name, None) {
+ if let Err(longhand_or_custom) = property_id.as_shorthand() {
+ return decls.contains(longhand_or_custom);
+ }
+ }
+
+ false
+ })
+}
+
+#[no_mangle]
pub unsafe extern "C" fn Servo_DeclarationBlock_SetIdentStringValue(
declarations: &LockedDeclarationBlock,
property: NonCustomCSSPropertyId,