Bug 1545430 - Implement selector-matching for ::part(). r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 01 May 2019 17:28:23 +0000
changeset 530954 5cac03a223e560c730e9e1e58ad4a6f93ebba4da
parent 530953 63c513e31abb4f4ed16154af0f38d734d3c0764a
child 530955 2fa4f9b9a4c6c39c878fb1848cd8ef468f232287
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1545430
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1545430 - Implement selector-matching for ::part(). r=heycam Also fairly straight-forward. This may get more complicated when we do part forwarding, if any. I've opened https://github.com/w3c/csswg-drafts/issues/3841 in what I think would be a cleaner model for forwarding. Differential Revision: https://phabricator.services.mozilla.com/D28063
dom/base/Element.cpp
servo/components/style/gecko/snapshot.rs
servo/components/style/gecko/snapshot_helpers.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/invalidation/element/element_wrapper.rs
xpcom/ds/StaticAtoms.py
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2590,17 +2590,17 @@ bool Element::ParseAttribute(int32_t aNa
                              nsIPrincipal* aMaybeScriptedPrincipal,
                              nsAttrValue& aResult) {
   if (aAttribute == nsGkAtoms::lang) {
     aResult.ParseAtom(aValue);
     return true;
   }
 
   if (aNamespaceID == kNameSpaceID_None) {
-    if (aAttribute == nsGkAtoms::_class) {
+    if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part) {
       aResult.ParseAtomArray(aValue);
       return true;
     }
 
     if (aAttribute == nsGkAtoms::id) {
       // Store id as an atom.  id="" means that the element has no id,
       // not that it has an emptystring as the id.
       if (aValue.IsEmpty()) {
--- a/servo/components/style/gecko/snapshot.rs
+++ b/servo/components/style/gecko/snapshot.rs
@@ -179,22 +179,32 @@ impl ElementSnapshot for GeckoElementSna
         if !self.has_any(Flags::Id) {
             return None;
         }
 
         snapshot_helpers::get_id(&*self.mAttrs)
     }
 
     #[inline]
+    fn is_part(&self, name: &Atom) -> bool {
+        let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) {
+            Some(attr) => attr,
+            None => return false,
+        };
+
+        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
+    }
+
+    #[inline]
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         if !self.has_any(Flags::MaybeClass) {
             return false;
         }
 
-        snapshot_helpers::has_class(name, case_sensitivity, &self.mClass)
+        snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass)
     }
 
     #[inline]
     fn each_class<F>(&self, callback: F)
     where
         F: FnMut(&Atom),
     {
         if !self.has_any(Flags::MaybeClass) {
--- a/servo/components/style/gecko/snapshot_helpers.rs
+++ b/servo/components/style/gecko/snapshot_helpers.rs
@@ -24,17 +24,17 @@ fn base_type(attr: &structs::nsAttrValue
 }
 
 #[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 {
+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_eStringBase {
         return Class::None;
     }
     if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase {
         return Class::One(ptr::<nsAtom>(attr));
     }
@@ -77,25 +77,25 @@ pub fn find_attr<'a>(
 }
 
 /// 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.
+/// 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(
+pub fn has_class_or_part(
     name: &Atom,
     case_sensitivity: CaseSensitivity,
     attr: &structs::nsAttrValue,
 ) -> bool {
-    match unsafe { get_class_from_attr(attr) } {
+    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 => {
                 atoms.iter().any(|atom| atom.mRawPtr == name.as_ptr())
             },
             CaseSensitivity::AsciiCaseInsensitive => unsafe {
                 atoms
@@ -109,17 +109,17 @@ pub fn has_class(
 /// Given an item, a callback, and a getter, execute `callback` for each class
 /// this `item` has.
 #[inline(always)]
 pub fn each_class<F>(attr: &structs::nsAttrValue, mut callback: F)
 where
     F: FnMut(&Atom),
 {
     unsafe {
-        match get_class_from_attr(attr) {
+        match get_class_or_part_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)
                 }
             },
         }
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -569,16 +569,21 @@ impl<'le> GeckoElement<'le> {
                 None => return &[],
             };
 
             attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
         }
     }
 
     #[inline(always)]
+    fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
+        snapshot_helpers::find_attr(self.attrs(), &atom!("part"))
+    }
+
+    #[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 {
@@ -2259,28 +2264,34 @@ impl<'le> ::selectors::Element for Gecko
         let element_id = match snapshot_helpers::get_id(self.attrs()) {
             Some(id) => id,
             None => return false,
         };
 
         case_sensitivity.eq_atom(element_id, id)
     }
 
-    fn is_part(&self, _name: &Atom) -> bool {
-        unimplemented!();
+    #[inline]
+    fn is_part(&self, name: &Atom) -> bool {
+        let attr = match self.get_part_attr() {
+            Some(c) => c,
+            None => return false,
+        };
+
+        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
     }
 
     #[inline(always)]
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         let attr = match self.get_class_attr() {
             Some(c) => c,
             None => return false,
         };
 
-        snapshot_helpers::has_class(name, case_sensitivity, attr)
+        snapshot_helpers::has_class_or_part(name, case_sensitivity, attr)
     }
 
     #[inline]
     fn is_html_element_in_html_document(&self) -> bool {
         self.is_html_element() && self.as_node().owner_doc().is_html_document()
     }
 
     #[inline]
--- a/servo/components/style/invalidation/element/element_wrapper.rs
+++ b/servo/components/style/invalidation/element/element_wrapper.rs
@@ -53,16 +53,20 @@ pub trait ElementSnapshot: Sized {
     /// The ID attribute per this snapshot. Should only be called if
     /// `has_attrs()` returns true.
     fn id_attr(&self) -> Option<&WeakAtom>;
 
     /// Whether this snapshot contains the class `name`. Should only be called
     /// if `has_attrs()` returns true.
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
 
+    /// Whether this snapshot represents the part named `name`. Should only be
+    /// called if `has_attrs()` returns true.
+    fn is_part(&self, name: &Atom) -> bool;
+
     /// A callback that should be called for each class of the snapshot. Should
     /// only be called if `has_attrs()` returns true.
     fn each_class<F>(&self, _: F)
     where
         F: FnMut(&Atom);
 
     /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
     fn lang_attr(&self) -> Option<AttrValue>;
@@ -335,18 +339,21 @@ where
         match self.snapshot() {
             Some(snapshot) if snapshot.has_attrs() => snapshot
                 .id_attr()
                 .map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)),
             _ => self.element.has_id(id, case_sensitivity),
         }
     }
 
-    fn is_part(&self, _name: &Atom) -> bool {
-        unimplemented!();
+    fn is_part(&self, name: &Atom) -> bool {
+        match self.snapshot() {
+            Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),
+            _ => self.element.is_part(name),
+        }
     }
 
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         match self.snapshot() {
             Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
             _ => self.element.has_class(name, case_sensitivity),
         }
     }
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -938,16 +938,17 @@ STATIC_ATOMS = [
     Atom("paint_order", "paint-order"),
     Atom("panel", "panel"),
     Atom("paragraph", "paragraph"),
     Atom("param", "param"),
     Atom("parameter", "parameter"),
     Atom("parent", "parent"),
     Atom("parentfocused", "parentfocused"),
     Atom("parsererror", "parsererror"),
+    Atom("part", "part"),
     Atom("password", "password"),
     Atom("pattern", "pattern"),
     Atom("patternSeparator", "pattern-separator"),
     Atom("perMille", "per-mille"),
     Atom("percent", "percent"),
     Atom("persist", "persist"),
     Atom("phase", "phase"),
     Atom("picture", "picture"),