Bug 1431421: Make GeckoElement::has_class more specialized. r?xidorn draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 18 Jan 2018 16:19:26 +0100
changeset 722179 0e56a5d5cae8fcb787cc7c97079e8b84c7b20f45
parent 722163 6c0b1564da8582b74d02b2c9398cd8e9609989e0
child 746545 92f17f02437612c66f9d8f8c919db091f5bb3682
push id96074
push userbmo:emilio@crisal.io
push dateThu, 18 Jan 2018 15:31:58 +0000
reviewersxidorn
bugs1431421
milestone59.0a1
Bug 1431421: Make GeckoElement::has_class more specialized. r?xidorn MozReview-Commit-ID: 7LiSEamTCkX
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
servo/components/style/gecko/snapshot.rs
servo/components/style/gecko/snapshot_helpers.rs
servo/components/style/gecko/wrapper.rs
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1038,16 +1038,31 @@ AttrHasSuffix(Implementor* aElement, nsA
     aValue->ToString(str);
     WITH_COMPARATOR(aIgnoreCase, c,
                     StringEndsWith(str, nsDependentAtomString(aStr), c))
   };
   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.
@@ -1106,63 +1121,68 @@ ClassOrClassList(Implementor* aElement, 
 
   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)         \
+  nsAtom* prefix_##AtomAttrValue(implementor_ aElement, nsAtom* aName)           \
   {                                                                              \
     return AtomAttrValue(aElement, aName);                                       \
   }                                                                              \
-  nsAtom* prefix_##LangValue(implementor_ aElement)                             \
+  nsAtom* prefix_##LangValue(implementor_ aElement)                              \
   {                                                                              \
     return LangValue(aElement);                                                  \
   }                                                                              \
-  bool prefix_##HasAttr(implementor_ aElement, nsAtom* aNS, nsAtom* aName)     \
+  bool prefix_##HasAttr(implementor_ aElement, nsAtom* aNS, nsAtom* aName)       \
   {                                                                              \
     return HasAttr(aElement, aNS, aName);                                        \
   }                                                                              \
-  bool prefix_##AttrEquals(implementor_ aElement, nsAtom* aNS,                  \
-                           nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)      \
+  bool prefix_##AttrEquals(implementor_ aElement, nsAtom* aNS,                   \
+                           nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)        \
   {                                                                              \
     return AttrEquals(aElement, aNS, aName, aStr, aIgnoreCase);                  \
   }                                                                              \
-  bool prefix_##AttrDashEquals(implementor_ aElement, nsAtom* aNS,              \
-                               nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)  \
+  bool prefix_##AttrDashEquals(implementor_ aElement, nsAtom* aNS,               \
+                               nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)    \
   {                                                                              \
     return AttrDashEquals(aElement, aNS, aName, aStr, aIgnoreCase);              \
   }                                                                              \
-  bool prefix_##AttrIncludes(implementor_ aElement, nsAtom* aNS,                \
-                             nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)    \
+  bool prefix_##AttrIncludes(implementor_ aElement, nsAtom* aNS,                 \
+                             nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)      \
   {                                                                              \
     return AttrIncludes(aElement, aNS, aName, aStr, aIgnoreCase);                \
   }                                                                              \
-  bool prefix_##AttrHasSubstring(implementor_ aElement, nsAtom* aNS,            \
-                                 nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)\
+  bool prefix_##AttrHasSubstring(implementor_ aElement, nsAtom* aNS,             \
+                                 nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)  \
   {                                                                              \
     return AttrHasSubstring(aElement, aNS, aName, aStr, aIgnoreCase);            \
   }                                                                              \
-  bool prefix_##AttrHasPrefix(implementor_ aElement, nsAtom* aNS,               \
-                              nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)   \
+  bool prefix_##AttrHasPrefix(implementor_ aElement, nsAtom* aNS,                \
+                              nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)     \
   {                                                                              \
     return AttrHasPrefix(aElement, aNS, aName, aStr, aIgnoreCase);               \
   }                                                                              \
-  bool prefix_##AttrHasSuffix(implementor_ aElement, nsAtom* aNS,               \
-                              nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)   \
+  bool prefix_##AttrHasSuffix(implementor_ aElement, nsAtom* aNS,                \
+                              nsAtom* aName, nsAtom* aStr, bool aIgnoreCase)     \
   {                                                                              \
     return AttrHasSuffix(aElement, aNS, aName, aStr, aIgnoreCase);               \
   }                                                                              \
-  uint32_t prefix_##ClassOrClassList(implementor_ aElement, nsAtom** aClass,    \
-                                     nsAtom*** aClassList)                      \
+  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)
 SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, const ServoElementSnapshot*)
 
 #undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS
 
 nsAtom*
 Gecko_Atomize(const char* aString, uint32_t aLength)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -191,34 +191,36 @@ bool Gecko_MatchesElement(mozilla::CSSPs
 bool Gecko_MatchLang(RawGeckoElementBorrowed element,
                      nsAtom* override_lang, bool has_override_lang,
                      const char16_t* value);
 nsAtom* Gecko_GetXMLLangValue(RawGeckoElementBorrowed element);
 nsIDocument::DocumentTheme Gecko_GetDocumentLWTheme(const nsIDocument* aDocument);
 
 // 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,  \
-                           nsAtom* str, bool ignoreCase);                    \
-  bool prefix_##AttrDashEquals(implementor_ element, nsAtom* ns,             \
-                               nsAtom* name, nsAtom* str, bool ignore_case);\
-  bool prefix_##AttrIncludes(implementor_ element, nsAtom* ns,               \
-                             nsAtom* name, nsAtom* str, bool ignore_case);  \
-  bool prefix_##AttrHasSubstring(implementor_ element, nsAtom* ns,           \
-                                 nsAtom* name, nsAtom* str,                 \
+  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,    \
+                           nsAtom* str, bool ignoreCase);                     \
+  bool prefix_##AttrDashEquals(implementor_ element, nsAtom* ns,              \
+                               nsAtom* name, nsAtom* str, bool ignore_case);  \
+  bool prefix_##AttrIncludes(implementor_ element, nsAtom* ns,                \
+                             nsAtom* name, nsAtom* str, bool ignore_case);    \
+  bool prefix_##AttrHasSubstring(implementor_ element, nsAtom* ns,            \
+                                 nsAtom* name, nsAtom* str,                   \
                                  bool ignore_case);                           \
-  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_##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);
 
 SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
 SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
                                               const ServoElementSnapshot*)
 
 #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
 
 // Style attributes.
--- a/servo/components/style/gecko/snapshot.rs
+++ b/servo/components/style/gecko/snapshot.rs
@@ -180,33 +180,37 @@ impl ElementSnapshot for GeckoElementSna
     }
 
     #[inline]
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         if !self.has_any(Flags::MaybeClass) {
             return false;
         }
 
-        snapshot_helpers::has_class(self.as_ptr(),
-                                    name,
-                                    case_sensitivity,
-                                    bindings::Gecko_SnapshotClassOrClassList)
+        snapshot_helpers::has_class(
+            self.as_ptr(),
+            name,
+            case_sensitivity,
+            bindings::Gecko_SnapshotHasClass,
+        )
     }
 
     #[inline]
     fn each_class<F>(&self, callback: F)
         where F: FnMut(&Atom)
     {
         if !self.has_any(Flags::MaybeClass) {
             return;
         }
 
-        snapshot_helpers::each_class(self.as_ptr(),
-                                     callback,
-                                     bindings::Gecko_SnapshotClassOrClassList)
+        snapshot_helpers::each_class(
+            self.as_ptr(),
+            callback,
+            bindings::Gecko_SnapshotClassOrClassList,
+        )
     }
 
     #[inline]
     fn lang_attr(&self) -> Option<Atom> {
         let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) };
         if ptr.is_null() {
             None
         } else {
--- a/servo/components/style/gecko/snapshot_helpers.rs
+++ b/servo/components/style/gecko/snapshot_helpers.rs
@@ -1,53 +1,44 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 //! Element an snapshot common logic.
 
-use CaseSensitivityExt;
 use gecko_bindings::structs::nsAtom;
-use gecko_string_cache::WeakAtom;
 use selectors::attr::CaseSensitivity;
 use std::{ptr, slice};
 use string_cache::Atom;
 
 /// 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 fn (T, *mut *mut nsAtom, *mut *mut *mut nsAtom) -> u32;
 
+/// 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 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,
     name: &Atom,
     case_sensitivity: CaseSensitivity,
-    getter: ClassOrClassList<T>,
+    getter: HasClass<T>,
 ) -> bool {
-    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 => false,
-            1 => case_sensitivity.eq_atom(name, WeakAtom::new(class)),
-            n => {
-                let classes = slice::from_raw_parts(list, n as usize);
-                match case_sensitivity {
-                    CaseSensitivity::CaseSensitive => {
-                        classes.iter().any(|ptr| &**name == WeakAtom::new(*ptr))
-                    }
-                    CaseSensitivity::AsciiCaseInsensitive => {
-                        classes.iter().any(|ptr| name.eq_ignore_ascii_case(WeakAtom::new(*ptr)))
-                    }
-                }
-            }
-        }
-    }
+    let ignore_case = match case_sensitivity {
+        CaseSensitivity::CaseSensitive => false,
+        CaseSensitivity::AsciiCaseInsensitive => true,
+    };
+
+    unsafe { getter(item, name.as_ptr(), ignore_case) }
 }
 
 
 /// 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,
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -2196,17 +2196,17 @@ impl<'le> ::selectors::Element for Gecko
         if !self.may_have_class() {
             return false;
         }
 
         snapshot_helpers::has_class(
             self.0,
             name,
             case_sensitivity,
-            Gecko_ClassOrClassList,
+            bindings::Gecko_HasClass,
         )
     }
 
     #[inline]
     fn is_html_element_in_html_document(&self) -> bool {
         self.is_html_element() &&
         self.as_node().owner_doc().is_html_document()
     }