Only cascade at a priority level rules that have declarations of that priority.

This commit is contained in:
Simon Sapin 2016-08-19 17:13:20 +02:00
parent 16bbc2f26e
commit a175c9981e
6 changed files with 99 additions and 36 deletions

View file

@ -767,6 +767,7 @@ impl Element {
.position(|&(ref decl, _)| decl.matches(property)); .position(|&(ref decl, _)| decl.matches(property));
if let Some(index) = index { if let Some(index) = index {
Arc::make_mut(&mut declarations.declarations).remove(index); Arc::make_mut(&mut declarations.declarations).remove(index);
declarations.recalc_any();
} }
} }
} }
@ -781,10 +782,11 @@ impl Element {
fn update(element: &Element, declarations: Vec<PropertyDeclaration>, fn update(element: &Element, declarations: Vec<PropertyDeclaration>,
importance: Importance) { importance: Importance) {
let mut inline_declarations = element.style_attribute().borrow_mut(); let mut inline_declarations = element.style_attribute().borrow_mut();
if let &mut Some(ref mut existing_declarations) = &mut *inline_declarations { if let &mut Some(ref mut declaration_block) = &mut *inline_declarations {
{
// Usually, the reference count will be 1 here. But transitions could make it greater // Usually, the reference count will be 1 here. But transitions could make it greater
// than that. // than that.
let existing_declarations = Arc::make_mut(&mut existing_declarations.declarations); let existing_declarations = Arc::make_mut(&mut declaration_block.declarations);
'outer: for incoming_declaration in declarations { 'outer: for incoming_declaration in declarations {
for existing_declaration in &mut *existing_declarations { for existing_declaration in &mut *existing_declarations {
@ -795,12 +797,15 @@ impl Element {
} }
existing_declarations.push((incoming_declaration, importance)); existing_declarations.push((incoming_declaration, importance));
} }
}
declaration_block.recalc_any();
return; return;
} }
*inline_declarations = Some(PropertyDeclarationBlock { *inline_declarations = Some(PropertyDeclarationBlock {
declarations: Arc::new(declarations.into_iter().map(|d| (d, importance)).collect()), declarations: Arc::new(declarations.into_iter().map(|d| (d, importance)).collect()),
any_important: importance.important(),
any_normal: !importance.important(),
}); });
} }

View file

@ -287,6 +287,28 @@ impl Importance {
pub struct PropertyDeclarationBlock { pub struct PropertyDeclarationBlock {
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")] #[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>, pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
/// Whether `self.declaration` contains at least one declaration with `Importance::Normal`
pub any_normal: bool,
/// Whether `self.declaration` contains at least one declaration with `Importance::Important`
pub any_important: bool,
}
impl PropertyDeclarationBlock {
pub fn recalc_any(&mut self) {
let mut any_normal = false;
let mut any_important = false;
for &(_, importance) in &*self.declarations {
if importance.important() {
any_important = true
} else {
any_normal = true
}
}
self.any_normal = any_normal;
self.any_important = any_important;
}
} }
impl ToCss for PropertyDeclarationBlock { impl ToCss for PropertyDeclarationBlock {
@ -542,6 +564,8 @@ impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser) pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser)
-> PropertyDeclarationBlock { -> PropertyDeclarationBlock {
let mut declarations = Vec::new(); let mut declarations = Vec::new();
let mut any_normal = false;
let mut any_important = false;
let parser = PropertyDeclarationParser { let parser = PropertyDeclarationParser {
context: context, context: context,
}; };
@ -549,6 +573,11 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
while let Some(declaration) = iter.next() { while let Some(declaration) = iter.next() {
match declaration { match declaration {
Ok((results, importance)) => { Ok((results, importance)) => {
if importance.important() {
any_important = true
} else {
any_normal = true
}
declarations.extend(results.into_iter().map(|d| (d, importance))) declarations.extend(results.into_iter().map(|d| (d, importance)))
} }
Err(range) => { Err(range) => {
@ -559,16 +588,24 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
} }
} }
} }
PropertyDeclarationBlock { let (declarations, removed_any) = deduplicate_property_declarations(declarations);
declarations: Arc::new(deduplicate_property_declarations(declarations)), let mut block = PropertyDeclarationBlock {
declarations: Arc::new(declarations),
any_normal: any_normal,
any_important: any_important,
};
if removed_any {
block.recalc_any();
} }
block
} }
/// Only keep the "winning" declaration for any given property, by importance then source order. /// Only keep the "winning" declaration for any given property, by importance then source order.
/// The input and output are in source order /// The input and output are in source order
fn deduplicate_property_declarations(declarations: Vec<(PropertyDeclaration, Importance)>) fn deduplicate_property_declarations(declarations: Vec<(PropertyDeclaration, Importance)>)
-> Vec<(PropertyDeclaration, Importance)> { -> (Vec<(PropertyDeclaration, Importance)>, bool) {
let mut removed_any = false;
let mut deduplicated = Vec::new(); let mut deduplicated = Vec::new();
let mut seen_normal = PropertyBitField::new(); let mut seen_normal = PropertyBitField::new();
let mut seen_important = PropertyBitField::new(); let mut seen_important = PropertyBitField::new();
@ -581,17 +618,20 @@ fn deduplicate_property_declarations(declarations: Vec<(PropertyDeclaration, Imp
% if not property.derived_from: % if not property.derived_from:
if importance.important() { if importance.important() {
if seen_important.get_${property.ident}() { if seen_important.get_${property.ident}() {
removed_any = true;
continue continue
} }
if seen_normal.get_${property.ident}() { if seen_normal.get_${property.ident}() {
remove_one(&mut deduplicated, |d| { remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::${property.camel_case}(..), _)) matches!(d, &(PropertyDeclaration::${property.camel_case}(..), _))
}) });
removed_any = true;
} }
seen_important.set_${property.ident}() seen_important.set_${property.ident}()
} else { } else {
if seen_normal.get_${property.ident}() || if seen_normal.get_${property.ident}() ||
seen_important.get_${property.ident}() { seen_important.get_${property.ident}() {
removed_any = true;
continue continue
} }
seen_normal.set_${property.ident}() seen_normal.set_${property.ident}()
@ -604,17 +644,20 @@ fn deduplicate_property_declarations(declarations: Vec<(PropertyDeclaration, Imp
PropertyDeclaration::Custom(ref name, _) => { PropertyDeclaration::Custom(ref name, _) => {
if importance.important() { if importance.important() {
if seen_custom_important.contains(name) { if seen_custom_important.contains(name) {
removed_any = true;
continue continue
} }
if seen_custom_normal.contains(name) { if seen_custom_normal.contains(name) {
remove_one(&mut deduplicated, |d| { remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::Custom(ref n, _), _) if n == name) matches!(d, &(PropertyDeclaration::Custom(ref n, _), _) if n == name)
}) });
removed_any = true;
} }
seen_custom_important.push(name.clone()) seen_custom_important.push(name.clone())
} else { } else {
if seen_custom_normal.contains(name) || if seen_custom_normal.contains(name) ||
seen_custom_important.contains(name) { seen_custom_important.contains(name) {
removed_any = true;
continue continue
} }
seen_custom_normal.push(name.clone()) seen_custom_normal.push(name.clone())
@ -624,7 +667,7 @@ fn deduplicate_property_declarations(declarations: Vec<(PropertyDeclaration, Imp
deduplicated.push((declaration, importance)) deduplicated.push((declaration, importance))
} }
deduplicated.reverse(); deduplicated.reverse();
deduplicated (deduplicated, removed_any)
} }
#[inline] #[inline]

View file

@ -164,8 +164,8 @@ impl Stylist {
// Take apart the StyleRule into individual Rules and insert // Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority. // them into the SelectorMap of that priority.
macro_rules! append( macro_rules! append(
($style_rule: ident, $priority: ident, $importance: expr) => { ($style_rule: ident, $priority: ident, $importance: expr, $any: ident) => {
if !$style_rule.declarations.declarations.is_empty() { if $style_rule.declarations.$any {
for selector in &$style_rule.selectors { for selector in &$style_rule.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element { let map = if let Some(ref pseudo) = selector.pseudo_element {
self.pseudos_map self.pseudos_map
@ -193,8 +193,8 @@ impl Stylist {
for rule in stylesheet.effective_rules(&self.device) { for rule in stylesheet.effective_rules(&self.device) {
match *rule { match *rule {
CSSRule::Style(ref style_rule) => { CSSRule::Style(ref style_rule) => {
append!(style_rule, normal, Importance::Normal); append!(style_rule, normal, Importance::Normal, any_normal);
append!(style_rule, important, Importance::Important); append!(style_rule, important, Importance::Important, any_important);
rules_source_order += 1; rules_source_order += 1;
for selector in &style_rule.selectors { for selector in &style_rule.selectors {
@ -396,6 +396,7 @@ impl Stylist {
// Step 4: Normal style attributes. // Step 4: Normal style attributes.
if let Some(ref sa) = style_attribute { if let Some(ref sa) = style_attribute {
if sa.any_normal {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE; relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push( Push::push(
applicable_declarations, applicable_declarations,
@ -403,6 +404,7 @@ impl Stylist {
sa.declarations.clone(), sa.declarations.clone(),
Importance::Normal)); Importance::Normal));
} }
}
debug!("style attr: {:?}", relations); debug!("style attr: {:?}", relations);
@ -416,12 +418,15 @@ impl Stylist {
// Step 6: `!important` style attributes. // Step 6: `!important` style attributes.
if let Some(ref sa) = style_attribute { if let Some(ref sa) = style_attribute {
if sa.any_important {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push( Push::push(
applicable_declarations, applicable_declarations,
DeclarationBlock::from_declarations( DeclarationBlock::from_declarations(
sa.declarations.clone(), sa.declarations.clone(),
Importance::Important)); Importance::Important));
} }
}
debug!("style attr important: {:?}", relations); debug!("style attr important: {:?}", relations);

View file

@ -40,10 +40,10 @@ macro_rules! sizeof_checker (
// Update the sizes here // Update the sizes here
sizeof_checker!(size_event_target, EventTarget, 40); sizeof_checker!(size_event_target, EventTarget, 40);
sizeof_checker!(size_node, Node, 160); sizeof_checker!(size_node, Node, 160);
sizeof_checker!(size_element, Element, 328); sizeof_checker!(size_element, Element, 336);
sizeof_checker!(size_htmlelement, HTMLElement, 344); sizeof_checker!(size_htmlelement, HTMLElement, 352);
sizeof_checker!(size_div, HTMLDivElement, 344); sizeof_checker!(size_div, HTMLDivElement, 352);
sizeof_checker!(size_span, HTMLSpanElement, 344); sizeof_checker!(size_span, HTMLSpanElement, 352);
sizeof_checker!(size_text, Text, 192); sizeof_checker!(size_text, Text, 192);
sizeof_checker!(size_characterdata, CharacterData, 192); sizeof_checker!(size_characterdata, CharacterData, 192);
sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16); sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16);

View file

@ -46,6 +46,8 @@ fn property_declaration_block_should_serialize_correctly() {
let block = PropertyDeclarationBlock { let block = PropertyDeclarationBlock {
declarations: Arc::new(declarations), declarations: Arc::new(declarations),
any_important: true,
any_normal: true,
}; };
let css_string = block.to_css_string(); let css_string = block.to_css_string();
@ -62,6 +64,8 @@ mod shorthand_serialization {
pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String { pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String {
let block = PropertyDeclarationBlock { let block = PropertyDeclarationBlock {
declarations: Arc::new(properties.into_iter().map(|d| (d, Importance::Normal)).collect()), declarations: Arc::new(properties.into_iter().map(|d| (d, Importance::Normal)).collect()),
any_important: false,
any_normal: true,
}; };
block.to_css_string() block.to_css_string()

View file

@ -93,6 +93,8 @@ fn test_parse_stylesheet() {
longhands::display::SpecifiedValue::none)), longhands::display::SpecifiedValue::none)),
Importance::Important), Importance::Important),
]), ]),
any_normal: false,
any_important: true,
}, },
}), }),
CSSRule::Style(StyleRule { CSSRule::Style(StyleRule {
@ -138,6 +140,8 @@ fn test_parse_stylesheet() {
longhands::display::SpecifiedValue::block)), longhands::display::SpecifiedValue::block)),
Importance::Normal), Importance::Normal),
]), ]),
any_normal: true,
any_important: false,
}, },
}), }),
CSSRule::Style(StyleRule { CSSRule::Style(StyleRule {
@ -192,6 +196,8 @@ fn test_parse_stylesheet() {
(PropertyDeclaration::BackgroundClip(DeclaredValue::Initial), (PropertyDeclaration::BackgroundClip(DeclaredValue::Initial),
Importance::Normal), Importance::Normal),
]), ]),
any_normal: true,
any_important: false,
}, },
}), }),
CSSRule::Keyframes(KeyframesRule { CSSRule::Keyframes(KeyframesRule {