servo: Merge #17570 - Implement :visited support for lazy pseudo-elements (from bzbarsky:visited-pseudos); r=jryans

<!-- 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=1364242

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- 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: ff19e69d2388725cc15784b12c897c755ab06b6b

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 3f8a4c1f743b5373d812bc451f4abdab9820c9cb
This commit is contained in:
Boris Zbarsky 2017-06-30 09:11:41 -07:00
Родитель a0fc47069d
Коммит 4fdbba3a10
5 изменённых файлов: 223 добавлений и 69 удалений

Просмотреть файл

@ -426,6 +426,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
&style_pseudo, &style_pseudo,
RuleInclusion::All, RuleInclusion::All,
data.styles.primary(), data.styles.primary(),
/* is_probe = */ false,
&ServoMetricsProvider) &ServoMetricsProvider)
.unwrap() .unwrap()
.clone() .clone()

Просмотреть файл

@ -184,7 +184,7 @@ impl Default for CascadeInputs {
impl CascadeInputs { impl CascadeInputs {
/// Construct inputs from previous cascade results, if any. /// Construct inputs from previous cascade results, if any.
fn new_from_style(style: &Arc<ComputedValues>) -> Self { pub fn new_from_style(style: &Arc<ComputedValues>) -> Self {
CascadeInputs { CascadeInputs {
rules: style.rules.clone(), rules: style.rules.clone(),
visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()), visited_rules: style.get_visited_style().and_then(|v| v.rules.clone()),
@ -204,6 +204,11 @@ impl CascadeInputs {
self.rules.as_mut() self.rules.as_mut()
} }
/// Gets a reference to the rule node, if any.
pub fn get_rules(&self) -> Option<&StrongRuleNode> {
self.rules.as_ref()
}
/// Gets a reference to the rule node. Panic if the element does not have /// Gets a reference to the rule node. Panic if the element does not have
/// rule node. /// rule node.
pub fn rules(&self) -> &StrongRuleNode { pub fn rules(&self) -> &StrongRuleNode {

Просмотреть файл

@ -151,7 +151,10 @@ impl CascadeVisitedMode {
fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode { fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode {
match *self { match *self {
CascadeVisitedMode::Unvisited => inputs.rules(), CascadeVisitedMode::Unvisited => inputs.rules(),
CascadeVisitedMode::Visited => inputs.visited_rules(), CascadeVisitedMode::Visited => match inputs.get_visited_rules() {
Some(rules) => rules,
None => inputs.rules(),
}
} }
} }
@ -166,7 +169,7 @@ impl CascadeVisitedMode {
/// Returns the computed values based on the cascade mode. In visited mode, /// Returns the computed values based on the cascade mode. In visited mode,
/// visited values are only returned if they already exist. If they don't, /// visited values are only returned if they already exist. If they don't,
/// we fallback to the regular, unvisited styles. /// we fallback to the regular, unvisited styles.
fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> { pub fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> {
if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() { if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() {
return values.visited_style(); return values.visited_style();
} }
@ -288,10 +291,35 @@ trait PrivateMatchMethods: TElement {
} }
} }
/// Get the ComputedValues (if any) for our inheritance parent.
fn get_inherited_style_and_parent(&self) -> ParentElementAndStyle<Self> {
let parent_el = self.inheritance_parent();
let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
let parent_style = parent_data.as_ref().map(|d| {
// Sometimes Gecko eagerly styles things without processing
// pending restyles first. In general we'd like to avoid this,
// but there can be good reasons (for example, needing to
// construct a frame for some small piece of newly-added
// content in order to do something specific with that frame,
// but not wanting to flush all of layout).
debug_assert!(cfg!(feature = "gecko") ||
parent_el.unwrap().has_current_styles(d));
d.styles.primary()
});
ParentElementAndStyle {
element: parent_el,
style: parent_style.cloned(),
}
}
/// A common path for the cascade used by both primary elements and eager /// A common path for the cascade used by both primary elements and eager
/// pseudo-elements after collecting the appropriate rules to use. /// pseudo-elements after collecting the appropriate rules to use.
/// ///
/// `primary_style` is expected to be Some for eager pseudo-elements. /// `primary_style` is expected to be Some for eager pseudo-elements.
///
/// `parent_info` is our style parent and its primary style, if
/// it's already been computed.
fn cascade_with_rules(&self, fn cascade_with_rules(&self,
shared_context: &SharedStyleContext, shared_context: &SharedStyleContext,
font_metrics_provider: &FontMetricsProvider, font_metrics_provider: &FontMetricsProvider,
@ -299,6 +327,7 @@ trait PrivateMatchMethods: TElement {
primary_style: Option<&Arc<ComputedValues>>, primary_style: Option<&Arc<ComputedValues>>,
cascade_target: CascadeTarget, cascade_target: CascadeTarget,
cascade_visited: CascadeVisitedMode, cascade_visited: CascadeVisitedMode,
parent_info: Option<&ParentElementAndStyle<Self>>,
visited_values_to_insert: Option<Arc<ComputedValues>>) visited_values_to_insert: Option<Arc<ComputedValues>>)
-> Arc<ComputedValues> { -> Arc<ComputedValues> {
let mut cascade_info = CascadeInfo::new(); let mut cascade_info = CascadeInfo::new();
@ -318,23 +347,18 @@ trait PrivateMatchMethods: TElement {
// Grab the inherited values. // Grab the inherited values.
let parent_el; let parent_el;
let parent_data; let element_and_style; // So parent_el and style_to_inherit_from are known live.
let style_to_inherit_from = match cascade_target { let style_to_inherit_from = match cascade_target {
CascadeTarget::Normal => { CascadeTarget::Normal => {
parent_el = self.inheritance_parent(); let info = match parent_info {
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data()); Some(element_and_style) => element_and_style,
let parent_style = parent_data.as_ref().map(|d| { None => {
// Sometimes Gecko eagerly styles things without processing element_and_style = self.get_inherited_style_and_parent();
// pending restyles first. In general we'd like to avoid this, &element_and_style
// but there can be good reasons (for example, needing to }
// construct a frame for some small piece of newly-added };
// content in order to do something specific with that frame, parent_el = info.element;
// but not wanting to flush all of layout). info.style.as_ref().map(|s| cascade_visited.values(s))
debug_assert!(cfg!(feature = "gecko") ||
parent_el.unwrap().has_current_styles(d));
d.styles.primary()
});
parent_style.map(|s| cascade_visited.values(s))
} }
CascadeTarget::EagerPseudo => { CascadeTarget::EagerPseudo => {
parent_el = Some(self.clone()); parent_el = Some(self.clone());
@ -391,11 +415,15 @@ trait PrivateMatchMethods: TElement {
/// pseudo-elements. /// pseudo-elements.
/// ///
/// `primary_style` is expected to be Some for eager pseudo-elements. /// `primary_style` is expected to be Some for eager pseudo-elements.
///
/// `parent_info` is our style parent and its primary style, if
/// it's already been computed.
fn cascade_internal(&self, fn cascade_internal(&self,
context: &StyleContext<Self>, context: &StyleContext<Self>,
primary_style: Option<&Arc<ComputedValues>>, primary_style: Option<&Arc<ComputedValues>>,
primary_inputs: &CascadeInputs, primary_inputs: &CascadeInputs,
eager_pseudo_inputs: Option<&CascadeInputs>, eager_pseudo_inputs: Option<&CascadeInputs>,
parent_info: Option<&ParentElementAndStyle<Self>>,
cascade_visited: CascadeVisitedMode) cascade_visited: CascadeVisitedMode)
-> Arc<ComputedValues> { -> Arc<ComputedValues> {
if let Some(pseudo) = self.implemented_pseudo_element() { if let Some(pseudo) = self.implemented_pseudo_element() {
@ -459,15 +487,19 @@ trait PrivateMatchMethods: TElement {
primary_style, primary_style,
cascade_target, cascade_target,
cascade_visited, cascade_visited,
parent_info,
visited_values_to_insert) visited_values_to_insert)
} }
/// Computes values and damage for the primary style of an element, setting /// Computes values and damage for the primary style of an element, setting
/// them on the ElementData. /// them on the ElementData.
///
/// `parent_info` is our style parent and its primary style.
fn cascade_primary(&self, fn cascade_primary(&self,
context: &mut StyleContext<Self>, context: &mut StyleContext<Self>,
data: &mut ElementData, data: &mut ElementData,
important_rules_changed: bool, important_rules_changed: bool,
parent_info: &ParentElementAndStyle<Self>,
cascade_visited: CascadeVisitedMode) cascade_visited: CascadeVisitedMode)
-> ChildCascadeRequirement { -> ChildCascadeRequirement {
debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited); debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited);
@ -485,7 +517,10 @@ trait PrivateMatchMethods: TElement {
// visited case. This early return is especially important for the // visited case. This early return is especially important for the
// `cascade_primary_and_pseudos` path since we rely on the state of // `cascade_primary_and_pseudos` path since we rely on the state of
// some previous matching run. // some previous matching run.
if !cascade_visited.has_rules(primary_inputs) { //
// Note that we cannot take this early return if our parent has
// visited style, because then we too have visited style.
if !cascade_visited.has_rules(primary_inputs) && !parent_info.has_visited_style() {
return ChildCascadeRequirement::CanSkipCascade return ChildCascadeRequirement::CanSkipCascade
} }
@ -494,6 +529,7 @@ trait PrivateMatchMethods: TElement {
None, None,
primary_inputs, primary_inputs,
None, None,
/* parent_info = */ None,
cascade_visited) cascade_visited)
}; };
@ -582,6 +618,7 @@ trait PrivateMatchMethods: TElement {
data.styles.get_primary(), data.styles.get_primary(),
primary_inputs, primary_inputs,
Some(pseudo_inputs), Some(pseudo_inputs),
/* parent_info = */ None,
cascade_visited) cascade_visited)
}; };
@ -625,6 +662,7 @@ trait PrivateMatchMethods: TElement {
Some(primary_style), Some(primary_style),
CascadeTarget::Normal, CascadeTarget::Normal,
CascadeVisitedMode::Unvisited, CascadeVisitedMode::Unvisited,
/* parent_info = */ None,
None)) None))
} }
@ -877,6 +915,23 @@ trait PrivateMatchMethods: TElement {
impl<E: TElement> PrivateMatchMethods for E {} impl<E: TElement> PrivateMatchMethods for E {}
/// A struct that holds an element we inherit from and its ComputedValues.
#[derive(Debug)]
struct ParentElementAndStyle<E: TElement> {
/// Our style parent element.
element: Option<E>,
/// Element's primary ComputedValues. Not a borrow because we can't prove
/// that the thing hanging off element won't change while we're passing this
/// struct around.
style: Option<Arc<ComputedValues>>,
}
impl<E: TElement> ParentElementAndStyle<E> {
fn has_visited_style(&self) -> bool {
self.style.as_ref().map_or(false, |v| { v.get_visited_style().is_some() })
}
}
/// Collects the outputs of the primary matching process, including the rule /// Collects the outputs of the primary matching process, including the rule
/// node and other associated data. /// node and other associated data.
#[derive(Debug)] #[derive(Debug)]
@ -944,13 +999,21 @@ pub trait MatchMethods : TElement {
let relevant_link_found = primary_results.relevant_link_found; let relevant_link_found = primary_results.relevant_link_found;
if relevant_link_found { if relevant_link_found {
self.match_primary(context, data, VisitedHandlingMode::RelevantLinkVisited); self.match_primary(context, data, VisitedHandlingMode::RelevantLinkVisited);
}
// Even if there is no relevant link, we need to cascade visited styles
// if our parent has visited styles.
let parent_and_styles = self.get_inherited_style_and_parent();
if relevant_link_found || parent_and_styles.has_visited_style() {
self.cascade_primary(context, data, important_rules_changed, self.cascade_primary(context, data, important_rules_changed,
&parent_and_styles,
CascadeVisitedMode::Visited); CascadeVisitedMode::Visited);
} }
// Cascade properties and compute primary values. // Cascade properties and compute primary values.
let child_cascade_requirement = let child_cascade_requirement =
self.cascade_primary(context, data, important_rules_changed, self.cascade_primary(context, data, important_rules_changed,
&parent_and_styles,
CascadeVisitedMode::Unvisited); CascadeVisitedMode::Unvisited);
// Match and cascade eager pseudo-elements. // Match and cascade eager pseudo-elements.
@ -1017,10 +1080,13 @@ pub trait MatchMethods : TElement {
// visited ComputedValues are placed within the regular ComputedValues, // visited ComputedValues are placed within the regular ComputedValues,
// which is immutable after the cascade. If there aren't any visited // which is immutable after the cascade. If there aren't any visited
// rules, these calls will return without cascading. // rules, these calls will return without cascading.
let parent_and_styles = self.get_inherited_style_and_parent();
self.cascade_primary(context, &mut data, important_rules_changed, self.cascade_primary(context, &mut data, important_rules_changed,
&parent_and_styles,
CascadeVisitedMode::Visited); CascadeVisitedMode::Visited);
let child_cascade_requirement = let child_cascade_requirement =
self.cascade_primary(context, &mut data, important_rules_changed, self.cascade_primary(context, &mut data, important_rules_changed,
&parent_and_styles,
CascadeVisitedMode::Unvisited); CascadeVisitedMode::Unvisited);
self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Visited); self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Visited);
self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Unvisited); self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Unvisited);
@ -1211,8 +1277,10 @@ pub trait MatchMethods : TElement {
// Compute rule nodes for eagerly-cascaded pseudo-elements. // Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false; let mut matches_different_pseudos = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| { SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
// For pseudo-elements, we only try to match visited rules if there // For eager pseudo-elements, we only try to match visited rules if
// are also unvisited rules. (This matches Gecko's behavior.) // there are also unvisited rules. (This matches Gecko's behavior
// for probing pseudo-elements, and for eager pseudo-elements Gecko
// does not try to resolve style if the probe says there isn't any.)
if visited_handling == VisitedHandlingMode::RelevantLinkVisited && if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
!context.cascade_inputs().pseudos.has(&pseudo) { !context.cascade_inputs().pseudos.has(&pseudo) {
return return
@ -1587,6 +1655,7 @@ pub trait MatchMethods : TElement {
Some(primary_style), Some(primary_style),
CascadeTarget::Normal, CascadeTarget::Normal,
CascadeVisitedMode::Unvisited, CascadeVisitedMode::Unvisited,
/* parent_info = */ None,
None) None)
} }

Просмотреть файл

@ -7,7 +7,7 @@
use {Atom, LocalName, Namespace}; use {Atom, LocalName, Namespace};
use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList}; use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
use bit_vec::BitVec; use bit_vec::BitVec;
use context::QuirksMode; use context::{CascadeInputs, QuirksMode};
use dom::TElement; use dom::TElement;
use element_state::ElementState; use element_state::ElementState;
use error_reporting::create_error_reporter; use error_reporting::create_error_reporter;
@ -16,6 +16,7 @@ use font_metrics::FontMetricsProvider;
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion}; use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
use invalidation::element::invalidation_map::InvalidationMap; use invalidation::element::invalidation_map::InvalidationMap;
use invalidation::media_queries::EffectiveMediaQueryResults; use invalidation::media_queries::EffectiveMediaQueryResults;
use matching::CascadeVisitedMode;
use media_queries::Device; use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues}; use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock}; use properties::{AnimationRules, PropertyDeclarationBlock};
@ -27,7 +28,7 @@ use selector_parser::{SelectorImpl, PseudoElement};
use selectors::attr::NamespaceConstraint; use selectors::attr::NamespaceConstraint;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS; use selectors::matching::{VisitedHandlingMode, AFFECTED_BY_PRESENTATIONAL_HINTS};
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorAndHashes}; use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorAndHashes};
use selectors::parser::{SelectorIter, SelectorMethods}; use selectors::parser::{SelectorIter, SelectorMethods};
use selectors::sink::Push; use selectors::sink::Push;
@ -679,47 +680,90 @@ impl Stylist {
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
parent_style: &ComputedValues, parent_style: &Arc<ComputedValues>,
is_probe: bool,
font_metrics: &FontMetricsProvider) font_metrics: &FontMetricsProvider)
-> Option<Arc<ComputedValues>> -> Option<Arc<ComputedValues>>
where E: TElement, where E: TElement,
{ {
let rule_node = let cascade_inputs =
self.lazy_pseudo_rules(guards, element, pseudo, rule_inclusion); self.lazy_pseudo_rules(guards, element, pseudo, is_probe, rule_inclusion);
self.compute_pseudo_element_style_with_rulenode(rule_node.as_ref(), self.compute_pseudo_element_style_with_inputs(&cascade_inputs,
guards, guards,
parent_style, parent_style,
font_metrics) font_metrics)
} }
/// Computes a pseudo-element style lazily using the given rulenode. This /// Computes a pseudo-element style lazily using the given CascadeInputs.
/// can be used for truly lazy pseudo-elements or to avoid redoing selector /// This can be used for truly lazy pseudo-elements or to avoid redoing
/// matching for eager pseudo-elements when we need to recompute their style /// selector matching for eager pseudo-elements when we need to recompute
/// with a new parent style. /// their style with a new parent style.
pub fn compute_pseudo_element_style_with_rulenode(&self, pub fn compute_pseudo_element_style_with_inputs(&self,
rule_node: Option<&StrongRuleNode>, inputs: &CascadeInputs,
guards: &StylesheetGuards, guards: &StylesheetGuards,
parent_style: &ComputedValues, parent_style: &Arc<ComputedValues>,
font_metrics: &FontMetricsProvider) font_metrics: &FontMetricsProvider)
-> Option<Arc<ComputedValues>> -> Option<Arc<ComputedValues>>
{ {
let rule_node = match rule_node { // We may have only visited rules in cases when we are actually
Some(rule_node) => rule_node, // resolving, not probing, pseudo-element style.
None => return None if !inputs.has_rules() && !inputs.has_visited_rules() {
return None
}
// We need to compute visited values if we have visited rules or if our
// parent has visited values.
let visited_values = if inputs.has_visited_rules() || parent_style.get_visited_style().is_some() {
// Slightly annoying: we know that inputs has either rules or
// visited rules, but we can't do inputs.rules() up front because
// maybe it just has visited rules, so can't unwrap_or.
let rule_node = match inputs.get_visited_rules() {
Some(rules) => rules,
None => inputs.rules()
};
// We want to use the visited bits (if any) from our parent style as
// our parent.
let mode = CascadeVisitedMode::Visited;
let inherited_style = mode.values(parent_style);
let computed =
properties::cascade(&self.device,
rule_node,
guards,
Some(inherited_style),
Some(inherited_style),
None,
None,
&create_error_reporter(),
font_metrics,
CascadeFlags::empty(),
self.quirks_mode);
Some(Arc::new(computed))
} else {
None
};
// We may not have non-visited rules, if we only had visited ones. In
// that case we want to use the root rulenode for our non-visited rules.
let root;
let rules = if let Some(rules) = inputs.get_rules() {
rules
} else {
root = self.rule_tree.root();
&root
}; };
// Read the comment on `precomputed_values_for_pseudo` to see why it's // Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here // difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the // (tl;dr: It doesn't apply for replaced elements and such, but the
// computed value is still "contents"). // computed value is still "contents").
// Bug 1364242: We need to add visited support for lazy pseudos
let computed = let computed =
properties::cascade(&self.device, properties::cascade(&self.device,
rule_node, rules,
guards, guards,
Some(parent_style), Some(parent_style),
Some(parent_style), Some(parent_style),
None, visited_values,
None, None,
&create_error_reporter(), &create_error_reporter(),
font_metrics, font_metrics,
@ -729,7 +773,7 @@ impl Stylist {
Some(Arc::new(computed)) Some(Arc::new(computed))
} }
/// Computes the rule node for a lazily-cascaded pseudo-element. /// Computes the cascade inputs for a lazily-cascaded pseudo-element.
/// ///
/// See the documentation on lazy pseudo-elements in /// See the documentation on lazy pseudo-elements in
/// docs/components/style.md /// docs/components/style.md
@ -737,14 +781,15 @@ impl Stylist {
guards: &StylesheetGuards, guards: &StylesheetGuards,
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
is_probe: bool,
rule_inclusion: RuleInclusion) rule_inclusion: RuleInclusion)
-> Option<StrongRuleNode> -> CascadeInputs
where E: TElement where E: TElement
{ {
let pseudo = pseudo.canonical(); let pseudo = pseudo.canonical();
debug_assert!(pseudo.is_lazy()); debug_assert!(pseudo.is_lazy());
if self.pseudos_map.get(&pseudo).is_none() { if self.pseudos_map.get(&pseudo).is_none() {
return None return CascadeInputs::default()
} }
// Apply the selector flags. We should be in sequential mode // Apply the selector flags. We should be in sequential mode
@ -777,7 +822,7 @@ impl Stylist {
} }
}; };
// Bug 1364242: We need to add visited support for lazy pseudos let mut inputs = CascadeInputs::default();
let mut declarations = ApplicableDeclarationList::new(); let mut declarations = ApplicableDeclarationList::new();
let mut matching_context = let mut matching_context =
MatchingContext::new(MatchingMode::ForStatelessPseudoElement, MatchingContext::new(MatchingMode::ForStatelessPseudoElement,
@ -792,19 +837,52 @@ impl Stylist {
&mut declarations, &mut declarations,
&mut matching_context, &mut matching_context,
&mut set_selector_flags); &mut set_selector_flags);
if declarations.is_empty() {
return None
}
let rule_node = if !declarations.is_empty() {
self.rule_tree.insert_ordered_rules_with_important( let rule_node = self.rule_tree.insert_ordered_rules_with_important(
declarations.into_iter().map(|a| a.order_and_level()), declarations.into_iter().map(|a| a.order_and_level()),
guards); guards);
if rule_node == self.rule_tree.root() { if rule_node != self.rule_tree.root() {
None inputs.set_rules(VisitedHandlingMode::AllLinksUnvisited,
} else { rule_node);
Some(rule_node) }
};
if is_probe && !inputs.has_rules() {
// When probing, don't compute visited styles if we have no
// unvisited styles.
return inputs;
} }
if matching_context.relevant_link_found {
let mut declarations = ApplicableDeclarationList::new();
let mut matching_context =
MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
None,
VisitedHandlingMode::RelevantLinkVisited,
self.quirks_mode);
self.push_applicable_declarations(element,
Some(&pseudo),
None,
None,
AnimationRules(None, None),
rule_inclusion,
&mut declarations,
&mut matching_context,
&mut set_selector_flags);
if !declarations.is_empty() {
let rule_node =
self.rule_tree.insert_ordered_rules_with_important(
declarations.into_iter().map(|a| a.order_and_level()),
guards);
if rule_node != self.rule_tree.root() {
inputs.set_rules(VisitedHandlingMode::RelevantLinkVisited,
rule_node);
}
}
}
inputs
} }
/// Set a given device, which may change the styles that apply to the /// Set a given device, which may change the styles that apply to the

Просмотреть файл

@ -10,7 +10,7 @@ use selectors::Element;
use std::env; use std::env;
use std::fmt::Write; use std::fmt::Write;
use std::ptr; use std::ptr;
use style::context::{QuirksMode, SharedStyleContext, StyleContext}; use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
use style::context::ThreadLocalStyleContext; use style::context::ThreadLocalStyleContext;
use style::data::{ElementData, ElementStyles, RestyleData}; use style::data::{ElementData, ElementStyles, RestyleData};
use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants}; use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants};
@ -1479,7 +1479,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
&pseudo, &pseudo,
RuleInclusion::All, RuleInclusion::All,
&data.styles, &data.styles,
ComputedValues::arc_from_borrowed(&inherited_style).map(|v| v.as_ref()), ComputedValues::arc_from_borrowed(&inherited_style),
&*doc_data, &*doc_data,
is_probe is_probe
); );
@ -1533,7 +1533,7 @@ fn get_pseudo_style(
pseudo: &PseudoElement, pseudo: &PseudoElement,
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
styles: &ElementStyles, styles: &ElementStyles,
inherited_styles: Option<&ComputedValues>, inherited_styles: Option<&Arc<ComputedValues>>,
doc_data: &PerDocumentStyleDataImpl, doc_data: &PerDocumentStyleDataImpl,
is_probe: bool, is_probe: bool,
) -> Option<Arc<ComputedValues>> { ) -> Option<Arc<ComputedValues>> {
@ -1550,20 +1550,20 @@ fn get_pseudo_style(
inherited_styles.unwrap_or(styles.primary()); inherited_styles.unwrap_or(styles.primary());
let guards = StylesheetGuards::same(guard); let guards = StylesheetGuards::same(guard);
let metrics = get_metrics_provider_for_product(); let metrics = get_metrics_provider_for_product();
let rule_node = match styles.pseudos.get(&pseudo) { let inputs = match styles.pseudos.get(&pseudo) {
Some(styles) => styles.rules.as_ref(), Some(styles) => CascadeInputs::new_from_style(styles),
None => None, None => return None,
}; };
doc_data.stylist doc_data.stylist
.compute_pseudo_element_style_with_rulenode( .compute_pseudo_element_style_with_inputs(
rule_node, &inputs,
&guards, &guards,
inherited_styles, inherited_styles,
&metrics) &metrics)
}, },
_ => { _ => {
debug_assert!(inherited_styles.is_none() || debug_assert!(inherited_styles.is_none() ||
ptr::eq(inherited_styles.unwrap(), ptr::eq(&**inherited_styles.unwrap(),
&**styles.primary())); &**styles.primary()));
styles.pseudos.get(&pseudo).cloned() styles.pseudos.get(&pseudo).cloned()
}, },
@ -1572,7 +1572,7 @@ fn get_pseudo_style(
PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"), PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
PseudoElementCascadeType::Lazy => { PseudoElementCascadeType::Lazy => {
debug_assert!(inherited_styles.is_none() || debug_assert!(inherited_styles.is_none() ||
ptr::eq(inherited_styles.unwrap(), ptr::eq(&**inherited_styles.unwrap(),
&**styles.primary())); &**styles.primary()));
let base = if pseudo.inherits_from_default_values() { let base = if pseudo.inherits_from_default_values() {
doc_data.default_computed_values() doc_data.default_computed_values()
@ -1588,6 +1588,7 @@ fn get_pseudo_style(
&pseudo, &pseudo,
rule_inclusion, rule_inclusion,
base, base,
is_probe,
&metrics) &metrics)
}, },
}; };