Merge normal and important declarations in style rules.

Have a single Vec instead of two. Fix #3426
This commit is contained in:
Simon Sapin 2016-08-19 16:14:06 +02:00
parent 577d2dc3fa
commit 16bbc2f26e
11 changed files with 248 additions and 221 deletions

View file

@ -15,7 +15,7 @@ use properties::longhands::animation_iteration_count::computed_value::AnimationI
use properties::longhands::animation_play_state::computed_value::AnimationPlayState;
use properties::longhands::transition_timing_function::computed_value::StartEnd;
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
use properties::{self, ComputedValues};
use properties::{self, ComputedValues, Importance};
use selector_matching::DeclarationBlock;
use std::sync::Arc;
use std::sync::mpsc::Sender;
@ -385,7 +385,8 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
KeyframesStepValue::Declarations(ref declarations) => {
let declaration_block = DeclarationBlock {
declarations: declarations.clone(),
mixed_declarations: declarations.clone(),
importance: Importance::Normal,
source_order: 0,
specificity: ::std::u32::MAX,
};

View file

@ -5,9 +5,9 @@
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
use cssparser::{DeclarationListParser, DeclarationParser};
use parser::{ParserContext, log_css_error};
use properties::PropertyDeclaration;
use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty;
use properties::{PropertyDeclaration, Importance};
use std::sync::Arc;
/// A number from 1 to 100, indicating the percentage of the animation where
@ -72,7 +72,12 @@ impl KeyframeSelector {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Keyframe {
pub selector: KeyframeSelector,
pub declarations: Arc<Vec<PropertyDeclaration>>,
/// `!important` is not allowed in keyframe declarations,
/// so the second value of these tuples is always `Importance::Normal`.
/// But including them enables `compute_style_for_animation_step` to create a `DeclarationBlock`
/// by cloning an `Arc<_>` (incrementing a reference count) rather than re-creating a `Vec<_>`.
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
}
/// A keyframes step value. This can be a synthetised keyframes animation, that
@ -82,7 +87,8 @@ pub struct Keyframe {
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue {
Declarations(Arc<Vec<PropertyDeclaration>>),
/// See `Keyframe::declarations`s docs about the presence of `Importance`.
Declarations(Arc<Vec<(PropertyDeclaration, Importance)>>),
ComputedValues,
}
@ -108,7 +114,7 @@ impl KeyframesStep {
value: KeyframesStepValue) -> Self {
let declared_timing_function = match value {
KeyframesStepValue::Declarations(ref declarations) => {
declarations.iter().any(|prop_decl| {
declarations.iter().any(|&(ref prop_decl, _)| {
match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => true,
_ => false,
@ -148,8 +154,8 @@ fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
let mut ret = vec![];
// NB: declarations are already deduplicated, so we don't have to check for
// it here.
for declaration in keyframe.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(&declaration) {
for &(ref declaration, _) in keyframe.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(declaration) {
ret.push(property);
}
}
@ -247,7 +253,7 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() {
match declaration {
Ok(d) => declarations.extend(d),
Ok(d) => declarations.extend(d.into_iter().map(|d| (d, Importance::Normal))),
Err(range) => {
let pos = range.start;
let message = format!("Unsupported keyframe property declaration: '{}'",

View file

@ -14,7 +14,7 @@ use context::{StyleContext, SharedStyleContext};
use data::PrivateStyleData;
use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
use properties::longhands::display::computed_value as display;
use properties::{ComputedValues, PropertyDeclaration, cascade};
use properties::{ComputedValues, cascade};
use selector_impl::{TheSelectorImpl, PseudoElement};
use selector_matching::{DeclarationBlock, Stylist};
use selectors::bloom::BloomFilter;
@ -118,15 +118,11 @@ impl<'a> ApplicableDeclarationsCacheQuery<'a> {
impl<'a> PartialEq for ApplicableDeclarationsCacheQuery<'a> {
fn eq(&self, other: &ApplicableDeclarationsCacheQuery<'a>) -> bool {
if self.declarations.len() != other.declarations.len() {
return false
}
for (this, other) in self.declarations.iter().zip(other.declarations) {
if !arc_ptr_eq(&this.declarations, &other.declarations) {
return false
}
}
true
self.declarations.len() == other.declarations.len() &&
self.declarations.iter().zip(other.declarations).all(|(this, other)| {
arc_ptr_eq(&this.mixed_declarations, &other.mixed_declarations) &&
this.importance == other.importance
})
}
}
impl<'a> Eq for ApplicableDeclarationsCacheQuery<'a> {}
@ -143,8 +139,9 @@ impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> {
for declaration in self.declarations {
// Each declaration contians an Arc, which is a stable
// pointer; we use that for hashing and equality.
let ptr = &*declaration.declarations as *const Vec<PropertyDeclaration>;
let ptr: *const Vec<_> = &*declaration.mixed_declarations;
ptr.hash(state);
declaration.importance.hash(state);
}
}
}

View file

@ -14,8 +14,6 @@ use std::ascii::AsciiExt;
use std::boxed::Box as StdBox;
use std::collections::HashSet;
use std::fmt::{self, Write};
use std::iter::{Iterator, Chain, Zip, Repeat, repeat};
use std::slice;
use std::sync::Arc;
use app_units::Au;
@ -264,7 +262,7 @@ mod property_bit_field {
% endfor
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Importance {
/// Indicates a declaration without `!important`.
Normal,
@ -288,21 +286,7 @@ impl Importance {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PropertyDeclarationBlock {
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub important: Arc<Vec<PropertyDeclaration>>,
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub normal: Arc<Vec<PropertyDeclaration>>,
}
impl PropertyDeclarationBlock {
/// Provides an iterator of all declarations, with indication of !important value
pub fn declarations(&self) -> Chain<
Zip<slice::Iter<PropertyDeclaration>, Repeat<Importance>>,
Zip<slice::Iter<PropertyDeclaration>, Repeat<Importance>>
> {
let normal = self.normal.iter().zip(repeat(Importance::Normal));
let important = self.important.iter().zip(repeat(Importance::Important));
normal.chain(important)
}
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
}
impl ToCss for PropertyDeclarationBlock {
@ -316,7 +300,7 @@ impl ToCss for PropertyDeclarationBlock {
let mut already_serialized = Vec::new();
// Step 3
for (declaration, importance) in self.declarations() {
for &(ref declaration, importance) in &*self.declarations {
// Step 3.1
let property = declaration.name();
@ -330,7 +314,7 @@ impl ToCss for PropertyDeclarationBlock {
if !shorthands.is_empty() {
// Step 3.3.1
let mut longhands = self.declarations()
let mut longhands = self.declarations.iter()
.filter(|d| !already_serialized.contains(&d.0.name()))
.collect::<Vec<_>>();
@ -342,7 +326,7 @@ impl ToCss for PropertyDeclarationBlock {
let mut current_longhands = Vec::new();
let mut important_count = 0;
for &(longhand, longhand_importance) in longhands.iter() {
for &&(ref longhand, longhand_importance) in longhands.iter() {
let longhand_name = longhand.name();
if properties.iter().any(|p| &longhand_name == *p) {
current_longhands.push(longhand);
@ -386,7 +370,7 @@ impl ToCss for PropertyDeclarationBlock {
for current_longhand in current_longhands {
// Substep 9
already_serialized.push(current_longhand.name());
let index_to_remove = longhands.iter().position(|l| l.0 == current_longhand);
let index_to_remove = longhands.iter().position(|l| l.0 == *current_longhand);
if let Some(index) = index_to_remove {
// Substep 10
longhands.remove(index);
@ -557,8 +541,7 @@ impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser)
-> PropertyDeclarationBlock {
let mut important_declarations = Vec::new();
let mut normal_declarations = Vec::new();
let mut declarations = Vec::new();
let parser = PropertyDeclarationParser {
context: context,
};
@ -566,11 +549,7 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
while let Some(declaration) = iter.next() {
match declaration {
Ok((results, importance)) => {
if importance.important() {
important_declarations.extend(results);
} else {
normal_declarations.extend(results);
}
declarations.extend(results.into_iter().map(|d| (d, importance)))
}
Err(range) => {
let pos = range.start;
@ -581,46 +560,79 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
}
}
PropertyDeclarationBlock {
important: Arc::new(deduplicate_property_declarations(important_declarations)),
normal: Arc::new(deduplicate_property_declarations(normal_declarations)),
declarations: Arc::new(deduplicate_property_declarations(declarations)),
}
}
/// Only keep the last declaration for any given property.
/// Only keep the "winning" declaration for any given property, by importance then source order.
/// The input and output are in source order
fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>)
-> Vec<PropertyDeclaration> {
let mut deduplicated = vec![];
let mut seen = PropertyBitField::new();
let mut seen_custom = Vec::new();
for declaration in declarations.into_iter().rev() {
fn deduplicate_property_declarations(declarations: Vec<(PropertyDeclaration, Importance)>)
-> Vec<(PropertyDeclaration, Importance)> {
let mut deduplicated = Vec::new();
let mut seen_normal = PropertyBitField::new();
let mut seen_important = PropertyBitField::new();
let mut seen_custom_normal = Vec::new();
let mut seen_custom_important = Vec::new();
for (declaration, importance) in declarations.into_iter().rev() {
match declaration {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(..) => {
% if not property.derived_from:
if seen.get_${property.ident}() {
continue
if importance.important() {
if seen_important.get_${property.ident}() {
continue
}
if seen_normal.get_${property.ident}() {
remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::${property.camel_case}(..), _))
})
}
seen_important.set_${property.ident}()
} else {
if seen_normal.get_${property.ident}() ||
seen_important.get_${property.ident}() {
continue
}
seen_normal.set_${property.ident}()
}
seen.set_${property.ident}()
% else:
unreachable!();
% endif
},
% endfor
PropertyDeclaration::Custom(ref name, _) => {
if seen_custom.contains(name) {
continue
if importance.important() {
if seen_custom_important.contains(name) {
continue
}
if seen_custom_normal.contains(name) {
remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::Custom(ref n, _), _) if n == name)
})
}
seen_custom_important.push(name.clone())
} else {
if seen_custom_normal.contains(name) ||
seen_custom_important.contains(name) {
continue
}
seen_custom_normal.push(name.clone())
}
seen_custom.push(name.clone())
}
}
deduplicated.push(declaration)
deduplicated.push((declaration, importance))
}
deduplicated.reverse();
deduplicated
}
#[inline]
fn remove_one<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, mut remove_this: F) {
let previous_len = v.len();
v.retain(|x| !remove_this(x));
debug_assert_eq!(v.len(), previous_len - 1);
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CSSWideKeyword {
@ -1701,7 +1713,7 @@ fn cascade_with_cached_declarations(
// Declaration blocks are stored in increasing precedence order,
// we want them in decreasing order here.
for sub_list in applicable_declarations.iter().rev() {
for declaration in sub_list.declarations.iter().rev() {
for declaration in sub_list.iter().rev() {
match *declaration {
% for style_struct in data.active_style_structs():
% for property in style_struct.longhands:
@ -1832,7 +1844,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
let mut custom_properties = None;
let mut seen_custom = HashSet::new();
for sub_list in applicable_declarations.iter().rev() {
for declaration in sub_list.declarations.iter().rev() {
for declaration in sub_list.iter().rev() {
match *declaration {
PropertyDeclaration::Custom(ref name, ref value) => {
::custom_properties::cascade(
@ -1890,7 +1902,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
ComputedValues::do_cascade_property(|cascade_property| {
% for category_to_cascade_now in ["early", "other"]:
for sub_list in applicable_declarations.iter().rev() {
for declaration in sub_list.declarations.iter().rev() {
for declaration in sub_list.iter().rev() {
if let PropertyDeclaration::Custom(..) = *declaration {
continue
}

View file

@ -9,7 +9,7 @@ use element_state::*;
use error_reporting::StdoutErrorReporter;
use keyframes::KeyframesAnimation;
use media_queries::{Device, MediaType};
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock, ComputedValues};
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock, ComputedValues, Importance};
use quickersort::sort_by;
use restyle_hints::{RestyleHint, DependencySet};
use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
@ -25,6 +25,7 @@ use std::collections::HashMap;
use std::fmt;
use std::hash::BuildHasherDefault;
use std::hash::Hash;
use std::slice;
use std::sync::Arc;
use string_cache::Atom;
use style_traits::viewport::ViewportConstraints;
@ -163,8 +164,8 @@ impl Stylist {
// Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority.
macro_rules! append(
($style_rule: ident, $priority: ident) => {
if !$style_rule.declarations.$priority.is_empty() {
($style_rule: ident, $priority: ident, $importance: expr) => {
if !$style_rule.declarations.declarations.is_empty() {
for selector in &$style_rule.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element {
self.pseudos_map
@ -179,7 +180,8 @@ impl Stylist {
selector: selector.complex_selector.clone(),
declarations: DeclarationBlock {
specificity: selector.specificity,
declarations: $style_rule.declarations.$priority.clone(),
mixed_declarations: $style_rule.declarations.declarations.clone(),
importance: $importance,
source_order: rules_source_order,
},
});
@ -191,8 +193,8 @@ impl Stylist {
for rule in stylesheet.effective_rules(&self.device) {
match *rule {
CSSRule::Style(ref style_rule) => {
append!(style_rule, normal);
append!(style_rule, important);
append!(style_rule, normal, Importance::Normal);
append!(style_rule, important, Importance::Important);
rules_source_order += 1;
for selector in &style_rule.selectors {
@ -397,7 +399,9 @@ impl Stylist {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(sa.normal.clone()));
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Normal));
}
debug!("style attr: {:?}", relations);
@ -414,7 +418,9 @@ impl Stylist {
if let Some(ref sa) = style_attribute {
Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(sa.important.clone()));
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Important));
}
debug!("style attr important: {:?}", relations);
@ -829,20 +835,64 @@ pub struct Rule {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, Clone)]
pub struct DeclarationBlock {
pub declarations: Arc<Vec<PropertyDeclaration>>,
/// Contains declarations of either importance, but only those of self.importance are relevant.
/// Use DeclarationBlock::iter
pub mixed_declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
pub importance: Importance,
pub source_order: usize,
pub specificity: u32,
}
impl DeclarationBlock {
#[inline]
pub fn from_declarations(declarations: Arc<Vec<PropertyDeclaration>>) -> Self {
pub fn from_declarations(declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
importance: Importance)
-> Self {
DeclarationBlock {
declarations: declarations,
mixed_declarations: declarations,
importance: importance,
source_order: 0,
specificity: 0,
}
}
pub fn iter(&self) -> DeclarationBlockIter {
DeclarationBlockIter {
iter: self.mixed_declarations.iter(),
importance: self.importance,
}
}
}
pub struct DeclarationBlockIter<'a> {
iter: slice::Iter<'a, (PropertyDeclaration, Importance)>,
importance: Importance,
}
impl<'a> Iterator for DeclarationBlockIter<'a> {
type Item = &'a PropertyDeclaration;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
while let Some(&(ref declaration, importance)) = self.iter.next() {
if importance == self.importance {
return Some(declaration)
}
}
None
}
}
impl<'a> DoubleEndedIterator for DeclarationBlockIter<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(&(ref declaration, importance)) = self.iter.next_back() {
if importance == self.importance {
return Some(declaration)
}
}
None
}
}
fn find_push<Str: Eq + Hash>(map: &mut HashMap<Str, Vec<Rule>>, key: Str, value: Rule) {