style: Update layer ordering to follow the spec

The bitfield approach worked when the layer order was in pre-order, but
the spec was changed to make it work like post-order and I don't think
there's a way to keep it working like that, so keep the layer order in a
separate data structure that we look up when going from Rule to
ApplicableDeclarationBlock.

This is just a vector index operation so hopefully shouldn't be too bad.

This patch intentionally regresses @keyframe handling to some extent,
since we need a bit more complicated approach and it seemed worth
implementing in a separate patch.

Depends on D129380

Differential Revision: https://phabricator.services.mozilla.com/D129381
This commit is contained in:
Emilio Cobos Álvarez 2023-05-27 17:36:01 +02:00 committed by Oriol Brufau
parent 5f2a29659f
commit 1b2ef21c8c
6 changed files with 190 additions and 164 deletions

View file

@ -88,13 +88,19 @@ impl ApplicableDeclarationBlock {
source: StyleSource::from_declarations(declarations),
bits: ApplicableDeclarationBits::new(0, level),
specificity: 0,
layer_order: LayerOrder::first(),
layer_order: LayerOrder::root(),
}
}
/// Constructs an applicable declaration block from the given components
/// Constructs an applicable declaration block from the given components.
#[inline]
pub fn new(source: StyleSource, source_order: u32, level: CascadeLevel, specificity: u32, layer_order: LayerOrder) -> Self {
pub fn new(
source: StyleSource,
source_order: u32,
level: CascadeLevel,
specificity: u32,
layer_order: LayerOrder,
) -> Self {
ApplicableDeclarationBlock {
source,
bits: ApplicableDeclarationBits::new(source_order, level),

View file

@ -12,7 +12,7 @@ use crate::selector_map::SelectorMap;
use crate::selector_parser::PseudoElement;
use crate::shared_lock::Locked;
use crate::stylesheets::Origin;
use crate::stylist::{AuthorStylesEnabled, Rule, RuleInclusion, Stylist};
use crate::stylist::{AuthorStylesEnabled, CascadeData, Rule, RuleInclusion, Stylist};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use servo_arc::ArcBorrow;
use smallvec::SmallVec;
@ -173,7 +173,7 @@ where
};
self.in_tree(None, |collector| {
collector.collect_rules_in_map(map, cascade_level);
collector.collect_rules_in_map(map, cascade_level, cascade_data);
});
}
@ -214,7 +214,7 @@ where
}
#[inline]
fn collect_rules_in_list(&mut self, part_rules: &[Rule], cascade_level: CascadeLevel) {
fn collect_rules_in_list(&mut self, part_rules: &[Rule], cascade_level: CascadeLevel, cascade_data: &CascadeData) {
debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
SelectorMap::get_matching_rules(
self.element,
@ -223,11 +223,12 @@ where
&mut self.context,
&mut self.flags_setter,
cascade_level,
cascade_data,
);
}
#[inline]
fn collect_rules_in_map(&mut self, map: &SelectorMap<Rule>, cascade_level: CascadeLevel) {
fn collect_rules_in_map(&mut self, map: &SelectorMap<Rule>, cascade_level: CascadeLevel, cascade_data: &CascadeData) {
debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
map.get_all_matching_rules(
self.element,
@ -236,6 +237,7 @@ where
&mut self.context,
&mut self.flags_setter,
cascade_level,
cascade_data,
);
}
@ -277,7 +279,7 @@ where
let cascade_level = CascadeLevel::AuthorNormal {
shadow_cascade_order,
};
collector.collect_rules_in_map(slotted_rules, cascade_level);
collector.collect_rules_in_map(slotted_rules, cascade_level, data);
});
}
}
@ -303,7 +305,7 @@ where
let cascade_level = CascadeLevel::same_tree_author_normal();
self.in_shadow_tree(containing_shadow.host(), |collector| {
if let Some(map) = cascade_data.normal_rules(collector.pseudo_element) {
collector.collect_rules_in_map(map, cascade_level);
collector.collect_rules_in_map(map, cascade_level, cascade_data);
}
// Collect rules from :host::part() and such
@ -319,7 +321,7 @@ where
hash_target.each_part(|part| {
if let Some(part_rules) = part_rules.get(&part.0) {
collector.collect_rules_in_list(part_rules, cascade_level);
collector.collect_rules_in_list(part_rules, cascade_level, cascade_data);
}
});
});
@ -352,7 +354,7 @@ where
let cascade_level = CascadeLevel::AuthorNormal {
shadow_cascade_order,
};
collector.collect_rules_in_map(host_rules, cascade_level);
collector.collect_rules_in_map(host_rules, cascade_level, style_data);
});
}
@ -386,18 +388,17 @@ where
let inner_shadow_host = inner_shadow.host();
let outer_shadow = inner_shadow_host.containing_shadow();
let part_rules = match outer_shadow {
Some(shadow) => shadow
.style_data()
.and_then(|data| data.part_rules(self.pseudo_element)),
None => self
let cascade_data = match outer_shadow {
Some(shadow) => shadow.style_data(),
None => Some(self
.stylist
.cascade_data()
.borrow_for_origin(Origin::Author)
.part_rules(self.pseudo_element),
),
};
if let Some(part_rules) = part_rules {
if let Some(cascade_data) = cascade_data {
if let Some(part_rules) = cascade_data.part_rules(self.pseudo_element) {
let containing_host = outer_shadow.map(|s| s.host());
let cascade_level = CascadeLevel::AuthorNormal {
shadow_cascade_order,
@ -405,12 +406,13 @@ where
self.in_tree(containing_host, |collector| {
for p in &parts {
if let Some(part_rules) = part_rules.get(&p.0) {
collector.collect_rules_in_list(part_rules, cascade_level);
collector.collect_rules_in_list(part_rules, cascade_level, cascade_data);
}
}
});
shadow_cascade_order.inc();
}
}
inner_shadow = match outer_shadow {
Some(s) => s,

View file

@ -12,7 +12,7 @@ use crate::hash::map as hash_map;
use crate::hash::{HashMap, HashSet};
use crate::rule_tree::CascadeLevel;
use crate::selector_parser::SelectorImpl;
use crate::stylist::Rule;
use crate::stylist::{Rule, CascadeData};
use crate::{Atom, LocalName, Namespace, WeakAtom};
use fallible::FallibleVec;
use hashglobe::FailedAllocationError;
@ -189,6 +189,7 @@ impl SelectorMap<Rule> {
context: &mut MatchingContext<E::Impl>,
flags_setter: &mut F,
cascade_level: CascadeLevel,
cascade_data: &CascadeData,
) where
E: TElement,
F: FnMut(&E, ElementSelectorFlags),
@ -207,6 +208,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
);
}
@ -219,6 +221,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
)
}
}
@ -232,6 +235,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
)
}
});
@ -246,6 +250,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
)
}
});
@ -259,6 +264,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
)
}
@ -270,6 +276,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
)
}
@ -280,6 +287,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
cascade_level,
cascade_data,
);
}
@ -291,6 +299,7 @@ impl SelectorMap<Rule> {
context: &mut MatchingContext<E::Impl>,
flags_setter: &mut F,
cascade_level: CascadeLevel,
cascade_data: &CascadeData,
) where
E: TElement,
F: FnMut(&E, ElementSelectorFlags),
@ -304,7 +313,7 @@ impl SelectorMap<Rule> {
context,
flags_setter,
) {
matching_rules.push(rule.to_applicable_declaration_block(cascade_level));
matching_rules.push(rule.to_applicable_declaration_block(cascade_level, cascade_data));
}
}
}

View file

@ -14,7 +14,7 @@ use crate::properties::{PropertyDeclarationId, SourcePropertyDeclaration};
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
use crate::shared_lock::{Locked, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::layer_rule::LayerOrder;
use crate::stylesheets::layer_rule::LayerId;
use crate::stylesheets::rule_parser::VendorPrefix;
use crate::stylesheets::{CssRuleType, StylesheetContents};
use crate::values::{serialize_percentage, KeyframesName};
@ -358,8 +358,8 @@ pub struct KeyframesAnimation {
pub properties_changed: LonghandIdSet,
/// Vendor prefix type the @keyframes has.
pub vendor_prefix: Option<VendorPrefix>,
/// The order of the cascade layer the keyframe rule was in.
pub layer_order: LayerOrder,
/// The id of the cascade layer the keyframe rule was in.
pub layer_id: LayerId,
}
/// Get all the animated properties in a keyframes animation.
@ -412,14 +412,14 @@ impl KeyframesAnimation {
pub fn from_keyframes(
keyframes: &[Arc<Locked<Keyframe>>],
vendor_prefix: Option<VendorPrefix>,
layer_order: LayerOrder,
layer_id: LayerId,
guard: &SharedRwLockReadGuard,
) -> Self {
let mut result = KeyframesAnimation {
steps: vec![],
properties_changed: LonghandIdSet::new(),
vendor_prefix,
layer_order,
layer_id,
};
if keyframes.is_empty() {

View file

@ -19,94 +19,36 @@ use smallvec::SmallVec;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
/// The order of a given layer. We encode in a 32-bit integer as follows:
///
/// * 0 is reserved for the initial (top-level) layer.
/// * Top 7 bits are for top level layer order.
/// * The 25 remaining bits are split in 5 chunks of 5 bits each, for each
/// nesting level.
///
/// This scheme this gives up to 127 layers in the top level, and up to 31
/// children layers in nested levels, with a max of 6 nesting levels over all.
///
/// This seemingly complicated scheme is to avoid fixing up layer orders after
/// the cascade data rebuild.
///
/// An alternative approach that would allow improving those limits would be to
/// make layers have a sequential identifier, and sort layer order after the
/// fact. But that complicates incremental cascade data rebuild.
/// The order of a given layer.
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]
pub struct LayerOrder(u32);
impl LayerOrder {
const FIRST_LEVEL_BITS: usize = 7;
const CHILD_BITS: usize = 5;
const FIRST_LEVEL_MASK: u32 = 0b11111110_00000000_00000000_00000000;
const CHILD_MASK: u32 = 0b00011111;
/// Get the raw value.
pub fn raw(self) -> u32 {
self.0
/// The order of the root layer.
pub const fn root() -> Self {
Self(std::u32::MAX)
}
/// The top level layer (implicit) is zero.
#[inline]
pub const fn top_level() -> Self {
/// The first cascade layer order.
pub const fn first() -> Self {
Self(0)
}
/// The first layer order.
/// Increment the cascade layer order.
#[inline]
pub const fn first() -> Self {
Self(1 << (32 - Self::FIRST_LEVEL_BITS))
pub fn inc(&mut self) {
self.0 += 1;
}
}
fn child_bit_offset(self) -> usize {
if self.0 & (Self::CHILD_MASK << 5) != 0 {
return 0; // We're at the last or next-to-last level.
}
if self.0 & (Self::CHILD_MASK << 10) != 0 {
return 5;
}
if self.0 & (Self::CHILD_MASK << 15) != 0 {
return 10;
}
if self.0 & (Self::CHILD_MASK << 20) != 0 {
return 15;
}
if self.0 != 0 {
return 20;
}
return 25;
}
/// The id of a given layer, a sequentially-increasing identifier.
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]
pub struct LayerId(pub u32);
fn sibling_bit_mask_max_and_offset(self) -> (u32, u32, u32) {
debug_assert_ne!(self.0, 0, "Top layer should have no siblings");
for offset in &[0, 5, 10, 15, 20] {
let mask = Self::CHILD_MASK << *offset;
if self.0 & mask != 0 {
return (mask, (1 << Self::CHILD_BITS) - 1, *offset);
}
}
return (Self::FIRST_LEVEL_MASK, (1 << Self::FIRST_LEVEL_BITS) - 1, 25);
}
/// Generate the layer order for our first child.
pub fn for_child(self) -> Self {
Self(self.0 | (1 << self.child_bit_offset()))
}
/// Generate the layer order for our next sibling. Might return the same
/// order when our limits overflow.
pub fn for_next_sibling(self) -> Self {
let (mask, max_index, offset) = self.sibling_bit_mask_max_and_offset();
let self_index = (self.0 & mask) >> offset;
let next_index = if self_index == max_index {
self_index
} else {
self_index + 1
};
Self((self.0 & !mask) | (next_index << offset))
impl LayerId {
/// The id of the root layer.
pub const fn root() -> Self {
Self(0)
}
}

View file

@ -25,7 +25,7 @@ use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
use crate::stylesheets::layer_rule::{LayerName, LayerOrder};
use crate::stylesheets::layer_rule::{LayerName, LayerId, LayerOrder};
use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
use crate::stylesheets::{StyleRule, StylesheetInDocument, StylesheetContents};
#[cfg(feature = "gecko")]
@ -293,6 +293,8 @@ impl CascadeDataCacheEntry for UserAgentCascadeData {
)?;
}
new_data.cascade_data.compute_layer_order();
Ok(Arc::new(new_data))
}
@ -1867,12 +1869,21 @@ impl PartElementAndPseudoRules {
}
}
#[derive(Debug, Clone, MallocSizeOf)]
struct LayerOrderState {
/// The order for this layer.
#[derive(Clone, Debug, MallocSizeOf)]
struct CascadeLayer {
id: LayerId,
order: LayerOrder,
/// The order for the next registered child layer.
next_child: LayerOrder,
children: Vec<LayerId>,
}
impl CascadeLayer {
const fn root() -> Self {
Self {
id: LayerId::root(),
order: LayerOrder::root(),
children: vec![],
}
}
}
/// Data resulting from performing the CSS cascade that is specific to a given
@ -1941,10 +1952,10 @@ pub struct CascadeData {
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
/// A map from cascade layer name to layer order.
layer_order: FxHashMap<LayerName, LayerOrderState>,
layer_id: FxHashMap<LayerName, LayerId>,
/// The next layer order for the top level cascade data.
next_layer_order: LayerOrder,
/// The list of cascade layers, indexed by their layer id.
layers: SmallVec<[CascadeLayer; 1]>,
/// Effective media query results cached from the last rebuild.
effective_media_query_results: EffectiveMediaQueryResults,
@ -1986,8 +1997,8 @@ impl CascadeData {
// somewhat gnarly.
selectors_for_cache_revalidation: SelectorMap::new_without_attribute_bucketing(),
animations: Default::default(),
layer_order: Default::default(),
next_layer_order: LayerOrder::first(),
layer_id: Default::default(),
layers: smallvec::smallvec![CascadeLayer::root()],
extra_data: ExtraStyleData::default(),
effective_media_query_results: EffectiveMediaQueryResults::new(),
rules_source_order: 0,
@ -2034,6 +2045,8 @@ impl CascadeData {
result.is_ok()
});
self.compute_layer_order();
result
}
@ -2095,6 +2108,43 @@ impl CascadeData {
self.part_rules.is_some()
}
#[inline]
fn layer_order_for(&self, id: LayerId) -> LayerOrder {
self.layers[id.0 as usize].order
}
fn compute_layer_order(&mut self) {
debug_assert_ne!(self.layers.len(), 0, "There should be at least the root layer!");
if self.layers.len() == 1 {
return; // Nothing to do
}
let (first, remaining) = self.layers.split_at_mut(1);
let root = &mut first[0];
let mut order = LayerOrder::first();
compute_layer_order_for_subtree(root, remaining, &mut order);
// NOTE(emilio): This is a bit trickier than it should to avoid having
// to clone() around layer indices.
fn compute_layer_order_for_subtree(
parent: &mut CascadeLayer,
remaining_layers: &mut [CascadeLayer],
order: &mut LayerOrder,
) {
for child in parent.children.iter() {
debug_assert!(parent.id < *child, "Children are always registered after parents");
let child_index = (child.0 - parent.id.0 - 1) as usize;
let (first, remaining) = remaining_layers.split_at_mut(child_index + 1);
let child = &mut first[child_index];
compute_layer_order_for_subtree(child, remaining, order);
}
if parent.id != LayerId::root() {
parent.order = *order;
order.inc();
}
}
}
/// Collects all the applicable media query results into `results`.
///
/// This duplicates part of the logic in `add_stylesheet`, which is
@ -2151,7 +2201,7 @@ impl CascadeData {
guard: &SharedRwLockReadGuard,
rebuild_kind: SheetRebuildKind,
mut current_layer: &mut LayerName,
current_layer_order: LayerOrder,
current_layer_id: LayerId,
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), FailedAllocationError>
where
@ -2174,6 +2224,7 @@ impl CascadeData {
if pseudo.is_precomputed() {
debug_assert!(selector.is_universal());
debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent);
debug_assert_eq!(current_layer_id, LayerId::root());
precomputed_pseudo_element_decls
.as_mut()
@ -2184,7 +2235,7 @@ impl CascadeData {
self.rules_source_order,
CascadeLevel::UANormal,
selector.specificity(),
current_layer_order,
LayerOrder::root(),
));
continue;
}
@ -2200,7 +2251,7 @@ impl CascadeData {
hashes,
locked.clone(),
self.rules_source_order,
current_layer_order,
current_layer_id,
);
if rebuild_kind.should_rebuild_invalidation() {
@ -2277,22 +2328,24 @@ impl CascadeData {
e.insert(KeyframesAnimation::from_keyframes(
&keyframes_rule.keyframes,
keyframes_rule.vendor_prefix.clone(),
current_layer_order,
current_layer_id,
guard,
));
},
Entry::Occupied(mut e) => {
// Don't let a prefixed keyframes animation override
// a non-prefixed one on the same layer.
// a non-prefixed one.
//
// TODO(emilio): This will need to be harder for
// layers.
let needs_insert =
current_layer_order > e.get().layer_order ||
(current_layer_order == e.get().layer_order &&
(keyframes_rule.vendor_prefix.is_none() || e.get().vendor_prefix.is_some()));
keyframes_rule.vendor_prefix.is_none() ||
e.get().vendor_prefix.is_some();
if needs_insert {
e.insert(KeyframesAnimation::from_keyframes(
&keyframes_rule.keyframes,
keyframes_rule.vendor_prefix.clone(),
current_layer_order,
current_layer_id,
guard,
));
}
@ -2360,32 +2413,38 @@ impl CascadeData {
continue;
}
fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> LayerOrder {
fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> LayerId {
// TODO: Measure what's more common / expensive, if
// layer.clone() or the double hash lookup in the insert
// case.
if let Some(ref mut state) = data.layer_order.get(layer) {
return state.order;
if let Some(id) = data.layer_id.get(layer) {
return *id;
}
// If the layer is not top-level, find the relevant parent.
let order = if layer.layer_names().len() > 1 {
let id = LayerId(data.layers.len() as u32);
let parent_layer_id = if layer.layer_names().len() > 1 {
let mut parent = layer.clone();
parent.0.pop();
let mut parent_state = data.layer_order.get_mut(&parent).expect("Parent layers should be registered before child layers");
let order = parent_state.next_child;
parent_state.next_child = order.for_next_sibling();
order
*data.layer_id
.get_mut(&parent)
.expect("Parent layers should be registered before child layers")
} else {
let order = data.next_layer_order;
data.next_layer_order = order.for_next_sibling();
order
LayerId::root()
};
data.layer_order.insert(layer.clone(), LayerOrderState {
order,
next_child: order.for_child(),
data.layers[parent_layer_id.0 as usize].children.push(id);
data.layers.push(CascadeLayer {
id,
// NOTE(emilio): Order is evaluated after rebuild in
// compute_layer_order.
order: LayerOrder::first(),
children: vec![],
});
order
data.layer_id.insert(layer.clone(), id);
id
}
fn maybe_register_layers(
@ -2393,7 +2452,7 @@ impl CascadeData {
name: Option<&LayerName>,
current_layer: &mut LayerName,
pushed_layers: &mut usize,
) -> LayerOrder {
) -> LayerId {
let anon_name;
let name = match name {
Some(name) => name,
@ -2403,18 +2462,18 @@ impl CascadeData {
},
};
let mut order = LayerOrder::top_level();
let mut id = LayerId::root();
for name in name.layer_names() {
current_layer.0.push(name.clone());
order = maybe_register_layer(data, &current_layer);
id = maybe_register_layer(data, &current_layer);
*pushed_layers += 1;
}
debug_assert_ne!(order, LayerOrder::top_level());
order
debug_assert_ne!(id, LayerId::root());
id
}
let mut layer_names_to_pop = 0;
let mut children_layer_order = current_layer_order;
let mut children_layer_id = current_layer_id;
match *rule {
CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard);
@ -2423,7 +2482,7 @@ impl CascadeData {
.saw_effective(import_rule);
}
if let Some(ref layer) = import_rule.layer {
children_layer_order = maybe_register_layers(
children_layer_id = maybe_register_layers(
self,
layer.name.as_ref(),
&mut current_layer,
@ -2444,7 +2503,7 @@ impl CascadeData {
let layer_rule = lock.read_with(guard);
match layer_rule.kind {
LayerRuleKind::Block { ref name, .. } => {
children_layer_order = maybe_register_layers(
children_layer_id = maybe_register_layers(
self,
name.as_ref(),
&mut current_layer,
@ -2482,7 +2541,7 @@ impl CascadeData {
guard,
rebuild_kind,
current_layer,
children_layer_order,
children_layer_id,
precomputed_pseudo_element_decls.as_deref_mut(),
)?;
}
@ -2527,7 +2586,7 @@ impl CascadeData {
guard,
rebuild_kind,
&mut current_layer,
LayerOrder::top_level(),
LayerId::root(),
precomputed_pseudo_element_decls.as_deref_mut(),
)?;
@ -2646,8 +2705,9 @@ impl CascadeData {
host_rules.clear();
}
self.animations.clear();
self.layer_order.clear();
self.next_layer_order = LayerOrder::first();
self.layer_id.clear();
self.layers.clear();
self.layers.push(CascadeLayer::root());
self.extra_data.clear();
self.rules_source_order = 0;
self.num_selectors = 0;
@ -2736,8 +2796,8 @@ pub struct Rule {
/// we could repurpose that storage here if we needed to.
pub source_order: u32,
/// The current layer order of this style rule.
pub layer_order: LayerOrder,
/// The current layer id of this style rule.
pub layer_id: LayerId,
/// The actual style rule.
#[cfg_attr(
@ -2765,9 +2825,16 @@ impl Rule {
pub fn to_applicable_declaration_block(
&self,
level: CascadeLevel,
cascade_data: &CascadeData,
) -> ApplicableDeclarationBlock {
let source = StyleSource::from_rule(self.style_rule.clone());
ApplicableDeclarationBlock::new(source, self.source_order, level, self.specificity(), self.layer_order)
ApplicableDeclarationBlock::new(
source,
self.source_order,
level,
self.specificity(),
cascade_data.layer_order_for(self.layer_id),
)
}
/// Creates a new Rule.
@ -2776,14 +2843,14 @@ impl Rule {
hashes: AncestorHashes,
style_rule: Arc<Locked<StyleRule>>,
source_order: u32,
layer_order: LayerOrder,
layer_id: LayerId,
) -> Self {
Rule {
selector,
hashes,
style_rule,
source_order,
layer_order,
layer_id,
}
}
}