mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
parent
8ae546f7ea
commit
e3a256803d
8 changed files with 197 additions and 55 deletions
|
@ -94,6 +94,16 @@ pub enum MatchingMode {
|
|||
ForStatelessPseudoElement,
|
||||
}
|
||||
|
||||
/// The mode to use when matching unvisited and visited links.
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum VisitedHandlingMode {
|
||||
/// All links are matched as if they are unvisted.
|
||||
AllLinksUnvisited,
|
||||
/// A element's "relevant link" is the element being matched if it is a link
|
||||
/// or the nearest ancestor link. The relevant link is matched as though it
|
||||
/// is visited, and all other links are matched as if they are unvisited.
|
||||
RelevantLinkVisited,
|
||||
}
|
||||
|
||||
/// Data associated with the matching process for a element. This context is
|
||||
/// used across many selectors for an element, so it's not appropriate for
|
||||
|
@ -106,6 +116,13 @@ pub struct MatchingContext<'a> {
|
|||
pub matching_mode: MatchingMode,
|
||||
/// The bloom filter used to fast-reject selectors.
|
||||
pub bloom_filter: Option<&'a BloomFilter>,
|
||||
/// Input that controls how matching for links is handled.
|
||||
pub visited_handling: VisitedHandlingMode,
|
||||
/// Output that records whether we encountered a "relevant link" while
|
||||
/// matching _any_ selector for this element. (This differs from
|
||||
/// `RelevantLinkStatus` which tracks the status for the _current_ selector
|
||||
/// only.)
|
||||
relevant_link_found: bool,
|
||||
}
|
||||
|
||||
impl<'a> MatchingContext<'a> {
|
||||
|
@ -118,6 +135,8 @@ impl<'a> MatchingContext<'a> {
|
|||
relations: StyleRelations::empty(),
|
||||
matching_mode: matching_mode,
|
||||
bloom_filter: bloom_filter,
|
||||
visited_handling: VisitedHandlingMode::AllLinksUnvisited,
|
||||
relevant_link_found: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +175,100 @@ fn may_match<E>(sel: &SelectorInner<E::Impl>,
|
|||
true
|
||||
}
|
||||
|
||||
/// Tracks whether we are currently looking for relevant links for a given
|
||||
/// 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)]
|
||||
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,
|
||||
/// Found a relevant link for the element being matched.
|
||||
Found,
|
||||
}
|
||||
|
||||
impl Default for RelevantLinkStatus {
|
||||
fn default() -> Self {
|
||||
RelevantLinkStatus::NotLooking
|
||||
}
|
||||
}
|
||||
|
||||
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 *self != RelevantLinkStatus::Looking {
|
||||
return *self
|
||||
}
|
||||
|
||||
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
|
||||
// this field might already be true from a previous selector).
|
||||
context.relevant_link_found = true;
|
||||
// Also return `Found` to update the relevant link status for _this_
|
||||
// specific selector's matching process.
|
||||
RelevantLinkStatus::Found
|
||||
}
|
||||
|
||||
/// Returns whether an element is considered visited for the purposes of
|
||||
/// matching. This is true only if the element is a link, an relevant link
|
||||
/// exists for the element, and the visited handling mode is set to accept
|
||||
/// relevant links as visited.
|
||||
pub fn is_visited<E>(&self, element: &E, context: &MatchingContext) -> bool
|
||||
where E: Element,
|
||||
{
|
||||
if !element.is_link() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Non-relevant links are always unvisited.
|
||||
if *self != RelevantLinkStatus::Found {
|
||||
return false
|
||||
}
|
||||
|
||||
context.visited_handling == VisitedHandlingMode::RelevantLinkVisited
|
||||
}
|
||||
|
||||
/// Returns whether an element is considered unvisited for the purposes of
|
||||
/// matching. Assuming the element is a link, this is always true for
|
||||
/// non-relevant links, since only relevant links can potentially be treated
|
||||
/// as visited. If this is a relevant link, then is it unvisited if the
|
||||
/// visited handling mode is set to treat all links as unvisted (including
|
||||
/// relevant links).
|
||||
pub fn is_unvisited<E>(&self, element: &E, context: &MatchingContext) -> bool
|
||||
where E: Element,
|
||||
{
|
||||
if !element.is_link() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Non-relevant links are always unvisited.
|
||||
if *self != RelevantLinkStatus::Found {
|
||||
return true
|
||||
}
|
||||
|
||||
context.visited_handling == VisitedHandlingMode::AllLinksUnvisited
|
||||
}
|
||||
}
|
||||
|
||||
/// A result of selector matching, includes 3 failure types,
|
||||
///
|
||||
/// NotMatchedAndRestartFromClosestLaterSibling
|
||||
|
@ -267,6 +380,7 @@ pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl
|
|||
match matches_complex_selector_internal(iter,
|
||||
element,
|
||||
context,
|
||||
RelevantLinkStatus::Looking,
|
||||
flags_setter) {
|
||||
SelectorMatchingResult::Matched => true,
|
||||
_ => false
|
||||
|
@ -276,13 +390,16 @@ pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl
|
|||
fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>,
|
||||
element: &E,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: RelevantLinkStatus,
|
||||
flags_setter: &mut F)
|
||||
-> SelectorMatchingResult
|
||||
where E: Element,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
let mut relevant_link = relevant_link.examine_potential_link(element, context);
|
||||
|
||||
let matches_all_simple_selectors = selector_iter.all(|simple| {
|
||||
matches_simple_selector(simple, element, context, flags_setter)
|
||||
matches_simple_selector(simple, element, context, &relevant_link, flags_setter)
|
||||
});
|
||||
|
||||
let combinator = selector_iter.next_sequence();
|
||||
|
@ -300,6 +417,9 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
|
|||
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;
|
||||
(element.prev_sibling_element(),
|
||||
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
|
||||
}
|
||||
|
@ -321,6 +441,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
|
|||
let result = matches_complex_selector_internal(selector_iter.clone(),
|
||||
&element,
|
||||
context,
|
||||
relevant_link,
|
||||
flags_setter);
|
||||
match (result, c) {
|
||||
// Return the status immediately.
|
||||
|
@ -365,6 +486,7 @@ fn matches_simple_selector<E, F>(
|
|||
selector: &Component<E::Impl>,
|
||||
element: &E,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
flags_setter: &mut F)
|
||||
-> bool
|
||||
where E: Element,
|
||||
|
@ -465,7 +587,7 @@ fn matches_simple_selector<E, F>(
|
|||
)
|
||||
}
|
||||
Component::NonTSPseudoClass(ref pc) => {
|
||||
element.match_non_ts_pseudo_class(pc, context, flags_setter)
|
||||
element.match_non_ts_pseudo_class(pc, context, relevant_link, flags_setter)
|
||||
}
|
||||
Component::FirstChild => {
|
||||
matches_first_child(element, flags_setter)
|
||||
|
@ -509,7 +631,7 @@ fn matches_simple_selector<E, F>(
|
|||
matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
|
||||
}
|
||||
Component::Negation(ref negated) => {
|
||||
!negated.iter().all(|ss| matches_simple_selector(ss, element, context, flags_setter))
|
||||
!negated.iter().all(|ss| matches_simple_selector(ss, element, context, relevant_link, flags_setter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue