mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +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::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
|
||||||
use crate::shared_lock::{Locked, ToCssWithGuard};
|
use crate::shared_lock::{Locked, ToCssWithGuard};
|
||||||
use crate::str::CssStringWriter;
|
use crate::str::CssStringWriter;
|
||||||
use crate::stylesheets::layer_rule::LayerId;
|
|
||||||
use crate::stylesheets::rule_parser::VendorPrefix;
|
use crate::stylesheets::rule_parser::VendorPrefix;
|
||||||
use crate::stylesheets::{CssRuleType, StylesheetContents};
|
use crate::stylesheets::{CssRuleType, StylesheetContents};
|
||||||
use crate::values::{serialize_percentage, KeyframesName};
|
use crate::values::{serialize_percentage, KeyframesName};
|
||||||
|
@ -358,8 +357,6 @@ pub struct KeyframesAnimation {
|
||||||
pub properties_changed: LonghandIdSet,
|
pub properties_changed: LonghandIdSet,
|
||||||
/// Vendor prefix type the @keyframes has.
|
/// Vendor prefix type the @keyframes has.
|
||||||
pub vendor_prefix: Option<VendorPrefix>,
|
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.
|
/// Get all the animated properties in a keyframes animation.
|
||||||
|
@ -412,14 +409,12 @@ impl KeyframesAnimation {
|
||||||
pub fn from_keyframes(
|
pub fn from_keyframes(
|
||||||
keyframes: &[Arc<Locked<Keyframe>>],
|
keyframes: &[Arc<Locked<Keyframe>>],
|
||||||
vendor_prefix: Option<VendorPrefix>,
|
vendor_prefix: Option<VendorPrefix>,
|
||||||
layer_id: LayerId,
|
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut result = KeyframesAnimation {
|
let mut result = KeyframesAnimation {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
properties_changed: LonghandIdSet::new(),
|
properties_changed: LonghandIdSet::new(),
|
||||||
vendor_prefix,
|
vendor_prefix,
|
||||||
layer_id,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if keyframes.is_empty() {
|
if keyframes.is_empty() {
|
||||||
|
@ -500,8 +495,8 @@ pub fn parse_keyframe_list(
|
||||||
RuleListParser::new_for_nested_rule(
|
RuleListParser::new_for_nested_rule(
|
||||||
input,
|
input,
|
||||||
KeyframeListParser {
|
KeyframeListParser {
|
||||||
context: context,
|
context,
|
||||||
shared_lock: shared_lock,
|
shared_lock,
|
||||||
declarations: &mut declarations,
|
declarations: &mut declarations,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -57,6 +57,7 @@ use selectors::NthIndexCache;
|
||||||
use servo_arc::{Arc, ArcBorrow};
|
use servo_arc::{Arc, ArcBorrow};
|
||||||
use smallbitvec::SmallBitVec;
|
use smallbitvec::SmallBitVec;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{mem, ops};
|
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
|
/// This struct holds data which users of Stylist may want to extract
|
||||||
/// from stylesheets which can be done at the same time as updating.
|
/// from stylesheets which can be done at the same time as updating.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -1539,35 +1609,35 @@ impl Stylist {
|
||||||
pub struct ExtraStyleData {
|
pub struct ExtraStyleData {
|
||||||
/// A list of effective font-face rules and their origin.
|
/// A list of effective font-face rules and their origin.
|
||||||
#[cfg(feature = "gecko")]
|
#[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.
|
/// A list of effective font-feature-values rules.
|
||||||
#[cfg(feature = "gecko")]
|
#[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.
|
/// A map of effective counter-style rules.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
|
pub counter_styles: LayerOrderedMap<Arc<Locked<CounterStyleRule>>>,
|
||||||
|
|
||||||
/// A map of effective page rules.
|
/// A map of effective page rules.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub pages: Vec<Arc<Locked<PageRule>>>,
|
pub pages: LayerOrderedVec<Arc<Locked<PageRule>>>,
|
||||||
|
|
||||||
/// A map of effective scroll-timeline rules.
|
/// A map of effective scroll-timeline rules.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub scroll_timelines: PrecomputedHashMap<Atom, Arc<Locked<ScrollTimelineRule>>>,
|
pub scroll_timelines: LayerOrderedMap<Arc<Locked<ScrollTimelineRule>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl ExtraStyleData {
|
impl ExtraStyleData {
|
||||||
/// Add the given @font-face rule.
|
/// Add the given @font-face rule.
|
||||||
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
|
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>, layer: LayerId) {
|
||||||
self.font_faces.push(rule.clone());
|
self.font_faces.push(rule.clone(), layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given @font-feature-values rule.
|
/// Add the given @font-feature-values rule.
|
||||||
fn add_font_feature_values(&mut self, rule: &Arc<Locked<FontFeatureValuesRule>>) {
|
fn add_font_feature_values(&mut self, rule: &Arc<Locked<FontFeatureValuesRule>>, layer: LayerId) {
|
||||||
self.font_feature_values.push(rule.clone());
|
self.font_feature_values.push(rule.clone(), layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given @counter-style rule.
|
/// Add the given @counter-style rule.
|
||||||
|
@ -1575,14 +1645,15 @@ impl ExtraStyleData {
|
||||||
&mut self,
|
&mut self,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
rule: &Arc<Locked<CounterStyleRule>>,
|
rule: &Arc<Locked<CounterStyleRule>>,
|
||||||
) {
|
layer: LayerId,
|
||||||
|
) -> Result<(), FailedAllocationError> {
|
||||||
let name = rule.read_with(guard).name().0.clone();
|
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.
|
/// Add the given @page rule.
|
||||||
fn add_page(&mut self, rule: &Arc<Locked<PageRule>>) {
|
fn add_page(&mut self, rule: &Arc<Locked<PageRule>>, layer: LayerId) {
|
||||||
self.pages.push(rule.clone());
|
self.pages.push(rule.clone(), layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given @scroll-timeline rule.
|
/// Add the given @scroll-timeline rule.
|
||||||
|
@ -1590,15 +1661,20 @@ impl ExtraStyleData {
|
||||||
&mut self,
|
&mut self,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
rule: &Arc<Locked<ScrollTimelineRule>>,
|
rule: &Arc<Locked<ScrollTimelineRule>>,
|
||||||
|
layer: LayerId,
|
||||||
) -> 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(), layer)
|
||||||
.try_insert(name, rule.clone())
|
}
|
||||||
.map(|_| {})
|
|
||||||
|
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) {
|
fn clear(&mut self) {
|
||||||
#[cfg(feature = "gecko")]
|
#[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.
|
/// An iterator over the different ExtraStyleData.
|
||||||
pub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);
|
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
|
/// A map with all the animations at this `CascadeData`'s origin, indexed
|
||||||
/// by name.
|
/// by name.
|
||||||
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
|
animations: LayerOrderedMap<KeyframesAnimation>,
|
||||||
|
|
||||||
/// A map from cascade layer name to layer order.
|
/// A map from cascade layer name to layer order.
|
||||||
layer_id: FxHashMap<LayerName, LayerId>,
|
layer_id: FxHashMap<LayerName, LayerId>,
|
||||||
|
@ -2167,6 +2255,8 @@ impl CascadeData {
|
||||||
order.inc();
|
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`.
|
/// Collects all the applicable media query results into `results`.
|
||||||
|
@ -2340,65 +2430,37 @@ impl CascadeData {
|
||||||
self.rules_source_order += 1;
|
self.rules_source_order += 1;
|
||||||
},
|
},
|
||||||
CssRule::Keyframes(ref keyframes_rule) => {
|
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);
|
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
||||||
match self
|
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||||
.animations
|
let name = keyframes_rule.name.as_atom().clone();
|
||||||
.try_entry(keyframes_rule.name.as_atom().clone())?
|
let animation = KeyframesAnimation::from_keyframes(
|
||||||
{
|
&keyframes_rule.keyframes,
|
||||||
Entry::Vacant(e) => {
|
keyframes_rule.vendor_prefix.clone(),
|
||||||
e.insert(KeyframesAnimation::from_keyframes(
|
guard,
|
||||||
&keyframes_rule.keyframes,
|
);
|
||||||
keyframes_rule.vendor_prefix.clone(),
|
self.animations.try_insert_with(name, animation, current_layer_id, compare_keyframes_in_same_layer)?;
|
||||||
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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::ScrollTimeline(ref rule) => {
|
CssRule::ScrollTimeline(ref rule) => {
|
||||||
// Note: Bug 1733260: we may drop @scroll-timeline rule once this spec issue
|
// Note: Bug 1733260: we may drop @scroll-timeline rule once this spec issue
|
||||||
// https://github.com/w3c/csswg-drafts/issues/6674 gets landed.
|
// 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")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::FontFace(ref rule) => {
|
CssRule::FontFace(ref rule) => {
|
||||||
self.extra_data.add_font_face(rule);
|
self.extra_data.add_font_face(rule, current_layer_id);
|
||||||
},
|
},
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::FontFeatureValues(ref rule) => {
|
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")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::CounterStyle(ref rule) => {
|
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")]
|
#[cfg(feature = "gecko")]
|
||||||
CssRule::Page(ref rule) => {
|
CssRule::Page(ref rule) => {
|
||||||
self.extra_data.add_page(rule);
|
self.extra_data.add_page(rule, current_layer_id);
|
||||||
},
|
},
|
||||||
CssRule::Viewport(..) => {},
|
CssRule::Viewport(..) => {},
|
||||||
_ => {
|
_ => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue