style: Rewrite restyling to split between resolving styles and handling changes.

MozReview-Commit-ID: 4BzjbLbFebF
This commit is contained in:
Emilio Cobos Álvarez 2017-07-12 08:40:23 +02:00
parent 0ad2d39c30
commit c6d5dbbb01
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
8 changed files with 485 additions and 1500 deletions

View file

@ -6,7 +6,7 @@
use applicable_declarations::ApplicableDeclarationList;
use cascade_info::CascadeInfo;
use context::StyleContext;
use context::{CascadeInputs, ElementCascadeInputs, StyleContext};
use data::{ElementStyles, EagerPseudoStyles};
use dom::TElement;
use log::LogLevel::Trace;
@ -42,11 +42,37 @@ struct MatchingResults {
pub struct PrimaryStyle {
/// The style per se.
pub style: Arc<ComputedValues>,
}
/// Whether a relevant link was found while computing this style.
///
/// FIXME(emilio): Slightly out of place?
pub relevant_link_found: bool,
fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
where
E: TElement,
F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
{
let parent_el = element.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()
});
let mut layout_parent_el = parent_el.clone();
let layout_parent_data;
let mut layout_parent_style = parent_style;
if parent_style.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(layout_parent_data.styles.primary());
}
f(parent_style.map(|s| &**s), layout_parent_style.map(|s| &**s))
}
impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
@ -95,7 +121,7 @@ where
if should_compute_visited_style {
visited_style = Some(self.cascade_style(
visited_rules.as_ref().unwrap_or(&primary_results.rule_node),
visited_rules.as_ref(),
/* style_if_visited = */ None,
parent_style,
layout_parent_style,
@ -105,7 +131,7 @@ where
}
let style = self.cascade_style(
&primary_results.rule_node,
Some(&primary_results.rule_node),
visited_style,
parent_style,
layout_parent_style,
@ -113,7 +139,7 @@ where
/* pseudo = */ None,
);
PrimaryStyle { style, relevant_link_found, }
PrimaryStyle { style, }
}
@ -137,7 +163,7 @@ where
}
}
{
if self.element.implemented_pseudo_element().is_none() {
let layout_parent_style_for_pseudo =
if primary_style.style.is_display_contents() {
layout_parent_style
@ -163,6 +189,115 @@ where
}
}
/// Resolve an element's styles with the default inheritance parent/layout
/// parents.
pub fn resolve_style_with_default_parents(&mut self) -> ElementStyles {
with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
self.resolve_style(parent_style, layout_parent_style)
})
}
/// Cascade a set of rules, using the default parent for inheritance.
pub fn cascade_style_and_visited_with_default_parents(
&mut self,
inputs: CascadeInputs,
) -> Arc<ComputedValues> {
with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
self.cascade_style_and_visited(
inputs,
parent_style,
layout_parent_style,
/* pseudo = */ None
)
})
}
fn cascade_style_and_visited(
&mut self,
inputs: CascadeInputs,
parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
pseudo: Option<&PseudoElement>,
) -> Arc<ComputedValues> {
let mut style_if_visited = None;
if parent_style.map_or(false, |s| s.get_visited_style().is_some()) ||
inputs.visited_rules.is_some() {
style_if_visited = Some(self.cascade_style(
inputs.visited_rules.as_ref(),
/* style_if_visited = */ None,
parent_style,
layout_parent_style,
CascadeVisitedMode::Visited,
pseudo,
));
}
self.cascade_style(
inputs.rules.as_ref(),
style_if_visited,
parent_style,
layout_parent_style,
CascadeVisitedMode::Unvisited,
pseudo,
)
}
/// Cascade the element and pseudo-element styles with the default parents.
pub fn cascade_styles_with_default_parents(
&mut self,
inputs: ElementCascadeInputs,
) -> ElementStyles {
use properties::longhands::display::computed_value::T as display;
with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
let primary_style = PrimaryStyle {
style: self.cascade_style_and_visited(
inputs.primary,
parent_style,
layout_parent_style,
/* pseudo = */ None,
),
};
let mut pseudo_styles = EagerPseudoStyles::default();
let pseudo_array = inputs.pseudos.into_array();
if pseudo_array.is_none() ||
primary_style.style.get_box().clone_display() == display::none {
return ElementStyles {
primary: Some(primary_style.style),
pseudos: pseudo_styles,
}
}
{
let layout_parent_style_for_pseudo =
if primary_style.style.is_display_contents() {
layout_parent_style
} else {
Some(&*primary_style.style)
};
for (i, mut inputs) in pseudo_array.unwrap().iter_mut().enumerate() {
if let Some(inputs) = inputs.take() {
let pseudo = PseudoElement::from_eager_index(i);
pseudo_styles.set(
&pseudo,
self.cascade_style_and_visited(
inputs,
Some(&*primary_style.style),
layout_parent_style_for_pseudo,
Some(&pseudo),
)
)
}
}
}
ElementStyles {
primary: Some(primary_style.style),
pseudos: pseudo_styles,
}
})
}
fn resolve_pseudo_style(
&mut self,
pseudo: &PseudoElement,
@ -179,33 +314,23 @@ where
None => return None,
};
let mut visited_style = None;
if originating_element_style.relevant_link_found {
let visited_rules = self.match_pseudo(
let mut visited_rules = None;
if originating_element_style.style.get_visited_style().is_some() {
visited_rules = self.match_pseudo(
&originating_element_style.style,
pseudo,
VisitedHandlingMode::RelevantLinkVisited,
);
if let Some(ref rules) = visited_rules {
visited_style = Some(self.cascade_style(
rules,
/* style_if_visited = */ None,
Some(&originating_element_style.style),
layout_parent_style,
CascadeVisitedMode::Visited,
Some(pseudo),
));
}
}
Some(self.cascade_style(
&rules,
visited_style,
Some(self.cascade_style_and_visited(
CascadeInputs {
rules: Some(rules),
visited_rules
},
Some(&originating_element_style.style),
layout_parent_style,
CascadeVisitedMode::Unvisited,
Some(pseudo)
Some(pseudo),
))
}
@ -335,9 +460,9 @@ where
fn cascade_style(
&mut self,
rules: &StrongRuleNode,
rules: Option<&StrongRuleNode>,
style_if_visited: Option<Arc<ComputedValues>>,
parent_style: Option<&ComputedValues>,
mut parent_style: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
cascade_visited: CascadeVisitedMode,
pseudo: Option<&PseudoElement>,
@ -349,6 +474,9 @@ where
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
}
if cascade_visited.visited_dependent_only() {
parent_style = parent_style.map(|s| {
s.get_visited_style().map(|s| &**s).unwrap_or(s)
});
cascade_flags.insert(VISITED_DEPENDENT_ONLY);
}
if self.element.is_native_anonymous() || pseudo.is_some() {
@ -360,13 +488,12 @@ where
let values =
Arc::new(cascade(
self.context.shared.stylist.device(),
rules,
rules.unwrap_or(self.context.shared.stylist.rule_tree().root()),
&self.context.shared.guards,
parent_style,
layout_parent_style,
style_if_visited,
Some(&mut cascade_info),
&*self.context.shared.error_reporter,
&self.context.thread_local.font_metrics_provider,
cascade_flags,
self.context.shared.quirks_mode