mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
style: Deal with layers and at-rules
Differential Revision: https://phabricator.services.mozilla.com/D134010
This commit is contained in:
parent
da29cade57
commit
454a9777b3
2 changed files with 124 additions and 67 deletions
|
@ -14,7 +14,6 @@ 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::LayerId;
|
||||
use crate::stylesheets::rule_parser::VendorPrefix;
|
||||
use crate::stylesheets::{CssRuleType, StylesheetContents};
|
||||
use crate::values::{serialize_percentage, KeyframesName};
|
||||
|
@ -358,8 +357,6 @@ pub struct KeyframesAnimation {
|
|||
pub properties_changed: LonghandIdSet,
|
||||
/// Vendor prefix type the @keyframes has.
|
||||
pub vendor_prefix: Option<VendorPrefix>,
|
||||
/// 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 +409,12 @@ impl KeyframesAnimation {
|
|||
pub fn from_keyframes(
|
||||
keyframes: &[Arc<Locked<Keyframe>>],
|
||||
vendor_prefix: Option<VendorPrefix>,
|
||||
layer_id: LayerId,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> Self {
|
||||
let mut result = KeyframesAnimation {
|
||||
steps: vec![],
|
||||
properties_changed: LonghandIdSet::new(),
|
||||
vendor_prefix,
|
||||
layer_id,
|
||||
};
|
||||
|
||||
if keyframes.is_empty() {
|
||||
|
@ -500,8 +495,8 @@ pub fn parse_keyframe_list(
|
|||
RuleListParser::new_for_nested_rule(
|
||||
input,
|
||||
KeyframeListParser {
|
||||
context: context,
|
||||
shared_lock: shared_lock,
|
||||
context,
|
||||
shared_lock,
|
||||
declarations: &mut declarations,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -57,6 +57,7 @@ use selectors::NthIndexCache;
|
|||
use servo_arc::{Arc, ArcBorrow};
|
||||
use smallbitvec::SmallBitVec;
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Mutex;
|
||||
use std::{mem, ops};
|
||||
|
@ -1532,6 +1533,75 @@ impl Stylist {
|
|||
}
|
||||
}
|
||||
|
||||
/// A vector that is sorted in layer order.
|
||||
#[derive(Clone, Debug, Deref, MallocSizeOf)]
|
||||
pub struct LayerOrderedVec<T>(Vec<(T, LayerId)>);
|
||||
impl<T> Default for LayerOrderedVec<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A map that is sorted in layer order.
|
||||
#[derive(Clone, Debug, Deref, MallocSizeOf)]
|
||||
pub struct LayerOrderedMap<T>(PrecomputedHashMap<Atom, SmallVec<[(T, LayerId); 1]>>);
|
||||
impl<T> Default for LayerOrderedMap<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> LayerOrderedVec<T> {
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
fn push(&mut self, v: T, id: LayerId) {
|
||||
self.0.push((v, id));
|
||||
}
|
||||
fn sort(&mut self, layers: &[CascadeLayer]) {
|
||||
self.0.sort_by_key(|&(_, ref id)| layers[id.0 as usize].order)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> LayerOrderedMap<T> {
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
fn try_insert(&mut self, name: Atom, v: T, id: LayerId) -> Result<(), FailedAllocationError> {
|
||||
self.try_insert_with(name, v, id, |_, _| Ordering::Equal)
|
||||
}
|
||||
fn try_insert_with(&mut self, name: Atom, v: T, id: LayerId, cmp: impl Fn(&T, &T) -> Ordering) -> Result<(), FailedAllocationError> {
|
||||
let vec = self.0.try_entry(name)?.or_insert_with(Default::default);
|
||||
if let Some(&mut (ref mut val, ref last_id)) = vec.last_mut() {
|
||||
if *last_id == id {
|
||||
if cmp(&val, &v) != Ordering::Greater {
|
||||
*val = v;
|
||||
}
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
vec.push((v, id));
|
||||
Ok(())
|
||||
}
|
||||
fn sort(&mut self, layers: &[CascadeLayer]) {
|
||||
self.sort_with(layers, |_, _| Ordering::Equal)
|
||||
}
|
||||
fn sort_with(&mut self, layers: &[CascadeLayer], cmp: impl Fn(&T, &T) -> Ordering) {
|
||||
for (_, v) in self.0.iter_mut() {
|
||||
v.sort_by(|&(ref v1, ref id1), &(ref v2, ref id2)| {
|
||||
let order1 = layers[id1.0 as usize].order;
|
||||
let order2 = layers[id2.0 as usize].order;
|
||||
order1.cmp(&order2).then_with(|| cmp(v1, v2))
|
||||
})
|
||||
}
|
||||
}
|
||||
/// Get an entry on the LayerOrderedMap by name.
|
||||
pub fn get(&self, name: &Atom) -> Option<&T> {
|
||||
let vec = self.0.get(name)?;
|
||||
Some(&vec.last()?.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct holds data which users of Stylist may want to extract
|
||||
/// from stylesheets which can be done at the same time as updating.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -1539,35 +1609,35 @@ impl Stylist {
|
|||
pub struct ExtraStyleData {
|
||||
/// A list of effective font-face rules and their origin.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
|
||||
pub font_faces: LayerOrderedVec<Arc<Locked<FontFaceRule>>>,
|
||||
|
||||
/// A list of effective font-feature-values rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub font_feature_values: Vec<Arc<Locked<FontFeatureValuesRule>>>,
|
||||
pub font_feature_values: LayerOrderedVec<Arc<Locked<FontFeatureValuesRule>>>,
|
||||
|
||||
/// A map of effective counter-style rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
|
||||
pub counter_styles: LayerOrderedMap<Arc<Locked<CounterStyleRule>>>,
|
||||
|
||||
/// A map of effective page rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub pages: Vec<Arc<Locked<PageRule>>>,
|
||||
pub pages: LayerOrderedVec<Arc<Locked<PageRule>>>,
|
||||
|
||||
/// A map of effective scroll-timeline rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub scroll_timelines: PrecomputedHashMap<Atom, Arc<Locked<ScrollTimelineRule>>>,
|
||||
pub scroll_timelines: LayerOrderedMap<Arc<Locked<ScrollTimelineRule>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl ExtraStyleData {
|
||||
/// Add the given @font-face rule.
|
||||
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
|
||||
self.font_faces.push(rule.clone());
|
||||
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>, layer: LayerId) {
|
||||
self.font_faces.push(rule.clone(), layer);
|
||||
}
|
||||
|
||||
/// Add the given @font-feature-values rule.
|
||||
fn add_font_feature_values(&mut self, rule: &Arc<Locked<FontFeatureValuesRule>>) {
|
||||
self.font_feature_values.push(rule.clone());
|
||||
fn add_font_feature_values(&mut self, rule: &Arc<Locked<FontFeatureValuesRule>>, layer: LayerId) {
|
||||
self.font_feature_values.push(rule.clone(), layer);
|
||||
}
|
||||
|
||||
/// Add the given @counter-style rule.
|
||||
|
@ -1575,14 +1645,15 @@ impl ExtraStyleData {
|
|||
&mut self,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
rule: &Arc<Locked<CounterStyleRule>>,
|
||||
) {
|
||||
layer: LayerId,
|
||||
) -> Result<(), FailedAllocationError> {
|
||||
let name = rule.read_with(guard).name().0.clone();
|
||||
self.counter_styles.insert(name, rule.clone());
|
||||
self.counter_styles.try_insert(name, rule.clone(), layer)
|
||||
}
|
||||
|
||||
/// Add the given @page rule.
|
||||
fn add_page(&mut self, rule: &Arc<Locked<PageRule>>) {
|
||||
self.pages.push(rule.clone());
|
||||
fn add_page(&mut self, rule: &Arc<Locked<PageRule>>, layer: LayerId) {
|
||||
self.pages.push(rule.clone(), layer);
|
||||
}
|
||||
|
||||
/// Add the given @scroll-timeline rule.
|
||||
|
@ -1590,15 +1661,20 @@ impl ExtraStyleData {
|
|||
&mut self,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
rule: &Arc<Locked<ScrollTimelineRule>>,
|
||||
layer: LayerId,
|
||||
) -> Result<(), FailedAllocationError> {
|
||||
let name = rule.read_with(guard).name.as_atom().clone();
|
||||
self.scroll_timelines
|
||||
.try_insert(name, rule.clone())
|
||||
.map(|_| {})
|
||||
self.scroll_timelines.try_insert(name, rule.clone(), layer)
|
||||
}
|
||||
|
||||
fn sort_by_layer(&mut self, layers: &[CascadeLayer]) {
|
||||
self.font_faces.sort(layers);
|
||||
self.font_feature_values.sort(layers);
|
||||
self.counter_styles.sort(layers);
|
||||
self.pages.sort(layers);
|
||||
self.scroll_timelines.sort(layers);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraStyleData {
|
||||
fn clear(&mut self) {
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
|
@ -1611,6 +1687,18 @@ impl ExtraStyleData {
|
|||
}
|
||||
}
|
||||
|
||||
// Don't let a prefixed keyframes animation override
|
||||
// a non-prefixed one.
|
||||
fn compare_keyframes_in_same_layer(v1: &KeyframesAnimation, v2: &KeyframesAnimation) -> Ordering {
|
||||
if v1.vendor_prefix.is_some() == v2.vendor_prefix.is_some() {
|
||||
Ordering::Equal
|
||||
} else if v2.vendor_prefix.is_some() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the different ExtraStyleData.
|
||||
pub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);
|
||||
|
||||
|
@ -1966,7 +2054,7 @@ pub struct CascadeData {
|
|||
|
||||
/// A map with all the animations at this `CascadeData`'s origin, indexed
|
||||
/// by name.
|
||||
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
|
||||
animations: LayerOrderedMap<KeyframesAnimation>,
|
||||
|
||||
/// A map from cascade layer name to layer order.
|
||||
layer_id: FxHashMap<LayerName, LayerId>,
|
||||
|
@ -2167,6 +2255,8 @@ impl CascadeData {
|
|||
order.inc();
|
||||
}
|
||||
}
|
||||
self.extra_data.sort_by_layer(&self.layers);
|
||||
self.animations.sort_with(&self.layers, compare_keyframes_in_same_layer);
|
||||
}
|
||||
|
||||
/// Collects all the applicable media query results into `results`.
|
||||
|
@ -2340,65 +2430,37 @@ impl CascadeData {
|
|||
self.rules_source_order += 1;
|
||||
},
|
||||
CssRule::Keyframes(ref keyframes_rule) => {
|
||||
#[cfg(feature = "gecko")]
|
||||
use hashglobe::hash_map::Entry;
|
||||
#[cfg(feature = "servo")]
|
||||
use hashglobe::fake::Entry;
|
||||
|
||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
||||
match self
|
||||
.animations
|
||||
.try_entry(keyframes_rule.name.as_atom().clone())?
|
||||
{
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(KeyframesAnimation::from_keyframes(
|
||||
&keyframes_rule.keyframes,
|
||||
keyframes_rule.vendor_prefix.clone(),
|
||||
current_layer_id,
|
||||
guard,
|
||||
));
|
||||
},
|
||||
Entry::Occupied(mut e) => {
|
||||
// Don't let a prefixed keyframes animation override
|
||||
// a non-prefixed one.
|
||||
//
|
||||
// TODO(emilio): This will need to be harder for
|
||||
// layers.
|
||||
let needs_insert = 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_id,
|
||||
guard,
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||
let name = keyframes_rule.name.as_atom().clone();
|
||||
let animation = KeyframesAnimation::from_keyframes(
|
||||
&keyframes_rule.keyframes,
|
||||
keyframes_rule.vendor_prefix.clone(),
|
||||
guard,
|
||||
);
|
||||
self.animations.try_insert_with(name, animation, current_layer_id, compare_keyframes_in_same_layer)?;
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::ScrollTimeline(ref rule) => {
|
||||
// Note: Bug 1733260: we may drop @scroll-timeline rule once this spec issue
|
||||
// https://github.com/w3c/csswg-drafts/issues/6674 gets landed.
|
||||
self.extra_data.add_scroll_timeline(guard, rule)?;
|
||||
self.extra_data.add_scroll_timeline(guard, rule, current_layer_id)?;
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::FontFace(ref rule) => {
|
||||
self.extra_data.add_font_face(rule);
|
||||
self.extra_data.add_font_face(rule, current_layer_id);
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::FontFeatureValues(ref rule) => {
|
||||
self.extra_data.add_font_feature_values(rule);
|
||||
self.extra_data.add_font_feature_values(rule, current_layer_id);
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::CounterStyle(ref rule) => {
|
||||
self.extra_data.add_counter_style(guard, rule);
|
||||
self.extra_data.add_counter_style(guard, rule, current_layer_id);
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::Page(ref rule) => {
|
||||
self.extra_data.add_page(rule);
|
||||
self.extra_data.add_page(rule, current_layer_id);
|
||||
},
|
||||
CssRule::Viewport(..) => {},
|
||||
_ => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue