servo: Merge #17212 - Stylo: Visited selectors for nested links (from jryans:visited-selectors); r=emilio
authorJ. Ryan Stinnett <jryans@gmail.com>
Wed, 07 Jun 2017 12:49:34 -0700
changeset 413333 a39f666af494d11f0ca6b67e08db983e05d01e8a
parent 413332 4ae5d273a14975c2b5ddc609eb1abdf2bed5ff99
child 413334 6dbed508be86d1f4bfb2c2761e0557a4fd7addb2
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [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 #17212 - Stylo: Visited selectors for nested links (from jryans:visited-selectors); r=emilio https://bugzilla.mozilla.org/show_bug.cgi?id=1370358 Source-Repo: https://github.com/servo/servo Source-Revision: 0934df77fdbddcb6ae1534e3c1a150272aea73b2
servo/Cargo.lock
servo/components/script/dom/element.rs
servo/components/selectors/Cargo.toml
servo/components/selectors/lib.rs
servo/components/selectors/matching.rs
servo/components/selectors/parser.rs
servo/components/selectors/tree.rs
servo/components/style/restyle_hints.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -2515,16 +2515,17 @@ dependencies = [
 
 [[package]]
 name = "selectors"
 version = "0.19.0"
 dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_arc 0.0.1",
  "size_of_test 0.0.1",
  "smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -146,16 +146,22 @@ impl fmt::Debug for Element {
         try!(write!(f, "<{}", self.local_name));
         if let Some(ref id) = *self.id_attribute.borrow() {
             try!(write!(f, " id={}", id));
         }
         write!(f, ">")
     }
 }
 
+impl fmt::Debug for Root<Element> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        (**self).fmt(f)
+    }
+}
+
 #[derive(PartialEq, HeapSizeOf)]
 pub enum ElementCreator {
     ParserCreated(u64),
     ScriptCreated,
 }
 
 impl ElementCreator {
     pub fn is_parser_created(&self) -> bool {
--- a/servo/components/selectors/Cargo.toml
+++ b/servo/components/selectors/Cargo.toml
@@ -20,16 +20,17 @@ doctest = false
 
 [features]
 gecko_like_types = []
 
 [dependencies]
 bitflags = "0.7"
 matches = "0.1"
 cssparser = "0.13.7"
+log = "0.3"
 fnv = "1.0"
 phf = "0.7.18"
 precomputed-hash = "0.1"
 servo_arc = { path = "../servo_arc" }
 smallvec = "0.4"
 
 [dev-dependencies]
 size_of_test = {path = "../size_of_test"}
--- a/servo/components/selectors/lib.rs
+++ b/servo/components/selectors/lib.rs
@@ -1,14 +1,15 @@
 /* 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/. */
 
 #[macro_use] extern crate bitflags;
 #[macro_use] extern crate cssparser;
+#[macro_use] extern crate log;
 #[macro_use] extern crate matches;
 extern crate fnv;
 extern crate phf;
 extern crate precomputed_hash;
 #[cfg(test)] #[macro_use] extern crate size_of_test;
 extern crate servo_arc;
 extern crate smallvec;
 
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -193,17 +193,17 @@ fn may_match<E>(hashes: &AncestorHashes,
 /// complex selector. A "relevant link" is the element being matched if it is a
 /// link or the nearest ancestor link.
 ///
 /// `matches_complex_selector` creates a new instance of this for each complex
 /// selector we try to match for an element. This is done because `is_visited`
 /// and `is_unvisited` are based on relevant link state of only the current
 /// complex selector being matched (not the global relevant link status for all
 /// selectors in `MatchingContext`).
-#[derive(PartialEq, Eq, Copy, Clone)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
 pub enum RelevantLinkStatus {
     /// Looking for a possible relevant link.  This is the initial mode when
     /// matching a selector.
     Looking,
     /// Not looking for a relevant link.  We transition to this mode if we
     /// encounter a sibiling combinator (since only ancestor combinators are
     /// allowed for this purpose).
     NotLooking,
@@ -220,18 +220,20 @@ impl Default for RelevantLinkStatus {
 impl RelevantLinkStatus {
     /// If we found the relevant link for this element, record that in the
     /// overall matching context for the element as a whole and stop looking for
     /// addtional links.
     fn examine_potential_link<E>(&self, element: &E, context: &mut MatchingContext)
                                  -> RelevantLinkStatus
         where E: Element,
     {
+        // If a relevant link was previously found, we no longer want to look
+        // for links.  Only the nearest ancestor link is considered relevant.
         if *self != RelevantLinkStatus::Looking {
-            return *self
+            return RelevantLinkStatus::NotLooking
         }
 
         if !element.is_link() {
             return *self
         }
 
         // We found a relevant link. Record this in the `MatchingContext`,
         // where we track whether one was found for _any_ selector (meaning
@@ -402,38 +404,41 @@ pub fn matches_complex_selector<E, F>(co
             }
             _ => panic!("Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"),
         }
     }
 
     match matches_complex_selector_internal(iter,
                                             element,
                                             context,
-                                            RelevantLinkStatus::Looking,
+                                            &mut RelevantLinkStatus::Looking,
                                             flags_setter) {
         SelectorMatchingResult::Matched => true,
         _ => false
     }
 }
 
 fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>,
                                            element: &E,
                                            context: &mut MatchingContext,
-                                           relevant_link: RelevantLinkStatus,
+                                           relevant_link: &mut RelevantLinkStatus,
                                            flags_setter: &mut F)
                                            -> SelectorMatchingResult
      where E: Element,
            F: FnMut(&E, ElementSelectorFlags),
 {
-    let mut relevant_link = relevant_link.examine_potential_link(element, context);
+    *relevant_link = relevant_link.examine_potential_link(element, context);
 
     let matches_all_simple_selectors = selector_iter.all(|simple| {
         matches_simple_selector(simple, element, context, &relevant_link, flags_setter)
     });
 
+    debug!("Matching for {:?}, simple selector {:?}, relevant link {:?}",
+           element, selector_iter, relevant_link);
+
     let combinator = selector_iter.next_sequence();
     let siblings = combinator.map_or(false, |c| c.is_sibling());
     if siblings {
         flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
     }
 
     if !matches_all_simple_selectors {
         return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
@@ -441,17 +446,17 @@ fn matches_complex_selector_internal<E, 
 
     match combinator {
         None => SelectorMatchingResult::Matched,
         Some(c) => {
             let (mut next_element, candidate_not_found) = match c {
                 Combinator::NextSibling | Combinator::LaterSibling => {
                     // Only ancestor combinators are allowed while looking for
                     // relevant links, so switch to not looking.
-                    relevant_link = RelevantLinkStatus::NotLooking;
+                    *relevant_link = RelevantLinkStatus::NotLooking;
                     (element.prev_sibling_element(),
                      SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
                 }
                 Combinator::Child | Combinator::Descendant => {
                     (element.parent_element(),
                      SelectorMatchingResult::NotMatchedGlobally)
                 }
                 Combinator::PseudoElement => {
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -448,16 +448,26 @@ impl<'a, Impl: SelectorImpl> Iterator fo
                 self.next_combinator = Some(c);
                 None
             },
             Some(x) => Some(x),
         }
     }
 }
 
+impl<'a, Impl: SelectorImpl> fmt::Debug for SelectorIter<'a, Impl> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let iter = self.iter.clone().rev();
+        for component in iter {
+            component.to_css(f)?
+        }
+        Ok(())
+    }
+}
+
 /// An iterator over all simple selectors belonging to ancestors.
 pub struct AncestorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>);
 impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> {
     /// Creates an AncestorIter. The passed-in iterator is assumed to point to
     /// the beginning of the child sequence, which will be skipped.
     fn new(inner: SelectorIter<'a, Impl>) -> Self {
         let mut result = AncestorIter(inner);
         result.skip_until_ancestor();
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -3,18 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
 //! between layout and style.
 
 use attr::{AttrSelectorOperation, NamespaceConstraint};
 use matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus};
 use parser::SelectorImpl;
+use std::fmt::Debug;
 
-pub trait Element: Sized {
+pub trait Element: Sized + Debug {
     type Impl: SelectorImpl;
 
     fn parent_element(&self) -> Option<Self>;
 
     /// The parent of a given pseudo-element, after matching a pseudo-element
     /// selector.
     ///
     /// This is guaranteed to be called in a pseudo-element.
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -24,16 +24,17 @@ use selectors::matching::{ElementSelecto
 use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector};
 use selectors::parser::{AncestorHashes, Combinator, Component};
 use selectors::parser::{Selector, SelectorAndHashes, SelectorIter, SelectorMethods};
 use selectors::visitor::SelectorVisitor;
 use smallvec::SmallVec;
 use std::cell::Cell;
 use std::clone::Clone;
 use std::cmp;
+use std::fmt;
 
 /// When the ElementState of an element (like IN_HOVER_STATE) changes,
 /// certain pseudo-classes (like :hover) may require us to restyle that
 /// element, its siblings, and/or its descendants. Similarly, when various
 /// attributes of an element change, we may also need to restyle things with
 /// id, class, and attribute selectors. Doing this conservatively is
 /// expensive, and so we use RestyleHints to short-circuit work we know is
 /// unnecessary.
@@ -602,16 +603,25 @@ impl<'a, E> ElementWrapper<'a, E>
 
         match snapshot.state() {
             Some(state) => state ^ self.element.get_state(),
             None => ElementState::empty(),
         }
     }
 }
 
+impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
+    where E: TElement,
+{
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // Ignore other fields for now, can change later if needed.
+        self.element.fmt(f)
+    }
+}
+
 #[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 {