diff --git a/components/style/matching.rs b/components/style/matching.rs index e1390d9bd889..49c329d325c6 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -151,7 +151,10 @@ impl CascadeVisitedMode { fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode { match *self { 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. /// /// `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, shared_context: &SharedStyleContext, font_metrics_provider: &FontMetricsProvider, @@ -321,6 +327,7 @@ trait PrivateMatchMethods: TElement { primary_style: Option<&Arc>, cascade_target: CascadeTarget, cascade_visited: CascadeVisitedMode, + parent_info: Option<&ParentElementAndStyle>, visited_values_to_insert: Option>) -> Arc { 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 style_to_inherit_from = match cascade_target { CascadeTarget::Normal => { - element_and_style = self.get_inherited_style_and_parent(); - parent_el = element_and_style.element; - element_and_style.style.as_ref().map(|s| cascade_visited.values(s)) + 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 + } + }; + parent_el = info.element; + info.style.as_ref().map(|s| cascade_visited.values(s)) } CascadeTarget::EagerPseudo => { parent_el = Some(self.clone()); @@ -402,11 +415,15 @@ trait PrivateMatchMethods: TElement { /// 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, context: &StyleContext, primary_style: Option<&Arc>, primary_inputs: &CascadeInputs, eager_pseudo_inputs: Option<&CascadeInputs>, + parent_info: Option<&ParentElementAndStyle>, cascade_visited: CascadeVisitedMode) -> Arc { if let Some(pseudo) = self.implemented_pseudo_element() { @@ -470,15 +487,19 @@ trait PrivateMatchMethods: TElement { primary_style, cascade_target, cascade_visited, + parent_info, visited_values_to_insert) } /// Computes values and damage for the primary style of an element, setting /// them on the ElementData. + /// + /// `parent_info` is our style parent and its primary style. fn cascade_primary(&self, context: &mut StyleContext, data: &mut ElementData, important_rules_changed: bool, + parent_info: &ParentElementAndStyle, cascade_visited: CascadeVisitedMode) -> ChildCascadeRequirement { 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 // `cascade_primary_and_pseudos` path since we rely on the state of // 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 } @@ -505,6 +529,7 @@ trait PrivateMatchMethods: TElement { None, primary_inputs, None, + /* parent_info = */ None, cascade_visited) }; @@ -593,6 +618,7 @@ trait PrivateMatchMethods: TElement { data.styles.get_primary(), primary_inputs, Some(pseudo_inputs), + /* parent_info = */ None, cascade_visited) }; @@ -636,6 +662,7 @@ trait PrivateMatchMethods: TElement { Some(primary_style), CascadeTarget::Normal, CascadeVisitedMode::Unvisited, + /* parent_info = */ None, None)) } @@ -899,6 +926,12 @@ struct ParentElementAndStyle { style: Option>, } +impl ParentElementAndStyle { + 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 /// node and other associated data. #[derive(Debug)] @@ -966,13 +999,21 @@ pub trait MatchMethods : TElement { let relevant_link_found = primary_results.relevant_link_found; if relevant_link_found { 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, + &parent_and_styles, CascadeVisitedMode::Visited); } // Cascade properties and compute primary values. let child_cascade_requirement = self.cascade_primary(context, data, important_rules_changed, + &parent_and_styles, CascadeVisitedMode::Unvisited); // Match and cascade eager pseudo-elements. @@ -1039,10 +1080,13 @@ pub trait MatchMethods : TElement { // visited ComputedValues are placed within the regular ComputedValues, // which is immutable after the cascade. If there aren't any visited // 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, + &parent_and_styles, CascadeVisitedMode::Visited); let child_cascade_requirement = self.cascade_primary(context, &mut data, important_rules_changed, + &parent_and_styles, CascadeVisitedMode::Unvisited); self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Visited); self.cascade_pseudos(context, &mut data, CascadeVisitedMode::Unvisited); @@ -1611,6 +1655,7 @@ pub trait MatchMethods : TElement { Some(primary_style), CascadeTarget::Normal, CascadeVisitedMode::Unvisited, + /* parent_info = */ None, None) }