mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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
This commit is contained in:
parent
26c10339e3
commit
8bb7d98f0c
17 changed files with 354 additions and 251 deletions
|
@ -24,6 +24,7 @@ use crate::selector_parser::PseudoElement;
|
||||||
use crate::shared_lock::{Locked, SharedRwLock};
|
use crate::shared_lock::{Locked, SharedRwLock};
|
||||||
use crate::style_resolver::StyleResolverForElement;
|
use crate::style_resolver::StyleResolverForElement;
|
||||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||||
|
use crate::stylesheets::layer_rule::LayerOrder;
|
||||||
use crate::values::animated::{Animate, Procedure};
|
use crate::values::animated::{Animate, Procedure};
|
||||||
use crate::values::computed::{Time, TimingFunction};
|
use crate::values::computed::{Time, TimingFunction};
|
||||||
use crate::values::generics::box_::AnimationIterationCount;
|
use crate::values::generics::box_::AnimationIterationCount;
|
||||||
|
@ -290,6 +291,7 @@ impl IntermediateComputedKeyframe {
|
||||||
let rule_node = base_style.rules().clone();
|
let rule_node = base_style.rules().clone();
|
||||||
let new_node = context.stylist.rule_tree().update_rule_at_level(
|
let new_node = context.stylist.rule_tree().update_rule_at_level(
|
||||||
CascadeLevel::Animations,
|
CascadeLevel::Animations,
|
||||||
|
LayerOrder::root(),
|
||||||
Some(locked_block.borrow_arc()),
|
Some(locked_block.borrow_arc()),
|
||||||
&rule_node,
|
&rule_node,
|
||||||
&context.guards,
|
&context.guards,
|
||||||
|
|
|
@ -24,36 +24,102 @@ pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;
|
||||||
/// That's a limit that could be reached in realistic webpages, so we use
|
/// That's a limit that could be reached in realistic webpages, so we use
|
||||||
/// 24 bits and enforce defined behavior in the overflow case.
|
/// 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/
|
/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
|
||||||
/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
|
/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
|
||||||
const SOURCE_ORDER_SHIFT: usize = 0;
|
|
||||||
const SOURCE_ORDER_BITS: usize = 24;
|
const SOURCE_ORDER_BITS: usize = 24;
|
||||||
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
|
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
|
||||||
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT;
|
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX;
|
||||||
|
|
||||||
/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy
|
/// The cascade-level+layer order of this declaration.
|
||||||
/// for the different trade-offs there.
|
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||||
const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS;
|
pub struct CascadePriority {
|
||||||
|
cascade_level: CascadeLevel,
|
||||||
|
layer_order: LayerOrder,
|
||||||
|
}
|
||||||
|
|
||||||
/// Stores the source order of a block, the cascade level it belongs to, and the
|
#[allow(dead_code)]
|
||||||
/// counter needed to handle Shadow DOM cascade order properly.
|
fn size_assert() {
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
|
#[allow(unsafe_code)]
|
||||||
struct ApplicableDeclarationBits(u32);
|
unsafe { std::mem::transmute::<u32, CascadePriority>(0u32) };
|
||||||
|
}
|
||||||
|
|
||||||
impl ApplicableDeclarationBits {
|
impl PartialOrd for CascadePriority {
|
||||||
fn new(source_order: u32, cascade_level: CascadeLevel) -> Self {
|
#[inline]
|
||||||
Self(
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
(source_order & SOURCE_ORDER_MASK) |
|
Some(self.cmp(other))
|
||||||
((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT),
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
|
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 {
|
/// Returns the layer order.
|
||||||
self.0 & SOURCE_ORDER_MASK
|
#[inline]
|
||||||
|
pub fn layer_order(&self) -> LayerOrder {
|
||||||
|
self.layer_order
|
||||||
}
|
}
|
||||||
|
|
||||||
fn level(&self) -> CascadeLevel {
|
/// Returns the cascade level.
|
||||||
CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8)
|
#[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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
pub source: StyleSource,
|
||||||
/// The bits containing the source order, cascade level, and shadow cascade
|
/// The bits containing the source order, cascade level, and shadow cascade
|
||||||
/// order.
|
/// order.
|
||||||
bits: ApplicableDeclarationBits,
|
source_order: u32,
|
||||||
/// The specificity of the selector.
|
/// The specificity of the selector.
|
||||||
pub specificity: u32,
|
pub specificity: u32,
|
||||||
/// The layer order of the selector.
|
/// The cascade priority of the rule.
|
||||||
pub layer_order: LayerOrder,
|
pub cascade_priority: CascadePriority,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicableDeclarationBlock {
|
impl ApplicableDeclarationBlock {
|
||||||
|
@ -86,9 +152,9 @@ impl ApplicableDeclarationBlock {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ApplicableDeclarationBlock {
|
ApplicableDeclarationBlock {
|
||||||
source: StyleSource::from_declarations(declarations),
|
source: StyleSource::from_declarations(declarations),
|
||||||
bits: ApplicableDeclarationBits::new(0, level),
|
source_order: 0,
|
||||||
specificity: 0,
|
specificity: 0,
|
||||||
layer_order: LayerOrder::root(),
|
cascade_priority: CascadePriority::new(level, LayerOrder::root()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,29 +169,34 @@ impl ApplicableDeclarationBlock {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ApplicableDeclarationBlock {
|
ApplicableDeclarationBlock {
|
||||||
source,
|
source,
|
||||||
bits: ApplicableDeclarationBits::new(source_order, level),
|
source_order: source_order & SOURCE_ORDER_MASK,
|
||||||
specificity,
|
specificity,
|
||||||
layer_order,
|
cascade_priority: CascadePriority::new(level, layer_order),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the source order of the block.
|
/// Returns the source order of the block.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn source_order(&self) -> u32 {
|
pub fn source_order(&self) -> u32 {
|
||||||
self.bits.source_order()
|
self.source_order
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the cascade level of the block.
|
/// Returns the cascade level of the block.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn level(&self) -> CascadeLevel {
|
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
|
/// Convenience method to consume self and return the right thing for the
|
||||||
/// rule tree to iterate over.
|
/// rule tree to iterate over.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) {
|
pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
|
||||||
let level = self.level();
|
(self.source, self.cascade_priority)
|
||||||
(self.source, level)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
//!
|
//!
|
||||||
//! [custom]: https://drafts.csswg.org/css-variables/
|
//! [custom]: https://drafts.csswg.org/css-variables/
|
||||||
|
|
||||||
|
use crate::applicable_declarations::CascadePriority;
|
||||||
use crate::hash::map::Entry;
|
use crate::hash::map::Entry;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
|
use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
|
||||||
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
|
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
|
||||||
use crate::stylesheets::{Origin, PerOrigin};
|
|
||||||
use crate::Atom;
|
use crate::Atom;
|
||||||
use cssparser::{
|
use cssparser::{
|
||||||
CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType,
|
CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType,
|
||||||
|
@ -599,10 +599,10 @@ fn parse_env_function<'i, 't>(
|
||||||
/// properties.
|
/// properties.
|
||||||
pub struct CustomPropertiesBuilder<'a> {
|
pub struct CustomPropertiesBuilder<'a> {
|
||||||
seen: PrecomputedHashSet<&'a Name>,
|
seen: PrecomputedHashSet<&'a Name>,
|
||||||
reverted: PerOrigin<PrecomputedHashSet<&'a Name>>,
|
|
||||||
may_have_cycles: bool,
|
may_have_cycles: bool,
|
||||||
custom_properties: Option<CustomPropertiesMap>,
|
custom_properties: Option<CustomPropertiesMap>,
|
||||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||||
|
reverted: PrecomputedHashMap<&'a Name, (CascadePriority, bool)>,
|
||||||
device: &'a Device,
|
device: &'a Device,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,14 +620,16 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cascade a given custom property declaration.
|
/// 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 {
|
let CustomDeclaration {
|
||||||
ref name,
|
ref name,
|
||||||
ref value,
|
ref value,
|
||||||
} = *declaration;
|
} = *declaration;
|
||||||
|
|
||||||
if self.reverted.borrow_for_origin(&origin).contains(&name) {
|
if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&name) {
|
||||||
return;
|
if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let was_already_present = !self.seen.insert(name);
|
let was_already_present = !self.seen.insert(name);
|
||||||
|
@ -670,11 +672,10 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
map.insert(name.clone(), value);
|
map.insert(name.clone(), value);
|
||||||
},
|
},
|
||||||
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
||||||
CSSWideKeyword::Revert => {
|
CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => {
|
||||||
|
let origin_revert = keyword == CSSWideKeyword::Revert;
|
||||||
self.seen.remove(name);
|
self.seen.remove(name);
|
||||||
for origin in origin.following_including() {
|
self.reverted.insert(name, (priority, origin_revert));
|
||||||
self.reverted.borrow_mut_for_origin(&origin).insert(name);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
CSSWideKeyword::Initial => {
|
CSSWideKeyword::Initial => {
|
||||||
map.remove(name);
|
map.remove(name);
|
||||||
|
|
|
@ -23,6 +23,7 @@ use crate::selector_parser::{PseudoElement, RestyleDamage};
|
||||||
use crate::shared_lock::Locked;
|
use crate::shared_lock::Locked;
|
||||||
use crate::style_resolver::ResolvedElementStyles;
|
use crate::style_resolver::ResolvedElementStyles;
|
||||||
use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||||
|
use crate::stylesheets::layer_rule::LayerOrder;
|
||||||
use crate::stylist::RuleInclusion;
|
use crate::stylist::RuleInclusion;
|
||||||
use crate::traversal_flags::TraversalFlags;
|
use crate::traversal_flags::TraversalFlags;
|
||||||
use selectors::matching::ElementSelectorFlags;
|
use selectors::matching::ElementSelectorFlags;
|
||||||
|
@ -92,6 +93,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
fn replace_single_rule_node(
|
fn replace_single_rule_node(
|
||||||
context: &SharedStyleContext,
|
context: &SharedStyleContext,
|
||||||
level: CascadeLevel,
|
level: CascadeLevel,
|
||||||
|
layer_order: LayerOrder,
|
||||||
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
||||||
path: &mut StrongRuleNode,
|
path: &mut StrongRuleNode,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -101,6 +103,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
let mut important_rules_changed = false;
|
let mut important_rules_changed = false;
|
||||||
let new_node = stylist.rule_tree().update_rule_at_level(
|
let new_node = stylist.rule_tree().update_rule_at_level(
|
||||||
level,
|
level,
|
||||||
|
layer_order,
|
||||||
pdb,
|
pdb,
|
||||||
path,
|
path,
|
||||||
guards,
|
guards,
|
||||||
|
@ -145,12 +148,14 @@ trait PrivateMatchMethods: TElement {
|
||||||
result |= Self::replace_single_rule_node(
|
result |= Self::replace_single_rule_node(
|
||||||
context.shared,
|
context.shared,
|
||||||
CascadeLevel::same_tree_author_normal(),
|
CascadeLevel::same_tree_author_normal(),
|
||||||
|
LayerOrder::root(),
|
||||||
style_attribute,
|
style_attribute,
|
||||||
primary_rules,
|
primary_rules,
|
||||||
);
|
);
|
||||||
result |= Self::replace_single_rule_node(
|
result |= Self::replace_single_rule_node(
|
||||||
context.shared,
|
context.shared,
|
||||||
CascadeLevel::same_tree_author_important(),
|
CascadeLevel::same_tree_author_important(),
|
||||||
|
LayerOrder::root(),
|
||||||
style_attribute,
|
style_attribute,
|
||||||
primary_rules,
|
primary_rules,
|
||||||
);
|
);
|
||||||
|
@ -172,6 +177,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
Self::replace_single_rule_node(
|
Self::replace_single_rule_node(
|
||||||
context.shared,
|
context.shared,
|
||||||
CascadeLevel::SMILOverride,
|
CascadeLevel::SMILOverride,
|
||||||
|
LayerOrder::root(),
|
||||||
self.smil_override(),
|
self.smil_override(),
|
||||||
primary_rules,
|
primary_rules,
|
||||||
);
|
);
|
||||||
|
@ -181,6 +187,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
Self::replace_single_rule_node(
|
Self::replace_single_rule_node(
|
||||||
context.shared,
|
context.shared,
|
||||||
CascadeLevel::Transitions,
|
CascadeLevel::Transitions,
|
||||||
|
LayerOrder::root(),
|
||||||
self.transition_rule(&context.shared)
|
self.transition_rule(&context.shared)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|a| a.borrow_arc()),
|
.map(|a| a.borrow_arc()),
|
||||||
|
@ -192,6 +199,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
Self::replace_single_rule_node(
|
Self::replace_single_rule_node(
|
||||||
context.shared,
|
context.shared,
|
||||||
CascadeLevel::Animations,
|
CascadeLevel::Animations,
|
||||||
|
LayerOrder::root(),
|
||||||
self.animation_rule(&context.shared)
|
self.animation_rule(&context.shared)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|a| a.borrow_arc()),
|
.map(|a| a.borrow_arc()),
|
||||||
|
@ -589,12 +597,14 @@ trait PrivateMatchMethods: TElement {
|
||||||
Self::replace_single_rule_node(
|
Self::replace_single_rule_node(
|
||||||
&context.shared,
|
&context.shared,
|
||||||
CascadeLevel::Transitions,
|
CascadeLevel::Transitions,
|
||||||
|
LayerOrder::root(),
|
||||||
declarations.transitions.as_ref().map(|a| a.borrow_arc()),
|
declarations.transitions.as_ref().map(|a| a.borrow_arc()),
|
||||||
&mut rule_node,
|
&mut rule_node,
|
||||||
);
|
);
|
||||||
Self::replace_single_rule_node(
|
Self::replace_single_rule_node(
|
||||||
&context.shared,
|
&context.shared,
|
||||||
CascadeLevel::Animations,
|
CascadeLevel::Animations,
|
||||||
|
LayerOrder::root(),
|
||||||
declarations.animations.as_ref().map(|a| a.borrow_arc()),
|
declarations.animations.as_ref().map(|a| a.borrow_arc()),
|
||||||
&mut rule_node,
|
&mut rule_node,
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
//! The main cascading algorithm of the style system.
|
//! The main cascading algorithm of the style system.
|
||||||
|
|
||||||
|
use crate::applicable_declarations::CascadePriority;
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
use crate::custom_properties::CustomPropertiesBuilder;
|
use crate::custom_properties::CustomPropertiesBuilder;
|
||||||
use crate::dom::TElement;
|
use crate::dom::TElement;
|
||||||
|
@ -15,12 +16,13 @@ use crate::properties::{
|
||||||
ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
|
ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
|
||||||
};
|
};
|
||||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||||
use crate::rule_tree::StrongRuleNode;
|
use crate::rule_tree::{StrongRuleNode, CascadeLevel};
|
||||||
use crate::selector_parser::PseudoElement;
|
use crate::selector_parser::PseudoElement;
|
||||||
use crate::shared_lock::StylesheetGuards;
|
use crate::shared_lock::StylesheetGuards;
|
||||||
use crate::style_adjuster::StyleAdjuster;
|
use crate::style_adjuster::StyleAdjuster;
|
||||||
use crate::stylesheets::{Origin, PerOrigin};
|
use crate::stylesheets::{Origin, layer_rule::LayerOrder};
|
||||||
use crate::values::{computed, specified};
|
use crate::values::{computed, specified};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -115,6 +117,7 @@ struct DeclarationIterator<'a> {
|
||||||
declarations: DeclarationImportanceIterator<'a>,
|
declarations: DeclarationImportanceIterator<'a>,
|
||||||
origin: Origin,
|
origin: Origin,
|
||||||
importance: Importance,
|
importance: Importance,
|
||||||
|
priority: CascadePriority,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DeclarationIterator<'a> {
|
impl<'a> DeclarationIterator<'a> {
|
||||||
|
@ -128,8 +131,9 @@ impl<'a> DeclarationIterator<'a> {
|
||||||
let mut iter = Self {
|
let mut iter = Self {
|
||||||
guards,
|
guards,
|
||||||
current_rule_node: Some(rule_node),
|
current_rule_node: Some(rule_node),
|
||||||
origin: Origin::Author,
|
origin: Origin::UserAgent,
|
||||||
importance: Importance::Normal,
|
importance: Importance::Normal,
|
||||||
|
priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
|
||||||
declarations: DeclarationImportanceIterator::default(),
|
declarations: DeclarationImportanceIterator::default(),
|
||||||
restriction,
|
restriction,
|
||||||
};
|
};
|
||||||
|
@ -138,10 +142,11 @@ impl<'a> DeclarationIterator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_for_node(&mut self, node: &'a StrongRuleNode) {
|
fn update_for_node(&mut self, node: &'a StrongRuleNode) {
|
||||||
let origin = node.cascade_level().origin();
|
self.priority = node.cascade_priority();
|
||||||
self.origin = origin;
|
let level = self.priority.cascade_level();
|
||||||
self.importance = node.importance();
|
self.origin = level.origin();
|
||||||
let guard = match origin {
|
self.importance = level.importance();
|
||||||
|
let guard = match self.origin {
|
||||||
Origin::Author => self.guards.author,
|
Origin::Author => self.guards.author,
|
||||||
Origin::User | Origin::UserAgent => self.guards.ua_or_user,
|
Origin::User | Origin::UserAgent => self.guards.ua_or_user,
|
||||||
};
|
};
|
||||||
|
@ -153,7 +158,7 @@ impl<'a> DeclarationIterator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for DeclarationIterator<'a> {
|
impl<'a> Iterator for DeclarationIterator<'a> {
|
||||||
type Item = (&'a PropertyDeclaration, Origin);
|
type Item = (&'a PropertyDeclaration, CascadePriority);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
@ -163,20 +168,19 @@ impl<'a> Iterator for DeclarationIterator<'a> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let origin = self.origin;
|
|
||||||
if let Some(restriction) = self.restriction {
|
if let Some(restriction) = self.restriction {
|
||||||
// decl.id() is either a longhand or a custom
|
// decl.id() is either a longhand or a custom
|
||||||
// property. Custom properties are always allowed, but
|
// property. Custom properties are always allowed, but
|
||||||
// longhands are only allowed if they have our
|
// longhands are only allowed if they have our
|
||||||
// restriction flag set.
|
// restriction flag set.
|
||||||
if let PropertyDeclarationId::Longhand(id) = decl.id() {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some((decl, origin));
|
return Some((decl, self.priority));
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_node = self.current_rule_node.take()?.parent()?;
|
let next_node = self.current_rule_node.take()?.parent()?;
|
||||||
|
@ -259,7 +263,7 @@ pub fn apply_declarations<'a, E, I>(
|
||||||
) -> Arc<ComputedValues>
|
) -> Arc<ComputedValues>
|
||||||
where
|
where
|
||||||
E: TElement,
|
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!(layout_parent_style.is_none() || parent_style.is_some());
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
|
@ -278,14 +282,14 @@ where
|
||||||
|
|
||||||
let inherited_style = parent_style.unwrap_or(device.default_computed_values());
|
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 custom_properties = {
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
|
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
|
||||||
|
|
||||||
for (declaration, origin) in iter {
|
for (declaration, priority) in iter {
|
||||||
declarations.push((declaration, origin));
|
declarations.push((declaration, priority));
|
||||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
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>,
|
cascade_mode: CascadeMode<'a>,
|
||||||
seen: LonghandIdSet,
|
seen: LonghandIdSet,
|
||||||
author_specified: LonghandIdSet,
|
author_specified: LonghandIdSet,
|
||||||
reverted: PerOrigin<LonghandIdSet>,
|
reverted_set: LonghandIdSet,
|
||||||
|
reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
|
@ -504,6 +509,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
cascade_mode,
|
cascade_mode,
|
||||||
seen: LonghandIdSet::default(),
|
seen: LonghandIdSet::default(),
|
||||||
author_specified: LonghandIdSet::default(),
|
author_specified: LonghandIdSet::default(),
|
||||||
|
reverted_set: Default::default(),
|
||||||
reverted: Default::default(),
|
reverted: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -575,7 +581,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
||||||
) where
|
) where
|
||||||
Phase: CascadePhase,
|
Phase: CascadePhase,
|
||||||
I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
|
I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
|
||||||
{
|
{
|
||||||
let apply_reset = apply_reset == ApplyResetProperties::Yes;
|
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 ignore_colors = !self.context.builder.device.use_document_colors();
|
||||||
let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
|
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 declaration_id = declaration.id();
|
||||||
let longhand_id = match declaration_id {
|
let longhand_id = match declaration_id {
|
||||||
PropertyDeclarationId::Longhand(id) => id,
|
PropertyDeclarationId::Longhand(id) => id,
|
||||||
|
@ -616,12 +624,12 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
if self.reverted_set.contains(physical_longhand_id) {
|
||||||
.reverted
|
if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&physical_longhand_id) {
|
||||||
.borrow_for_origin(&origin)
|
if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
|
||||||
.contains(physical_longhand_id)
|
continue;
|
||||||
{
|
}
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only a few properties are allowed to depend on the visited state
|
// 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();
|
let is_unset = match declaration.get_css_wide_keyword() {
|
||||||
if let Some(CSSWideKeyword::Revert) = css_wide_keyword {
|
Some(keyword) => match keyword {
|
||||||
// We intentionally don't want to insert it into `self.seen`,
|
CSSWideKeyword::RevertLayer |
|
||||||
// `reverted` takes care of rejecting other declarations as
|
CSSWideKeyword::Revert => {
|
||||||
// needed.
|
let origin_revert = keyword == CSSWideKeyword::Revert;
|
||||||
for origin in origin.following_including() {
|
// We intentionally don't want to insert it into
|
||||||
self.reverted
|
// `self.seen`, `reverted` takes care of rejecting other
|
||||||
.borrow_mut_for_origin(&origin)
|
// declarations as needed.
|
||||||
.insert(physical_longhand_id);
|
self.reverted_set.insert(physical_longhand_id);
|
||||||
}
|
self.reverted.insert(physical_longhand_id, (priority, origin_revert));
|
||||||
continue;
|
continue;
|
||||||
}
|
},
|
||||||
|
CSSWideKeyword::Unset => true,
|
||||||
|
CSSWideKeyword::Inherit => inherited,
|
||||||
|
CSSWideKeyword::Initial => !inherited,
|
||||||
|
},
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
|
||||||
self.seen.insert(physical_longhand_id);
|
self.seen.insert(physical_longhand_id);
|
||||||
if origin == Origin::Author {
|
if origin == Origin::Author {
|
||||||
self.author_specified.insert(physical_longhand_id);
|
self.author_specified.insert(physical_longhand_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| match css_wide_keyword {
|
if is_unset {
|
||||||
CSSWideKeyword::Unset => true,
|
|
||||||
CSSWideKeyword::Inherit => inherited,
|
|
||||||
CSSWideKeyword::Initial => !inherited,
|
|
||||||
CSSWideKeyword::Revert => unreachable!(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if unset {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,17 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::applicable_declarations::CascadePriority;
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
use crate::custom_properties::CustomPropertiesBuilder;
|
use crate::custom_properties::CustomPropertiesBuilder;
|
||||||
use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
|
use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||||
|
use crate::rule_tree::CascadeLevel;
|
||||||
use crate::selector_parser::SelectorImpl;
|
use crate::selector_parser::SelectorImpl;
|
||||||
use crate::shared_lock::Locked;
|
use crate::shared_lock::Locked;
|
||||||
use crate::str::{CssString, CssStringWriter};
|
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 crate::values::computed::Context;
|
||||||
use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput};
|
use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput};
|
||||||
use cssparser::{AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind, Parser};
|
use cssparser::{AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind, Parser};
|
||||||
|
@ -898,7 +900,7 @@ impl PropertyDeclarationBlock {
|
||||||
|
|
||||||
for declaration in self.normal_declaration_iter() {
|
for declaration in self.normal_declaration_iter() {
|
||||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||||
builder.cascade(declaration, Origin::Author);
|
builder.cascade(declaration, CascadePriority::new(CascadeLevel::same_tree_author_normal(), LayerOrder::root()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -476,6 +476,7 @@
|
||||||
context.builder.inherit_${property.ident}();
|
context.builder.inherit_${property.ident}();
|
||||||
% endif
|
% endif
|
||||||
}
|
}
|
||||||
|
CSSWideKeyword::RevertLayer |
|
||||||
CSSWideKeyword::Revert => unreachable!("Should never get here"),
|
CSSWideKeyword::Revert => unreachable!("Should never get here"),
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -311,9 +311,9 @@ impl AnimationValue {
|
||||||
% for prop in data.longhands:
|
% for prop in data.longhands:
|
||||||
% if prop.animatable:
|
% if prop.animatable:
|
||||||
LonghandId::${prop.camel_case} => {
|
LonghandId::${prop.camel_case} => {
|
||||||
// FIXME(emilio, bug 1533327): I think
|
// FIXME(emilio, bug 1533327): I think revert (and
|
||||||
// CSSWideKeyword::Revert handling is not fine here, but
|
// revert-layer) handling is not fine here, but what to
|
||||||
// what to do instead?
|
// do instead?
|
||||||
//
|
//
|
||||||
// Seems we'd need the computed value as if it was
|
// Seems we'd need the computed value as if it was
|
||||||
// revert, somehow. Treating it as `unset` seems fine
|
// revert, somehow. Treating it as `unset` seems fine
|
||||||
|
@ -321,6 +321,7 @@ impl AnimationValue {
|
||||||
let style_struct = match declaration.keyword {
|
let style_struct = match declaration.keyword {
|
||||||
% if not prop.style_struct.inherited:
|
% if not prop.style_struct.inherited:
|
||||||
CSSWideKeyword::Revert |
|
CSSWideKeyword::Revert |
|
||||||
|
CSSWideKeyword::RevertLayer |
|
||||||
CSSWideKeyword::Unset |
|
CSSWideKeyword::Unset |
|
||||||
% endif
|
% endif
|
||||||
CSSWideKeyword::Initial => {
|
CSSWideKeyword::Initial => {
|
||||||
|
@ -328,6 +329,7 @@ impl AnimationValue {
|
||||||
},
|
},
|
||||||
% if prop.style_struct.inherited:
|
% if prop.style_struct.inherited:
|
||||||
CSSWideKeyword::Revert |
|
CSSWideKeyword::Revert |
|
||||||
|
CSSWideKeyword::RevertLayer |
|
||||||
CSSWideKeyword::Unset |
|
CSSWideKeyword::Unset |
|
||||||
% endif
|
% endif
|
||||||
CSSWideKeyword::Inherit => {
|
CSSWideKeyword::Inherit => {
|
||||||
|
|
|
@ -1051,6 +1051,8 @@ pub enum CSSWideKeyword {
|
||||||
Unset,
|
Unset,
|
||||||
/// The `revert` keyword.
|
/// The `revert` keyword.
|
||||||
Revert,
|
Revert,
|
||||||
|
/// The `revert-layer` keyword.
|
||||||
|
RevertLayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CSSWideKeyword {
|
impl CSSWideKeyword {
|
||||||
|
@ -1060,22 +1062,28 @@ impl CSSWideKeyword {
|
||||||
CSSWideKeyword::Inherit => "inherit",
|
CSSWideKeyword::Inherit => "inherit",
|
||||||
CSSWideKeyword::Unset => "unset",
|
CSSWideKeyword::Unset => "unset",
|
||||||
CSSWideKeyword::Revert => "revert",
|
CSSWideKeyword::Revert => "revert",
|
||||||
|
CSSWideKeyword::RevertLayer => "revert-layer",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CSSWideKeyword {
|
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, ()> {
|
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||||
let keyword = {
|
let keyword = {
|
||||||
let ident = input.expect_ident().map_err(|_| ())?;
|
let ident = input.expect_ident().map_err(|_| ())?;
|
||||||
match_ignore_ascii_case! { ident,
|
Self::from_ident(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(()),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
input.expect_exhausted().map_err(|_| ())?;
|
input.expect_exhausted().map_err(|_| ())?;
|
||||||
Ok(keyword)
|
Ok(keyword)
|
||||||
|
|
|
@ -148,7 +148,7 @@ where
|
||||||
f(self);
|
f(self);
|
||||||
if start != self.rules.len() {
|
if start != self.rules.len() {
|
||||||
self.rules[start..].sort_unstable_by_key(|block| {
|
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;
|
self.context.current_host = old_host;
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use crate::properties::Importance;
|
use crate::applicable_declarations::CascadePriority;
|
||||||
use crate::shared_lock::StylesheetGuards;
|
use crate::shared_lock::StylesheetGuards;
|
||||||
|
use crate::stylesheets::layer_rule::LayerOrder;
|
||||||
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -66,7 +67,7 @@ impl MallocSizeOf for RuleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
struct ChildKey(CascadeLevel, ptr::NonNull<()>);
|
struct ChildKey(CascadePriority, ptr::NonNull<()>);
|
||||||
unsafe impl Send for ChildKey {}
|
unsafe impl Send for ChildKey {}
|
||||||
unsafe impl Sync for ChildKey {}
|
unsafe impl Sync for ChildKey {}
|
||||||
|
|
||||||
|
@ -219,8 +220,8 @@ struct RuleNode {
|
||||||
/// None for the root node.
|
/// None for the root node.
|
||||||
source: Option<StyleSource>,
|
source: Option<StyleSource>,
|
||||||
|
|
||||||
/// The cascade level this rule is positioned at.
|
/// The cascade level + layer order this rule is positioned at.
|
||||||
level: CascadeLevel,
|
cascade_priority: CascadePriority,
|
||||||
|
|
||||||
/// The refcount of this node.
|
/// The refcount of this node.
|
||||||
///
|
///
|
||||||
|
@ -316,14 +317,14 @@ impl RuleNode {
|
||||||
root: WeakRuleNode,
|
root: WeakRuleNode,
|
||||||
parent: StrongRuleNode,
|
parent: StrongRuleNode,
|
||||||
source: StyleSource,
|
source: StyleSource,
|
||||||
level: CascadeLevel,
|
cascade_priority: CascadePriority,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert!(root.p.parent.is_none());
|
debug_assert!(root.p.parent.is_none());
|
||||||
RuleNode {
|
RuleNode {
|
||||||
root: Some(root),
|
root: Some(root),
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
source: Some(source),
|
source: Some(source),
|
||||||
level: level,
|
cascade_priority,
|
||||||
refcount: AtomicUsize::new(1),
|
refcount: AtomicUsize::new(1),
|
||||||
children: Default::default(),
|
children: Default::default(),
|
||||||
approximate_free_count: AtomicUsize::new(0),
|
approximate_free_count: AtomicUsize::new(0),
|
||||||
|
@ -336,7 +337,7 @@ impl RuleNode {
|
||||||
root: None,
|
root: None,
|
||||||
parent: None,
|
parent: None,
|
||||||
source: None,
|
source: None,
|
||||||
level: CascadeLevel::UANormal,
|
cascade_priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
|
||||||
refcount: AtomicUsize::new(1),
|
refcount: AtomicUsize::new(1),
|
||||||
approximate_free_count: AtomicUsize::new(0),
|
approximate_free_count: AtomicUsize::new(0),
|
||||||
children: Default::default(),
|
children: Default::default(),
|
||||||
|
@ -346,7 +347,7 @@ impl RuleNode {
|
||||||
|
|
||||||
fn key(&self) -> ChildKey {
|
fn key(&self) -> ChildKey {
|
||||||
ChildKey(
|
ChildKey(
|
||||||
self.level,
|
self.cascade_priority,
|
||||||
self.source
|
self.source
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Called key() on the root node")
|
.expect("Called key() on the root node")
|
||||||
|
@ -554,20 +555,20 @@ impl StrongRuleNode {
|
||||||
&self,
|
&self,
|
||||||
root: &StrongRuleNode,
|
root: &StrongRuleNode,
|
||||||
source: StyleSource,
|
source: StyleSource,
|
||||||
level: CascadeLevel,
|
cascade_priority: CascadePriority,
|
||||||
) -> StrongRuleNode {
|
) -> StrongRuleNode {
|
||||||
use parking_lot::RwLockUpgradableReadGuard;
|
use parking_lot::RwLockUpgradableReadGuard;
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.p.level <= level,
|
self.p.cascade_priority <= cascade_priority,
|
||||||
"Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
|
"Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
|
||||||
self.p.level,
|
self.p.cascade_priority,
|
||||||
level,
|
cascade_priority,
|
||||||
self.p.source,
|
self.p.source,
|
||||||
source,
|
source,
|
||||||
);
|
);
|
||||||
|
|
||||||
let key = ChildKey(level, source.key());
|
let key = ChildKey(cascade_priority, source.key());
|
||||||
let children = self.p.children.upgradable_read();
|
let children = self.p.children.upgradable_read();
|
||||||
if let Some(child) = children.get(&key, |node| node.p.key()) {
|
if let Some(child) = children.get(&key, |node| node.p.key()) {
|
||||||
// Sound to call because we read-locked the parent's children.
|
// Sound to call because we read-locked the parent's children.
|
||||||
|
@ -584,7 +585,7 @@ impl StrongRuleNode {
|
||||||
root.downgrade(),
|
root.downgrade(),
|
||||||
self.clone(),
|
self.clone(),
|
||||||
source,
|
source,
|
||||||
level,
|
cascade_priority,
|
||||||
)));
|
)));
|
||||||
// Sound to call because we still own a strong reference to
|
// Sound to call because we still own a strong reference to
|
||||||
// this node, through the `node` variable itself that we are
|
// this node, through the `node` variable itself that we are
|
||||||
|
@ -602,14 +603,22 @@ impl StrongRuleNode {
|
||||||
self.p.source.as_ref()
|
self.p.source.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The cascade level for this node
|
/// The cascade priority.
|
||||||
pub fn cascade_level(&self) -> CascadeLevel {
|
#[inline]
|
||||||
self.p.level
|
pub fn cascade_priority(&self) -> CascadePriority {
|
||||||
|
self.p.cascade_priority
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the importance that this rule node represents.
|
/// The cascade level.
|
||||||
pub fn importance(&self) -> Importance {
|
#[inline]
|
||||||
self.p.level.importance()
|
pub fn cascade_level(&self) -> CascadeLevel {
|
||||||
|
self.cascade_priority().cascade_level()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
/// Returns whether this node has any child, only intended for testing
|
||||||
|
|
|
@ -29,7 +29,7 @@ use crate::stylesheets::Origin;
|
||||||
/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
|
/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
|
||||||
/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
|
/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
|
||||||
pub enum CascadeLevel {
|
pub enum CascadeLevel {
|
||||||
/// Normal User-Agent rules.
|
/// Normal User-Agent rules.
|
||||||
UANormal,
|
UANormal,
|
||||||
|
@ -69,82 +69,44 @@ pub enum CascadeLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CascadeLevel {
|
impl CascadeLevel {
|
||||||
/// Pack this cascade level in a single byte.
|
/// Convert this level from "unimportant" to "important".
|
||||||
///
|
pub fn important(&self) -> Self {
|
||||||
/// We have 10 levels, which we can represent with 4 bits, and then a
|
match *self {
|
||||||
/// cascade order optionally, which we can clamp to three bits max, and
|
Self::UANormal => Self::UAImportant,
|
||||||
/// represent with a fourth bit for the sign.
|
Self::UserNormal => Self::UserImportant,
|
||||||
///
|
|
||||||
/// 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),
|
|
||||||
Self::AuthorNormal {
|
Self::AuthorNormal {
|
||||||
shadow_cascade_order,
|
shadow_cascade_order,
|
||||||
} => (3, shadow_cascade_order.0),
|
} => Self::AuthorImportant {
|
||||||
Self::SMILOverride => (4, 0),
|
shadow_cascade_order: -shadow_cascade_order,
|
||||||
Self::Animations => (5, 0),
|
},
|
||||||
Self::AuthorImportant {
|
Self::PresHints |
|
||||||
shadow_cascade_order,
|
Self::SMILOverride |
|
||||||
} => (6, shadow_cascade_order.0),
|
Self::Animations |
|
||||||
Self::UserImportant => (7, 0),
|
Self::AuthorImportant { .. } |
|
||||||
Self::UAImportant => (8, 0),
|
Self::UserImportant |
|
||||||
Self::Transitions => (9, 0),
|
Self::UAImportant |
|
||||||
};
|
Self::Transitions => *self,
|
||||||
|
|
||||||
debug_assert_eq!(discriminant & 0xf, discriminant);
|
|
||||||
if order == 0 {
|
|
||||||
return discriminant;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/// Convert this level from "important" to "non-important".
|
||||||
/// explained above.
|
pub fn unimportant(&self) -> Self {
|
||||||
#[inline]
|
match *self {
|
||||||
pub fn from_byte(b: u8) -> Self {
|
Self::UAImportant => Self::UANormal,
|
||||||
let order = {
|
Self::UserImportant => Self::UserNormal,
|
||||||
let abs = ((b & 0b01110000) >> 4) as i8;
|
Self::AuthorImportant {
|
||||||
let negative = b & 0b10000000 != 0;
|
shadow_cascade_order,
|
||||||
if negative {
|
} => Self::AuthorNormal {
|
||||||
-abs
|
shadow_cascade_order: -shadow_cascade_order,
|
||||||
} 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,
|
Self::PresHints |
|
||||||
5 => Self::Animations,
|
Self::SMILOverride |
|
||||||
6 => {
|
Self::Animations |
|
||||||
return Self::AuthorImportant {
|
Self::AuthorNormal { .. } |
|
||||||
shadow_cascade_order: ShadowCascadeOrder(order),
|
Self::UserNormal |
|
||||||
}
|
Self::UANormal |
|
||||||
},
|
Self::Transitions => *self,
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select a lock guard for this level
|
/// Select a lock guard for this level
|
||||||
|
@ -231,6 +193,12 @@ impl CascadeLevel {
|
||||||
pub struct ShadowCascadeOrder(i8);
|
pub struct ShadowCascadeOrder(i8);
|
||||||
|
|
||||||
impl ShadowCascadeOrder {
|
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
|
/// A level for the outermost shadow tree (the shadow tree we own, and the
|
||||||
/// ones from the slots we're slotted in).
|
/// ones from the slots we're slotted in).
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -256,7 +224,9 @@ impl ShadowCascadeOrder {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dec(&mut self) {
|
pub fn dec(&mut self) {
|
||||||
debug_assert!(self.0 < 0);
|
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
|
/// The level, moving inwards. We should only move inwards if we're
|
||||||
|
@ -264,7 +234,9 @@ impl ShadowCascadeOrder {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inc(&mut self) {
|
pub fn inc(&mut self) {
|
||||||
debug_assert_ne!(self.0, -1);
|
debug_assert_ne!(self.0, -1);
|
||||||
self.0 = self.0.saturating_add(1);
|
if self.0 != Self::MAX {
|
||||||
|
self.0 += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
//! The rule tree.
|
//! The rule tree.
|
||||||
|
|
||||||
use crate::applicable_declarations::ApplicableDeclarationList;
|
use crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority};
|
||||||
use crate::properties::{LonghandIdSet, PropertyDeclarationBlock};
|
use crate::properties::{LonghandIdSet, PropertyDeclarationBlock};
|
||||||
use crate::shared_lock::{Locked, StylesheetGuards};
|
use crate::shared_lock::{Locked, StylesheetGuards};
|
||||||
|
use crate::stylesheets::layer_rule::LayerOrder;
|
||||||
use servo_arc::{Arc, ArcBorrow};
|
use servo_arc::{Arc, ArcBorrow};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
@ -47,21 +48,22 @@ impl RuleTree {
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
) -> StrongRuleNode
|
) -> StrongRuleNode
|
||||||
where
|
where
|
||||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
I: Iterator<Item = (StyleSource, CascadePriority)>,
|
||||||
{
|
{
|
||||||
use self::CascadeLevel::*;
|
use self::CascadeLevel::*;
|
||||||
let mut current = self.root().clone();
|
let mut current = self.root().clone();
|
||||||
|
|
||||||
let mut found_important = false;
|
let mut found_important = false;
|
||||||
|
|
||||||
let mut important_author = SmallVec::<[(StyleSource, ShadowCascadeOrder); 4]>::new();
|
let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
|
||||||
|
let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
|
||||||
let mut important_user = SmallVec::<[StyleSource; 4]>::new();
|
let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
|
||||||
let mut important_ua = SmallVec::<[StyleSource; 4]>::new();
|
|
||||||
let mut transition = None;
|
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");
|
debug_assert!(!level.is_important(), "Important levels handled internally");
|
||||||
|
|
||||||
let any_important = {
|
let any_important = {
|
||||||
let pdb = source.read(level.guard(guards));
|
let pdb = source.read(level.guard(guards));
|
||||||
pdb.any_important()
|
pdb.any_important()
|
||||||
|
@ -70,13 +72,9 @@ impl RuleTree {
|
||||||
if any_important {
|
if any_important {
|
||||||
found_important = true;
|
found_important = true;
|
||||||
match level {
|
match level {
|
||||||
AuthorNormal {
|
AuthorNormal { .. } => important_author.push((source.clone(), priority.important())),
|
||||||
shadow_cascade_order,
|
UANormal => important_ua.push((source.clone(), priority.important())),
|
||||||
} => {
|
UserNormal => important_user.push((source.clone(), priority.important())),
|
||||||
important_author.push((source.clone(), shadow_cascade_order));
|
|
||||||
},
|
|
||||||
UANormal => important_ua.push(source.clone()),
|
|
||||||
UserNormal => important_user.push(source.clone()),
|
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -98,7 +96,7 @@ impl RuleTree {
|
||||||
debug_assert!(transition.is_none());
|
debug_assert!(transition.is_none());
|
||||||
transition = Some(source);
|
transition = Some(source);
|
||||||
} else {
|
} 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,
|
// Insert important declarations, in order of increasing importance,
|
||||||
// followed by any transition rule.
|
// followed by any transition rule.
|
||||||
//
|
//
|
||||||
// Inner shadow wins over same-tree, which wins over outer-shadow.
|
// Important rules are sorted differently from unimportant ones by
|
||||||
//
|
// shadow order and cascade order.
|
||||||
// We negate the shadow cascade order to preserve the right PartialOrd
|
|
||||||
// behavior.
|
|
||||||
if !important_author.is_empty() &&
|
if !important_author.is_empty() &&
|
||||||
important_author.first().unwrap().1 != important_author.last().unwrap().1
|
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
|
// inside the same chunk already sorted. Seems like we could try to
|
||||||
// keep a SmallVec-of-SmallVecs with the chunks and just iterate the
|
// keep a SmallVec-of-SmallVecs with the chunks and just iterate the
|
||||||
// outer in reverse.
|
// 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(..) {
|
for (source, priority) in important_author.drain(..) {
|
||||||
current = current.ensure_child(
|
current = current.ensure_child(self.root(), source, priority);
|
||||||
self.root(),
|
|
||||||
source,
|
|
||||||
AuthorImportant {
|
|
||||||
shadow_cascade_order: -shadow_cascade_order,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for source in important_user.drain(..) {
|
for (source, priority) in important_user.drain(..) {
|
||||||
current = current.ensure_child(self.root(), source, UserImportant);
|
current = current.ensure_child(self.root(), source, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
for source in important_ua.drain(..) {
|
for (source, priority) in important_ua.drain(..) {
|
||||||
current = current.ensure_child(self.root(), source, UAImportant);
|
current = current.ensure_child(self.root(), source, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(source) = transition {
|
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
|
current
|
||||||
|
@ -174,18 +168,18 @@ impl RuleTree {
|
||||||
/// return the corresponding rule node representing the last inserted one.
|
/// return the corresponding rule node representing the last inserted one.
|
||||||
pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
|
pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
|
||||||
where
|
where
|
||||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
I: Iterator<Item = (StyleSource, CascadePriority)>,
|
||||||
{
|
{
|
||||||
self.insert_ordered_rules_from(self.root().clone(), iter)
|
self.insert_ordered_rules_from(self.root().clone(), iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode
|
fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode
|
||||||
where
|
where
|
||||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
I: Iterator<Item = (StyleSource, CascadePriority)>,
|
||||||
{
|
{
|
||||||
let mut current = from;
|
let mut current = from;
|
||||||
for (source, level) in iter {
|
for (source, priority) in iter {
|
||||||
current = current.ensure_child(self.root(), source, level);
|
current = current.ensure_child(self.root(), source, priority);
|
||||||
}
|
}
|
||||||
current
|
current
|
||||||
}
|
}
|
||||||
|
@ -197,6 +191,7 @@ impl RuleTree {
|
||||||
pub fn update_rule_at_level(
|
pub fn update_rule_at_level(
|
||||||
&self,
|
&self,
|
||||||
level: CascadeLevel,
|
level: CascadeLevel,
|
||||||
|
layer_order: LayerOrder,
|
||||||
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
||||||
path: &StrongRuleNode,
|
path: &StrongRuleNode,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
|
@ -209,10 +204,10 @@ impl RuleTree {
|
||||||
|
|
||||||
// First walk up until the first less-or-equally specific rule.
|
// First walk up until the first less-or-equally specific rule.
|
||||||
let mut children = SmallVec::<[_; 10]>::new();
|
let mut children = SmallVec::<[_; 10]>::new();
|
||||||
while current.cascade_level() > level {
|
while current.cascade_priority().cascade_level() > level {
|
||||||
children.push((
|
children.push((
|
||||||
current.style_source().unwrap().clone(),
|
current.style_source().unwrap().clone(),
|
||||||
current.cascade_level(),
|
current.cascade_priority(),
|
||||||
));
|
));
|
||||||
current = current.parent().unwrap().clone();
|
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
|
// 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
|
// special cases, and replacing them for a `while` loop, avoiding the
|
||||||
// optimizations).
|
// optimizations).
|
||||||
if current.cascade_level() == level {
|
if current.cascade_priority().cascade_level() == level {
|
||||||
*important_rules_changed |= level.is_important();
|
*important_rules_changed |= level.is_important();
|
||||||
|
|
||||||
let current_decls = current.style_source().unwrap().as_declarations();
|
let current_decls = current.style_source().unwrap().as_declarations();
|
||||||
|
@ -267,7 +262,7 @@ impl RuleTree {
|
||||||
current = current.ensure_child(
|
current = current.ensure_child(
|
||||||
self.root(),
|
self.root(),
|
||||||
StyleSource::from_declarations(pdb.clone_arc()),
|
StyleSource::from_declarations(pdb.clone_arc()),
|
||||||
level,
|
CascadePriority::new(level, layer_order),
|
||||||
);
|
);
|
||||||
*important_rules_changed = true;
|
*important_rules_changed = true;
|
||||||
}
|
}
|
||||||
|
@ -276,7 +271,7 @@ impl RuleTree {
|
||||||
current = current.ensure_child(
|
current = current.ensure_child(
|
||||||
self.root(),
|
self.root(),
|
||||||
StyleSource::from_declarations(pdb.clone_arc()),
|
StyleSource::from_declarations(pdb.clone_arc()),
|
||||||
level,
|
CascadePriority::new(level, layer_order),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,7 +307,10 @@ impl RuleTree {
|
||||||
let mut children = SmallVec::<[_; 10]>::new();
|
let mut children = SmallVec::<[_; 10]>::new();
|
||||||
for node in iter {
|
for node in iter {
|
||||||
if !node.cascade_level().is_animation() {
|
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;
|
last = node;
|
||||||
}
|
}
|
||||||
|
@ -336,6 +334,7 @@ impl RuleTree {
|
||||||
let mut dummy = false;
|
let mut dummy = false;
|
||||||
self.update_rule_at_level(
|
self.update_rule_at_level(
|
||||||
CascadeLevel::Transitions,
|
CascadeLevel::Transitions,
|
||||||
|
LayerOrder::root(),
|
||||||
Some(pdb.borrow_arc()),
|
Some(pdb.borrow_arc()),
|
||||||
path,
|
path,
|
||||||
guards,
|
guards,
|
||||||
|
|
|
@ -19,14 +19,16 @@ use smallvec::SmallVec;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use style_traits::{CssWriter, ParseError, ToCss};
|
use style_traits::{CssWriter, ParseError, ToCss};
|
||||||
|
|
||||||
/// The order of a given layer.
|
/// The order of a given layer. We use 16 bits so that we can pack LayerOrder
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]
|
/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go
|
||||||
pub struct LayerOrder(u32);
|
/// 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 {
|
impl LayerOrder {
|
||||||
/// The order of the root layer.
|
/// The order of the root layer.
|
||||||
pub const fn root() -> Self {
|
pub const fn root() -> Self {
|
||||||
Self(std::u32::MAX)
|
Self(std::u16::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The first cascade layer order.
|
/// The first cascade layer order.
|
||||||
|
@ -37,7 +39,9 @@ impl LayerOrder {
|
||||||
/// Increment the cascade layer order.
|
/// Increment the cascade layer order.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inc(&mut self) {
|
pub fn inc(&mut self) {
|
||||||
self.0 += 1;
|
if self.0 != std::u16::MAX {
|
||||||
|
self.0 += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::ops::BitOrAssign;
|
||||||
/// Each style rule has an origin, which determines where it enters the cascade.
|
/// Each style rule has an origin, which determines where it enters the cascade.
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-cascade/#cascading-origins>
|
/// <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)]
|
#[repr(u8)]
|
||||||
pub enum Origin {
|
pub enum Origin {
|
||||||
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
|
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
//! Selector matching.
|
//! Selector matching.
|
||||||
|
|
||||||
use crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
|
use crate::applicable_declarations::{
|
||||||
|
ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority,
|
||||||
|
};
|
||||||
use crate::context::{CascadeInputs, QuirksMode};
|
use crate::context::{CascadeInputs, QuirksMode};
|
||||||
use crate::dom::{TElement, TShadowRoot};
|
use crate::dom::{TElement, TShadowRoot};
|
||||||
use crate::element_state::{DocumentState, ElementState};
|
use crate::element_state::{DocumentState, ElementState};
|
||||||
|
@ -1474,9 +1476,15 @@ impl Stylist {
|
||||||
/* pseudo = */ None,
|
/* pseudo = */ None,
|
||||||
self.rule_tree.root(),
|
self.rule_tree.root(),
|
||||||
guards,
|
guards,
|
||||||
block
|
block.declaration_importance_iter().map(|(declaration, _)| {
|
||||||
.declaration_importance_iter()
|
(
|
||||||
.map(|(declaration, _)| (declaration, Origin::Author)),
|
declaration,
|
||||||
|
CascadePriority::new(
|
||||||
|
CascadeLevel::same_tree_author_normal(),
|
||||||
|
LayerOrder::root(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
Some(parent_style),
|
Some(parent_style),
|
||||||
|
@ -1582,7 +1590,7 @@ impl ExtraStyleData {
|
||||||
&mut self,
|
&mut self,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
rule: &Arc<Locked<ScrollTimelineRule>>,
|
rule: &Arc<Locked<ScrollTimelineRule>>,
|
||||||
)-> Result<(), FailedAllocationError> {
|
) -> Result<(), FailedAllocationError> {
|
||||||
let name = rule.read_with(guard).name.as_atom().clone();
|
let name = rule.read_with(guard).name.as_atom().clone();
|
||||||
self.scroll_timelines
|
self.scroll_timelines
|
||||||
.try_insert(name, rule.clone())
|
.try_insert(name, rule.clone())
|
||||||
|
|
|
@ -438,15 +438,22 @@ impl CustomIdent {
|
||||||
ident: &CowRcStr<'i>,
|
ident: &CowRcStr<'i>,
|
||||||
excluding: &[&str],
|
excluding: &[&str],
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let valid = match_ignore_ascii_case! { ident,
|
use crate::properties::CSSWideKeyword;
|
||||||
"initial" | "inherit" | "unset" | "default" | "revert" => false,
|
// https://drafts.csswg.org/css-values-4/#custom-idents:
|
||||||
_ => true
|
//
|
||||||
};
|
// The CSS-wide keywords are not valid <custom-ident>s. The default
|
||||||
if !valid {
|
// 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(
|
return Err(
|
||||||
location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
|
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)) {
|
if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
|
||||||
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue