Look for relevant links while matching

Adjust the matching process to look for a "relevant link" while matching.  A
"relevant link" is the element being matched if it is a link or the nearest
ancestor link.

Matching for links now depends on the `VisitedHandlingMode`, which determines
whether all links match as if they are unvisited (the default) or if the
relevant link matches as visited (and all others remain unvisited).

If a relevant link is ever found for any selector, track this as part of the
`MatchingContext` object.  This is used in the next patch to determine if an
additional match and cascade should be performed to compute the styles when
visited.

MozReview-Commit-ID: 3xUbRo7vpuD
This commit is contained in:
J. Ryan Stinnett 2017-05-15 10:14:49 -05:00
parent 8ae546f7ea
commit e3a256803d
8 changed files with 197 additions and 55 deletions

View file

@ -65,7 +65,7 @@ use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::ElementExt;
use selectors::Element;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, RelevantLinkStatus};
use shared_lock::Locked;
use sink::Push;
use std::cell::RefCell;
@ -1236,6 +1236,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
context: &mut MatchingContext,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
@ -1243,8 +1244,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
use selectors::matching::*;
match *pseudo_class {
NonTSPseudoClass::AnyLink |
NonTSPseudoClass::Link |
NonTSPseudoClass::Visited |
NonTSPseudoClass::Active |
NonTSPseudoClass::Focus |
NonTSPseudoClass::Hover |
@ -1293,6 +1292,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
// here, to handle `:any-link` correctly.
self.get_state().intersects(pseudo_class.state_flag())
},
NonTSPseudoClass::Link => relevant_link.is_unvisited(self, context),
NonTSPseudoClass::Visited => relevant_link.is_visited(self, context),
NonTSPseudoClass::MozFirstNode => {
flags_setter(self, HAS_EDGE_CHILD_SELECTOR);
let mut elem = self.as_node();
@ -1369,6 +1370,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}
}
#[inline]
fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut context,
&RelevantLinkStatus::default(),
&mut |_, _| {})
}
fn get_id(&self) -> Option<Atom> {
if !self.has_id() {
return None;
@ -1420,14 +1430,6 @@ impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> {
}
impl<'le> ElementExt for GeckoElement<'le> {
#[inline]
fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut context,
&mut |_, _| {})
}
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) == 0

View file

@ -20,7 +20,7 @@ use selector_map::{SelectorMap, SelectorMapEntry};
use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
use selectors::Element;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, RelevantLinkStatus};
use selectors::matching::matches_selector;
use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods};
use selectors::visitor::SelectorVisitor;
@ -535,6 +535,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
context: &mut MatchingContext,
relevant_link: &RelevantLinkStatus,
_setter: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
@ -580,6 +581,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
if flag.is_empty() {
return self.element.match_non_ts_pseudo_class(pseudo_class,
context,
relevant_link,
&mut |_, _| {})
}
match self.snapshot().and_then(|s| s.state()) {
@ -587,6 +589,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
None => {
self.element.match_non_ts_pseudo_class(pseudo_class,
context,
relevant_link,
&mut |_, _| {})
}
}
@ -600,6 +603,14 @@ impl<'a, E> Element for ElementWrapper<'a, E>
self.element.match_pseudo_element(pseudo_element, context)
}
fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut context,
&RelevantLinkStatus::default(),
&mut |_, _| {})
}
fn parent_element(&self) -> Option<Self> {
self.element.parent_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))

View file

@ -103,9 +103,6 @@ pub enum PseudoElementCascadeType {
/// An extension to rust-selector's `Element` trait.
pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
/// Whether this element is a `link`.
fn is_link(&self) -> bool;
/// Whether this element should match user and author rules.
///
/// We use this for Native Anonymous Content in Gecko.

View file

@ -16,7 +16,6 @@ use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::Element;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{MatchingContext, MatchingMode};
use selectors::parser::SelectorMethods;
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
@ -601,13 +600,6 @@ impl ServoElementSnapshot {
}
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut context,
&mut |_, _| {})
}
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
true