Bug 1483964: Manually inline class and ID getters. r=xidorn

Somewhat ugly but hopefully not too much. Somehow it ends up removing more lines
than adding.

Differential Revision: https://phabricator.services.mozilla.com/D3536
This commit is contained in:
Emilio Cobos Álvarez 2018-08-15 01:29:40 +02:00
parent 557d043591
commit 5a92426ddb
10 changed files with 172 additions and 220 deletions

View File

@ -949,23 +949,12 @@ public:
* class attribute). This may be null if there are no classes, but that's not
* guaranteed (e.g. we could have class="").
*/
const nsAttrValue* GetClasses() const {
if (MayHaveClass()) {
return DoGetClasses();
}
return nullptr;
}
/**
* Hook for implementing GetClasses. This should only be called if the
* ElementMayHaveClass flag is set.
*
* Public only because Servo needs to call it too, and it ensures the
* precondition before calling this.
*/
const nsAttrValue* DoGetClasses() const
const nsAttrValue* GetClasses() const
{
MOZ_ASSERT(MayHaveClass(), "Unexpected call");
if (!MayHaveClass()) {
return nullptr;
}
if (IsSVGElement()) {
if (const nsAttrValue* value = GetSVGAnimatedClass()) {
return value;
@ -1967,7 +1956,7 @@ protected:
private:
/**
* Slow path for DoGetClasses, this should only be called for SVG elements.
* Slow path for GetClasses, this should only be called for SVG elements.
*/
const nsAttrValue* GetSVGAnimatedClass() const;

View File

@ -41,7 +41,7 @@ class DeclarationBlock;
#define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
#define NS_ATTRVALUE_BASETYPE_MASK (uintptr_t(3))
const uintptr_t NS_ATTRVALUE_BASETYPE_MASK = 3;
#define NS_ATTRVALUE_POINTERVALUE_MASK (~NS_ATTRVALUE_BASETYPE_MASK)
#define NS_ATTRVALUE_INTEGERTYPE_BITS 4

View File

@ -916,14 +916,6 @@ Gecko_IsBrowserFrame(RawGeckoElementBorrowed aElement)
return browserFrame && browserFrame->GetReallyIsBrowser();
}
template <typename Implementor>
static nsAtom*
AtomAttrValue(Implementor* aElement, nsAtom* aName)
{
const nsAttrValue* attr = aElement->GetParsedAttr(aName);
return attr ? attr->GetAtomValue() : nullptr;
}
template <typename Implementor>
static nsAtom*
LangValue(Implementor* aElement)
@ -1074,94 +1066,7 @@ AttrHasSuffix(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
/**
* Returns whether an element contains a class in its class list or not.
*/
template <typename Implementor>
static bool
HasClass(Implementor* aElement, nsAtom* aClass, bool aIgnoreCase)
{
const nsAttrValue* attr = aElement->DoGetClasses();
if (!attr) {
return false;
}
return attr->Contains(aClass, aIgnoreCase ? eIgnoreCase : eCaseMatters);
}
/**
* Gets the class or class list (if any) of the implementor. The calling
* convention here is rather hairy, and is optimized for getting Servo the
* information it needs for hot calls.
*
* The return value indicates the number of classes. If zero, neither outparam
* is valid. If one, the class_ outparam is filled with the atom of the class.
* If two or more, the classList outparam is set to point to an array of atoms
* representing the class list.
*
* The array is borrowed and the atoms are not addrefed. These values can be
* invalidated by any DOM mutation. Use them in a tight scope.
*/
template <typename Implementor>
static uint32_t
ClassOrClassList(Implementor* aElement, nsAtom** aClass, nsAtom*** aClassList)
{
const nsAttrValue* attr = aElement->DoGetClasses();
if (!attr) {
return 0;
}
// For class values with only whitespace, Gecko just stores a string. For the
// purposes of the style system, there is no class in this case.
nsAttrValue::ValueType type = attr->Type();
if (MOZ_UNLIKELY(type == nsAttrValue::eString)) {
MOZ_ASSERT(nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
attr->GetStringValue()).IsEmpty());
return 0;
}
// Single tokens are generally stored as an atom. Check that case.
if (type == nsAttrValue::eAtom) {
*aClass = attr->GetAtomValue();
return 1;
}
// At this point we should have an atom array. It is likely, but not
// guaranteed, that we have two or more elements in the array.
MOZ_ASSERT(type == nsAttrValue::eAtomArray);
nsTArray<RefPtr<nsAtom>>* atomArray = attr->GetAtomArrayValue();
uint32_t length = atomArray->Length();
// Special case: zero elements.
if (length == 0) {
return 0;
}
// Special case: one element.
if (length == 1) {
*aClass = atomArray->ElementAt(0);
return 1;
}
// General case: Two or more elements.
//
// Note: We could also expose this array as an array of nsCOMPtrs, since
// bindgen knows what those look like, and eliminate the reinterpret_cast.
// But it's not obvious that that would be preferable.
static_assert(sizeof(RefPtr<nsAtom>) == sizeof(nsAtom*), "Bad simplification");
static_assert(alignof(RefPtr<nsAtom>) == alignof(nsAtom*), "Bad simplification");
RefPtr<nsAtom>* elements = atomArray->Elements();
nsAtom** rawElements = reinterpret_cast<nsAtom**>(elements);
*aClassList = rawElements;
return atomArray->Length();
}
#define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsAtom* prefix_##AtomAttrValue(implementor_ aElement, nsAtom* aName) \
{ \
return AtomAttrValue(aElement, aName); \
} \
nsAtom* prefix_##LangValue(implementor_ aElement) \
{ \
return LangValue(aElement); \
@ -1199,16 +1104,7 @@ ClassOrClassList(Implementor* aElement, nsAtom** aClass, nsAtom*** aClassList)
nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) \
{ \
return AttrHasSuffix(aElement, aNS, aName, aStr, aIgnoreCase); \
} \
uint32_t prefix_##ClassOrClassList(implementor_ aElement, nsAtom** aClass, \
nsAtom*** aClassList) \
{ \
return ClassOrClassList(aElement, aClass, aClassList); \
} \
bool prefix_##HasClass(implementor_ aElement, nsAtom* aClass, bool aIgnoreCase)\
{ \
return HasClass(aElement, aClass, aIgnoreCase); \
} \
}
SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
@ -2969,3 +2865,22 @@ Gecko_IsMainThread()
{
return NS_IsMainThread();
}
const nsAttrValue*
Gecko_GetSVGAnimatedClass(RawGeckoElementBorrowed aElement)
{
MOZ_ASSERT(aElement->IsSVGElement());
return static_cast<const nsSVGElement*>(aElement)->GetAnimatedClassName();
}
bool
Gecko_AssertClassAttrValueIsSane(const nsAttrValue* aValue)
{
MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom ||
aValue->Type() == nsAttrValue::eString ||
aValue->Type() == nsAttrValue::eAtomArray);
MOZ_ASSERT_IF(aValue->Type() == nsAttrValue::eString,
nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
aValue->GetStringValue()).IsEmpty());
return true;
}

View File

@ -200,7 +200,6 @@ bool Gecko_IsBrowserFrame(RawGeckoElementBorrowed element);
// Attributes.
#define SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsAtom* prefix_##AtomAttrValue(implementor_ element, nsAtom* attribute); \
nsAtom* prefix_##LangValue(implementor_ element); \
bool prefix_##HasAttr(implementor_ element, nsAtom* ns, nsAtom* name); \
bool prefix_##AttrEquals(implementor_ element, nsAtom* ns, nsAtom* name, \
@ -215,11 +214,11 @@ bool Gecko_IsBrowserFrame(RawGeckoElementBorrowed element);
bool prefix_##AttrHasPrefix(implementor_ element, nsAtom* ns, \
nsAtom* name, nsAtom* str, bool ignore_case); \
bool prefix_##AttrHasSuffix(implementor_ element, nsAtom* ns, \
nsAtom* name, nsAtom* str, bool ignore_case); \
uint32_t prefix_##ClassOrClassList(implementor_ element, nsAtom** class_, \
nsAtom*** classList); \
bool prefix_##HasClass(implementor_ element, nsAtom* class_, \
bool ignore_case);
nsAtom* name, nsAtom* str, bool ignore_case);
bool Gecko_AssertClassAttrValueIsSane(const nsAttrValue*);
const nsAttrValue* Gecko_GetSVGAnimatedClass(RawGeckoElementBorrowed);
SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
@ -653,7 +652,6 @@ nsCSSKeyword Gecko_LookupCSSKeyword(const uint8_t* string, uint32_t len);
const char* Gecko_CSSKeywordString(nsCSSKeyword keyword, uint32_t* len);
bool Gecko_IsDocumentBody(RawGeckoElementBorrowed element);
// We use an int32_t here instead of a LookAndFeel::ColorID
// because forward-declaring a nested enum/struct is impossible
nscolor Gecko_GetLookAndFeelSystemColor(int32_t color_id,

View File

@ -197,6 +197,7 @@ rusty-enums = [
whitelist-vars = [
"NS_AUTHOR_SPECIFIED_.*",
"NS_THEME_.*",
"NS_ATTRVALUE_.*",
"NODE_.*",
"ELEMENT_.*",
"NS_FONT_.*",
@ -476,6 +477,7 @@ structs-types = [
"mozilla::StyleDisplayMode",
"ServoRawOffsetArc",
"DeclarationBlockMutationClosure",
"nsAttrValue",
"nsIContent",
"nsINode",
"nsIDocument",

View File

@ -134,12 +134,6 @@ public:
return nullptr;
}
const nsAttrValue* DoGetClasses() const
{
MOZ_ASSERT(HasAttrs());
return &mClass;
}
bool IsInChromeDocument() const { return mIsInChromeDocument; }
bool SupportsLangAttr() const { return mSupportsLangAttr; }

View File

@ -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]

View File

@ -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<T> =
unsafe extern "C" fn(T, *mut *mut nsAtom, *mut *mut *mut nsAtom) -> u32;
enum Class<'a> {
None,
One(*const nsAtom),
More(&'a [structs::RefPtr<nsAtom>]),
}
/// 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<T> = 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<T>(
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<T>(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::<nsAtom>(attr));
}
debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eOtherBase);
let container = ptr::<structs::MiscContainer>(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::<nsAtom>(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<T>,
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<F, T>(item: T, mut callback: F, getter: ClassOrClassList<T>)
#[inline(always)]
pub fn each_class<F>(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)
}
},
}
}
}
}

View File

@ -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<Self> {
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<F>(&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]

View File

@ -263,7 +263,7 @@ impl fmt::Display for WeakAtom {
impl Atom {
/// Execute a callback with the atom represented by `ptr`.
pub unsafe fn with<F, R>(ptr: *mut nsAtom, callback: F) -> R
pub unsafe fn with<F, R>(ptr: *const nsAtom, callback: F) -> R
where
F: FnOnce(&Atom) -> R,
{