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:
Hiroyuki Ikezoe 2017-04-21 12:17:20 +09:00
parent 17dc598d99
commit 973d8287a9
7 changed files with 73 additions and 23 deletions

View file

@ -16,9 +16,9 @@ use gecko_bindings::structs::RawGeckoPresContextOwned;
use media_queries::MediaType;
use parser::ParserContext;
use properties::ComputedValues;
use std::ascii::AsciiExt;
use std::fmt::{self, Write};
use std::sync::Arc;
use str::starts_with_ignore_ascii_case;
use string_cache::Atom;
use style_traits::ToCss;
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>
where F: FnMut(&'static nsMediaFeature) -> bool,
{

View file

@ -18,7 +18,7 @@ use shared_lock::{SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
use std::fmt;
use std::sync::Arc;
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
/// keyframe should run.
@ -239,6 +239,8 @@ pub struct KeyframesAnimation {
pub steps: Vec<KeyframesStep>,
/// The properties that change in this animation.
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.
@ -275,11 +277,14 @@ impl KeyframesAnimation {
///
/// Otherwise, this will compute and sort the steps used for the animation,
/// 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 {
let mut result = KeyframesAnimation {
steps: vec![],
properties_changed: vec![],
vendor_prefix: vendor_prefix,
};
if keyframes.is_empty() {

View file

@ -7,6 +7,7 @@
#![deny(missing_docs)]
use num_traits::ToPrimitive;
use std::ascii::AsciiExt;
use std::convert::AsRef;
use std::iter::{Filter, Peekable};
use std::str::Split;
@ -144,3 +145,9 @@ pub fn str_join<I, T>(strs: I, join: &str) -> String
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)
}

View file

@ -36,6 +36,7 @@ use std::cell::Cell;
use std::fmt;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use str::starts_with_ignore_ascii_case;
use style_traits::ToCss;
use stylist::FnvHashMap;
use supports::SupportsCondition;
@ -529,6 +530,8 @@ pub struct KeyframesRule {
pub name: Atom,
/// The keyframes specified for this CSS rule.
pub keyframes: Vec<Arc<Locked<Keyframe>>>,
/// Vendor prefix type the @keyframes has.
pub vendor_prefix: Option<VendorPrefix>,
}
impl ToCssWithGuard for KeyframesRule {
@ -913,6 +916,15 @@ pub enum State {
Invalid = 5,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
/// Vendor prefix.
pub enum VendorPrefix {
/// -moz prefix.
Moz,
/// -webkit prefix.
WebKit,
}
enum AtRulePrelude {
/// A @font-face rule prelude.
@ -923,8 +935,8 @@ enum AtRulePrelude {
Supports(SupportsCondition),
/// A @viewport rule prelude.
Viewport,
/// A @keyframes rule, with its animation name.
Keyframes(Atom),
/// A @keyframes rule, with its animation name and vendor prefix if exists.
Keyframes(Atom, Option<VendorPrefix>),
/// A @page rule prelude.
Page,
}
@ -1111,14 +1123,21 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
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() {
Ok(Token::Ident(ref value)) if value != "none" => Atom::from(&**value),
Ok(Token::QuotedString(value)) => Atom::from(&*value),
_ => return Err(())
};
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(Atom::from(name))))
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(Atom::from(name), prefix)))
},
"page" => {
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(
try!(ViewportRule::parse(&context, input))))))
}
AtRulePrelude::Keyframes(name) => {
AtRulePrelude::Keyframes(name, prefix) => {
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
name: name,
keyframes: parse_keyframe_list(&context, input, self.shared_lock),
vendor_prefix: prefix,
}))))
}
AtRulePrelude::Page => {

View file

@ -348,10 +348,17 @@ impl Stylist {
CssRule::Keyframes(ref keyframes_rule) => {
let keyframes_rule = keyframes_rule.read_with(guard);
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
let animation = KeyframesAnimation::from_keyframes(
&keyframes_rule.keyframes, guard);
debug!("Found valid keyframe animation: {:?}", animation);
self.animations.insert(keyframes_rule.name.clone(), animation);
// Don't let a prefixed keyframes animation override a non-prefixed one.
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
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) => {
extra_data.add_font_face(&rule, stylesheet.origin);