diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /servo/components/style/gecko/snapshot_helpers.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/gecko/snapshot_helpers.rs')
-rw-r--r-- | servo/components/style/gecko/snapshot_helpers.rs | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/servo/components/style/gecko/snapshot_helpers.rs b/servo/components/style/gecko/snapshot_helpers.rs new file mode 100644 index 0000000000..ab2d08eaf8 --- /dev/null +++ b/servo/components/style/gecko/snapshot_helpers.rs @@ -0,0 +1,316 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! Element an snapshot common logic. + +use crate::dom::TElement; +use crate::gecko::wrapper::namespace_id_to_atom; +use crate::gecko_bindings::bindings; +use crate::gecko_bindings::structs::{self, nsAtom}; +use crate::invalidation::element::element_wrapper::ElementSnapshot; +use crate::selector_parser::{AttrValue, SnapshotMap}; +use crate::string_cache::WeakAtom; +use crate::values::AtomIdent; +use crate::{Atom, CaseSensitivityExt, LocalName, Namespace}; +use selectors::attr::{ + AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint, +}; +use smallvec::SmallVec; + +/// A function that, given an element of type `T`, allows you to get a single +/// class or a class list. +enum Class<'a> { + None, + One(*const nsAtom), + More(&'a [structs::RefPtr<nsAtom>]), +} + +#[inline(always)] +fn base_type(attr: &structs::nsAttrValue) -> structs::nsAttrValue_ValueBaseType { + (attr.mBits & structs::NS_ATTRVALUE_BASETYPE_MASK) as structs::nsAttrValue_ValueBaseType +} + +#[inline(always)] +unsafe fn ptr<T>(attr: &structs::nsAttrValue) -> *const T { + (attr.mBits & !structs::NS_ATTRVALUE_BASETYPE_MASK) as *const T +} + +#[inline(always)] +unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class { + debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr)); + let base_type = base_type(attr); + if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase { + return Class::One(ptr::<nsAtom>(attr)); + } + if base_type == structs::nsAttrValue_ValueBaseType_eOtherBase { + let container = ptr::<structs::MiscContainer>(attr); + debug_assert_eq!( + (*container).mType, + structs::nsAttrValue_ValueType_eAtomArray + ); + // NOTE: Bindgen doesn't deal with AutoTArray, so cast it below. + let attr_array: *const _ = *(*container) + .__bindgen_anon_1 + .mValue + .as_ref() + .__bindgen_anon_1 + .mAtomArray + .as_ref(); + let array = + (*attr_array).mArray.as_ptr() as *const structs::nsTArray<structs::RefPtr<nsAtom>>; + return Class::More(&**array); + } + debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eStringBase); + Class::None +} + +#[inline(always)] +unsafe fn get_id_from_attr(attr: &structs::nsAttrValue) -> &WeakAtom { + debug_assert_eq!( + base_type(attr), + structs::nsAttrValue_ValueBaseType_eAtomBase + ); + WeakAtom::new(ptr::<nsAtom>(attr)) +} + +impl structs::nsAttrName { + #[inline] + fn is_nodeinfo(&self) -> bool { + self.mBits & 1 != 0 + } + + #[inline] + unsafe fn as_nodeinfo(&self) -> &structs::NodeInfo { + debug_assert!(self.is_nodeinfo()); + &*((self.mBits & !1) as *const structs::NodeInfo) + } + + #[inline] + fn namespace_id(&self) -> i32 { + if !self.is_nodeinfo() { + return structs::kNameSpaceID_None; + } + unsafe { self.as_nodeinfo() }.mInner.mNamespaceID + } + + /// Returns the attribute name as an atom pointer. + #[inline] + pub fn name(&self) -> *const nsAtom { + if self.is_nodeinfo() { + unsafe { self.as_nodeinfo() }.mInner.mName + } else { + self.mBits as *const nsAtom + } + } +} + +/// Find an attribute value with a given name and no namespace. +#[inline(always)] +pub fn find_attr<'a>( + attrs: &'a [structs::AttrArray_InternalAttr], + name: &Atom, +) -> Option<&'a structs::nsAttrValue> { + attrs + .iter() + .find(|attr| attr.mName.mBits == name.as_ptr() as usize) + .map(|attr| &attr.mValue) +} + +/// Finds the id attribute from a list of attributes. +#[inline(always)] +pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> { + Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) }) +} + +#[inline(always)] +pub(super) fn each_exported_part( + attrs: &[structs::AttrArray_InternalAttr], + name: &AtomIdent, + mut callback: impl FnMut(&AtomIdent), +) { + let attr = match find_attr(attrs, &atom!("exportparts")) { + Some(attr) => attr, + None => return, + }; + let mut length = 0; + let atoms = unsafe { bindings::Gecko_Element_ExportedParts(attr, name.as_ptr(), &mut length) }; + if atoms.is_null() { + return; + } + + unsafe { + for atom in std::slice::from_raw_parts(atoms, length) { + AtomIdent::with(*atom, &mut callback) + } + } +} + +#[inline(always)] +pub(super) fn imported_part( + attrs: &[structs::AttrArray_InternalAttr], + name: &AtomIdent, +) -> Option<AtomIdent> { + let attr = find_attr(attrs, &atom!("exportparts"))?; + let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) }; + if atom.is_null() { + return None; + } + Some(AtomIdent(unsafe { Atom::from_raw(atom) })) +} + +/// Given a class or part name, a case sensitivity, and an array of attributes, +/// returns whether the attribute has that name. +#[inline(always)] +pub fn has_class_or_part( + name: &AtomIdent, + case_sensitivity: CaseSensitivity, + attr: &structs::nsAttrValue, +) -> bool { + match unsafe { get_class_or_part_from_attr(attr) } { + Class::None => false, + Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) }, + Class::More(atoms) => match case_sensitivity { + CaseSensitivity::CaseSensitive => { + let name_ptr = name.as_ptr(); + atoms.iter().any(|atom| atom.mRawPtr == name_ptr) + }, + CaseSensitivity::AsciiCaseInsensitive => unsafe { + atoms + .iter() + .any(|atom| WeakAtom::new(atom.mRawPtr).eq_ignore_ascii_case(name)) + }, + }, + } +} + +/// Given an item, a callback, and a getter, execute `callback` for each class +/// or part name this `item` has. +#[inline(always)] +pub fn each_class_or_part<F>(attr: &structs::nsAttrValue, mut callback: F) +where + F: FnMut(&AtomIdent), +{ + unsafe { + match get_class_or_part_from_attr(attr) { + Class::None => {}, + Class::One(atom) => AtomIdent::with(atom, callback), + Class::More(atoms) => { + for atom in atoms { + AtomIdent::with(atom.mRawPtr, &mut callback) + } + }, + } + } +} + +/// Returns a list of classes that were either added to or removed from the +/// element since the snapshot. +pub fn classes_changed<E: TElement>(element: &E, snapshots: &SnapshotMap) -> SmallVec<[Atom; 8]> { + debug_assert!(element.has_snapshot(), "Why bothering?"); + let snapshot = snapshots.get(element).expect("has_snapshot lied"); + if !snapshot.class_changed() { + return SmallVec::new(); + } + + let mut classes_changed = SmallVec::<[Atom; 8]>::new(); + snapshot.each_class(|c| { + if !element.has_class(c, CaseSensitivity::CaseSensitive) { + classes_changed.push(c.0.clone()); + } + }); + element.each_class(|c| { + if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) { + classes_changed.push(c.0.clone()); + } + }); + + classes_changed +} + +/// Returns whether a given attribute selector matches given the internal attrs. +#[inline(always)] +pub(crate) fn attr_matches( + attrs: &[structs::AttrArray_InternalAttr], + ns: &NamespaceConstraint<&Namespace>, + local_name: &LocalName, + operation: &AttrSelectorOperation<&AttrValue>, +) -> bool { + let name_ptr = local_name.as_ptr(); + for attr in attrs { + if attr.mName.name() != name_ptr { + continue; + } + + if attr_matches_checked_name(attr, ns, operation) { + return true; + } + + // The name matched but the value or namespace didn't. The only reason to check the other + // attributes now would be to find one with the same name but a different namespace. + if *ns != NamespaceConstraint::Any { + // We don't want to look for other namespaces, so we're done. + return false; + } + } + false +} + +/// Returns whether a given attribute selector matches given a single attribute, +/// for the case where the caller has already found an attribute with the right name. +fn attr_matches_checked_name( + attr: &structs::AttrArray_InternalAttr, + ns: &NamespaceConstraint<&Namespace>, + operation: &AttrSelectorOperation<&AttrValue>, +) -> bool { + let ns_matches = match *ns { + NamespaceConstraint::Any => true, + NamespaceConstraint::Specific(ns) => { + if *ns == ns!() { + !attr.mName.is_nodeinfo() + } else { + ns.as_ptr() == unsafe { namespace_id_to_atom(attr.mName.namespace_id()) } + } + }, + }; + + if !ns_matches { + return false; + } + + let (operator, case_sensitivity, value) = match *operation { + AttrSelectorOperation::Exists => return true, + AttrSelectorOperation::WithValue { + operator, + case_sensitivity, + value, + } => (operator, case_sensitivity, value), + }; + let ignore_case = match case_sensitivity { + CaseSensitivity::CaseSensitive => false, + CaseSensitivity::AsciiCaseInsensitive => true, + }; + let value = value.as_ptr(); + unsafe { + match operator { + AttrSelectorOperator::Equal => { + bindings::Gecko_AttrEquals(&attr.mValue, value, ignore_case) + }, + AttrSelectorOperator::Includes => { + bindings::Gecko_AttrIncludes(&attr.mValue, value, ignore_case) + }, + AttrSelectorOperator::DashMatch => { + bindings::Gecko_AttrDashEquals(&attr.mValue, value, ignore_case) + }, + AttrSelectorOperator::Prefix => { + bindings::Gecko_AttrHasPrefix(&attr.mValue, value, ignore_case) + }, + AttrSelectorOperator::Suffix => { + bindings::Gecko_AttrHasSuffix(&attr.mValue, value, ignore_case) + }, + AttrSelectorOperator::Substring => { + bindings::Gecko_AttrHasSubstring(&attr.mValue, value, ignore_case) + }, + } + } +} |