diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 9d366073045..d6c3180acd2 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -13,7 +13,7 @@ use crate::media_queries::Device; use crate::properties::{ComputedValues, StyleBuilder}; use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword}; use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; -use crate::properties::CASCADE_PROPERTY; +use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags}; use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_tree::StrongRuleNode; use crate::selector_parser::PseudoElement; @@ -411,6 +411,7 @@ struct Cascade<'a, 'b: 'a> { context: &'a mut computed::Context<'b>, cascade_mode: CascadeMode<'a>, seen: LonghandIdSet, + author_specified: LonghandIdSet, reverted: PerOrigin, } @@ -420,6 +421,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { context, cascade_mode, seen: LonghandIdSet::default(), + author_specified: LonghandIdSet::default(), reverted: Default::default(), } } @@ -557,6 +559,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { } self.seen.insert(physical_longhand_id); + if origin == Origin::Author { + self.author_specified.insert(physical_longhand_id); + } let unset = css_wide_keyword.map_or(false, |css_wide_keyword| { match css_wide_keyword { @@ -679,6 +684,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { if let Some(svg) = builder.get_svg_if_mutated() { svg.fill_arrays(); } + + } + + if self.author_specified.contains_any(LonghandIdSet::border_background_properties()) { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND); + } + if self.author_specified.contains_any(LonghandIdSet::padding_properties()) { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING); } #[cfg(feature = "servo")] @@ -699,12 +712,26 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { None => return false, }; - let cached_style = match cache.find(guards, &self.context.builder) { + let builder = &mut self.context.builder; + + let cached_style = match cache.find(guards, &builder) { Some(style) => style, None => return false, }; - self.context.builder.copy_reset_from(cached_style); + builder.copy_reset_from(cached_style); + + // We're using the same reset style as another element, and we'll skip + // applying the relevant properties. So we need to do the relevant + // bookkeeping here to keep these two bits correct. + // + // Note that all the properties involved are non-inherited, so we don't + // need to do anything else other than just copying the bits over. + let reset_props_bits = + ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND | + ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING; + builder.add_flags(cached_style.flags & reset_props_bits); + true } diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index b5294093adf..221363ba4b1 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -70,6 +70,20 @@ bitflags! { /// Whether this element is inside an `opacity: 0` subtree. const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 12; + + /// Whether there are author-specified rules for border-* properties + /// (except border-image-*), background-color, or background-image. + /// + /// TODO(emilio): Maybe do include border-image, see: + /// + /// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845 + const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 13; + + /// Whether there are author-specified rules for padding-* properties. + /// + /// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see + /// https://github.com/w3c/csswg-drafts/issues/4777 + const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 14; } } diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index c39dc6f23da..b739ca63173 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -750,6 +750,52 @@ static ${name}: LonghandIdSet = LonghandIdSet { }; +<% + logical_groups = defaultdict(list) + for prop in data.longhands: + if prop.logical_group: + logical_groups[prop.logical_group].append(prop) + + for group, props in logical_groups.iteritems(): + logical_count = sum(1 for p in props if p.logical) + if logical_count * 2 != len(props): + raise RuntimeError("Logical group {} has ".format(group) + + "unbalanced logical / physical properties") + + FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data) + FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data) + MARKER_RESTRICTIONS = PropertyRestrictions.marker(data) + PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data) + CUE_RESTRICTIONS = PropertyRestrictions.cue(data) + + def restriction_flags(property): + name = property.name + flags = [] + if name in FIRST_LINE_RESTRICTIONS: + flags.append("APPLIES_TO_FIRST_LINE") + if name in FIRST_LETTER_RESTRICTIONS: + flags.append("APPLIES_TO_FIRST_LETTER") + if name in PLACEHOLDER_RESTRICTIONS: + flags.append("APPLIES_TO_PLACEHOLDER") + if name in MARKER_RESTRICTIONS: + flags.append("APPLIES_TO_MARKER") + if name in CUE_RESTRICTIONS: + flags.append("APPLIES_TO_CUE") + return flags + +%> + +/// A group for properties which may override each other +/// via logical resolution. +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub enum LogicalGroup { + % for group in logical_groups.iterkeys(): + /// ${group} + ${to_camel_case(group)}, + % endfor +} + + /// A set of longhand properties #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)] pub struct LonghandIdSet { @@ -837,6 +883,29 @@ impl LonghandIdSet { &HAS_NO_EFFECT_ON_SCROLLBARS } + /// Returns the set of padding properties for the purpose of disabling + /// native appearance. + #[inline] + pub fn padding_properties() -> &'static Self { + <% assert "padding" in logical_groups %> + ${static_longhand_id_set( + "PADDING_PROPERTIES", + lambda p: p.logical_group == "padding" + )} + &PADDING_PROPERTIES + } + + /// Returns the set of border properties for the purpose of disabling native + /// appearance. + #[inline] + pub fn border_background_properties() -> &'static Self { + ${static_longhand_id_set( + "BORDER_BACKGROUND_PROPERTIES", + lambda p: (p.logical_group and p.logical_group.startswith("border")) or p.name in ["background-color", "background-image"] + )} + &BORDER_BACKGROUND_PROPERTIES + } + /// Iterate over the current longhand id set. pub fn iter(&self) -> LonghandIdSetIterator { LonghandIdSetIterator { longhands: self, cur: 0, } @@ -998,51 +1067,6 @@ bitflags! { } } -<% - logical_groups = defaultdict(list) - for prop in data.longhands: - if prop.logical_group: - logical_groups[prop.logical_group].append(prop) - - for group, props in logical_groups.iteritems(): - logical_count = sum(1 for p in props if p.logical) - if logical_count * 2 != len(props): - raise RuntimeError("Logical group {} has ".format(group) + - "unbalanced logical / physical properties") - - FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data) - FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data) - MARKER_RESTRICTIONS = PropertyRestrictions.marker(data) - PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data) - CUE_RESTRICTIONS = PropertyRestrictions.cue(data) - - def restriction_flags(property): - name = property.name - flags = [] - if name in FIRST_LINE_RESTRICTIONS: - flags.append("APPLIES_TO_FIRST_LINE") - if name in FIRST_LETTER_RESTRICTIONS: - flags.append("APPLIES_TO_FIRST_LETTER") - if name in PLACEHOLDER_RESTRICTIONS: - flags.append("APPLIES_TO_PLACEHOLDER") - if name in MARKER_RESTRICTIONS: - flags.append("APPLIES_TO_MARKER") - if name in CUE_RESTRICTIONS: - flags.append("APPLIES_TO_CUE") - return flags - -%> - -/// A group for properties which may override each other -/// via logical resolution. -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -pub enum LogicalGroup { - % for group in logical_groups.iterkeys(): - /// ${group} - ${to_camel_case(group)}, - % endfor -} - /// An identifier for a given longhand property. #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] #[repr(u16)] diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index ddac85a8075..484cdee5183 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -7,8 +7,6 @@ //! The rule tree. use crate::applicable_declarations::ApplicableDeclarationList; -#[cfg(feature = "gecko")] -use crate::gecko::selector_parser::PseudoElement; use crate::hash::{self, FxHashMap}; use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; @@ -1418,198 +1416,6 @@ impl StrongRuleNode { } } - /// Returns true if any properties specified by `rule_type_mask` was set by - /// an author rule. - #[cfg(feature = "gecko")] - pub fn has_author_specified_rules( - &self, - mut element: E, - mut pseudo: Option, - guards: &StylesheetGuards, - rule_type_mask: u32, - author_colors_allowed: bool, - ) -> bool - where - E: crate::dom::TElement, - { - use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BACKGROUND; - use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BORDER; - use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_PADDING; - use crate::properties::{CSSWideKeyword, LonghandId}; - use crate::properties::{PropertyDeclaration, PropertyDeclarationId}; - use std::borrow::Cow; - - // Reset properties: - const BACKGROUND_PROPS: &'static [LonghandId] = - &[LonghandId::BackgroundColor, LonghandId::BackgroundImage]; - - const BORDER_PROPS: &'static [LonghandId] = &[ - LonghandId::BorderTopColor, - LonghandId::BorderTopStyle, - LonghandId::BorderTopWidth, - LonghandId::BorderRightColor, - LonghandId::BorderRightStyle, - LonghandId::BorderRightWidth, - LonghandId::BorderBottomColor, - LonghandId::BorderBottomStyle, - LonghandId::BorderBottomWidth, - LonghandId::BorderLeftColor, - LonghandId::BorderLeftStyle, - LonghandId::BorderLeftWidth, - LonghandId::BorderTopLeftRadius, - LonghandId::BorderTopRightRadius, - LonghandId::BorderBottomRightRadius, - LonghandId::BorderBottomLeftRadius, - LonghandId::BorderInlineStartColor, - LonghandId::BorderInlineStartStyle, - LonghandId::BorderInlineStartWidth, - LonghandId::BorderInlineEndColor, - LonghandId::BorderInlineEndStyle, - LonghandId::BorderInlineEndWidth, - LonghandId::BorderBlockStartColor, - LonghandId::BorderBlockStartStyle, - LonghandId::BorderBlockStartWidth, - LonghandId::BorderBlockEndColor, - LonghandId::BorderBlockEndStyle, - LonghandId::BorderBlockEndWidth, - ]; - - const PADDING_PROPS: &'static [LonghandId] = &[ - LonghandId::PaddingTop, - LonghandId::PaddingRight, - LonghandId::PaddingBottom, - LonghandId::PaddingLeft, - LonghandId::PaddingInlineStart, - LonghandId::PaddingInlineEnd, - LonghandId::PaddingBlockStart, - LonghandId::PaddingBlockEnd, - ]; - - // Set of properties that we are currently interested in. - let mut properties = LonghandIdSet::new(); - - if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 { - for id in BACKGROUND_PROPS { - properties.insert(*id); - } - } - if rule_type_mask & NS_AUTHOR_SPECIFIED_BORDER != 0 { - for id in BORDER_PROPS { - properties.insert(*id); - } - } - if rule_type_mask & NS_AUTHOR_SPECIFIED_PADDING != 0 { - for id in PADDING_PROPS { - properties.insert(*id); - } - } - - // If author colors are not allowed, don't look at those properties - // (except for background-color which is special and we handle below). - if !author_colors_allowed { - properties.remove_all(LonghandIdSet::ignored_when_colors_disabled()); - if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 { - properties.insert(LonghandId::BackgroundColor); - } - } - - let mut element_rule_node = Cow::Borrowed(self); - - loop { - // We need to be careful not to count styles covered up by - // user-important or UA-important declarations. But we do want to - // catch explicit inherit styling in those and check our parent - // element to see whether we have user styling for those properties. - // Note that we don't care here about inheritance due to lack of a - // specified value, since all the properties we care about are reset - // properties. - - let mut inherited_properties = LonghandIdSet::new(); - let mut have_explicit_ua_inherit = false; - - for node in element_rule_node.self_and_ancestors() { - let source = node.style_source(); - let declarations = if source.is_some() { - source - .as_ref() - .unwrap() - .read(node.cascade_level().guard(guards)) - .declaration_importance_iter() - } else { - continue; - }; - - // Iterate over declarations of the longhands we care about. - let node_importance = node.importance(); - let longhands = declarations.rev().filter_map(|(declaration, importance)| { - if importance != node_importance { - return None; - } - match declaration.id() { - PropertyDeclarationId::Longhand(id) => Some((id, declaration)), - _ => None, - } - }); - - let is_author = node.cascade_level().origin() == Origin::Author; - for (id, declaration) in longhands { - if !properties.contains(id) { - continue; - } - - if is_author { - if !author_colors_allowed { - if let PropertyDeclaration::BackgroundColor(ref color) = *declaration { - if color.is_transparent() { - return true; - } - continue; - } - } - return true; - } - - // This property was set by a non-author rule. - // Stop looking for it in this element's rule - // nodes. - properties.remove(id); - - // However, if it is inherited, then it might be - // inherited from an author rule from an - // ancestor element's rule nodes. - if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) { - have_explicit_ua_inherit = true; - inherited_properties.insert(id); - } - } - } - - if !have_explicit_ua_inherit { - break; - } - - // Continue to the parent element and search for the inherited properties. - if let Some(pseudo) = pseudo.take() { - if pseudo.inherits_from_default_values() { - break; - } - } else { - element = match element.inheritance_parent() { - Some(parent) => parent, - None => break, - }; - - let parent_data = element.mutate_data().unwrap(); - let parent_rule_node = parent_data.styles.primary().rules().clone(); - element_rule_node = Cow::Owned(parent_rule_node); - } - - properties = inherited_properties; - } - - false - } - /// Returns true if there is either animation or transition level rule. pub fn has_animation_or_transition_rules(&self) -> bool { self.self_and_ancestors() diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 75da811f08d..987a0567432 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -1630,7 +1630,7 @@ pub enum Appearance { RadioLabel, /// nsRangeFrame and its subparts Range, - RangeThumb, + RangeThumb, // FIXME: This should not be exposed to content. /// The resizer background area in a status bar for the resizer widget in /// the corner of a window. #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] @@ -1856,6 +1856,14 @@ pub enum Appearance { Count, } +impl Appearance { + /// Returns whether we're the `none` value. + #[inline] + pub fn is_none(self) -> bool { + self == Appearance::None + } +} + /// A kind of break between two boxes. /// /// https://drafts.csswg.org/css-break/#break-between