mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
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:
parent
5f2a29659f
commit
1b2ef21c8c
6 changed files with 190 additions and 164 deletions
|
@ -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, ¤t_layer);
|
||||
id = maybe_register_layer(data, ¤t_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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue