mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
style: Clean up cascade rule iteration. r=nordzilla
The current API was pretty awkward as a result of two things: * Not being able to create empty iterators for smallbitvec. * We used to call the `F` function multiple times, but turns out that collecting the declarations in a SmallVec was a perf win. So clean this up so that it looks more similar to other APIs, taking an iterator directly. This is a bit more code, but hopefully easier to understand (and also hopefully easier to optimize). The motivation for this work is that I plan to investigate rebasing / landing https://github.com/servo/servo/pull/20151, and I don't want more instantiations of apply_declarations and such. Differential Revision: https://phabricator.services.mozilla.com/D74369
This commit is contained in:
parent
6bae0b99ca
commit
ec6ecf7d21
4 changed files with 105 additions and 71 deletions
|
@ -978,7 +978,13 @@ where
|
||||||
} => {
|
} => {
|
||||||
let guard = declarations.read_with(context.guards.author);
|
let guard = declarations.read_with(context.guards.author);
|
||||||
|
|
||||||
let iter = || {
|
// This currently ignores visited styles, which seems acceptable,
|
||||||
|
// as existing browsers don't appear to animate visited styles.
|
||||||
|
let computed = properties::apply_declarations::<E, _>(
|
||||||
|
context.stylist.device(),
|
||||||
|
/* pseudo = */ None,
|
||||||
|
previous_style.rules(),
|
||||||
|
&context.guards,
|
||||||
// It's possible to have !important properties in keyframes
|
// It's possible to have !important properties in keyframes
|
||||||
// so we have to filter them out.
|
// so we have to filter them out.
|
||||||
// See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
|
// See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
|
||||||
|
@ -986,17 +992,7 @@ where
|
||||||
guard
|
guard
|
||||||
.normal_declaration_iter()
|
.normal_declaration_iter()
|
||||||
.filter(|declaration| declaration.is_animatable())
|
.filter(|declaration| declaration.is_animatable())
|
||||||
.map(|decl| (decl, Origin::Author))
|
.map(|decl| (decl, Origin::Author)),
|
||||||
};
|
|
||||||
|
|
||||||
// This currently ignores visited styles, which seems acceptable,
|
|
||||||
// as existing browsers don't appear to animate visited styles.
|
|
||||||
let computed = properties::apply_declarations::<E, _, _>(
|
|
||||||
context.stylist.device(),
|
|
||||||
/* pseudo = */ None,
|
|
||||||
previous_style.rules(),
|
|
||||||
&context.guards,
|
|
||||||
iter,
|
|
||||||
Some(previous_style),
|
Some(previous_style),
|
||||||
Some(previous_style),
|
Some(previous_style),
|
||||||
Some(previous_style),
|
Some(previous_style),
|
||||||
|
|
|
@ -10,8 +10,8 @@ use crate::dom::TElement;
|
||||||
use crate::font_metrics::FontMetricsProvider;
|
use crate::font_metrics::FontMetricsProvider;
|
||||||
use crate::logical_geometry::WritingMode;
|
use crate::logical_geometry::WritingMode;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::properties::{ComputedValues, StyleBuilder};
|
use crate::properties::{ComputedValues, StyleBuilder, Importance};
|
||||||
use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword};
|
use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword, PropertyFlags};
|
||||||
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
|
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
|
||||||
use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags};
|
use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags};
|
||||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||||
|
@ -20,7 +20,6 @@ use crate::selector_parser::PseudoElement;
|
||||||
use crate::stylesheets::{Origin, PerOrigin};
|
use crate::stylesheets::{Origin, PerOrigin};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use crate::shared_lock::StylesheetGuards;
|
use crate::shared_lock::StylesheetGuards;
|
||||||
use smallbitvec::SmallBitVec;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -108,6 +107,84 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DeclarationIterator<'a> {
|
||||||
|
// Global to the iteration.
|
||||||
|
guards: &'a StylesheetGuards<'a>,
|
||||||
|
restriction: Option<PropertyFlags>,
|
||||||
|
// The rule we're iterating over.
|
||||||
|
current_rule_node: Option<&'a StrongRuleNode>,
|
||||||
|
// Per rule state.
|
||||||
|
declarations: DeclarationImportanceIterator<'a>,
|
||||||
|
origin: Origin,
|
||||||
|
importance: Importance,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DeclarationIterator<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn new(rule_node: &'a StrongRuleNode, guards: &'a StylesheetGuards, pseudo: Option<&PseudoElement>) -> Self {
|
||||||
|
let restriction = pseudo.and_then(|p| p.property_restriction());
|
||||||
|
let mut iter = Self {
|
||||||
|
guards,
|
||||||
|
current_rule_node: Some(rule_node),
|
||||||
|
origin: Origin::Author,
|
||||||
|
importance: Importance::Normal,
|
||||||
|
declarations: DeclarationImportanceIterator::default(),
|
||||||
|
restriction,
|
||||||
|
};
|
||||||
|
iter.update_for_node(rule_node);
|
||||||
|
iter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_for_node(&mut self, node: &'a StrongRuleNode) {
|
||||||
|
let origin = node.cascade_level().origin();
|
||||||
|
self.origin = origin;
|
||||||
|
self.importance = node.importance();
|
||||||
|
let guard = match origin {
|
||||||
|
Origin::Author => self.guards.author,
|
||||||
|
Origin::User | Origin::UserAgent => self.guards.ua_or_user,
|
||||||
|
};
|
||||||
|
self.declarations = match node.style_source() {
|
||||||
|
Some(source) => source.read(guard).declaration_importance_iter(),
|
||||||
|
None => DeclarationImportanceIterator::default(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for DeclarationIterator<'a> {
|
||||||
|
type Item = (&'a PropertyDeclaration, Origin);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
if let Some((decl, importance)) = self.declarations.next_back() {
|
||||||
|
if self.importance != importance {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = self.origin;
|
||||||
|
if let Some(restriction) = self.restriction {
|
||||||
|
// decl.id() is either a longhand or a custom
|
||||||
|
// property. Custom properties are always allowed, but
|
||||||
|
// longhands are only allowed if they have our
|
||||||
|
// restriction flag set.
|
||||||
|
if let PropertyDeclarationId::Longhand(id) = decl.id() {
|
||||||
|
if !id.flags().contains(restriction) && origin != Origin::UserAgent {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some((decl, origin));
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_node = self.current_rule_node.take()?.parent()?;
|
||||||
|
self.current_rule_node = Some(next_node);
|
||||||
|
self.update_for_node(next_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cascade_rules<E>(
|
fn cascade_rules<E>(
|
||||||
device: &Device,
|
device: &Device,
|
||||||
pseudo: Option<&PseudoElement>,
|
pseudo: Option<&PseudoElement>,
|
||||||
|
@ -130,54 +207,12 @@ where
|
||||||
parent_style.is_some(),
|
parent_style.is_some(),
|
||||||
parent_style_ignoring_first_line.is_some()
|
parent_style_ignoring_first_line.is_some()
|
||||||
);
|
);
|
||||||
let empty = SmallBitVec::new();
|
|
||||||
let restriction = pseudo.and_then(|p| p.property_restriction());
|
|
||||||
let iter_declarations = || {
|
|
||||||
rule_node.self_and_ancestors().flat_map(|node| {
|
|
||||||
let origin = node.cascade_level().origin();
|
|
||||||
let node_importance = node.importance();
|
|
||||||
let guard = match origin {
|
|
||||||
Origin::Author => guards.author,
|
|
||||||
Origin::User | Origin::UserAgent => guards.ua_or_user,
|
|
||||||
};
|
|
||||||
let declarations = match node.style_source() {
|
|
||||||
Some(source) => source
|
|
||||||
.read(guard)
|
|
||||||
.declaration_importance_iter(),
|
|
||||||
None => DeclarationImportanceIterator::new(&[], &empty),
|
|
||||||
};
|
|
||||||
|
|
||||||
declarations
|
|
||||||
// Yield declarations later in source order (with more precedence) first.
|
|
||||||
.rev()
|
|
||||||
.filter_map(move |(declaration, declaration_importance)| {
|
|
||||||
if let Some(restriction) = restriction {
|
|
||||||
// declaration.id() is either a longhand or a custom
|
|
||||||
// property. Custom properties are always allowed, but
|
|
||||||
// longhands are only allowed if they have our
|
|
||||||
// restriction flag set.
|
|
||||||
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
|
|
||||||
if !id.flags().contains(restriction) && origin != Origin::UserAgent {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if declaration_importance == node_importance {
|
|
||||||
Some((declaration, origin))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
apply_declarations(
|
apply_declarations(
|
||||||
device,
|
device,
|
||||||
pseudo,
|
pseudo,
|
||||||
rule_node,
|
rule_node,
|
||||||
guards,
|
guards,
|
||||||
iter_declarations,
|
DeclarationIterator::new(rule_node, guards, pseudo),
|
||||||
parent_style,
|
parent_style,
|
||||||
parent_style_ignoring_first_line,
|
parent_style_ignoring_first_line,
|
||||||
layout_parent_style,
|
layout_parent_style,
|
||||||
|
@ -208,12 +243,12 @@ pub enum CascadeMode<'a> {
|
||||||
|
|
||||||
/// NOTE: This function expects the declaration with more priority to appear
|
/// NOTE: This function expects the declaration with more priority to appear
|
||||||
/// first.
|
/// first.
|
||||||
pub fn apply_declarations<'a, E, F, I>(
|
pub fn apply_declarations<'a, E, I>(
|
||||||
device: &Device,
|
device: &Device,
|
||||||
pseudo: Option<&PseudoElement>,
|
pseudo: Option<&PseudoElement>,
|
||||||
rules: &StrongRuleNode,
|
rules: &StrongRuleNode,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
iter_declarations: F,
|
iter: I,
|
||||||
parent_style: Option<&ComputedValues>,
|
parent_style: Option<&ComputedValues>,
|
||||||
parent_style_ignoring_first_line: Option<&ComputedValues>,
|
parent_style_ignoring_first_line: Option<&ComputedValues>,
|
||||||
layout_parent_style: Option<&ComputedValues>,
|
layout_parent_style: Option<&ComputedValues>,
|
||||||
|
@ -226,7 +261,6 @@ pub fn apply_declarations<'a, E, F, I>(
|
||||||
) -> Arc<ComputedValues>
|
) -> Arc<ComputedValues>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
F: Fn() -> I,
|
|
||||||
I: Iterator<Item = (&'a PropertyDeclaration, Origin)>,
|
I: Iterator<Item = (&'a PropertyDeclaration, Origin)>,
|
||||||
{
|
{
|
||||||
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
||||||
|
@ -253,7 +287,7 @@ where
|
||||||
device,
|
device,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (declaration, origin) in iter_declarations() {
|
for (declaration, origin) in iter {
|
||||||
declarations.push((declaration, origin));
|
declarations.push((declaration, origin));
|
||||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||||
builder.cascade(declaration, origin);
|
builder.cascade(declaration, origin);
|
||||||
|
|
|
@ -110,9 +110,17 @@ pub struct DeclarationImportanceIterator<'a> {
|
||||||
iter: Zip<Iter<'a, PropertyDeclaration>, smallbitvec::Iter<'a>>,
|
iter: Zip<Iter<'a, PropertyDeclaration>, smallbitvec::Iter<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Default for DeclarationImportanceIterator<'a> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
iter: [].iter().zip(smallbitvec::Iter::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> DeclarationImportanceIterator<'a> {
|
impl<'a> DeclarationImportanceIterator<'a> {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
|
fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
|
||||||
DeclarationImportanceIterator {
|
DeclarationImportanceIterator {
|
||||||
iter: declarations.iter().zip(important.iter()),
|
iter: declarations.iter().zip(important.iter()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1318,12 +1318,6 @@ impl Stylist {
|
||||||
use crate::font_metrics::get_metrics_provider_for_product;
|
use crate::font_metrics::get_metrics_provider_for_product;
|
||||||
|
|
||||||
let block = declarations.read_with(guards.author);
|
let block = declarations.read_with(guards.author);
|
||||||
let iter_declarations = || {
|
|
||||||
block
|
|
||||||
.declaration_importance_iter()
|
|
||||||
.map(|(declaration, _)| (declaration, Origin::Author))
|
|
||||||
};
|
|
||||||
|
|
||||||
let metrics = get_metrics_provider_for_product();
|
let metrics = get_metrics_provider_for_product();
|
||||||
|
|
||||||
// We don't bother inserting these declarations in the rule tree, since
|
// We don't bother inserting these declarations in the rule tree, since
|
||||||
|
@ -1332,12 +1326,14 @@ impl Stylist {
|
||||||
// TODO(emilio): Now that we fixed bug 1493420, we should consider
|
// TODO(emilio): Now that we fixed bug 1493420, we should consider
|
||||||
// reversing this as it shouldn't be slow anymore, and should avoid
|
// reversing this as it shouldn't be slow anymore, and should avoid
|
||||||
// generating two instantiations of apply_declarations.
|
// generating two instantiations of apply_declarations.
|
||||||
properties::apply_declarations::<E, _, _>(
|
properties::apply_declarations::<E, _>(
|
||||||
&self.device,
|
&self.device,
|
||||||
/* pseudo = */ None,
|
/* pseudo = */ None,
|
||||||
self.rule_tree.root(),
|
self.rule_tree.root(),
|
||||||
guards,
|
guards,
|
||||||
iter_declarations,
|
block
|
||||||
|
.declaration_importance_iter()
|
||||||
|
.map(|(declaration, _)| (declaration, Origin::Author)),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue