mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
style: Simplify the implementation of HasAuthorSpecifiedRules.
This patch computes the author-specified properties during the CSS cascade, and removes the complex rule-tree-based implementation that tries to do the cascade again. This changes behavior in two ways, one of them which is not observable to content, I believe: * revert now re-enables the native styling. This was brought up in https://github.com/w3c/csswg-drafts/issues/4777 and I think it is a bug-fix. This is observable to content, and I'm adding a test for it. * We don't look at inherited styles from our ancestors when `inherit` is specified in a non-author stylesheet. This was introduced for bug 452969 but we don't seem to inherit background anymore for file controls or such. It seems back then file controls used to have a text-field. I audited forms.css and ua.css and we don't explicitly inherit padding / border / background-color into any nested form control. We keep the distinction between border/background and padding, because the later has some callers. I think we should try to align with Chromium in the long run and remove the padding bit. We need to give an appearance to the range-thumb and such so that we can assert that we don't call HasAuthorSpecifiedRules on non-themed stuff. I used a new internal value for that. Differential Revision: https://phabricator.services.mozilla.com/D67722
This commit is contained in:
parent
7d438cd816
commit
414edb5a4a
5 changed files with 122 additions and 243 deletions
|
@ -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<LonghandIdSet>,
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -750,6 +750,52 @@ static ${name}: LonghandIdSet = LonghandIdSet {
|
|||
};
|
||||
</%def>
|
||||
|
||||
<%
|
||||
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)]
|
||||
|
|
|
@ -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<E>(
|
||||
&self,
|
||||
mut element: E,
|
||||
mut pseudo: Option<PseudoElement>,
|
||||
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()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue