mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
style: Introduce StyleResolverForElement.
This still doesn't make use of it so far, but I prefer introducing it atomically, then introduce its usage. MozReview-Commit-ID: 9dRUsl3srHp
This commit is contained in:
parent
fcf85e4658
commit
b4c8ba3029
2 changed files with 371 additions and 0 deletions
|
@ -123,6 +123,7 @@ pub mod selector_map;
|
|||
pub mod selector_parser;
|
||||
pub mod shared_lock;
|
||||
pub mod sharing;
|
||||
pub mod style_resolver;
|
||||
pub mod stylist;
|
||||
#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
|
||||
pub mod sequential;
|
||||
|
|
370
components/style/style_resolver.rs
Normal file
370
components/style/style_resolver.rs
Normal file
|
@ -0,0 +1,370 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Style resolution for a given element or pseudo-element.
|
||||
|
||||
use applicable_declarations::ApplicableDeclarationList;
|
||||
use cascade_info::CascadeInfo;
|
||||
use context::StyleContext;
|
||||
use data::{ElementStyles, EagerPseudoStyles};
|
||||
use dom::TElement;
|
||||
use log::LogLevel::Trace;
|
||||
use matching::{CascadeVisitedMode, MatchMethods};
|
||||
use properties::{AnimationRules, CascadeFlags, ComputedValues};
|
||||
use properties::{IS_ROOT_ELEMENT, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
|
||||
use properties::{VISITED_DEPENDENT_ONLY, cascade};
|
||||
use rule_tree::StrongRuleNode;
|
||||
use selector_parser::{PseudoElement, SelectorImpl};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, VisitedHandlingMode};
|
||||
use stylearc::Arc;
|
||||
use stylist::RuleInclusion;
|
||||
|
||||
/// A struct that takes care of resolving the style of a given element.
|
||||
pub struct StyleResolverForElement<'a, 'b, E>
|
||||
where
|
||||
'b: 'a,
|
||||
E: TElement + MatchMethods + 'static,
|
||||
{
|
||||
element: E,
|
||||
context: &'a mut StyleContext<'b, E>,
|
||||
rule_inclusion: RuleInclusion,
|
||||
}
|
||||
|
||||
struct MatchingResults {
|
||||
rule_node: StrongRuleNode,
|
||||
relevant_link_found: bool,
|
||||
}
|
||||
|
||||
/// The primary style of an element or an element-backed pseudo-element.
|
||||
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,
|
||||
}
|
||||
|
||||
impl<'a, 'b, E> StyleResolverForElement<'a, 'b, E>
|
||||
where
|
||||
'b: 'a,
|
||||
E: TElement + MatchMethods + 'static,
|
||||
{
|
||||
/// Trivially construct a new StyleResolverForElement.
|
||||
pub fn new(
|
||||
element: E,
|
||||
context: &'a mut StyleContext<'b, E>,
|
||||
rule_inclusion: RuleInclusion,
|
||||
) -> Self {
|
||||
Self { element, context, rule_inclusion, }
|
||||
}
|
||||
|
||||
/// Resolve just the style of a given element.
|
||||
pub fn resolve_primary_style(
|
||||
&mut self,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
) -> PrimaryStyle {
|
||||
let primary_results =
|
||||
self.match_primary(VisitedHandlingMode::AllLinksUnvisited);
|
||||
|
||||
let relevant_link_found = primary_results.relevant_link_found;
|
||||
|
||||
let visited_rules = if relevant_link_found {
|
||||
let visited_matching_results =
|
||||
self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
|
||||
Some(visited_matching_results.rule_node)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut visited_style = None;
|
||||
let should_compute_visited_style =
|
||||
relevant_link_found ||
|
||||
parent_style.and_then(|s| s.get_visited_style()).is_some();
|
||||
|
||||
if should_compute_visited_style {
|
||||
visited_style = Some(self.cascade_style(
|
||||
visited_rules.as_ref().unwrap_or(&primary_results.rule_node),
|
||||
/* style_if_visited = */ None,
|
||||
parent_style,
|
||||
layout_parent_style,
|
||||
CascadeVisitedMode::Visited,
|
||||
/* pseudo = */ None,
|
||||
));
|
||||
}
|
||||
|
||||
let style = self.cascade_style(
|
||||
&primary_results.rule_node,
|
||||
visited_style,
|
||||
parent_style,
|
||||
layout_parent_style,
|
||||
CascadeVisitedMode::Unvisited,
|
||||
/* pseudo = */ None,
|
||||
);
|
||||
|
||||
PrimaryStyle { style, relevant_link_found, }
|
||||
}
|
||||
|
||||
|
||||
/// Resolve the style of a given element, and all its eager pseudo-elements.
|
||||
pub fn resolve_style(
|
||||
&mut self,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
) -> ElementStyles {
|
||||
use properties::longhands::display::computed_value::T as display;
|
||||
|
||||
let primary_style =
|
||||
self.resolve_primary_style(parent_style, layout_parent_style);
|
||||
|
||||
let mut pseudo_styles = EagerPseudoStyles::default();
|
||||
if primary_style.style.get_box().clone_display() == display::none {
|
||||
return ElementStyles {
|
||||
// FIXME(emilio): Remove the Option<>.
|
||||
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)
|
||||
};
|
||||
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||
let pseudo_style = self.resolve_pseudo_style(
|
||||
&pseudo,
|
||||
&primary_style,
|
||||
layout_parent_style_for_pseudo
|
||||
);
|
||||
if let Some(style) = pseudo_style {
|
||||
pseudo_styles.set(&pseudo, style);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ElementStyles {
|
||||
// FIXME(emilio): Remove the Option<>.
|
||||
primary: Some(primary_style.style),
|
||||
pseudos: pseudo_styles,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_pseudo_style(
|
||||
&mut self,
|
||||
pseudo: &PseudoElement,
|
||||
originating_element_style: &PrimaryStyle,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
) -> Option<Arc<ComputedValues>> {
|
||||
let rules = self.match_pseudo(
|
||||
&originating_element_style.style,
|
||||
pseudo,
|
||||
VisitedHandlingMode::AllLinksUnvisited
|
||||
);
|
||||
let rules = match rules {
|
||||
Some(rules) => rules,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let mut visited_style = None;
|
||||
if originating_element_style.relevant_link_found {
|
||||
let 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(&originating_element_style.style),
|
||||
layout_parent_style,
|
||||
CascadeVisitedMode::Unvisited,
|
||||
Some(pseudo)
|
||||
))
|
||||
}
|
||||
|
||||
fn match_primary(
|
||||
&mut self,
|
||||
visited_handling: VisitedHandlingMode,
|
||||
) -> MatchingResults {
|
||||
debug!("Match primary for {:?}, visited: {:?}",
|
||||
self.element, visited_handling);
|
||||
let mut applicable_declarations = ApplicableDeclarationList::new();
|
||||
|
||||
let map = &mut self.context.thread_local.selector_flags;
|
||||
let bloom_filter = self.context.thread_local.bloom_filter.filter();
|
||||
let mut matching_context =
|
||||
MatchingContext::new_for_visited(
|
||||
MatchingMode::Normal,
|
||||
Some(bloom_filter),
|
||||
visited_handling,
|
||||
self.context.shared.quirks_mode
|
||||
);
|
||||
|
||||
let stylist = &self.context.shared.stylist;
|
||||
let implemented_pseudo = self.element.implemented_pseudo_element();
|
||||
{
|
||||
let resolving_element = self.element;
|
||||
let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
|
||||
resolving_element.apply_selector_flags(map, element, flags);
|
||||
};
|
||||
|
||||
// Compute the primary rule node.
|
||||
stylist.push_applicable_declarations(
|
||||
&self.element,
|
||||
implemented_pseudo.as_ref(),
|
||||
self.element.style_attribute(),
|
||||
self.element.get_smil_override(),
|
||||
self.element.get_animation_rules(),
|
||||
self.rule_inclusion,
|
||||
&mut applicable_declarations,
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags,
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(emilio): This is a hack for animations, and should go away.
|
||||
self.element.unset_dirty_style_attribute();
|
||||
|
||||
let relevant_link_found = matching_context.relevant_link_found;
|
||||
let rule_node = stylist.rule_tree().compute_rule_node(
|
||||
&mut applicable_declarations,
|
||||
&self.context.shared.guards
|
||||
);
|
||||
|
||||
if log_enabled!(Trace) {
|
||||
trace!("Matched rules:");
|
||||
for rn in rule_node.self_and_ancestors() {
|
||||
let source = rn.style_source();
|
||||
if source.is_some() {
|
||||
trace!(" > {:?}", source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MatchingResults { rule_node, relevant_link_found }
|
||||
}
|
||||
|
||||
fn match_pseudo(
|
||||
&mut self,
|
||||
originating_element_style: &ComputedValues,
|
||||
pseudo_element: &PseudoElement,
|
||||
visited_handling: VisitedHandlingMode,
|
||||
) -> Option<StrongRuleNode> {
|
||||
debug!("Match pseudo {:?} for {:?}, visited: {:?}",
|
||||
self.element, pseudo_element, visited_handling);
|
||||
debug_assert!(pseudo_element.is_eager() || pseudo_element.is_lazy());
|
||||
debug_assert!(self.element.implemented_pseudo_element().is_none(),
|
||||
"Element pseudos can't have any other pseudo.");
|
||||
|
||||
let mut applicable_declarations = ApplicableDeclarationList::new();
|
||||
|
||||
let stylist = &self.context.shared.stylist;
|
||||
|
||||
if !self.element.may_generate_pseudo(pseudo_element, originating_element_style) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bloom_filter = self.context.thread_local.bloom_filter.filter();
|
||||
|
||||
let mut matching_context =
|
||||
MatchingContext::new_for_visited(
|
||||
MatchingMode::ForStatelessPseudoElement,
|
||||
Some(bloom_filter),
|
||||
visited_handling,
|
||||
self.context.shared.quirks_mode
|
||||
);
|
||||
|
||||
let map = &mut self.context.thread_local.selector_flags;
|
||||
let resolving_element = self.element;
|
||||
let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
|
||||
resolving_element.apply_selector_flags(map, element, flags);
|
||||
};
|
||||
|
||||
// NB: We handle animation rules for ::before and ::after when
|
||||
// traversing them.
|
||||
stylist.push_applicable_declarations(
|
||||
&self.element,
|
||||
Some(pseudo_element),
|
||||
None,
|
||||
None,
|
||||
AnimationRules(None, None),
|
||||
self.rule_inclusion,
|
||||
&mut applicable_declarations,
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags
|
||||
);
|
||||
|
||||
if applicable_declarations.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rule_node = stylist.rule_tree().compute_rule_node(
|
||||
&mut applicable_declarations,
|
||||
&self.context.shared.guards
|
||||
);
|
||||
|
||||
Some(rule_node)
|
||||
}
|
||||
|
||||
fn cascade_style(
|
||||
&mut self,
|
||||
rules: &StrongRuleNode,
|
||||
style_if_visited: Option<Arc<ComputedValues>>,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
cascade_visited: CascadeVisitedMode,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
) -> Arc<ComputedValues> {
|
||||
let mut cascade_info = CascadeInfo::new();
|
||||
let mut cascade_flags = CascadeFlags::empty();
|
||||
|
||||
if self.element.skip_root_and_item_based_display_fixup() {
|
||||
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP);
|
||||
}
|
||||
if cascade_visited.visited_dependent_only() {
|
||||
cascade_flags.insert(VISITED_DEPENDENT_ONLY);
|
||||
}
|
||||
if self.element.is_native_anonymous() || pseudo.is_some() {
|
||||
cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
|
||||
} else if self.element.is_root() {
|
||||
cascade_flags.insert(IS_ROOT_ELEMENT);
|
||||
}
|
||||
|
||||
let values =
|
||||
Arc::new(cascade(
|
||||
self.context.shared.stylist.device(),
|
||||
rules,
|
||||
&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
|
||||
));
|
||||
|
||||
cascade_info.finish(&self.element.as_node());
|
||||
values
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue