mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Move match and cascade temporaries to CurrentElementInfo
Before this change, the `ComputedStyle` struct that is part of permanent style data per element holds 2 `StrongRuleNode`s (unvisited and visited) and 2 `Arc<ComputedValues>` (unvisited and visited). Both rule nodes and the visited values don't actually need to be here. This patch moves these 3 to new temporary storage in `CascadeInputs` on `CurrentElementInfo` during the match and cascade process. Rule nodes are pushed down inside the `ComputedValues` for later access after the cascade. (Visited values were already available there.) The permanent style data per element now has just the `Arc<ComputedValues>` for itself and eager pseudo-elements (plus the `RestyleHint`). MozReview-Commit-ID: 3wq52ERMpdi
This commit is contained in:
parent
c3b2a2f4de
commit
2b5c56e6a8
19 changed files with 738 additions and 746 deletions
|
@ -9,8 +9,8 @@
|
|||
|
||||
use applicable_declarations::ApplicableDeclarationList;
|
||||
use cascade_info::CascadeInfo;
|
||||
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||
use data::{ComputedStyle, ElementData, RestyleData};
|
||||
use context::{CascadeInputs, SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||
use data::{ElementData, ElementStyles, RestyleData};
|
||||
use dom::{TElement, TNode};
|
||||
use font_metrics::FontMetricsProvider;
|
||||
use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
|
||||
|
@ -146,55 +146,98 @@ pub enum CascadeVisitedMode {
|
|||
/// depending on the current cascade mode.
|
||||
impl CascadeVisitedMode {
|
||||
/// Returns whether there is a rule node based on the cascade mode.
|
||||
fn has_rules(&self, style: &ComputedStyle) -> bool {
|
||||
/// Rules will be present after matching or pulled from a previous cascade
|
||||
/// if no matching is expected. For visited, this means rules exist only
|
||||
/// if a revelant link existed when matching was last done.
|
||||
fn has_rules(&self, inputs: &CascadeInputs) -> bool {
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => true,
|
||||
CascadeVisitedMode::Visited => style.has_visited_rules(),
|
||||
CascadeVisitedMode::Unvisited => inputs.has_rules(),
|
||||
CascadeVisitedMode::Visited => inputs.has_visited_rules(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the rule node based on the cascade mode.
|
||||
fn rules<'a>(&self, style: &'a ComputedStyle) -> &'a StrongRuleNode {
|
||||
fn rules<'a>(&self, inputs: &'a CascadeInputs) -> &'a StrongRuleNode {
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => &style.rules,
|
||||
CascadeVisitedMode::Visited => style.visited_rules(),
|
||||
CascadeVisitedMode::Unvisited => inputs.rules(),
|
||||
CascadeVisitedMode::Visited => inputs.visited_rules(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable rules node based on the cascade mode, if any.
|
||||
fn get_rules_mut<'a>(&self, style: &'a mut ComputedStyle) -> Option<&'a mut StrongRuleNode> {
|
||||
fn get_rules_mut<'a>(&self, inputs: &'a mut CascadeInputs) -> Option<&'a mut StrongRuleNode> {
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => Some(&mut style.rules),
|
||||
CascadeVisitedMode::Visited => style.get_visited_rules_mut(),
|
||||
CascadeVisitedMode::Unvisited => inputs.get_rules_mut(),
|
||||
CascadeVisitedMode::Visited => inputs.get_visited_rules_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// we fallback to the regular, unvisited styles.
|
||||
fn values<'a>(&self, style: &'a ComputedStyle) -> &'a Arc<ComputedValues> {
|
||||
let mut values = style.values();
|
||||
|
||||
fn values<'a>(&self, values: &'a Arc<ComputedValues>) -> &'a Arc<ComputedValues> {
|
||||
if *self == CascadeVisitedMode::Visited && values.get_visited_style().is_some() {
|
||||
values = values.visited_style();
|
||||
return values.visited_style();
|
||||
}
|
||||
|
||||
values
|
||||
}
|
||||
|
||||
/// Set the computed values based on the cascade mode.
|
||||
fn set_values(&self, style: &mut ComputedStyle, values: Arc<ComputedValues>) {
|
||||
/// Set the primary computed values based on the cascade mode.
|
||||
fn set_primary_values(&self,
|
||||
styles: &mut ElementStyles,
|
||||
inputs: &mut CascadeInputs,
|
||||
values: Arc<ComputedValues>) {
|
||||
// Unvisited values are stored in permanent storage on `ElementData`.
|
||||
// Visited values are stored temporarily in `CascadeInputs` and then
|
||||
// folded into the unvisited values when they cascade.
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => style.values = Some(values),
|
||||
CascadeVisitedMode::Visited => style.set_visited_values(values),
|
||||
CascadeVisitedMode::Unvisited => styles.primary = Some(values),
|
||||
CascadeVisitedMode::Visited => inputs.set_visited_values(values),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the computed values based on the cascade mode.
|
||||
fn take_values(&self, style: &mut ComputedStyle) -> Option<Arc<ComputedValues>> {
|
||||
/// Set the primary computed values based on the cascade mode.
|
||||
fn set_pseudo_values(&self,
|
||||
styles: &mut ElementStyles,
|
||||
inputs: &mut CascadeInputs,
|
||||
pseudo: &PseudoElement,
|
||||
values: Arc<ComputedValues>) {
|
||||
// Unvisited values are stored in permanent storage on `ElementData`.
|
||||
// Visited values are stored temporarily in `CascadeInputs` and then
|
||||
// folded into the unvisited values when they cascade.
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => style.values.take(),
|
||||
CascadeVisitedMode::Visited => style.take_visited_values(),
|
||||
CascadeVisitedMode::Unvisited => styles.pseudos.set(pseudo, values),
|
||||
CascadeVisitedMode::Visited => inputs.set_visited_values(values),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the primary computed values based on the cascade mode.
|
||||
fn take_primary_values(&self,
|
||||
styles: &mut ElementStyles,
|
||||
inputs: &mut CascadeInputs)
|
||||
-> Option<Arc<ComputedValues>> {
|
||||
// Unvisited values are stored in permanent storage on `ElementData`.
|
||||
// Visited values are stored temporarily in `CascadeInputs` and then
|
||||
// folded into the unvisited values when they cascade.
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => styles.primary.take(),
|
||||
CascadeVisitedMode::Visited => inputs.take_visited_values(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the pseudo computed values based on the cascade mode.
|
||||
fn take_pseudo_values(&self,
|
||||
styles: &mut ElementStyles,
|
||||
inputs: &mut CascadeInputs,
|
||||
pseudo: &PseudoElement)
|
||||
-> Option<Arc<ComputedValues>> {
|
||||
// Unvisited values are stored in permanent storage on `ElementData`.
|
||||
// Visited values are stored temporarily in `CascadeInputs` and then
|
||||
// folded into the unvisited values when they cascade.
|
||||
match *self {
|
||||
CascadeVisitedMode::Unvisited => styles.pseudos.take(pseudo),
|
||||
CascadeVisitedMode::Visited => inputs.take_visited_values(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,7 +289,7 @@ trait PrivateMatchMethods: TElement {
|
|||
};
|
||||
|
||||
let is_display_contents =
|
||||
current.borrow_data().unwrap().styles().primary.values().is_display_contents();
|
||||
current.borrow_data().unwrap().styles.primary().is_display_contents();
|
||||
|
||||
if !is_display_contents {
|
||||
return current;
|
||||
|
@ -254,11 +297,15 @@ trait PrivateMatchMethods: TElement {
|
|||
}
|
||||
}
|
||||
|
||||
/// A common path for the cascade used by both primary elements and eager
|
||||
/// pseudo-elements after collecting the appropriate rules to use.
|
||||
///
|
||||
/// `primary_style` is expected to be Some for eager pseudo-elements.
|
||||
fn cascade_with_rules(&self,
|
||||
shared_context: &SharedStyleContext,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
rule_node: &StrongRuleNode,
|
||||
primary_style: &ComputedStyle,
|
||||
primary_style: Option<&Arc<ComputedValues>>,
|
||||
cascade_target: CascadeTarget,
|
||||
cascade_visited: CascadeVisitedMode,
|
||||
visited_values_to_insert: Option<Arc<ComputedValues>>)
|
||||
|
@ -294,13 +341,13 @@ trait PrivateMatchMethods: TElement {
|
|||
// but not wanting to flush all of layout).
|
||||
debug_assert!(cfg!(feature = "gecko") ||
|
||||
parent_el.unwrap().has_current_styles(d));
|
||||
&d.styles().primary
|
||||
d.styles.primary()
|
||||
});
|
||||
parent_style.map(|s| cascade_visited.values(s))
|
||||
}
|
||||
CascadeTarget::EagerPseudo => {
|
||||
parent_el = Some(self.clone());
|
||||
Some(cascade_visited.values(primary_style))
|
||||
Some(cascade_visited.values(primary_style.unwrap()))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -310,7 +357,7 @@ trait PrivateMatchMethods: TElement {
|
|||
if style_to_inherit_from.map_or(false, |s| s.is_display_contents()) {
|
||||
layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
|
||||
layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
|
||||
layout_parent_style = Some(cascade_visited.values(&layout_parent_data.styles().primary));
|
||||
layout_parent_style = Some(cascade_visited.values(layout_parent_data.styles.primary()));
|
||||
}
|
||||
|
||||
let style_to_inherit_from = style_to_inherit_from.map(|x| &**x);
|
||||
|
@ -349,14 +396,19 @@ trait PrivateMatchMethods: TElement {
|
|||
values
|
||||
}
|
||||
|
||||
/// A common path for the cascade used by both primary elements and eager
|
||||
/// pseudo-elements.
|
||||
///
|
||||
/// `primary_style` is expected to be Some for eager pseudo-elements.
|
||||
fn cascade_internal(&self,
|
||||
context: &StyleContext<Self>,
|
||||
primary_style: &ComputedStyle,
|
||||
eager_pseudo_style: Option<&ComputedStyle>,
|
||||
primary_style: Option<&Arc<ComputedValues>>,
|
||||
primary_inputs: &CascadeInputs,
|
||||
eager_pseudo_inputs: Option<&CascadeInputs>,
|
||||
cascade_visited: CascadeVisitedMode)
|
||||
-> Arc<ComputedValues> {
|
||||
if let Some(pseudo) = self.implemented_pseudo_element() {
|
||||
debug_assert!(eager_pseudo_style.is_none());
|
||||
debug_assert!(eager_pseudo_inputs.is_none());
|
||||
|
||||
// This is an element-backed pseudo, just grab the styles from the
|
||||
// parent if it's eager, and recascade otherwise.
|
||||
|
@ -377,10 +429,10 @@ trait PrivateMatchMethods: TElement {
|
|||
debug_assert!(pseudo.is_before_or_after());
|
||||
let parent = self.parent_element().unwrap();
|
||||
if !parent.may_have_animations() ||
|
||||
primary_style.rules.get_animation_rules().is_empty() {
|
||||
primary_inputs.rules().get_animation_rules().is_empty() {
|
||||
let parent_data = parent.borrow_data().unwrap();
|
||||
let pseudo_style =
|
||||
parent_data.styles().pseudos.get(&pseudo).unwrap();
|
||||
parent_data.styles.pseudos.get(&pseudo).unwrap();
|
||||
let values = cascade_visited.values(pseudo_style);
|
||||
return values.clone()
|
||||
}
|
||||
|
@ -390,18 +442,21 @@ trait PrivateMatchMethods: TElement {
|
|||
// Find possible visited computed styles to insert within the regular
|
||||
// computed values we are about to create.
|
||||
let visited_values_to_insert = if cascade_visited.visited_values_for_insertion() {
|
||||
match eager_pseudo_style {
|
||||
match eager_pseudo_inputs {
|
||||
Some(ref s) => s.clone_visited_values(),
|
||||
None => primary_style.clone_visited_values(),
|
||||
None => primary_inputs.clone_visited_values(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Grab the rule node.
|
||||
let style = eager_pseudo_style.unwrap_or(primary_style);
|
||||
let rule_node = cascade_visited.rules(style);
|
||||
let cascade_target = if eager_pseudo_style.is_some() {
|
||||
let inputs = eager_pseudo_inputs.unwrap_or(primary_inputs);
|
||||
// We'd really like to take the rules here to avoid refcount traffic,
|
||||
// but animation's usage of `apply_declarations` make this tricky.
|
||||
// See bug 1375525.
|
||||
let rule_node = cascade_visited.rules(inputs);
|
||||
let cascade_target = if eager_pseudo_inputs.is_some() {
|
||||
CascadeTarget::EagerPseudo
|
||||
} else {
|
||||
CascadeTarget::Normal
|
||||
|
@ -426,23 +481,30 @@ trait PrivateMatchMethods: TElement {
|
|||
-> ChildCascadeRequirement {
|
||||
debug!("Cascade primary for {:?}, visited: {:?}", self, cascade_visited);
|
||||
|
||||
// Collect some values.
|
||||
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||
let mut primary_style = &mut styles.primary;
|
||||
// If there was no relevant link, we won't have any visited rules, so
|
||||
// there may not be anything do for the 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_style) {
|
||||
return ChildCascadeRequirement::CanSkipCascade
|
||||
}
|
||||
let mut old_values = cascade_visited.take_values(primary_style);
|
||||
let mut old_values = cascade_visited.take_primary_values(
|
||||
&mut data.styles,
|
||||
context.cascade_inputs_mut().primary_mut()
|
||||
);
|
||||
|
||||
// Compute the new values.
|
||||
let mut new_values = self.cascade_internal(context,
|
||||
primary_style,
|
||||
None,
|
||||
cascade_visited);
|
||||
let mut new_values = {
|
||||
let primary_inputs = context.cascade_inputs().primary();
|
||||
|
||||
// If there was no relevant link at the time of matching, we won't
|
||||
// have any visited rules, so there may not be anything do for the
|
||||
// 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) {
|
||||
return ChildCascadeRequirement::CanSkipCascade
|
||||
}
|
||||
|
||||
// Compute the new values.
|
||||
self.cascade_internal(context,
|
||||
None,
|
||||
primary_inputs,
|
||||
None,
|
||||
cascade_visited)
|
||||
};
|
||||
|
||||
// NB: Animations for pseudo-elements in Gecko are handled while
|
||||
// traversing the pseudo-elements themselves.
|
||||
|
@ -451,7 +513,6 @@ trait PrivateMatchMethods: TElement {
|
|||
self.process_animations(context,
|
||||
&mut old_values,
|
||||
&mut new_values,
|
||||
primary_style,
|
||||
important_rules_changed);
|
||||
}
|
||||
|
||||
|
@ -460,7 +521,7 @@ trait PrivateMatchMethods: TElement {
|
|||
if cascade_visited.should_accumulate_damage() {
|
||||
child_cascade_requirement =
|
||||
self.accumulate_damage(&context.shared,
|
||||
restyle,
|
||||
&mut data.restyle,
|
||||
old_values.as_ref().map(|v| v.as_ref()),
|
||||
&new_values,
|
||||
None);
|
||||
|
@ -483,7 +544,10 @@ trait PrivateMatchMethods: TElement {
|
|||
}
|
||||
|
||||
// Set the new computed values.
|
||||
cascade_visited.set_values(primary_style, new_values);
|
||||
let primary_inputs = context.cascade_inputs_mut().primary_mut();
|
||||
cascade_visited.set_primary_values(&mut data.styles,
|
||||
primary_inputs,
|
||||
new_values);
|
||||
|
||||
// Return whether the damage indicates we must cascade new inherited
|
||||
// values into children.
|
||||
|
@ -498,31 +562,52 @@ trait PrivateMatchMethods: TElement {
|
|||
pseudo: &PseudoElement,
|
||||
cascade_visited: CascadeVisitedMode) {
|
||||
debug_assert!(pseudo.is_eager());
|
||||
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||
let mut pseudo_style = styles.pseudos.get_mut(pseudo).unwrap();
|
||||
// If there was no relevant link, we won't have any visited rules, so
|
||||
// there may not be anything do for the 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(pseudo_style) {
|
||||
return
|
||||
}
|
||||
let old_values = cascade_visited.take_values(pseudo_style);
|
||||
|
||||
let new_values = self.cascade_internal(context,
|
||||
&styles.primary,
|
||||
Some(pseudo_style),
|
||||
cascade_visited);
|
||||
let old_values = cascade_visited.take_pseudo_values(
|
||||
&mut data.styles,
|
||||
context.cascade_inputs_mut().pseudos.get_mut(pseudo).unwrap(),
|
||||
pseudo
|
||||
);
|
||||
|
||||
let new_values = {
|
||||
let pseudo_inputs = context.cascade_inputs().pseudos
|
||||
.get(pseudo).unwrap();
|
||||
|
||||
// If there was no relevant link at the time of matching, we won't
|
||||
// have any visited rules, so there may not be anything do for the
|
||||
// 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(pseudo_inputs) {
|
||||
return
|
||||
}
|
||||
|
||||
// Primary inputs should already have rules populated since it's
|
||||
// always processed before eager pseudos.
|
||||
let primary_inputs = context.cascade_inputs().primary();
|
||||
debug_assert!(cascade_visited.has_rules(primary_inputs));
|
||||
|
||||
self.cascade_internal(context,
|
||||
data.styles.get_primary(),
|
||||
primary_inputs,
|
||||
Some(pseudo_inputs),
|
||||
cascade_visited)
|
||||
};
|
||||
|
||||
if cascade_visited.should_accumulate_damage() {
|
||||
self.accumulate_damage(&context.shared,
|
||||
restyle,
|
||||
old_values.as_ref().map(|v| &**v),
|
||||
&mut data.restyle,
|
||||
old_values.as_ref().map(|v| v.as_ref()),
|
||||
&new_values,
|
||||
Some(pseudo));
|
||||
}
|
||||
|
||||
cascade_visited.set_values(pseudo_style, new_values);
|
||||
let pseudo_inputs = context.cascade_inputs_mut().pseudos
|
||||
.get_mut(pseudo).unwrap();
|
||||
cascade_visited.set_pseudo_values(&mut data.styles,
|
||||
pseudo_inputs,
|
||||
pseudo,
|
||||
new_values);
|
||||
}
|
||||
|
||||
/// get_after_change_style removes the transition rules from the ComputedValues.
|
||||
|
@ -530,9 +615,9 @@ trait PrivateMatchMethods: TElement {
|
|||
#[cfg(feature = "gecko")]
|
||||
fn get_after_change_style(&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
primary_style: &ComputedStyle)
|
||||
primary_style: &Arc<ComputedValues>)
|
||||
-> Option<Arc<ComputedValues>> {
|
||||
let rule_node = &primary_style.rules;
|
||||
let rule_node = primary_style.rules();
|
||||
let without_transition_rules =
|
||||
context.shared.stylist.rule_tree().remove_transition_rule_if_applicable(rule_node);
|
||||
if without_transition_rules == *rule_node {
|
||||
|
@ -546,7 +631,7 @@ trait PrivateMatchMethods: TElement {
|
|||
Some(self.cascade_with_rules(context.shared,
|
||||
&context.thread_local.font_metrics_provider,
|
||||
&without_transition_rules,
|
||||
primary_style,
|
||||
Some(primary_style),
|
||||
CascadeTarget::Normal,
|
||||
CascadeVisitedMode::Unvisited,
|
||||
None))
|
||||
|
@ -586,7 +671,6 @@ trait PrivateMatchMethods: TElement {
|
|||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
new_values: &mut Arc<ComputedValues>,
|
||||
primary_style: &ComputedStyle,
|
||||
important_rules_changed: bool) {
|
||||
use context::{CASCADE_RESULTS, CSS_ANIMATIONS, CSS_TRANSITIONS, EFFECT_PROPERTIES};
|
||||
use context::UpdateAnimationsTasks;
|
||||
|
@ -599,7 +683,7 @@ trait PrivateMatchMethods: TElement {
|
|||
let before_change_style = if self.might_need_transitions_update(old_values.as_ref().map(|s| &**s),
|
||||
new_values) {
|
||||
let after_change_style = if self.has_css_transitions() {
|
||||
self.get_after_change_style(context, primary_style)
|
||||
self.get_after_change_style(context, new_values)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -652,7 +736,6 @@ trait PrivateMatchMethods: TElement {
|
|||
context: &mut StyleContext<Self>,
|
||||
old_values: &mut Option<Arc<ComputedValues>>,
|
||||
new_values: &mut Arc<ComputedValues>,
|
||||
_primary_style: &ComputedStyle,
|
||||
_important_rules_changed: bool) {
|
||||
use animation;
|
||||
|
||||
|
@ -857,7 +940,7 @@ pub trait MatchMethods : TElement {
|
|||
CascadeVisitedMode::Unvisited);
|
||||
|
||||
// Match and cascade eager pseudo-elements.
|
||||
if !data.styles().is_display_none() {
|
||||
if !data.styles.is_display_none() {
|
||||
self.match_pseudos(context, data, VisitedHandlingMode::AllLinksUnvisited);
|
||||
|
||||
// If there's a relevant link involved, match and cascade eager
|
||||
|
@ -876,7 +959,7 @@ pub trait MatchMethods : TElement {
|
|||
}
|
||||
|
||||
// If we have any pseudo elements, indicate so in the primary StyleRelations.
|
||||
if !data.styles().pseudos.is_empty() {
|
||||
if !data.styles.pseudos.is_empty() {
|
||||
primary_results.relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||
}
|
||||
|
||||
|
@ -899,7 +982,7 @@ pub trait MatchMethods : TElement {
|
|||
context.thread_local
|
||||
.style_sharing_candidate_cache
|
||||
.insert_if_possible(self,
|
||||
data.styles().primary.values(),
|
||||
data.styles.primary(),
|
||||
primary_results.relations,
|
||||
validation_data,
|
||||
dom_depth);
|
||||
|
@ -943,6 +1026,10 @@ pub trait MatchMethods : TElement {
|
|||
{
|
||||
debug!("Match primary for {:?}, visited: {:?}", self, visited_handling);
|
||||
|
||||
let mut primary_inputs = context.thread_local.current_element_info
|
||||
.as_mut().unwrap()
|
||||
.cascade_inputs.ensure_primary();
|
||||
|
||||
let only_default_rules = context.shared.traversal_flags.for_default_styles();
|
||||
let implemented_pseudo = self.implemented_pseudo_element();
|
||||
if let Some(ref pseudo) = implemented_pseudo {
|
||||
|
@ -964,8 +1051,8 @@ pub trait MatchMethods : TElement {
|
|||
let parent = self.parent_element().unwrap();
|
||||
let parent_data = parent.borrow_data().unwrap();
|
||||
let pseudo_style =
|
||||
parent_data.styles().pseudos.get(&pseudo).unwrap();
|
||||
let mut rules = pseudo_style.rules.clone();
|
||||
parent_data.styles.pseudos.get(&pseudo).unwrap();
|
||||
let mut rules = pseudo_style.rules().clone();
|
||||
if parent.may_have_animations() {
|
||||
let animation_rules = data.get_animation_rules();
|
||||
|
||||
|
@ -998,20 +1085,9 @@ pub trait MatchMethods : TElement {
|
|||
self.has_animations() &&
|
||||
data.has_styles() &&
|
||||
data.important_rules_are_different(&rules,
|
||||
&context.shared.guards);
|
||||
&context.shared.guards);
|
||||
|
||||
let rules_changed = match visited_handling {
|
||||
VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
|
||||
unreachable!("We should never try to selector match with \
|
||||
AllLinksVisitedAndUnvisited");
|
||||
},
|
||||
VisitedHandlingMode::AllLinksUnvisited => {
|
||||
data.set_primary_rules(rules)
|
||||
},
|
||||
VisitedHandlingMode::RelevantLinkVisited => {
|
||||
data.styles_mut().primary.set_visited_rules(rules)
|
||||
},
|
||||
};
|
||||
let rules_changed = primary_inputs.set_rules(visited_handling, rules);
|
||||
|
||||
return MatchingResults::new(rules_changed, important_rules_changed)
|
||||
}
|
||||
|
@ -1084,18 +1160,7 @@ pub trait MatchMethods : TElement {
|
|||
&context.shared.guards
|
||||
);
|
||||
|
||||
let rules_changed = match visited_handling {
|
||||
VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
|
||||
unreachable!("We should never try to selector match with \
|
||||
AllLinksVisitedAndUnvisited");
|
||||
},
|
||||
VisitedHandlingMode::AllLinksUnvisited => {
|
||||
data.set_primary_rules(primary_rule_node)
|
||||
},
|
||||
VisitedHandlingMode::RelevantLinkVisited => {
|
||||
data.styles_mut().primary.set_visited_rules(primary_rule_node)
|
||||
},
|
||||
};
|
||||
let rules_changed = primary_inputs.set_rules(visited_handling, primary_rule_node);
|
||||
|
||||
MatchingResults::new_from_context(rules_changed,
|
||||
important_rules_changed,
|
||||
|
@ -1118,11 +1183,6 @@ pub trait MatchMethods : TElement {
|
|||
|
||||
let mut applicable_declarations = ApplicableDeclarationList::new();
|
||||
|
||||
let map = &mut context.thread_local.selector_flags;
|
||||
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
|
||||
self.apply_selector_flags(map, element, flags);
|
||||
};
|
||||
|
||||
// Borrow the stuff we need here so the borrow checker doesn't get mad
|
||||
// at us later in the closure.
|
||||
let stylist = &context.shared.stylist;
|
||||
|
@ -1134,57 +1194,69 @@ pub trait MatchMethods : TElement {
|
|||
RuleInclusion::All
|
||||
};
|
||||
|
||||
let bloom_filter = context.thread_local.bloom_filter.filter();
|
||||
|
||||
let mut matching_context =
|
||||
MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
|
||||
Some(bloom_filter),
|
||||
visited_handling,
|
||||
context.shared.quirks_mode);
|
||||
|
||||
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
||||
let mut matches_different_pseudos = false;
|
||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||
let bloom_filter = context.thread_local.bloom_filter.filter();
|
||||
|
||||
let mut matching_context =
|
||||
MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement,
|
||||
Some(bloom_filter),
|
||||
visited_handling,
|
||||
context.shared.quirks_mode);
|
||||
|
||||
// For pseudo-elements, we only try to match visited rules if there
|
||||
// are also unvisited rules. (This matches Gecko's behavior.)
|
||||
if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
|
||||
!data.styles().pseudos.has(&pseudo) {
|
||||
!context.cascade_inputs().pseudos.has(&pseudo) {
|
||||
return
|
||||
}
|
||||
|
||||
if !self.may_generate_pseudo(&pseudo, data.styles().primary.values()) {
|
||||
if !self.may_generate_pseudo(&pseudo, data.styles.primary()) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_assert!(applicable_declarations.is_empty());
|
||||
// NB: We handle animation rules for ::before and ::after when
|
||||
// traversing them.
|
||||
stylist.push_applicable_declarations(self,
|
||||
Some(&pseudo),
|
||||
None,
|
||||
None,
|
||||
AnimationRules(None, None),
|
||||
rule_inclusion,
|
||||
&mut applicable_declarations,
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags);
|
||||
{
|
||||
let map = &mut context.thread_local.selector_flags;
|
||||
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
|
||||
self.apply_selector_flags(map, element, flags);
|
||||
};
|
||||
|
||||
let pseudos = &mut data.styles_mut().pseudos;
|
||||
debug_assert!(applicable_declarations.is_empty());
|
||||
// NB: We handle animation rules for ::before and ::after when
|
||||
// traversing them.
|
||||
stylist.push_applicable_declarations(self,
|
||||
Some(&pseudo),
|
||||
None,
|
||||
None,
|
||||
AnimationRules(None, None),
|
||||
rule_inclusion,
|
||||
&mut applicable_declarations,
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags);
|
||||
}
|
||||
|
||||
let pseudos = &mut context.thread_local.current_element_info
|
||||
.as_mut().unwrap()
|
||||
.cascade_inputs.pseudos;
|
||||
if !applicable_declarations.is_empty() {
|
||||
let rules = stylist.rule_tree().compute_rule_node(
|
||||
&mut applicable_declarations,
|
||||
&guards
|
||||
);
|
||||
matches_different_pseudos |= pseudos.add_rules(
|
||||
matches_different_pseudos |= !data.styles.pseudos.has(&pseudo);
|
||||
pseudos.add_rules(
|
||||
&pseudo,
|
||||
visited_handling,
|
||||
rules
|
||||
);
|
||||
} else {
|
||||
matches_different_pseudos |= pseudos.remove_rules(
|
||||
matches_different_pseudos |= data.styles.pseudos.has(&pseudo);
|
||||
pseudos.remove_rules(
|
||||
&pseudo,
|
||||
visited_handling
|
||||
);
|
||||
data.styles.pseudos.take(&pseudo);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1292,14 +1364,13 @@ pub trait MatchMethods : TElement {
|
|||
fn replace_rules(
|
||||
&self,
|
||||
replacements: RestyleHint,
|
||||
context: &StyleContext<Self>,
|
||||
data: &mut ElementData
|
||||
context: &mut StyleContext<Self>,
|
||||
) -> bool {
|
||||
let mut result = false;
|
||||
result |= self.replace_rules_internal(replacements, context, data,
|
||||
result |= self.replace_rules_internal(replacements, context,
|
||||
CascadeVisitedMode::Unvisited);
|
||||
if !context.shared.traversal_flags.for_animation_only() {
|
||||
result |= self.replace_rules_internal(replacements, context, data,
|
||||
result |= self.replace_rules_internal(replacements, context,
|
||||
CascadeVisitedMode::Visited);
|
||||
}
|
||||
result
|
||||
|
@ -1312,8 +1383,7 @@ pub trait MatchMethods : TElement {
|
|||
fn replace_rules_internal(
|
||||
&self,
|
||||
replacements: RestyleHint,
|
||||
context: &StyleContext<Self>,
|
||||
data: &mut ElementData,
|
||||
context: &mut StyleContext<Self>,
|
||||
cascade_visited: CascadeVisitedMode
|
||||
) -> bool {
|
||||
use properties::PropertyDeclarationBlock;
|
||||
|
@ -1322,8 +1392,11 @@ pub trait MatchMethods : TElement {
|
|||
debug_assert!(replacements.intersects(RestyleHint::replacements()) &&
|
||||
(replacements & !RestyleHint::replacements()).is_empty());
|
||||
|
||||
let element_styles = &mut data.styles_mut();
|
||||
let primary_rules = match cascade_visited.get_rules_mut(&mut element_styles.primary) {
|
||||
let stylist = &context.shared.stylist;
|
||||
let guards = &context.shared.guards;
|
||||
|
||||
let mut primary_inputs = context.cascade_inputs_mut().primary_mut();
|
||||
let primary_rules = match cascade_visited.get_rules_mut(primary_inputs) {
|
||||
Some(r) => r,
|
||||
None => return false,
|
||||
};
|
||||
|
@ -1331,8 +1404,8 @@ pub trait MatchMethods : TElement {
|
|||
let replace_rule_node = |level: CascadeLevel,
|
||||
pdb: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
||||
path: &mut StrongRuleNode| -> bool {
|
||||
let new_node = context.shared.stylist.rule_tree()
|
||||
.update_rule_at_level(level, pdb, path, &context.shared.guards);
|
||||
let new_node = stylist.rule_tree()
|
||||
.update_rule_at_level(level, pdb, path, guards);
|
||||
match new_node {
|
||||
Some(n) => {
|
||||
*path = n;
|
||||
|
@ -1455,8 +1528,9 @@ pub trait MatchMethods : TElement {
|
|||
// which pseudos match), so now we can just iterate what we have. This
|
||||
// does mean collecting owned pseudos, so that the borrow checker will
|
||||
// let us pass the mutable |data| to the cascade function.
|
||||
let matched_pseudos = data.styles().pseudos.keys();
|
||||
let matched_pseudos = context.cascade_inputs().pseudos.keys();
|
||||
for pseudo in matched_pseudos {
|
||||
debug!("Cascade pseudo for {:?} {:?}", self, pseudo);
|
||||
self.cascade_eager_pseudo(context, data, &pseudo, cascade_visited);
|
||||
}
|
||||
}
|
||||
|
@ -1465,17 +1539,17 @@ pub trait MatchMethods : TElement {
|
|||
fn get_base_style(&self,
|
||||
shared_context: &SharedStyleContext,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
primary_style: &ComputedStyle,
|
||||
pseudo_style: Option<&ComputedStyle>)
|
||||
primary_style: &Arc<ComputedValues>,
|
||||
pseudo_style: Option<&Arc<ComputedValues>>)
|
||||
-> Arc<ComputedValues> {
|
||||
let relevant_style = pseudo_style.unwrap_or(primary_style);
|
||||
let rule_node = &relevant_style.rules;
|
||||
let rule_node = relevant_style.rules();
|
||||
let without_animation_rules =
|
||||
shared_context.stylist.rule_tree().remove_animation_rules(rule_node);
|
||||
if without_animation_rules == *rule_node {
|
||||
// Note that unwrapping here is fine, because the style is
|
||||
// only incomplete during the styling process.
|
||||
return relevant_style.values.as_ref().unwrap().clone();
|
||||
return relevant_style.clone();
|
||||
}
|
||||
|
||||
// This currently ignores visited styles, which seems acceptable,
|
||||
|
@ -1483,7 +1557,7 @@ pub trait MatchMethods : TElement {
|
|||
self.cascade_with_rules(shared_context,
|
||||
font_metrics_provider,
|
||||
&without_animation_rules,
|
||||
primary_style,
|
||||
Some(primary_style),
|
||||
CascadeTarget::Normal,
|
||||
CascadeVisitedMode::Unvisited,
|
||||
None)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue