diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2023-06-06 16:49:13 +0200 |
---|---|---|
committer | Oriol Brufau <obrufau@igalia.com> | 2023-06-09 10:22:22 +0200 |
commit | 8bb7d98f0ce2fdfa49f71c1379dd2bcde3aeac29 (patch) | |
tree | 962d77a26b4632f822381045d6d23bceaedded89 | |
parent | 26c10339e332dbfe3607be57d509e25e30267523 (diff) | |
download | servo-8bb7d98f0ce2fdfa49f71c1379dd2bcde3aeac29.tar.gz servo-8bb7d98f0ce2fdfa49f71c1379dd2bcde3aeac29.zip |
style: Add support for the revert-layer keyword
This patch looks bigger than it is, but it's mostly because
of plumbing.
To implement revert-layer we need not only the cascade origin of the
declaration, but the whole cascade level, plus also the layer order.
In order to do this, encapsulate these two things inside a 32-bit
`CascadePriority` struct and plumb it through the rule tree and so on.
This allows us to remove the packing and unpacking of CascadeLevel,
though I kept the ShadowCascadeOrder limit for now in case we need to
reintroduce it.
Fix `!important` behavior of layers while at it (implementing it in
`CascadeLevel::cmp`, spec quote included since it was tricky to find)
since some revert-layer tests were depending on it.
The style attribute test is failing now, but follow-up commit fixes
it, see spec issue.
In terms of the actual keyword implementation, it's sort of
straight-forward: We implement revert and revert-layer in a shared
way, by storing the cascade priority that reverted it.
Differential Revision: https://phabricator.services.mozilla.com/D133372
-rw-r--r-- | components/style/animation.rs | 2 | ||||
-rw-r--r-- | components/style/applicable_declarations.rs | 139 | ||||
-rw-r--r-- | components/style/custom_properties.rs | 19 | ||||
-rw-r--r-- | components/style/matching.rs | 10 | ||||
-rw-r--r-- | components/style/properties/cascade.rs | 97 | ||||
-rw-r--r-- | components/style/properties/declaration_block.rs | 6 | ||||
-rw-r--r-- | components/style/properties/helpers.mako.rs | 1 | ||||
-rw-r--r-- | components/style/properties/helpers/animated_properties.mako.rs | 8 | ||||
-rw-r--r-- | components/style/properties/properties.mako.rs | 24 | ||||
-rw-r--r-- | components/style/rule_collector.rs | 2 | ||||
-rw-r--r-- | components/style/rule_tree/core.rs | 47 | ||||
-rw-r--r-- | components/style/rule_tree/level.rs | 118 | ||||
-rw-r--r-- | components/style/rule_tree/mod.rs | 85 | ||||
-rw-r--r-- | components/style/stylesheets/layer_rule.rs | 14 | ||||
-rw-r--r-- | components/style/stylesheets/origin.rs | 2 | ||||
-rw-r--r-- | components/style/stylist.rs | 18 | ||||
-rw-r--r-- | components/style/values/mod.rs | 17 |
17 files changed, 356 insertions, 253 deletions
diff --git a/components/style/animation.rs b/components/style/animation.rs index 57ce98141a7..7f0c7e6a588 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -24,6 +24,7 @@ use crate::selector_parser::PseudoElement; use crate::shared_lock::{Locked, SharedRwLock}; use crate::style_resolver::StyleResolverForElement; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; +use crate::stylesheets::layer_rule::LayerOrder; use crate::values::animated::{Animate, Procedure}; use crate::values::computed::{Time, TimingFunction}; use crate::values::generics::box_::AnimationIterationCount; @@ -290,6 +291,7 @@ impl IntermediateComputedKeyframe { let rule_node = base_style.rules().clone(); let new_node = context.stylist.rule_tree().update_rule_at_level( CascadeLevel::Animations, + LayerOrder::root(), Some(locked_block.borrow_arc()), &rule_node, &context.guards, diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs index 7f2d3928d2d..cdb78d05405 100644 --- a/components/style/applicable_declarations.rs +++ b/components/style/applicable_declarations.rs @@ -24,36 +24,102 @@ pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>; /// That's a limit that could be reached in realistic webpages, so we use /// 24 bits and enforce defined behavior in the overflow case. /// +/// Note that right now this restriction could be lifted if wanted (because we +/// no longer stash the cascade level in the remaining bits), but we keep it in +/// place in case we come up with a use-case for them, lacking reports of the +/// current limit being too small. +/// /// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/ /// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3 -const SOURCE_ORDER_SHIFT: usize = 0; const SOURCE_ORDER_BITS: usize = 24; const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1; -const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT; - -/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy -/// for the different trade-offs there. -const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS; - -/// Stores the source order of a block, the cascade level it belongs to, and the -/// counter needed to handle Shadow DOM cascade order properly. -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)] -struct ApplicableDeclarationBits(u32); - -impl ApplicableDeclarationBits { - fn new(source_order: u32, cascade_level: CascadeLevel) -> Self { - Self( - (source_order & SOURCE_ORDER_MASK) | - ((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT), - ) +const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX; + +/// The cascade-level+layer order of this declaration. +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +pub struct CascadePriority { + cascade_level: CascadeLevel, + layer_order: LayerOrder, +} + +#[allow(dead_code)] +fn size_assert() { + #[allow(unsafe_code)] + unsafe { std::mem::transmute::<u32, CascadePriority>(0u32) }; +} + +impl PartialOrd for CascadePriority { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for CascadePriority { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.cascade_level + .cmp(&other.cascade_level) + .then_with(|| { + let ordering = self.layer_order.cmp(&other.layer_order); + // https://drafts.csswg.org/css-cascade-5/#cascade-layering + // + // Cascade layers (like declarations) are ordered by order + // of appearance. When comparing declarations that belong to + // different layers, then for normal rules the declaration + // whose cascade layer is last wins, and for important rules + // the declaration whose cascade layer is first wins. + // + // FIXME: This creates somewhat surprising behavior for the + // style attribute, see + // https://github.com/w3c/csswg-drafts/issues/6872 + if self.cascade_level.is_important() { + ordering.reverse() + } else { + ordering + } + }) + } +} + +impl CascadePriority { + /// Construct a new CascadePriority for a given (level, order) pair. + pub fn new(cascade_level: CascadeLevel, layer_order: LayerOrder) -> Self { + Self { cascade_level, layer_order } } - fn source_order(&self) -> u32 { - self.0 & SOURCE_ORDER_MASK + /// Returns the layer order. + #[inline] + pub fn layer_order(&self) -> LayerOrder { + self.layer_order + } + + /// Returns the cascade level. + #[inline] + pub fn cascade_level(&self) -> CascadeLevel { + self.cascade_level + } + + /// Whether this declaration should be allowed if `revert` or `revert-layer` + /// have been specified on a given origin. + /// + /// `self` is the priority at which the `revert` or `revert-layer` keyword + /// have been specified. + pub fn allows_when_reverted(&self, other: &Self, origin_revert: bool) -> bool { + if origin_revert { + other.cascade_level.origin() < self.cascade_level.origin() + } else { + other.unimportant() < self.unimportant() + } } - fn level(&self) -> CascadeLevel { - CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8) + /// Convert this priority from "important" to "non-important", if needed. + pub fn unimportant(&self) -> Self { + Self::new(self.cascade_level().unimportant(), self.layer_order()) + } + + /// Convert this priority from "non-important" to "important", if needed. + pub fn important(&self) -> Self { + Self::new(self.cascade_level().important(), self.layer_order()) } } @@ -69,11 +135,11 @@ pub struct ApplicableDeclarationBlock { pub source: StyleSource, /// The bits containing the source order, cascade level, and shadow cascade /// order. - bits: ApplicableDeclarationBits, + source_order: u32, /// The specificity of the selector. pub specificity: u32, - /// The layer order of the selector. - pub layer_order: LayerOrder, + /// The cascade priority of the rule. + pub cascade_priority: CascadePriority, } impl ApplicableDeclarationBlock { @@ -86,9 +152,9 @@ impl ApplicableDeclarationBlock { ) -> Self { ApplicableDeclarationBlock { source: StyleSource::from_declarations(declarations), - bits: ApplicableDeclarationBits::new(0, level), + source_order: 0, specificity: 0, - layer_order: LayerOrder::root(), + cascade_priority: CascadePriority::new(level, LayerOrder::root()), } } @@ -103,29 +169,34 @@ impl ApplicableDeclarationBlock { ) -> Self { ApplicableDeclarationBlock { source, - bits: ApplicableDeclarationBits::new(source_order, level), + source_order: source_order & SOURCE_ORDER_MASK, specificity, - layer_order, + cascade_priority: CascadePriority::new(level, layer_order), } } /// Returns the source order of the block. #[inline] pub fn source_order(&self) -> u32 { - self.bits.source_order() + self.source_order } /// Returns the cascade level of the block. #[inline] pub fn level(&self) -> CascadeLevel { - self.bits.level() + self.cascade_priority.cascade_level() + } + + /// Returns the cascade level of the block. + #[inline] + pub fn layer_order(&self) -> LayerOrder { + self.cascade_priority.layer_order() } /// Convenience method to consume self and return the right thing for the /// rule tree to iterate over. #[inline] - pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) { - let level = self.level(); - (self.source, level) + pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) { + (self.source, self.cascade_priority) } } diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs index 427fe03439c..de887396c55 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -6,11 +6,11 @@ //! //! [custom]: https://drafts.csswg.org/css-variables/ +use crate::applicable_declarations::CascadePriority; use crate::hash::map::Entry; use crate::media_queries::Device; use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue}; use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher}; -use crate::stylesheets::{Origin, PerOrigin}; use crate::Atom; use cssparser::{ CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType, @@ -599,10 +599,10 @@ fn parse_env_function<'i, 't>( /// properties. pub struct CustomPropertiesBuilder<'a> { seen: PrecomputedHashSet<&'a Name>, - reverted: PerOrigin<PrecomputedHashSet<&'a Name>>, may_have_cycles: bool, custom_properties: Option<CustomPropertiesMap>, inherited: Option<&'a Arc<CustomPropertiesMap>>, + reverted: PrecomputedHashMap<&'a Name, (CascadePriority, bool)>, device: &'a Device, } @@ -620,14 +620,16 @@ impl<'a> CustomPropertiesBuilder<'a> { } /// Cascade a given custom property declaration. - pub fn cascade(&mut self, declaration: &'a CustomDeclaration, origin: Origin) { + pub fn cascade(&mut self, declaration: &'a CustomDeclaration, priority: CascadePriority) { let CustomDeclaration { ref name, ref value, } = *declaration; - if self.reverted.borrow_for_origin(&origin).contains(&name) { - return; + if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&name) { + if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) { + return; + } } let was_already_present = !self.seen.insert(name); @@ -670,11 +672,10 @@ impl<'a> CustomPropertiesBuilder<'a> { map.insert(name.clone(), value); }, CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword { - CSSWideKeyword::Revert => { + CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => { + let origin_revert = keyword == CSSWideKeyword::Revert; self.seen.remove(name); - for origin in origin.following_including() { - self.reverted.borrow_mut_for_origin(&origin).insert(name); - } + self.reverted.insert(name, (priority, origin_revert)); }, CSSWideKeyword::Initial => { map.remove(name); diff --git a/components/style/matching.rs b/components/style/matching.rs index 9b267cd8b46..92fdee3f97b 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -23,6 +23,7 @@ use crate::selector_parser::{PseudoElement, RestyleDamage}; use crate::shared_lock::Locked; use crate::style_resolver::ResolvedElementStyles; use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement}; +use crate::stylesheets::layer_rule::LayerOrder; use crate::stylist::RuleInclusion; use crate::traversal_flags::TraversalFlags; use selectors::matching::ElementSelectorFlags; @@ -92,6 +93,7 @@ trait PrivateMatchMethods: TElement { fn replace_single_rule_node( context: &SharedStyleContext, level: CascadeLevel, + layer_order: LayerOrder, pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>, path: &mut StrongRuleNode, ) -> bool { @@ -101,6 +103,7 @@ trait PrivateMatchMethods: TElement { let mut important_rules_changed = false; let new_node = stylist.rule_tree().update_rule_at_level( level, + layer_order, pdb, path, guards, @@ -145,12 +148,14 @@ trait PrivateMatchMethods: TElement { result |= Self::replace_single_rule_node( context.shared, CascadeLevel::same_tree_author_normal(), + LayerOrder::root(), style_attribute, primary_rules, ); result |= Self::replace_single_rule_node( context.shared, CascadeLevel::same_tree_author_important(), + LayerOrder::root(), style_attribute, primary_rules, ); @@ -172,6 +177,7 @@ trait PrivateMatchMethods: TElement { Self::replace_single_rule_node( context.shared, CascadeLevel::SMILOverride, + LayerOrder::root(), self.smil_override(), primary_rules, ); @@ -181,6 +187,7 @@ trait PrivateMatchMethods: TElement { Self::replace_single_rule_node( context.shared, CascadeLevel::Transitions, + LayerOrder::root(), self.transition_rule(&context.shared) .as_ref() .map(|a| a.borrow_arc()), @@ -192,6 +199,7 @@ trait PrivateMatchMethods: TElement { Self::replace_single_rule_node( context.shared, CascadeLevel::Animations, + LayerOrder::root(), self.animation_rule(&context.shared) .as_ref() .map(|a| a.borrow_arc()), @@ -589,12 +597,14 @@ trait PrivateMatchMethods: TElement { Self::replace_single_rule_node( &context.shared, CascadeLevel::Transitions, + LayerOrder::root(), declarations.transitions.as_ref().map(|a| a.borrow_arc()), &mut rule_node, ); Self::replace_single_rule_node( &context.shared, CascadeLevel::Animations, + LayerOrder::root(), declarations.animations.as_ref().map(|a| a.borrow_arc()), &mut rule_node, ); diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 6b8c4cf4d37..dfdb2a2b1c8 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -4,6 +4,7 @@ //! The main cascading algorithm of the style system. +use crate::applicable_declarations::CascadePriority; use crate::context::QuirksMode; use crate::custom_properties::CustomPropertiesBuilder; use crate::dom::TElement; @@ -15,12 +16,13 @@ use crate::properties::{ ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY, }; use crate::rule_cache::{RuleCache, RuleCacheConditions}; -use crate::rule_tree::StrongRuleNode; +use crate::rule_tree::{StrongRuleNode, CascadeLevel}; use crate::selector_parser::PseudoElement; use crate::shared_lock::StylesheetGuards; use crate::style_adjuster::StyleAdjuster; -use crate::stylesheets::{Origin, PerOrigin}; +use crate::stylesheets::{Origin, layer_rule::LayerOrder}; use crate::values::{computed, specified}; +use fxhash::FxHashMap; use servo_arc::Arc; use smallvec::SmallVec; use std::borrow::Cow; @@ -115,6 +117,7 @@ struct DeclarationIterator<'a> { declarations: DeclarationImportanceIterator<'a>, origin: Origin, importance: Importance, + priority: CascadePriority, } impl<'a> DeclarationIterator<'a> { @@ -128,8 +131,9 @@ impl<'a> DeclarationIterator<'a> { let mut iter = Self { guards, current_rule_node: Some(rule_node), - origin: Origin::Author, + origin: Origin::UserAgent, importance: Importance::Normal, + priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()), declarations: DeclarationImportanceIterator::default(), restriction, }; @@ -138,10 +142,11 @@ impl<'a> DeclarationIterator<'a> { } 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 { + self.priority = node.cascade_priority(); + let level = self.priority.cascade_level(); + self.origin = level.origin(); + self.importance = level.importance(); + let guard = match self.origin { Origin::Author => self.guards.author, Origin::User | Origin::UserAgent => self.guards.ua_or_user, }; @@ -153,7 +158,7 @@ impl<'a> DeclarationIterator<'a> { } impl<'a> Iterator for DeclarationIterator<'a> { - type Item = (&'a PropertyDeclaration, Origin); + type Item = (&'a PropertyDeclaration, CascadePriority); #[inline] fn next(&mut self) -> Option<Self::Item> { @@ -163,20 +168,19 @@ impl<'a> Iterator for DeclarationIterator<'a> { 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 { + if !id.flags().contains(restriction) && self.origin != Origin::UserAgent { continue; } } } - return Some((decl, origin)); + return Some((decl, self.priority)); } let next_node = self.current_rule_node.take()?.parent()?; @@ -259,7 +263,7 @@ pub fn apply_declarations<'a, E, I>( ) -> Arc<ComputedValues> where E: TElement, - I: Iterator<Item = (&'a PropertyDeclaration, Origin)>, + I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>, { debug_assert!(layout_parent_style.is_none() || parent_style.is_some()); debug_assert_eq!( @@ -278,14 +282,14 @@ where let inherited_style = parent_style.unwrap_or(device.default_computed_values()); - let mut declarations = SmallVec::<[(&_, Origin); 32]>::new(); + let mut declarations = SmallVec::<[(&_, CascadePriority); 32]>::new(); let custom_properties = { let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device); - for (declaration, origin) in iter { - declarations.push((declaration, origin)); + for (declaration, priority) in iter { + declarations.push((declaration, priority)); if let PropertyDeclaration::Custom(ref declaration) = *declaration { - builder.cascade(declaration, origin); + builder.cascade(declaration, priority); } } @@ -494,7 +498,8 @@ struct Cascade<'a, 'b: 'a> { cascade_mode: CascadeMode<'a>, seen: LonghandIdSet, author_specified: LonghandIdSet, - reverted: PerOrigin<LonghandIdSet>, + reverted_set: LonghandIdSet, + reverted: FxHashMap<LonghandId, (CascadePriority, bool)>, } impl<'a, 'b: 'a> Cascade<'a, 'b> { @@ -504,6 +509,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { cascade_mode, seen: LonghandIdSet::default(), author_specified: LonghandIdSet::default(), + reverted_set: Default::default(), reverted: Default::default(), } } @@ -575,7 +581,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache, ) where Phase: CascadePhase, - I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>, + I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>, { let apply_reset = apply_reset == ApplyResetProperties::Yes; @@ -589,7 +595,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { let ignore_colors = !self.context.builder.device.use_document_colors(); let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new(); - for (declaration, origin) in declarations { + for (declaration, priority) in declarations { + let origin = priority.cascade_level().origin(); + let declaration_id = declaration.id(); let longhand_id = match declaration_id { PropertyDeclarationId::Longhand(id) => id, @@ -616,12 +624,12 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { continue; } - if self - .reverted - .borrow_for_origin(&origin) - .contains(physical_longhand_id) - { - continue; + if self.reverted_set.contains(physical_longhand_id) { + if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&physical_longhand_id) { + if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) { + continue; + } + } } // Only a few properties are allowed to depend on the visited state @@ -653,32 +661,31 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { ); } - let css_wide_keyword = declaration.get_css_wide_keyword(); - if let Some(CSSWideKeyword::Revert) = css_wide_keyword { - // We intentionally don't want to insert it into `self.seen`, - // `reverted` takes care of rejecting other declarations as - // needed. - for origin in origin.following_including() { - self.reverted - .borrow_mut_for_origin(&origin) - .insert(physical_longhand_id); - } - continue; - } + let is_unset = match declaration.get_css_wide_keyword() { + Some(keyword) => match keyword { + CSSWideKeyword::RevertLayer | + CSSWideKeyword::Revert => { + let origin_revert = keyword == CSSWideKeyword::Revert; + // We intentionally don't want to insert it into + // `self.seen`, `reverted` takes care of rejecting other + // declarations as needed. + self.reverted_set.insert(physical_longhand_id); + self.reverted.insert(physical_longhand_id, (priority, origin_revert)); + continue; + }, + CSSWideKeyword::Unset => true, + CSSWideKeyword::Inherit => inherited, + CSSWideKeyword::Initial => !inherited, + }, + None => false, + }; 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 { - CSSWideKeyword::Unset => true, - CSSWideKeyword::Inherit => inherited, - CSSWideKeyword::Initial => !inherited, - CSSWideKeyword::Revert => unreachable!(), - }); - - if unset { + if is_unset { continue; } diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 980d3de11ff..01b390f0fca 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -7,15 +7,17 @@ #![deny(missing_docs)] use super::*; +use crate::applicable_declarations::CascadePriority; use crate::context::QuirksMode; use crate::custom_properties::CustomPropertiesBuilder; use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; use crate::parser::ParserContext; use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; +use crate::rule_tree::CascadeLevel; use crate::selector_parser::SelectorImpl; use crate::shared_lock::Locked; use crate::str::{CssString, CssStringWriter}; -use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; +use crate::stylesheets::{CssRuleType, Origin, UrlExtraData, layer_rule::LayerOrder}; use crate::values::computed::Context; use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput}; use cssparser::{AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind, Parser}; @@ -898,7 +900,7 @@ impl PropertyDeclarationBlock { for declaration in self.normal_declaration_iter() { if let PropertyDeclaration::Custom(ref declaration) = *declaration { - builder.cascade(declaration, Origin::Author); + builder.cascade(declaration, CascadePriority::new(CascadeLevel::same_tree_author_normal(), LayerOrder::root())); } } diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index e46024feba1..91be2d6011b 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -476,6 +476,7 @@ context.builder.inherit_${property.ident}(); % endif } + CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => unreachable!("Should never get here"), } return; diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 2f9e692f740..f9067c0e9a8 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -311,9 +311,9 @@ impl AnimationValue { % for prop in data.longhands: % if prop.animatable: LonghandId::${prop.camel_case} => { - // FIXME(emilio, bug 1533327): I think - // CSSWideKeyword::Revert handling is not fine here, but - // what to do instead? + // FIXME(emilio, bug 1533327): I think revert (and + // revert-layer) handling is not fine here, but what to + // do instead? // // Seems we'd need the computed value as if it was // revert, somehow. Treating it as `unset` seems fine @@ -321,6 +321,7 @@ impl AnimationValue { let style_struct = match declaration.keyword { % if not prop.style_struct.inherited: CSSWideKeyword::Revert | + CSSWideKeyword::RevertLayer | CSSWideKeyword::Unset | % endif CSSWideKeyword::Initial => { @@ -328,6 +329,7 @@ impl AnimationValue { }, % if prop.style_struct.inherited: CSSWideKeyword::Revert | + CSSWideKeyword::RevertLayer | CSSWideKeyword::Unset | % endif CSSWideKeyword::Inherit => { diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 4ee388dfe6c..cdecfaae895 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1051,6 +1051,8 @@ pub enum CSSWideKeyword { Unset, /// The `revert` keyword. Revert, + /// The `revert-layer` keyword. + RevertLayer, } impl CSSWideKeyword { @@ -1060,22 +1062,28 @@ impl CSSWideKeyword { CSSWideKeyword::Inherit => "inherit", CSSWideKeyword::Unset => "unset", CSSWideKeyword::Revert => "revert", + CSSWideKeyword::RevertLayer => "revert-layer", } } } impl CSSWideKeyword { + /// Parses a CSS wide keyword from a CSS identifier. + pub fn from_ident(ident: &str) -> Result<Self, ()> { + Ok(match_ignore_ascii_case! { ident, + "initial" => CSSWideKeyword::Initial, + "inherit" => CSSWideKeyword::Inherit, + "unset" => CSSWideKeyword::Unset, + "revert" => CSSWideKeyword::Revert, + "revert-layer" if static_prefs::pref!("layout.css.cascade-layers.enabled") => CSSWideKeyword::RevertLayer, + _ => return Err(()), + }) + } + fn parse(input: &mut Parser) -> Result<Self, ()> { let keyword = { let ident = input.expect_ident().map_err(|_| ())?; - match_ignore_ascii_case! { ident, - // If modifying this set of keyword, also update values::CustomIdent::from_ident - "initial" => CSSWideKeyword::Initial, - "inherit" => CSSWideKeyword::Inherit, - "unset" => CSSWideKeyword::Unset, - "revert" => CSSWideKeyword::Revert, - _ => return Err(()), - } + Self::from_ident(ident)? }; input.expect_exhausted().map_err(|_| ())?; Ok(keyword) diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index 021c0176416..513c2d91566 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -148,7 +148,7 @@ where f(self); if start != self.rules.len() { self.rules[start..].sort_unstable_by_key(|block| { - (block.layer_order, block.specificity, block.source_order()) + (block.layer_order(), block.specificity, block.source_order()) }); } self.context.current_host = old_host; diff --git a/components/style/rule_tree/core.rs b/components/style/rule_tree/core.rs index ae1ba7bed94..fe1214faf65 100644 --- a/components/style/rule_tree/core.rs +++ b/components/style/rule_tree/core.rs @@ -4,8 +4,9 @@ #![allow(unsafe_code)] -use crate::properties::Importance; +use crate::applicable_declarations::CascadePriority; use crate::shared_lock::StylesheetGuards; +use crate::stylesheets::layer_rule::LayerOrder; use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps}; use parking_lot::RwLock; use smallvec::SmallVec; @@ -66,7 +67,7 @@ impl MallocSizeOf for RuleTree { } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -struct ChildKey(CascadeLevel, ptr::NonNull<()>); +struct ChildKey(CascadePriority, ptr::NonNull<()>); unsafe impl Send for ChildKey {} unsafe impl Sync for ChildKey {} @@ -219,8 +220,8 @@ struct RuleNode { /// None for the root node. source: Option<StyleSource>, - /// The cascade level this rule is positioned at. - level: CascadeLevel, + /// The cascade level + layer order this rule is positioned at. + cascade_priority: CascadePriority, /// The refcount of this node. /// @@ -316,14 +317,14 @@ impl RuleNode { root: WeakRuleNode, parent: StrongRuleNode, source: StyleSource, - level: CascadeLevel, + cascade_priority: CascadePriority, ) -> Self { debug_assert!(root.p.parent.is_none()); RuleNode { root: Some(root), parent: Some(parent), source: Some(source), - level: level, + cascade_priority, refcount: AtomicUsize::new(1), children: Default::default(), approximate_free_count: AtomicUsize::new(0), @@ -336,7 +337,7 @@ impl RuleNode { root: None, parent: None, source: None, - level: CascadeLevel::UANormal, + cascade_priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()), refcount: AtomicUsize::new(1), approximate_free_count: AtomicUsize::new(0), children: Default::default(), @@ -346,7 +347,7 @@ impl RuleNode { fn key(&self) -> ChildKey { ChildKey( - self.level, + self.cascade_priority, self.source .as_ref() .expect("Called key() on the root node") @@ -554,20 +555,20 @@ impl StrongRuleNode { &self, root: &StrongRuleNode, source: StyleSource, - level: CascadeLevel, + cascade_priority: CascadePriority, ) -> StrongRuleNode { use parking_lot::RwLockUpgradableReadGuard; debug_assert!( - self.p.level <= level, + self.p.cascade_priority <= cascade_priority, "Should be ordered (instead {:?} > {:?}), from {:?} and {:?}", - self.p.level, - level, + self.p.cascade_priority, + cascade_priority, self.p.source, source, ); - let key = ChildKey(level, source.key()); + let key = ChildKey(cascade_priority, source.key()); let children = self.p.children.upgradable_read(); if let Some(child) = children.get(&key, |node| node.p.key()) { // Sound to call because we read-locked the parent's children. @@ -584,7 +585,7 @@ impl StrongRuleNode { root.downgrade(), self.clone(), source, - level, + cascade_priority, ))); // Sound to call because we still own a strong reference to // this node, through the `node` variable itself that we are @@ -602,14 +603,22 @@ impl StrongRuleNode { self.p.source.as_ref() } - /// The cascade level for this node + /// The cascade priority. + #[inline] + pub fn cascade_priority(&self) -> CascadePriority { + self.p.cascade_priority + } + + /// The cascade level. + #[inline] pub fn cascade_level(&self) -> CascadeLevel { - self.p.level + self.cascade_priority().cascade_level() } - /// Get the importance that this rule node represents. - pub fn importance(&self) -> Importance { - self.p.level.importance() + /// The importance. + #[inline] + pub fn importance(&self) -> crate::properties::Importance { + self.cascade_level().importance() } /// Returns whether this node has any child, only intended for testing diff --git a/components/style/rule_tree/level.rs b/components/style/rule_tree/level.rs index c46e63796ad..b8cbe55ed9c 100644 --- a/components/style/rule_tree/level.rs +++ b/components/style/rule_tree/level.rs @@ -29,7 +29,7 @@ use crate::stylesheets::Origin; /// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints /// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading #[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)] pub enum CascadeLevel { /// Normal User-Agent rules. UANormal, @@ -69,82 +69,44 @@ pub enum CascadeLevel { } impl CascadeLevel { - /// Pack this cascade level in a single byte. - /// - /// We have 10 levels, which we can represent with 4 bits, and then a - /// cascade order optionally, which we can clamp to three bits max, and - /// represent with a fourth bit for the sign. - /// - /// So this creates: SOOODDDD - /// - /// Where `S` is the sign of the order (one if negative, 0 otherwise), `O` - /// is the absolute value of the order, and `D`s are the discriminant. - #[inline] - pub fn to_byte_lossy(&self) -> u8 { - let (discriminant, order) = match *self { - Self::UANormal => (0, 0), - Self::UserNormal => (1, 0), - Self::PresHints => (2, 0), + /// Convert this level from "unimportant" to "important". + pub fn important(&self) -> Self { + match *self { + Self::UANormal => Self::UAImportant, + Self::UserNormal => Self::UserImportant, Self::AuthorNormal { shadow_cascade_order, - } => (3, shadow_cascade_order.0), - Self::SMILOverride => (4, 0), - Self::Animations => (5, 0), - Self::AuthorImportant { - shadow_cascade_order, - } => (6, shadow_cascade_order.0), - Self::UserImportant => (7, 0), - Self::UAImportant => (8, 0), - Self::Transitions => (9, 0), - }; - - debug_assert_eq!(discriminant & 0xf, discriminant); - if order == 0 { - return discriminant; + } => Self::AuthorImportant { + shadow_cascade_order: -shadow_cascade_order, + }, + Self::PresHints | + Self::SMILOverride | + Self::Animations | + Self::AuthorImportant { .. } | + Self::UserImportant | + Self::UAImportant | + Self::Transitions => *self, } - - let negative = order < 0; - let value = std::cmp::min(order.abs() as u8, 0b111); - (negative as u8) << 7 | value << 4 | discriminant } - /// Convert back from the single-byte representation of the cascade level - /// explained above. - #[inline] - pub fn from_byte(b: u8) -> Self { - let order = { - let abs = ((b & 0b01110000) >> 4) as i8; - let negative = b & 0b10000000 != 0; - if negative { - -abs - } else { - abs - } - }; - let discriminant = b & 0xf; - let level = match discriminant { - 0 => Self::UANormal, - 1 => Self::UserNormal, - 2 => Self::PresHints, - 3 => { - return Self::AuthorNormal { - shadow_cascade_order: ShadowCascadeOrder(order), - } - }, - 4 => Self::SMILOverride, - 5 => Self::Animations, - 6 => { - return Self::AuthorImportant { - shadow_cascade_order: ShadowCascadeOrder(order), - } + /// Convert this level from "important" to "non-important". + pub fn unimportant(&self) -> Self { + match *self { + Self::UAImportant => Self::UANormal, + Self::UserImportant => Self::UserNormal, + Self::AuthorImportant { + shadow_cascade_order, + } => Self::AuthorNormal { + shadow_cascade_order: -shadow_cascade_order, }, - 7 => Self::UserImportant, - 8 => Self::UAImportant, - 9 => Self::Transitions, - _ => unreachable!("Didn't expect {} as a discriminant", discriminant), - }; - debug_assert_eq!(order, 0, "Didn't expect an order value for {:?}", level); - level + Self::PresHints | + Self::SMILOverride | + Self::Animations | + Self::AuthorNormal { .. } | + Self::UserNormal | + Self::UANormal | + Self::Transitions => *self, + } } /// Select a lock guard for this level @@ -231,6 +193,12 @@ impl CascadeLevel { pub struct ShadowCascadeOrder(i8); impl ShadowCascadeOrder { + /// We keep a maximum of 3 bits of order as a limit so that we can pack + /// CascadeLevel in one byte by using half of it for the order, if that ends + /// up being necessary. + const MAX: i8 = 0b111; + const MIN: i8 = -Self::MAX; + /// A level for the outermost shadow tree (the shadow tree we own, and the /// ones from the slots we're slotted in). #[inline] @@ -256,7 +224,9 @@ impl ShadowCascadeOrder { #[inline] pub fn dec(&mut self) { debug_assert!(self.0 < 0); - self.0 = self.0.saturating_sub(1); + if self.0 != Self::MIN { + self.0 -= 1; + } } /// The level, moving inwards. We should only move inwards if we're @@ -264,7 +234,9 @@ impl ShadowCascadeOrder { #[inline] pub fn inc(&mut self) { debug_assert_ne!(self.0, -1); - self.0 = self.0.saturating_add(1); + if self.0 != Self::MAX { + self.0 += 1; + } } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index e50382255ca..c8705165776 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -6,9 +6,10 @@ //! The rule tree. -use crate::applicable_declarations::ApplicableDeclarationList; +use crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority}; use crate::properties::{LonghandIdSet, PropertyDeclarationBlock}; use crate::shared_lock::{Locked, StylesheetGuards}; +use crate::stylesheets::layer_rule::LayerOrder; use servo_arc::{Arc, ArcBorrow}; use smallvec::SmallVec; use std::io::{self, Write}; @@ -47,21 +48,22 @@ impl RuleTree { guards: &StylesheetGuards, ) -> StrongRuleNode where - I: Iterator<Item = (StyleSource, CascadeLevel)>, + I: Iterator<Item = (StyleSource, CascadePriority)>, { use self::CascadeLevel::*; let mut current = self.root().clone(); let mut found_important = false; - let mut important_author = SmallVec::<[(StyleSource, ShadowCascadeOrder); 4]>::new(); - - let mut important_user = SmallVec::<[StyleSource; 4]>::new(); - let mut important_ua = SmallVec::<[StyleSource; 4]>::new(); + let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new(); + let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new(); + let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new(); let mut transition = None; - for (source, level) in iter { + for (source, priority) in iter { + let level = priority.cascade_level(); debug_assert!(!level.is_important(), "Important levels handled internally"); + let any_important = { let pdb = source.read(level.guard(guards)); pdb.any_important() @@ -70,13 +72,9 @@ impl RuleTree { if any_important { found_important = true; match level { - AuthorNormal { - shadow_cascade_order, - } => { - important_author.push((source.clone(), shadow_cascade_order)); - }, - UANormal => important_ua.push(source.clone()), - UserNormal => important_user.push(source.clone()), + AuthorNormal { .. } => important_author.push((source.clone(), priority.important())), + UANormal => important_ua.push((source.clone(), priority.important())), + UserNormal => important_user.push((source.clone(), priority.important())), _ => {}, }; } @@ -98,7 +96,7 @@ impl RuleTree { debug_assert!(transition.is_none()); transition = Some(source); } else { - current = current.ensure_child(self.root(), source, level); + current = current.ensure_child(self.root(), source, priority); } } @@ -110,10 +108,8 @@ impl RuleTree { // Insert important declarations, in order of increasing importance, // followed by any transition rule. // - // Inner shadow wins over same-tree, which wins over outer-shadow. - // - // We negate the shadow cascade order to preserve the right PartialOrd - // behavior. + // Important rules are sorted differently from unimportant ones by + // shadow order and cascade order. if !important_author.is_empty() && important_author.first().unwrap().1 != important_author.last().unwrap().1 { @@ -129,29 +125,27 @@ impl RuleTree { // inside the same chunk already sorted. Seems like we could try to // keep a SmallVec-of-SmallVecs with the chunks and just iterate the // outer in reverse. - important_author.sort_by_key(|&(_, order)| -order); + important_author.sort_by_key(|&(_, priority)| priority); } - for (source, shadow_cascade_order) in important_author.drain(..) { - current = current.ensure_child( - self.root(), - source, - AuthorImportant { - shadow_cascade_order: -shadow_cascade_order, - }, - ); + for (source, priority) in important_author.drain(..) { + current = current.ensure_child(self.root(), source, priority); } - for source in important_user.drain(..) { - current = current.ensure_child(self.root(), source, UserImportant); + for (source, priority) in important_user.drain(..) { + current = current.ensure_child(self.root(), source, priority); } - for source in important_ua.drain(..) { - current = current.ensure_child(self.root(), source, UAImportant); + for (source, priority) in important_ua.drain(..) { + current = current.ensure_child(self.root(), source, priority); } if let Some(source) = transition { - current = current.ensure_child(self.root(), source, Transitions); + current = current.ensure_child( + self.root(), + source, + CascadePriority::new(Transitions, LayerOrder::root()), + ); } current @@ -174,18 +168,18 @@ impl RuleTree { /// return the corresponding rule node representing the last inserted one. pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode where - I: Iterator<Item = (StyleSource, CascadeLevel)>, + I: Iterator<Item = (StyleSource, CascadePriority)>, { self.insert_ordered_rules_from(self.root().clone(), iter) } fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode where - I: Iterator<Item = (StyleSource, CascadeLevel)>, + I: Iterator<Item = (StyleSource, CascadePriority)>, { let mut current = from; - for (source, level) in iter { - current = current.ensure_child(self.root(), source, level); + for (source, priority) in iter { + current = current.ensure_child(self.root(), source, priority); } current } @@ -197,6 +191,7 @@ impl RuleTree { pub fn update_rule_at_level( &self, level: CascadeLevel, + layer_order: LayerOrder, pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>, path: &StrongRuleNode, guards: &StylesheetGuards, @@ -209,10 +204,10 @@ impl RuleTree { // First walk up until the first less-or-equally specific rule. let mut children = SmallVec::<[_; 10]>::new(); - while current.cascade_level() > level { + while current.cascade_priority().cascade_level() > level { children.push(( current.style_source().unwrap().clone(), - current.cascade_level(), + current.cascade_priority(), )); current = current.parent().unwrap().clone(); } @@ -227,7 +222,7 @@ impl RuleTree { // to special-case (isn't hard, it's just about removing the `if` and // special cases, and replacing them for a `while` loop, avoiding the // optimizations). - if current.cascade_level() == level { + if current.cascade_priority().cascade_level() == level { *important_rules_changed |= level.is_important(); let current_decls = current.style_source().unwrap().as_declarations(); @@ -267,7 +262,7 @@ impl RuleTree { current = current.ensure_child( self.root(), StyleSource::from_declarations(pdb.clone_arc()), - level, + CascadePriority::new(level, layer_order), ); *important_rules_changed = true; } @@ -276,7 +271,7 @@ impl RuleTree { current = current.ensure_child( self.root(), StyleSource::from_declarations(pdb.clone_arc()), - level, + CascadePriority::new(level, layer_order), ); } } @@ -312,7 +307,10 @@ impl RuleTree { let mut children = SmallVec::<[_; 10]>::new(); for node in iter { if !node.cascade_level().is_animation() { - children.push((node.style_source().unwrap().clone(), node.cascade_level())); + children.push(( + node.style_source().unwrap().clone(), + node.cascade_priority(), + )); } last = node; } @@ -336,6 +334,7 @@ impl RuleTree { let mut dummy = false; self.update_rule_at_level( CascadeLevel::Transitions, + LayerOrder::root(), Some(pdb.borrow_arc()), path, guards, diff --git a/components/style/stylesheets/layer_rule.rs b/components/style/stylesheets/layer_rule.rs index ee066d813e2..1b7b0cb169e 100644 --- a/components/style/stylesheets/layer_rule.rs +++ b/components/style/stylesheets/layer_rule.rs @@ -19,14 +19,16 @@ use smallvec::SmallVec; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, ToCss}; -/// The order of a given layer. -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)] -pub struct LayerOrder(u32); +/// The order of a given layer. We use 16 bits so that we can pack LayerOrder +/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go +/// back to packing CascadeLevel in a single byte as we did before. +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)] +pub struct LayerOrder(u16); impl LayerOrder { /// The order of the root layer. pub const fn root() -> Self { - Self(std::u32::MAX) + Self(std::u16::MAX) } /// The first cascade layer order. @@ -37,7 +39,9 @@ impl LayerOrder { /// Increment the cascade layer order. #[inline] pub fn inc(&mut self) { - self.0 += 1; + if self.0 != std::u16::MAX { + self.0 += 1; + } } } diff --git a/components/style/stylesheets/origin.rs b/components/style/stylesheets/origin.rs index a65b61fca13..27ad3fa184a 100644 --- a/components/style/stylesheets/origin.rs +++ b/components/style/stylesheets/origin.rs @@ -10,7 +10,7 @@ use std::ops::BitOrAssign; /// Each style rule has an origin, which determines where it enters the cascade. /// /// <https://drafts.csswg.org/css-cascade/#cascading-origins> -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)] #[repr(u8)] pub enum Origin { /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent> diff --git a/components/style/stylist.rs b/components/style/stylist.rs index a48a7103f84..c0cc2f238bb 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -4,7 +4,9 @@ //! Selector matching. -use crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList}; +use crate::applicable_declarations::{ + ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority, +}; use crate::context::{CascadeInputs, QuirksMode}; use crate::dom::{TElement, TShadowRoot}; use crate::element_state::{DocumentState, ElementState}; @@ -1474,9 +1476,15 @@ impl Stylist { /* pseudo = */ None, self.rule_tree.root(), guards, - block - .declaration_importance_iter() - .map(|(declaration, _)| (declaration, Origin::Author)), + block.declaration_importance_iter().map(|(declaration, _)| { + ( + declaration, + CascadePriority::new( + CascadeLevel::same_tree_author_normal(), + LayerOrder::root(), + ), + ) + }), Some(parent_style), Some(parent_style), Some(parent_style), @@ -1582,7 +1590,7 @@ impl ExtraStyleData { &mut self, guard: &SharedRwLockReadGuard, rule: &Arc<Locked<ScrollTimelineRule>>, - )-> Result<(), FailedAllocationError> { + ) -> Result<(), FailedAllocationError> { let name = rule.read_with(guard).name.as_atom().clone(); self.scroll_timelines .try_insert(name, rule.clone()) diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index 626fc32ff18..ad09320c42b 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -438,15 +438,22 @@ impl CustomIdent { ident: &CowRcStr<'i>, excluding: &[&str], ) -> Result<Self, ParseError<'i>> { - let valid = match_ignore_ascii_case! { ident, - "initial" | "inherit" | "unset" | "default" | "revert" => false, - _ => true - }; - if !valid { + use crate::properties::CSSWideKeyword; + // https://drafts.csswg.org/css-values-4/#custom-idents: + // + // The CSS-wide keywords are not valid <custom-ident>s. The default + // keyword is reserved and is also not a valid <custom-ident>. + // + if CSSWideKeyword::from_ident(ident).is_ok() || ident.eq_ignore_ascii_case("default") { return Err( location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())) ); } + + // https://drafts.csswg.org/css-values-4/#custom-idents: + // + // Excluded keywords are excluded in all ASCII case permutations. + // if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) { Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } else { |