commit 1fae47ef94c8c474d3837bb0a120e0230dd904f8
parent 0345ccee7c6a06b1ef162a6910f8848483d764d7
Author: Diego Escalante <descalante@mozilla.com>
Date: Tue, 2 Dec 2025 20:31:22 +0000
Bug 1986629 - Create AttributeProvider trait and bindings to query element attribute values. r=emilio,firefox-style-system-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D274611
Diffstat:
4 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp
@@ -869,6 +869,11 @@ bool Gecko_IsSelectListBox(const Element* aElement) {
return select && !select->IsCombobox();
}
+bool Gecko_LookupAttrValue(const Element* aElement, const nsAtom& aName,
+ nsAString& aResult) {
+ return aElement->GetAttr(&aName, aResult);
+}
+
template <typename Implementor>
static nsAtom* LangValue(Implementor* aElement) {
// TODO(emilio): Deduplicate a bit with nsIContent::GetLang().
diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h
@@ -154,6 +154,8 @@ bool Gecko_HasActiveViewTransitionTypes(
#define SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsAtom* prefix_##LangValue(implementor_ element);
+bool Gecko_LookupAttrValue(const mozilla::dom::Element* aElement,
+ const nsAtom& aName, nsAString& aResult);
bool Gecko_AttrEquals(const nsAttrValue*, const nsAtom*, bool aIgnoreCase);
bool Gecko_AttrDashEquals(const nsAttrValue*, const nsAtom*, bool aIgnoreCase);
bool Gecko_AttrIncludes(const nsAttrValue*, const nsAtom*, bool aIgnoreCase);
diff --git a/servo/components/style/dom.rs b/servo/components/style/dom.rs
@@ -385,7 +385,15 @@ pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {
/// The element trait, the main abstraction the style crate acts over.
pub trait TElement:
- Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + SelectorsElement<Impl = SelectorImpl>
+ Eq
+ + PartialEq
+ + Debug
+ + Hash
+ + Sized
+ + Copy
+ + Clone
+ + SelectorsElement<Impl = SelectorImpl>
+ + AttributeProvider
{
/// The concrete node type.
type ConcreteNode: TNode<ConcreteElement = Self>;
@@ -917,6 +925,22 @@ pub trait TElement:
}
}
+/// The attribute provider trait
+pub trait AttributeProvider {
+ /// Return the value of the given custom attibute if it exists.
+ fn get_attr(&self, attr: &LocalName) -> Option<String>;
+}
+
+/// A dummy AttributeProvider that returns none to any attribute query.
+#[derive(Clone, Debug, PartialEq)]
+pub struct DummyAttributeProvider;
+
+impl AttributeProvider for DummyAttributeProvider {
+ fn get_attr(&self, _attr: &LocalName) -> Option<String> {
+ None
+ }
+}
+
/// TNode and TElement aren't Send because we want to be careful and explicit
/// about our parallel traversal. However, there are certain situations
/// (including but not limited to the traversal) where we need to send DOM
diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs
@@ -18,7 +18,10 @@ use crate::applicable_declarations::ApplicableDeclarationBlock;
use crate::bloom::each_relevant_element_hash;
use crate::context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
use crate::data::ElementData;
-use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot};
+use crate::dom::{
+ AttributeProvider, LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode,
+ TShadowRoot,
+};
use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
use crate::gecko::snapshot_helpers;
use crate::gecko_bindings::bindings;
@@ -71,6 +74,7 @@ use app_units::Au;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use dom::{DocumentState, ElementState};
use euclid::default::Size2D;
+use nsstring::nsString;
use rustc_hash::FxHashMap;
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
use selectors::bloom::{BloomFilter, BLOOM_HASH_MASK};
@@ -1818,6 +1822,19 @@ impl<'le> TElement for GeckoElement<'le> {
}
}
+impl<'le> AttributeProvider for GeckoElement<'le> {
+ fn get_attr(&self, attr: &LocalName) -> Option<String> {
+ //TODO(bug 2003334): Avoid unnecessary string copies/conversions here.
+ let mut result = nsString::new();
+
+ if unsafe { bindings::Gecko_LookupAttrValue(self.0, attr.0.as_ptr(), &mut *result) } {
+ Some(result.to_string())
+ } else {
+ None
+ }
+ }
+}
+
impl<'le> PartialEq for GeckoElement<'le> {
#[inline]
fn eq(&self, other: &Self) -> bool {