servo: Merge #16873 - Fix dynamic updates when :dir matching changes in stylo (from bzbarsky:fix-dir-matching); r=emilio
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 15 May 2017 11:40:40 -0500
changeset 358476 b733db3d681b1f9dd3e72773188ec565d0beaeb8
parent 358475 ddd4f6f93fabc7f90771a53f93f9444acc7534f3
child 358477 78c489199cd822bab5764654db7a6585af4bde3b
push id31827
push usercbook@mozilla.com
push dateTue, 16 May 2017 10:34:19 +0000
treeherdermozilla-central@49365d675cbb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone55.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
servo: Merge #16873 - Fix dynamic updates when :dir matching changes in stylo (from bzbarsky:fix-dir-matching); r=emilio This is the servo part of https://bugzilla.mozilla.org/show_bug.cgi?id=1364280 <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix https://bugzilla.mozilla.org/show_bug.cgi?id=1364280 <!-- Either: --> - [ ] There are tests for these changes OR - [X] These changes do not require tests because Gecko has lots of tests for this and this is Gecko-only. <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: ff5f90386b88eb08b483c56004f59ac2ab3df7ca
servo/components/style/element_state.rs
servo/components/style/restyle_hints.rs
--- a/servo/components/style/element_state.rs
+++ b/servo/components/style/element_state.rs
@@ -110,14 +110,19 @@ bitflags! {
         /// Non-standard & undocumented.
         const IN_HANDLER_CLICK_TO_PLAY_STATE = 1 << 40,
         /// Non-standard & undocumented.
         const IN_HANDLER_VULNERABLE_UPDATABLE_STATE = 1 << 41,
         /// Non-standard & undocumented.
         const IN_HANDLER_VULNERABLE_NO_UPDATE_STATE = 1 << 42,
         /// https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo
         const IN_FOCUS_WITHIN_STATE = 1 << 43,
+        /// :dir matching; the states are used for dynamic change detection.
+        /// State that elements that match :dir(ltr) are in.
+        const IN_LTR_STATE = 1 << 44,
+        /// State that elements that match :dir(rtl) are in.
+        const IN_RTL_STATE = 1 << 45,
         /// Non-standard & undocumented.
         const IN_AUTOFILL_STATE = 1 << 50,
         /// Non-standard & undocumented.
         const IN_AUTOFILL_PREVIEW_STATE = 1 << 51,
     }
 }
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -309,16 +309,32 @@ impl<'a, E> MatchAttr for ElementWrapper
         match self.snapshot() {
             Some(snapshot) if snapshot.has_attrs()
                 => snapshot.match_attr_suffix(attr, value),
             _   => self.element.match_attr_suffix(attr, value)
         }
     }
 }
 
+#[cfg(feature = "gecko")]
+fn dir_selector_to_state(s: &[u16]) -> ElementState {
+    // Jump through some hoops to deal with our Box<[u16]> thing.
+    const LTR: [u16; 4] = [b'l' as u16, b't' as u16, b'r' as u16, 0];
+    const RTL: [u16; 4] = [b'r' as u16, b't' as u16, b'l' as u16, 0];
+    if LTR == *s {
+        IN_LTR_STATE
+    } else if RTL == *s {
+        IN_RTL_STATE
+    } else {
+        // :dir(something-random) is a valid selector, but shouldn't
+        // match anything.
+        ElementState::empty()
+    }
+}
+
 impl<'a, E> Element for ElementWrapper<'a, E>
     where E: TElement,
 {
     fn match_non_ts_pseudo_class<F>(&self,
                                     pseudo_class: &NonTSPseudoClass,
                                     relations: &mut StyleRelations,
                                     _setter: &mut F)
                                     -> bool
@@ -331,16 +347,41 @@ impl<'a, E> Element for ElementWrapper<'
             use selectors::matching::matches_complex_selector;
             if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
                 return selectors.iter().any(|s| {
                     matches_complex_selector(s, self, relations, _setter)
                 })
             }
         }
 
+        // :dir needs special handling.  It's implemented in terms of state
+        // flags, but which state flag it maps to depends on the argument to
+        // :dir.  That means we can't just add its state flags to the
+        // NonTSPseudoClass, because if we added all of them there, and tested
+        // via intersects() here, we'd get incorrect behavior for :not(:dir())
+        // cases.
+        //
+        // FIXME(bz): How can I set this up so once Servo adds :dir() support we
+        // don't forget to update this code?
+        #[cfg(feature = "gecko")]
+        {
+            if let NonTSPseudoClass::Dir(ref s) = *pseudo_class {
+                let selector_flag = dir_selector_to_state(s);
+                if selector_flag.is_empty() {
+                    // :dir() with some random argument; does not match.
+                    return false;
+                }
+                let state = match self.snapshot().and_then(|s| s.state()) {
+                    Some(snapshot_state) => snapshot_state,
+                    None => self.element.get_state(),
+                };
+                return state.contains(selector_flag);
+            }
+        }
+
         let flag = pseudo_class.state_flag();
         if flag.is_empty() {
             return self.element.match_non_ts_pseudo_class(pseudo_class,
                                                           relations,
                                                           &mut |_, _| {})
         }
         match self.snapshot().and_then(|s| s.state()) {
             Some(snapshot_state) => snapshot_state.intersects(flag),
@@ -420,16 +461,20 @@ impl<'a, E> Element for ElementWrapper<'
                 => snapshot.each_class(callback),
             _   => self.element.each_class(callback)
         }
     }
 }
 
 fn selector_to_state(sel: &Component<SelectorImpl>) -> ElementState {
     match *sel {
+        // FIXME(bz): How can I set this up so once Servo adds :dir() support we
+        // don't forget to update this code?
+        #[cfg(feature = "gecko")]
+        Component::NonTSPseudoClass(NonTSPseudoClass::Dir(ref s)) => dir_selector_to_state(s),
         Component::NonTSPseudoClass(ref pc) => pc.state_flag(),
         _ => ElementState::empty(),
     }
 }
 
 fn is_attr_selector(sel: &Component<SelectorImpl>) -> bool {
     match *sel {
         Component::ID(_) |