diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index d70c6259bbf..fc4a9121ae4 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -51,10 +51,6 @@ impl GeckoElementSnapshot { (self.mContains as u8 & flags as u8) != 0 } - fn as_ptr(&self) -> *const Self { - self - } - /// Returns true if the snapshot has stored state for pseudo-classes /// that depend on things other than `ElementState`. #[inline] @@ -184,14 +180,7 @@ impl ElementSnapshot for GeckoElementSnapshot { return None; } - let ptr = unsafe { bindings::Gecko_SnapshotAtomAttrValue(self, atom!("id").as_ptr()) }; - - // FIXME(emilio): This should assert, since this flag is exact. - if ptr.is_null() { - None - } else { - Some(unsafe { WeakAtom::new(ptr) }) - } + snapshot_helpers::get_id(&*self.mAttrs) } #[inline] @@ -200,12 +189,7 @@ impl ElementSnapshot for GeckoElementSnapshot { return false; } - snapshot_helpers::has_class( - self.as_ptr(), - name, - case_sensitivity, - bindings::Gecko_SnapshotHasClass, - ) + snapshot_helpers::has_class(name, case_sensitivity, &self.mClass) } #[inline] @@ -217,11 +201,7 @@ impl ElementSnapshot for GeckoElementSnapshot { return; } - snapshot_helpers::each_class( - self.as_ptr(), - callback, - bindings::Gecko_SnapshotClassOrClassList, - ) + snapshot_helpers::each_class(&self.mClass, callback) } #[inline] diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs index 3f84de7fd50..6c40f242d5f 100644 --- a/components/style/gecko/snapshot_helpers.rs +++ b/components/style/gecko/snapshot_helpers.rs @@ -4,58 +4,114 @@ //! Element an snapshot common logic. -use gecko_bindings::structs::nsAtom; +use CaseSensitivityExt; +use gecko_bindings::bindings; +use gecko_bindings::structs::{self, nsAtom}; use selectors::attr::CaseSensitivity; -use std::{ptr, slice}; -use string_cache::Atom; +use string_cache::{Atom, WeakAtom}; /// A function that, given an element of type `T`, allows you to get a single /// class or a class list. -pub type ClassOrClassList = - unsafe extern "C" fn(T, *mut *mut nsAtom, *mut *mut *mut nsAtom) -> u32; +enum Class<'a> { + None, + One(*const nsAtom), + More(&'a [structs::RefPtr]), +} -/// A function to return whether an element of type `T` has a given class. -/// -/// The `bool` argument represents whether it should compare case-insensitively -/// or not. -pub type HasClass = unsafe extern "C" fn(T, *mut nsAtom, bool) -> bool; - -/// Given an item `T`, a class name, and a getter function, return whether that -/// element has the class that `name` represents. #[inline(always)] -pub fn has_class( - item: T, +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(attr: &structs::nsAttrValue) -> *const T { + (attr.mBits & !structs::NS_ATTRVALUE_BASETYPE_MASK) as *const T +} + +#[inline(always)] +unsafe fn get_class_from_attr(attr: &structs::nsAttrValue) -> Class { + debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr)); + let base_type = base_type(attr); + if base_type == structs::nsAttrValue_ValueBaseType_eStringBase { + return Class::None; + } + if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase { + return Class::One(ptr::(attr)); + } + debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eOtherBase); + + let container = ptr::(attr); + debug_assert_eq!((*container).mType, structs::nsAttrValue_ValueType_eAtomArray); + let array = + (*container).__bindgen_anon_1.mValue.as_ref().__bindgen_anon_1.mAtomArray.as_ref(); + Class::More(&***array) +} + +#[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::(attr)) +} + +/// 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"))?) }) +} + +/// Given a class name, a case sensitivity, and an array of attributes, returns +/// whether the class has the attribute that name represents. +#[inline(always)] +pub fn has_class( name: &Atom, case_sensitivity: CaseSensitivity, - getter: HasClass, + attr: &structs::nsAttrValue, ) -> bool { - let ignore_case = match case_sensitivity { - CaseSensitivity::CaseSensitive => false, - CaseSensitivity::AsciiCaseInsensitive => true, - }; - - unsafe { getter(item, name.as_ptr(), ignore_case) } + match unsafe { get_class_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 => { + atoms.iter().any(|atom| atom.mRawPtr == name.as_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 /// this `item` has. -pub fn each_class(item: T, mut callback: F, getter: ClassOrClassList) +#[inline(always)] +pub fn each_class(attr: &structs::nsAttrValue, mut callback: F) where F: FnMut(&Atom), { unsafe { - let mut class: *mut nsAtom = ptr::null_mut(); - let mut list: *mut *mut nsAtom = ptr::null_mut(); - let length = getter(item, &mut class, &mut list); - match length { - 0 => {}, - 1 => Atom::with(class, callback), - n => { - let classes = slice::from_raw_parts(list, n as usize); - for c in classes { - Atom::with(*c, &mut callback) + match get_class_from_attr(attr) { + Class::None => {}, + Class::One(atom) => Atom::with(atom, callback), + Class::More(atoms) => { + for atom in atoms { + Atom::with(atom.mRawPtr, &mut callback) } - }, + } } } } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index cf1a2b56ab6..1ba850696d1 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -32,7 +32,6 @@ use gecko_bindings::bindings; use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWTheme}; use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetPreviousSibling, Gecko_GetNextStyleChild}; use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags}; -use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_ElementHasAnimations; use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations; use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions; @@ -581,6 +580,34 @@ impl<'le> fmt::Debug for GeckoElement<'le> { } impl<'le> GeckoElement<'le> { + #[inline(always)] + fn attrs(&self) -> &[structs::AttrArray_InternalAttr] { + unsafe { + let attrs = match self.0._base.mAttrs.mImpl.mPtr.as_ref() { + Some(attrs) => attrs, + None => return &[], + }; + + attrs.mBuffer.as_slice(attrs.mAttrCount as usize) + } + } + + #[inline(always)] + fn get_class_attr(&self) -> Option<&structs::nsAttrValue> { + if !self.may_have_class() { + return None; + } + + if self.is_svg_element() { + let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() }; + if let Some(c) = svg_class { + return Some(c) + } + } + + snapshot_helpers::find_attr(self.attrs(), &atom!("class")) + } + #[inline] fn closest_anon_subtree_root_parent(&self) -> Option { debug_assert!(self.is_in_native_anonymous_subtree()); @@ -1281,26 +1308,19 @@ impl<'le> TElement for GeckoElement<'le> { return None; } - let ptr = unsafe { bindings::Gecko_AtomAttrValue(self.0, atom!("id").as_ptr()) }; - - // FIXME(emilio): Pretty sure the has_id flag is exact and we could - // assert here. - if ptr.is_null() { - None - } else { - Some(unsafe { WeakAtom::new(ptr) }) - } + snapshot_helpers::get_id(self.attrs()) } fn each_class(&self, callback: F) where F: FnMut(&Atom), { - if !self.may_have_class() { - return; - } + let attr = match self.get_class_attr() { + Some(c) => c, + None => return, + }; - snapshot_helpers::each_class(self.0, callback, Gecko_ClassOrClassList) + snapshot_helpers::each_class(attr, callback) } #[inline] @@ -2265,24 +2285,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { return false; } - unsafe { - let ptr = bindings::Gecko_AtomAttrValue(self.0, atom!("id").as_ptr()); + let element_id = match snapshot_helpers::get_id(self.attrs()) { + Some(id) => id, + None => return false, + }; - if ptr.is_null() { - false - } else { - case_sensitivity.eq_atom(WeakAtom::new(ptr), id) - } - } + case_sensitivity.eq_atom(element_id, id) } #[inline(always)] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { - if !self.may_have_class() { - return false; - } + let attr = match self.get_class_attr() { + Some(c) => c, + None => return false, + }; - snapshot_helpers::has_class(self.0, name, case_sensitivity, bindings::Gecko_HasClass) + snapshot_helpers::has_class(name, case_sensitivity, attr) } #[inline] diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs index e70c7a47c36..70217f233de 100644 --- a/components/style/gecko_string_cache/mod.rs +++ b/components/style/gecko_string_cache/mod.rs @@ -263,7 +263,7 @@ impl fmt::Display for WeakAtom { impl Atom { /// Execute a callback with the atom represented by `ptr`. - pub unsafe fn with(ptr: *mut nsAtom, callback: F) -> R + pub unsafe fn with(ptr: *const nsAtom, callback: F) -> R where F: FnOnce(&Atom) -> R, {