mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Support vendor prefix keyframes rule.
If there are multiple prefixed/non-prefixed @keyframes with the same name; * non-prefixed rule overrides earlier rules. * prefixed rule overrides earlier prefixed rules.
This commit is contained in:
parent
17dc598d99
commit
973d8287a9
7 changed files with 73 additions and 23 deletions
|
@ -16,9 +16,9 @@ use gecko_bindings::structs::RawGeckoPresContextOwned;
|
||||||
use media_queries::MediaType;
|
use media_queries::MediaType;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use std::ascii::AsciiExt;
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use str::starts_with_ignore_ascii_case;
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
|
@ -340,11 +340,6 @@ impl MediaExpressionValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
|
|
||||||
string.len() > prefix.len() &&
|
|
||||||
string[0..prefix.len()].eq_ignore_ascii_case(prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_feature<F>(mut f: F) -> Option<&'static nsMediaFeature>
|
fn find_feature<F>(mut f: F) -> Option<&'static nsMediaFeature>
|
||||||
where F: FnMut(&'static nsMediaFeature) -> bool,
|
where F: FnMut(&'static nsMediaFeature) -> bool,
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,7 @@ use shared_lock::{SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use stylesheets::{CssRuleType, MemoryHoleReporter, Stylesheet};
|
use stylesheets::{CssRuleType, MemoryHoleReporter, Stylesheet, VendorPrefix};
|
||||||
|
|
||||||
/// A number from 0 to 1, indicating the percentage of the animation when this
|
/// A number from 0 to 1, indicating the percentage of the animation when this
|
||||||
/// keyframe should run.
|
/// keyframe should run.
|
||||||
|
@ -239,6 +239,8 @@ pub struct KeyframesAnimation {
|
||||||
pub steps: Vec<KeyframesStep>,
|
pub steps: Vec<KeyframesStep>,
|
||||||
/// The properties that change in this animation.
|
/// The properties that change in this animation.
|
||||||
pub properties_changed: Vec<TransitionProperty>,
|
pub properties_changed: Vec<TransitionProperty>,
|
||||||
|
/// Vendor prefix type the @keyframes has.
|
||||||
|
pub vendor_prefix: Option<VendorPrefix>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all the animated properties in a keyframes animation.
|
/// Get all the animated properties in a keyframes animation.
|
||||||
|
@ -275,11 +277,14 @@ impl KeyframesAnimation {
|
||||||
///
|
///
|
||||||
/// Otherwise, this will compute and sort the steps used for the animation,
|
/// Otherwise, this will compute and sort the steps used for the animation,
|
||||||
/// and return the animation object.
|
/// and return the animation object.
|
||||||
pub fn from_keyframes(keyframes: &[Arc<Locked<Keyframe>>], guard: &SharedRwLockReadGuard)
|
pub fn from_keyframes(keyframes: &[Arc<Locked<Keyframe>>],
|
||||||
|
vendor_prefix: Option<VendorPrefix>,
|
||||||
|
guard: &SharedRwLockReadGuard)
|
||||||
-> Self {
|
-> Self {
|
||||||
let mut result = KeyframesAnimation {
|
let mut result = KeyframesAnimation {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
properties_changed: vec![],
|
properties_changed: vec![],
|
||||||
|
vendor_prefix: vendor_prefix,
|
||||||
};
|
};
|
||||||
|
|
||||||
if keyframes.is_empty() {
|
if keyframes.is_empty() {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
use std::iter::{Filter, Peekable};
|
use std::iter::{Filter, Peekable};
|
||||||
use std::str::Split;
|
use std::str::Split;
|
||||||
|
@ -144,3 +145,9 @@ pub fn str_join<I, T>(strs: I, join: &str) -> String
|
||||||
acc
|
acc
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if a given string has a given prefix with case-insensitive match.
|
||||||
|
pub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
|
||||||
|
string.len() > prefix.len() &&
|
||||||
|
string[0..prefix.len()].eq_ignore_ascii_case(prefix)
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ use std::cell::Cell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use str::starts_with_ignore_ascii_case;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use stylist::FnvHashMap;
|
use stylist::FnvHashMap;
|
||||||
use supports::SupportsCondition;
|
use supports::SupportsCondition;
|
||||||
|
@ -529,6 +530,8 @@ pub struct KeyframesRule {
|
||||||
pub name: Atom,
|
pub name: Atom,
|
||||||
/// The keyframes specified for this CSS rule.
|
/// The keyframes specified for this CSS rule.
|
||||||
pub keyframes: Vec<Arc<Locked<Keyframe>>>,
|
pub keyframes: Vec<Arc<Locked<Keyframe>>>,
|
||||||
|
/// Vendor prefix type the @keyframes has.
|
||||||
|
pub vendor_prefix: Option<VendorPrefix>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCssWithGuard for KeyframesRule {
|
impl ToCssWithGuard for KeyframesRule {
|
||||||
|
@ -913,6 +916,15 @@ pub enum State {
|
||||||
Invalid = 5,
|
Invalid = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
/// Vendor prefix.
|
||||||
|
pub enum VendorPrefix {
|
||||||
|
/// -moz prefix.
|
||||||
|
Moz,
|
||||||
|
/// -webkit prefix.
|
||||||
|
WebKit,
|
||||||
|
}
|
||||||
|
|
||||||
enum AtRulePrelude {
|
enum AtRulePrelude {
|
||||||
/// A @font-face rule prelude.
|
/// A @font-face rule prelude.
|
||||||
|
@ -923,8 +935,8 @@ enum AtRulePrelude {
|
||||||
Supports(SupportsCondition),
|
Supports(SupportsCondition),
|
||||||
/// A @viewport rule prelude.
|
/// A @viewport rule prelude.
|
||||||
Viewport,
|
Viewport,
|
||||||
/// A @keyframes rule, with its animation name.
|
/// A @keyframes rule, with its animation name and vendor prefix if exists.
|
||||||
Keyframes(Atom),
|
Keyframes(Atom, Option<VendorPrefix>),
|
||||||
/// A @page rule prelude.
|
/// A @page rule prelude.
|
||||||
Page,
|
Page,
|
||||||
}
|
}
|
||||||
|
@ -1111,14 +1123,21 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keyframes" => {
|
"keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
|
||||||
|
let prefix = if starts_with_ignore_ascii_case(name, "-webkit-") {
|
||||||
|
Some(VendorPrefix::WebKit)
|
||||||
|
} else if starts_with_ignore_ascii_case(name, "-moz-") {
|
||||||
|
Some(VendorPrefix::Moz)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let name = match input.next() {
|
let name = match input.next() {
|
||||||
Ok(Token::Ident(ref value)) if value != "none" => Atom::from(&**value),
|
Ok(Token::Ident(ref value)) if value != "none" => Atom::from(&**value),
|
||||||
Ok(Token::QuotedString(value)) => Atom::from(&*value),
|
Ok(Token::QuotedString(value)) => Atom::from(&*value),
|
||||||
_ => return Err(())
|
_ => return Err(())
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(Atom::from(name))))
|
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(Atom::from(name), prefix)))
|
||||||
},
|
},
|
||||||
"page" => {
|
"page" => {
|
||||||
if cfg!(feature = "gecko") {
|
if cfg!(feature = "gecko") {
|
||||||
|
@ -1157,11 +1176,12 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
|
||||||
Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
|
Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
|
||||||
try!(ViewportRule::parse(&context, input))))))
|
try!(ViewportRule::parse(&context, input))))))
|
||||||
}
|
}
|
||||||
AtRulePrelude::Keyframes(name) => {
|
AtRulePrelude::Keyframes(name, prefix) => {
|
||||||
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
|
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
|
||||||
Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
|
Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
|
||||||
name: name,
|
name: name,
|
||||||
keyframes: parse_keyframe_list(&context, input, self.shared_lock),
|
keyframes: parse_keyframe_list(&context, input, self.shared_lock),
|
||||||
|
vendor_prefix: prefix,
|
||||||
}))))
|
}))))
|
||||||
}
|
}
|
||||||
AtRulePrelude::Page => {
|
AtRulePrelude::Page => {
|
||||||
|
|
|
@ -348,10 +348,17 @@ impl Stylist {
|
||||||
CssRule::Keyframes(ref keyframes_rule) => {
|
CssRule::Keyframes(ref keyframes_rule) => {
|
||||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||||
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
||||||
let animation = KeyframesAnimation::from_keyframes(
|
|
||||||
&keyframes_rule.keyframes, guard);
|
// Don't let a prefixed keyframes animation override a non-prefixed one.
|
||||||
debug!("Found valid keyframe animation: {:?}", animation);
|
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
|
||||||
self.animations.insert(keyframes_rule.name.clone(), animation);
|
self.animations.get(&keyframes_rule.name).map_or(true, |rule|
|
||||||
|
rule.vendor_prefix.is_some());
|
||||||
|
if needs_insertion {
|
||||||
|
let animation = KeyframesAnimation::from_keyframes(
|
||||||
|
&keyframes_rule.keyframes, keyframes_rule.vendor_prefix.clone(), guard);
|
||||||
|
debug!("Found valid keyframe animation: {:?}", animation);
|
||||||
|
self.animations.insert(keyframes_rule.name.clone(), animation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CssRule::FontFace(ref rule) => {
|
CssRule::FontFace(ref rule) => {
|
||||||
extra_data.add_font_face(&rule, stylesheet.origin);
|
extra_data.add_font_face(&rule, stylesheet.origin);
|
||||||
|
|
|
@ -14,10 +14,13 @@ use style::values::specified::{LengthOrPercentageOrAuto, NoCalcLength};
|
||||||
fn test_empty_keyframe() {
|
fn test_empty_keyframe() {
|
||||||
let shared_lock = SharedRwLock::new();
|
let shared_lock = SharedRwLock::new();
|
||||||
let keyframes = vec![];
|
let keyframes = vec![];
|
||||||
let animation = KeyframesAnimation::from_keyframes(&keyframes, &shared_lock.read());
|
let animation = KeyframesAnimation::from_keyframes(&keyframes,
|
||||||
|
/* vendor_prefix = */ None,
|
||||||
|
&shared_lock.read());
|
||||||
let expected = KeyframesAnimation {
|
let expected = KeyframesAnimation {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
properties_changed: vec![],
|
properties_changed: vec![],
|
||||||
|
vendor_prefix: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
||||||
|
@ -32,10 +35,13 @@ fn test_no_property_in_keyframe() {
|
||||||
block: Arc::new(shared_lock.wrap(PropertyDeclarationBlock::new()))
|
block: Arc::new(shared_lock.wrap(PropertyDeclarationBlock::new()))
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
let animation = KeyframesAnimation::from_keyframes(&keyframes, &shared_lock.read());
|
let animation = KeyframesAnimation::from_keyframes(&keyframes,
|
||||||
|
/* vendor_prefix = */ None,
|
||||||
|
&shared_lock.read());
|
||||||
let expected = KeyframesAnimation {
|
let expected = KeyframesAnimation {
|
||||||
steps: vec![],
|
steps: vec![],
|
||||||
properties_changed: vec![],
|
properties_changed: vec![],
|
||||||
|
vendor_prefix: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
||||||
|
@ -78,7 +84,9 @@ fn test_missing_property_in_initial_keyframe() {
|
||||||
block: declarations_on_final_keyframe.clone(),
|
block: declarations_on_final_keyframe.clone(),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
let animation = KeyframesAnimation::from_keyframes(&keyframes, &shared_lock.read());
|
let animation = KeyframesAnimation::from_keyframes(&keyframes,
|
||||||
|
/* vendor_prefix = */ None,
|
||||||
|
&shared_lock.read());
|
||||||
let expected = KeyframesAnimation {
|
let expected = KeyframesAnimation {
|
||||||
steps: vec![
|
steps: vec![
|
||||||
KeyframesStep {
|
KeyframesStep {
|
||||||
|
@ -93,6 +101,7 @@ fn test_missing_property_in_initial_keyframe() {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
|
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
|
||||||
|
vendor_prefix: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
||||||
|
@ -135,7 +144,9 @@ fn test_missing_property_in_final_keyframe() {
|
||||||
block: declarations_on_final_keyframe.clone(),
|
block: declarations_on_final_keyframe.clone(),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
let animation = KeyframesAnimation::from_keyframes(&keyframes, &shared_lock.read());
|
let animation = KeyframesAnimation::from_keyframes(&keyframes,
|
||||||
|
/* vendor_prefix = */ None,
|
||||||
|
&shared_lock.read());
|
||||||
let expected = KeyframesAnimation {
|
let expected = KeyframesAnimation {
|
||||||
steps: vec![
|
steps: vec![
|
||||||
KeyframesStep {
|
KeyframesStep {
|
||||||
|
@ -150,6 +161,7 @@ fn test_missing_property_in_final_keyframe() {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
|
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
|
||||||
|
vendor_prefix: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
||||||
|
@ -184,7 +196,9 @@ fn test_missing_keyframe_in_both_of_initial_and_final_keyframe() {
|
||||||
block: declarations.clone(),
|
block: declarations.clone(),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
let animation = KeyframesAnimation::from_keyframes(&keyframes, &shared_lock.read());
|
let animation = KeyframesAnimation::from_keyframes(&keyframes,
|
||||||
|
/* vendor_prefix = */ None,
|
||||||
|
&shared_lock.read());
|
||||||
let expected = KeyframesAnimation {
|
let expected = KeyframesAnimation {
|
||||||
steps: vec![
|
steps: vec![
|
||||||
KeyframesStep {
|
KeyframesStep {
|
||||||
|
@ -209,6 +223,7 @@ fn test_missing_keyframe_in_both_of_initial_and_final_keyframe() {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
|
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
|
||||||
|
vendor_prefix: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
|
||||||
|
|
|
@ -245,7 +245,8 @@ fn test_parse_stylesheet() {
|
||||||
Importance::Normal),
|
Importance::Normal),
|
||||||
]))),
|
]))),
|
||||||
})),
|
})),
|
||||||
]
|
],
|
||||||
|
vendor_prefix: None,
|
||||||
})))
|
})))
|
||||||
|
|
||||||
], &stylesheet.shared_lock),
|
], &stylesheet.shared_lock),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue