mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
selectors: Simplify :visited by only using the "is inside link" information.
Right now we go through a lot of hoops to see if we ever see a relevant link. However, that information is not needed: if the element is a link, we'll always need to compute its visited style because its its own relevant link. If the element inherits from a link, we need to also compute the visited style anyway. So the "has a relevant link been found" is pretty useless when we know what are we inheriting from. The branches at the beginning of matches_complex_selector_internal were affecting performance, and there are no good reasons to keep them. I've verified that this passes all the visited tests in mozilla central, and that the test-cases too-flaky to be landed still pass.
This commit is contained in:
parent
e4bb3a102e
commit
3119db724a
10 changed files with 128 additions and 208 deletions
|
@ -76,7 +76,7 @@ use selector_parser::{AttrValue, Direction, PseudoClassStringArg};
|
|||
use selectors::{Element, OpaqueElement};
|
||||
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
||||
use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode};
|
||||
use selectors::matching::VisitedHandlingMode;
|
||||
use selectors::sink::Push;
|
||||
use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
|
||||
use shared_lock::Locked;
|
||||
|
@ -1977,7 +1977,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
context: &mut MatchingContext<Self::Impl>,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
visited_handling: VisitedHandlingMode,
|
||||
flags_setter: &mut F,
|
||||
) -> bool
|
||||
where
|
||||
|
@ -2037,8 +2037,12 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
self.get_state().intersects(pseudo_class.state_flag())
|
||||
},
|
||||
NonTSPseudoClass::AnyLink => self.is_link(),
|
||||
NonTSPseudoClass::Link => relevant_link.is_unvisited(self, context),
|
||||
NonTSPseudoClass::Visited => relevant_link.is_visited(self, context),
|
||||
NonTSPseudoClass::Link => {
|
||||
self.is_link() && visited_handling.matches_unvisited()
|
||||
}
|
||||
NonTSPseudoClass::Visited => {
|
||||
self.is_link() && visited_handling.matches_visited()
|
||||
}
|
||||
NonTSPseudoClass::MozFirstNode => {
|
||||
flags_setter(self, ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
|
||||
let mut elem = self.as_node();
|
||||
|
|
|
@ -406,9 +406,8 @@ where
|
|||
&mut self,
|
||||
visited_handling_mode: VisitedHandlingMode,
|
||||
dependency: &Dependency,
|
||||
relevant_link_found: &mut bool,
|
||||
) -> bool {
|
||||
let (matches_now, relevant_link_found_now) = {
|
||||
let matches_now = {
|
||||
let mut context = MatchingContext::new_for_visited(
|
||||
MatchingMode::Normal,
|
||||
None,
|
||||
|
@ -426,10 +425,10 @@ where
|
|||
&mut |_, _| {},
|
||||
);
|
||||
|
||||
(matches_now, context.relevant_link_found)
|
||||
matches_now
|
||||
};
|
||||
|
||||
let (matched_then, relevant_link_found_then) = {
|
||||
let matched_then = {
|
||||
let mut context = MatchingContext::new_for_visited(
|
||||
MatchingMode::Normal,
|
||||
None,
|
||||
|
@ -447,15 +446,12 @@ where
|
|||
&mut |_, _| {},
|
||||
);
|
||||
|
||||
(matched_then, context.relevant_link_found)
|
||||
matched_then
|
||||
};
|
||||
|
||||
*relevant_link_found = relevant_link_found_now;
|
||||
|
||||
// Check for mismatches in both the match result and also the status
|
||||
// of whether a relevant link was found.
|
||||
matched_then != matches_now ||
|
||||
relevant_link_found_now != relevant_link_found_then
|
||||
matched_then != matches_now
|
||||
}
|
||||
|
||||
fn scan_dependency(
|
||||
|
@ -464,20 +460,18 @@ where
|
|||
is_visited_dependent: VisitedDependent,
|
||||
) {
|
||||
debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {:?})",
|
||||
self.element,
|
||||
dependency,
|
||||
is_visited_dependent);
|
||||
self.element,
|
||||
dependency,
|
||||
is_visited_dependent,
|
||||
);
|
||||
|
||||
if !self.dependency_may_be_relevant(dependency) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut relevant_link_found = false;
|
||||
|
||||
let should_account_for_dependency = self.check_dependency(
|
||||
VisitedHandlingMode::AllLinksUnvisited,
|
||||
VisitedHandlingMode::AllLinksVisitedAndUnvisited,
|
||||
dependency,
|
||||
&mut relevant_link_found,
|
||||
);
|
||||
|
||||
if should_account_for_dependency {
|
||||
|
@ -500,11 +494,12 @@ where
|
|||
//
|
||||
// NOTE: This thing is actually untested because testing it is flaky,
|
||||
// see the tests that were added and then backed out in bug 1328509.
|
||||
if is_visited_dependent == VisitedDependent::Yes && relevant_link_found {
|
||||
if is_visited_dependent == VisitedDependent::Yes &&
|
||||
self.element.is_link()
|
||||
{
|
||||
let should_account_for_dependency = self.check_dependency(
|
||||
VisitedHandlingMode::RelevantLinkVisited,
|
||||
dependency,
|
||||
&mut false,
|
||||
);
|
||||
|
||||
if should_account_for_dependency {
|
||||
|
|
|
@ -11,8 +11,8 @@ use element_state::ElementState;
|
|||
use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
|
||||
use selectors::{Element, OpaqueElement};
|
||||
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
|
||||
use selectors::context::VisitedHandlingMode;
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
||||
use selectors::matching::RelevantLinkStatus;
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
|
||||
|
@ -153,7 +153,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
context: &mut MatchingContext<Self::Impl>,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
visited_handling: VisitedHandlingMode,
|
||||
_setter: &mut F,
|
||||
) -> bool
|
||||
where
|
||||
|
@ -196,14 +196,16 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
return state.contains(selector_flag);
|
||||
}
|
||||
|
||||
// For :link and :visited, we don't actually want to test the element
|
||||
// state directly. Instead, we use the `relevant_link` to determine if
|
||||
// they match.
|
||||
// For :link and :visited, we don't actually want to test the
|
||||
// element state directly.
|
||||
//
|
||||
// Instead, we use the `visited_handling` to determine if they
|
||||
// match.
|
||||
NonTSPseudoClass::Link => {
|
||||
return relevant_link.is_unvisited(self, context);
|
||||
return self.is_link() && visited_handling.matches_unvisited()
|
||||
}
|
||||
NonTSPseudoClass::Visited => {
|
||||
return relevant_link.is_visited(self, context);
|
||||
return self.is_link() && visited_handling.matches_visited()
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
|
@ -238,7 +240,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
return self.element.match_non_ts_pseudo_class(
|
||||
pseudo_class,
|
||||
context,
|
||||
relevant_link,
|
||||
visited_handling,
|
||||
&mut |_, _| {},
|
||||
)
|
||||
}
|
||||
|
@ -248,7 +250,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
self.element.match_non_ts_pseudo_class(
|
||||
pseudo_class,
|
||||
context,
|
||||
relevant_link,
|
||||
visited_handling,
|
||||
&mut |_, _| {},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ where
|
|||
|
||||
struct MatchingResults {
|
||||
rule_node: StrongRuleNode,
|
||||
relevant_link_found: bool,
|
||||
}
|
||||
|
||||
/// A style returned from the resolver machinery.
|
||||
|
@ -158,15 +157,17 @@ where
|
|||
let primary_results =
|
||||
self.match_primary(VisitedHandlingMode::AllLinksUnvisited);
|
||||
|
||||
let relevant_link_found = primary_results.relevant_link_found;
|
||||
let inside_link =
|
||||
parent_style.map_or(false, |s| s.visited_style().is_some());
|
||||
|
||||
let visited_rules = if relevant_link_found {
|
||||
let visited_matching_results =
|
||||
self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
|
||||
Some(visited_matching_results.rule_node)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let visited_rules =
|
||||
if inside_link || self.element.is_link() {
|
||||
let visited_matching_results =
|
||||
self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
|
||||
Some(visited_matching_results.rule_node)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.cascade_primary_style(
|
||||
CascadeInputs {
|
||||
|
@ -446,7 +447,6 @@ where
|
|||
// FIXME(emilio): This is a hack for animations, and should go away.
|
||||
self.element.unset_dirty_style_attribute();
|
||||
|
||||
let relevant_link_found = matching_context.relevant_link_found;
|
||||
let rule_node = stylist.rule_tree().compute_rule_node(
|
||||
&mut applicable_declarations,
|
||||
&self.context.shared.guards
|
||||
|
@ -462,7 +462,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
MatchingResults { rule_node, relevant_link_found }
|
||||
MatchingResults { rule_node, }
|
||||
}
|
||||
|
||||
fn match_pseudo(
|
||||
|
|
|
@ -825,15 +825,16 @@ impl Stylist {
|
|||
where
|
||||
E: TElement,
|
||||
{
|
||||
let cascade_inputs =
|
||||
self.lazy_pseudo_rules(
|
||||
guards,
|
||||
element,
|
||||
pseudo,
|
||||
is_probe,
|
||||
rule_inclusion,
|
||||
matching_fn
|
||||
);
|
||||
let cascade_inputs = self.lazy_pseudo_rules(
|
||||
guards,
|
||||
element,
|
||||
parent_style,
|
||||
pseudo,
|
||||
is_probe,
|
||||
rule_inclusion,
|
||||
matching_fn
|
||||
);
|
||||
|
||||
self.compute_pseudo_element_style_with_inputs(
|
||||
&cascade_inputs,
|
||||
pseudo,
|
||||
|
@ -981,10 +982,11 @@ impl Stylist {
|
|||
///
|
||||
/// See the documentation on lazy pseudo-elements in
|
||||
/// docs/components/style.md
|
||||
pub fn lazy_pseudo_rules<E>(
|
||||
fn lazy_pseudo_rules<E>(
|
||||
&self,
|
||||
guards: &StylesheetGuards,
|
||||
element: &E,
|
||||
parent_style: &ComputedValues,
|
||||
pseudo: &PseudoElement,
|
||||
is_probe: bool,
|
||||
rule_inclusion: RuleInclusion,
|
||||
|
@ -1028,13 +1030,13 @@ impl Stylist {
|
|||
|
||||
let mut inputs = CascadeInputs::default();
|
||||
let mut declarations = ApplicableDeclarationList::new();
|
||||
let mut matching_context =
|
||||
MatchingContext::new(
|
||||
MatchingMode::ForStatelessPseudoElement,
|
||||
None,
|
||||
None,
|
||||
self.quirks_mode,
|
||||
);
|
||||
let mut matching_context = MatchingContext::new(
|
||||
MatchingMode::ForStatelessPseudoElement,
|
||||
None,
|
||||
None,
|
||||
self.quirks_mode,
|
||||
);
|
||||
|
||||
matching_context.pseudo_element_matching_fn = matching_fn;
|
||||
|
||||
self.push_applicable_declarations(
|
||||
|
@ -1062,7 +1064,7 @@ impl Stylist {
|
|||
return inputs;
|
||||
}
|
||||
|
||||
if matching_context.relevant_link_found {
|
||||
if parent_style.visited_style().is_some() {
|
||||
let mut declarations = ApplicableDeclarationList::new();
|
||||
let mut matching_context =
|
||||
MatchingContext::new_for_visited(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue