style: Use CascadeFlags for what they're for.

Now that we have an Element around on cascade, we can stop using the cascade
flags mechanism to pass various element-related state, like "is this element the
root", or "should it use the item-based display fixup".

That fixes handwaviness in the handling of those flags from style reparenting,
and code duplication to handle tricky stuff like :visited.

There are a number of other changes that are worth noticing:

 * skip_root_and_item_based_display_fixup is renamed to skip_item_display_fixup:

   TElement::is_root() already implies being the document element, which by
   definition is not native anonymous and not a pseudo-element.

   Thus, you never get fixed-up if your NAC or a pseudo, which is what the code
   tried to avoid, so the only fixup with a point is the item one, which is
   necessary.

 * The pseudo-element probing code was refactored to return early a
   Option::<CascadeInputs>::None, which is nicer than what it was doing.

 * The visited_links_enabled check has moved to selector-matching time. The rest
   of the checks aren't based on whether the element is a link, or are properly
   guarded by parent_style.visited_style().is_some() or visited_rules.is_some().

   Thus you can transitively infer that no element will end up with a :visited
   style, not even from style reparenting.

Anyway, the underlying reason why I want the element in StyleAdjuster is because
we're going to implement an adjustment in there depending on the tag of the
element (converting display: contents to display: none depending on the tag), so
computing that information eagerly, including a hash lookup, wouldn't be nice.
This commit is contained in:
Emilio Cobos Álvarez 2018-01-22 23:53:03 +01:00
parent 104f5c2553
commit cd04664fb9
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
11 changed files with 233 additions and 298 deletions

View file

@ -22,6 +22,7 @@ use malloc_size_of::MallocUnconditionalShallowSizeOf;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock};
use rule_cache::{RuleCache, RuleCacheConditions};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry};
use selector_parser::{SelectorImpl, PerPseudoElementMap, PseudoElement};
@ -650,7 +651,6 @@ impl Stylist {
guards: &StylesheetGuards,
pseudo: &PseudoElement,
parent: Option<&ComputedValues>,
cascade_flags: CascadeFlags,
font_metrics: &FontMetricsProvider,
) -> Arc<ComputedValues>
where
@ -668,7 +668,6 @@ impl Stylist {
guards,
pseudo,
parent,
cascade_flags,
font_metrics,
rule_node
)
@ -684,25 +683,23 @@ impl Stylist {
guards: &StylesheetGuards,
pseudo: &PseudoElement,
parent: Option<&ComputedValues>,
cascade_flags: CascadeFlags,
font_metrics: &FontMetricsProvider,
rule_node: StrongRuleNode
rules: StrongRuleNode
) -> Arc<ComputedValues>
where
E: TElement,
{
self.compute_pseudo_element_style_with_inputs::<E>(
&CascadeInputs {
rules: Some(rule_node),
CascadeInputs {
rules: Some(rules),
visited_rules: None,
},
pseudo,
guards,
parent,
font_metrics,
cascade_flags,
None,
).unwrap()
)
}
/// Returns the rule node for given precomputed pseudo-element.
@ -757,44 +754,10 @@ impl Stylist {
E: TElement,
{
use font_metrics::ServoMetricsProvider;
// For most (but not all) pseudo-elements, we inherit all values from the parent.
let inherit_all = match *pseudo {
// Anonymous table flows shouldn't inherit their parents properties in order
// to avoid doubling up styles such as transformations.
PseudoElement::ServoAnonymousTableCell |
PseudoElement::ServoAnonymousTableRow |
PseudoElement::ServoText |
PseudoElement::ServoInputText => false,
PseudoElement::ServoAnonymousBlock |
// For tables, we do want style to inherit, because TableWrapper is responsible
// for handling clipping and scrolling, while Table is responsible for creating
// stacking contexts. StackingContextCollectionFlags makes sure this is processed
// properly.
PseudoElement::ServoAnonymousTable |
PseudoElement::ServoAnonymousTableWrapper |
PseudoElement::ServoTableWrapper |
PseudoElement::ServoInlineBlockWrapper |
PseudoElement::ServoInlineAbsolute => true,
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Selection |
PseudoElement::DetailsSummary |
PseudoElement::DetailsContent => {
unreachable!("That pseudo doesn't represent an anonymous box!")
}
};
let mut cascade_flags = CascadeFlags::empty();
if inherit_all {
cascade_flags.insert(CascadeFlags::INHERIT_ALL);
}
self.precomputed_values_for_pseudo::<E>(
guards,
&pseudo,
Some(parent_style),
cascade_flags,
&ServoMetricsProvider,
)
}
@ -828,17 +791,16 @@ impl Stylist {
is_probe,
rule_inclusion,
matching_fn
);
)?;
self.compute_pseudo_element_style_with_inputs(
&cascade_inputs,
Some(self.compute_pseudo_element_style_with_inputs(
cascade_inputs,
pseudo,
guards,
Some(parent_style),
font_metrics,
CascadeFlags::empty(),
Some(element),
)
))
}
/// Computes a pseudo-element style lazily using the given CascadeInputs.
@ -847,23 +809,16 @@ impl Stylist {
/// their style with a new parent style.
pub fn compute_pseudo_element_style_with_inputs<E>(
&self,
inputs: &CascadeInputs,
inputs: CascadeInputs,
pseudo: &PseudoElement,
guards: &StylesheetGuards,
parent_style: Option<&ComputedValues>,
font_metrics: &FontMetricsProvider,
cascade_flags: CascadeFlags,
element: Option<E>,
) -> Option<Arc<ComputedValues>>
) -> Arc<ComputedValues>
where
E: TElement,
{
// We may have only visited rules in cases when we are actually
// resolving, not probing, pseudo-element style.
if inputs.rules.is_none() && inputs.visited_rules.is_none() {
return None
}
// FIXME(emilio): The lack of layout_parent_style here could be
// worrying, but we're probably dropping the display fixup for
// pseudos other than before and after, so it's probably ok.
@ -876,17 +831,18 @@ impl Stylist {
// <fieldset style="display: contents">. That is, the computed value of
// display for the fieldset is "contents", even though it's not the used
// value, so we don't need to adjust in a different way anyway.
Some(self.compute_style_with_inputs(
inputs,
self.cascade_style_and_visited(
element,
Some(pseudo),
inputs,
guards,
parent_style,
parent_style,
parent_style,
font_metrics,
cascade_flags,
element,
))
/* rule_cache = */ None,
&mut RuleCacheConditions::default(),
)
}
/// Computes a style using the given CascadeInputs. This can be used to
@ -904,38 +860,43 @@ impl Stylist {
///
/// is_link should be true if we're computing style for a link; that affects
/// how :visited handling is done.
pub fn compute_style_with_inputs<E>(
pub fn cascade_style_and_visited<E>(
&self,
inputs: &CascadeInputs,
element: Option<E>,
pseudo: Option<&PseudoElement>,
inputs: CascadeInputs,
guards: &StylesheetGuards,
parent_style: Option<&ComputedValues>,
parent_style_ignoring_first_line: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
font_metrics: &FontMetricsProvider,
cascade_flags: CascadeFlags,
element: Option<E>,
rule_cache: Option<&RuleCache>,
rule_cache_conditions: &mut RuleCacheConditions,
) -> Arc<ComputedValues>
where
E: TElement,
{
debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
let cascade_flags =
pseudo.map_or(CascadeFlags::empty(), |p| p.cascade_flags());
// We need to compute visited values if we have visited rules or if our
// parent has visited values.
let mut visited_values = None;
if inputs.visited_rules.is_some() ||
parent_style.and_then(|s| s.visited_style()).is_some()
{
// At this point inputs may have visited rules, or rules, or both,
// or neither (e.g. if it's a text style it may have neither). So
// we have to be a bit careful here.
// At this point inputs may have visited rules, or rules.
let rule_node = match inputs.visited_rules.as_ref() {
Some(rules) => rules,
None => inputs.rules.as_ref().unwrap_or(self.rule_tree().root()),
None => inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
};
let inherited_style;
let inherited_style_ignoring_first_line;
let layout_parent_style_for_visited;
if cascade_flags.contains(CascadeFlags::IS_LINK) {
if pseudo.is_none() && element.unwrap().is_link() {
// We just want to use our parent style as our parent.
inherited_style = parent_style;
inherited_style_ignoring_first_line = parent_style_ignoring_first_line;
@ -969,24 +930,22 @@ impl Stylist {
font_metrics,
cascade_flags | CascadeFlags::VISITED_DEPENDENT_ONLY,
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
rule_cache,
rule_cache_conditions,
element,
));
}
// We may not have non-visited rules, if we only had visited ones. In
// that case we want to use the root rulenode for our non-visited rules.
let rules = inputs.rules.as_ref().unwrap_or(self.rule_tree.root());
// Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the
// computed value is still "contents").
//
// FIXME(emilio): We should assert that it holds if pseudo.is_none()!
properties::cascade::<E>(
&self.device,
pseudo,
rules,
inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
guards,
parent_style,
parent_style_ignoring_first_line,
@ -995,9 +954,9 @@ impl Stylist {
font_metrics,
cascade_flags,
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
None,
rule_cache,
rule_cache_conditions,
element,
)
}
@ -1014,7 +973,7 @@ impl Stylist {
is_probe: bool,
rule_inclusion: RuleInclusion,
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
) -> CascadeInputs
) -> Option<CascadeInputs>
where
E: TElement
{
@ -1051,7 +1010,6 @@ impl Stylist {
}
};
let mut inputs = CascadeInputs::default();
let mut declarations = ApplicableDeclarationList::new();
let mut matching_context = MatchingContext::new(
MatchingMode::ForStatelessPseudoElement,
@ -1074,19 +1032,14 @@ impl Stylist {
&mut set_selector_flags
);
if !declarations.is_empty() {
let rule_node =
self.rule_tree.compute_rule_node(&mut declarations, guards);
debug_assert!(rule_node != *self.rule_tree.root());
inputs.rules = Some(rule_node);
if declarations.is_empty() && is_probe {
return None;
}
if is_probe && inputs.rules.is_none() {
// When probing, don't compute visited styles if we have no
// unvisited styles.
return inputs;
}
let rules =
self.rule_tree.compute_rule_node(&mut declarations, guards);
let mut visited_rules = None;
if parent_style.visited_style().is_some() {
let mut declarations = ApplicableDeclarationList::new();
let mut matching_context =
@ -1114,14 +1067,15 @@ impl Stylist {
let rule_node =
self.rule_tree.insert_ordered_rules_with_important(
declarations.drain().map(|a| a.order_and_level()),
guards);
guards,
);
if rule_node != *self.rule_tree.root() {
inputs.visited_rules = Some(rule_node);
visited_rules = Some(rule_node);
}
}
}
inputs
Some(CascadeInputs { rules: Some(rules), visited_rules })
}
/// Set a given device, which may change the styles that apply to the
@ -1596,7 +1550,7 @@ impl Stylist {
self.quirks_mode,
/* rule_cache = */ None,
&mut Default::default(),
None,
/* element = */ None,
)
}