Make sure to compute visited style if our parent has visited style.

Part 4 of the fix for Gecko bug 1364242: https://bugzilla.mozilla.org/show_bug.cgi?id=1364242
This commit is contained in:
Boris Zbarsky 2017-06-29 17:11:57 -07:00
parent 759038e687
commit 824139e615

View file

@ -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(),
}
} }
} }
@ -314,6 +317,9 @@ trait PrivateMatchMethods: TElement {
/// 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,
@ -321,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();
@ -343,9 +350,15 @@ trait PrivateMatchMethods: TElement {
let element_and_style; // So parent_el and style_to_inherit_from are known live. 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 => {
let info = match parent_info {
Some(element_and_style) => element_and_style,
None => {
element_and_style = self.get_inherited_style_and_parent(); element_and_style = self.get_inherited_style_and_parent();
parent_el = element_and_style.element; &element_and_style
element_and_style.style.as_ref().map(|s| cascade_visited.values(s)) }
};
parent_el = info.element;
info.style.as_ref().map(|s| cascade_visited.values(s))
} }
CascadeTarget::EagerPseudo => { CascadeTarget::EagerPseudo => {
parent_el = Some(self.clone()); parent_el = Some(self.clone());
@ -402,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() {
@ -470,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);
@ -496,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
} }
@ -505,6 +529,7 @@ trait PrivateMatchMethods: TElement {
None, None,
primary_inputs, primary_inputs,
None, None,
/* parent_info = */ None,
cascade_visited) cascade_visited)
}; };
@ -593,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)
}; };
@ -636,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))
} }
@ -899,6 +926,12 @@ struct ParentElementAndStyle<E: TElement> {
style: Option<Arc<ComputedValues>>, 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)]
@ -966,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.
@ -1039,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);
@ -1611,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)
} }