aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/gecko/media_queries.rs7
-rw-r--r--components/style/keyframes.rs9
-rw-r--r--components/style/str.rs7
-rw-r--r--components/style/stylesheets.rs30
-rw-r--r--components/style/stylist.rs15
-rw-r--r--tests/unit/style/keyframes.rs25
-rw-r--r--tests/unit/style/stylesheets.rs3
7 files changed, 73 insertions, 23 deletions
diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs
index ffa3c95ed4f..4aff56ee8d5 100644
--- a/components/style/gecko/media_queries.rs
+++ b/components/style/gecko/media_queries.rs
@@ -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,
{
diff --git a/components/style/keyframes.rs b/components/style/keyframes.rs
index 49f2cad25b7..542481b287c 100644
--- a/components/style/keyframes.rs
+++ b/components/style/keyframes.rs
@@ -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() {
diff --git a/components/style/str.rs b/components/style/str.rs
index f5778828d1a..96305a59f1f 100644
--- a/components/style/str.rs
+++ b/components/style/str.rs
@@ -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)
+}
diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs
index 53abf04ef69..3cab9329a7f 100644
--- a/components/style/stylesheets.rs
+++ b/components/style/stylesheets.rs
@@ -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 => {
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index 2781f50c287..b24ee89f303 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -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);
diff --git a/tests/unit/style/keyframes.rs b/tests/unit/style/keyframes.rs
index 9fe2f0d3f39..4f96b5f88a9 100644
--- a/tests/unit/style/keyframes.rs
+++ b/tests/unit/style/keyframes.rs
@@ -14,10 +14,13 @@ use style::values::specified::{LengthOrPercentageOrAuto, NoCalcLength};
fn test_empty_keyframe() {
let shared_lock = SharedRwLock::new();
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 {
steps: vec![],
properties_changed: vec![],
+ vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
@@ -32,10 +35,13 @@ fn test_no_property_in_keyframe() {
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 {
steps: vec![],
properties_changed: vec![],
+ vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
@@ -78,7 +84,9 @@ fn test_missing_property_in_initial_keyframe() {
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 {
steps: vec![
KeyframesStep {
@@ -93,6 +101,7 @@ fn test_missing_property_in_initial_keyframe() {
},
],
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
+ vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
@@ -135,7 +144,9 @@ fn test_missing_property_in_final_keyframe() {
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 {
steps: vec![
KeyframesStep {
@@ -150,6 +161,7 @@ fn test_missing_property_in_final_keyframe() {
},
],
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
+ vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
@@ -184,7 +196,9 @@ fn test_missing_keyframe_in_both_of_initial_and_final_keyframe() {
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 {
steps: vec![
KeyframesStep {
@@ -209,6 +223,7 @@ fn test_missing_keyframe_in_both_of_initial_and_final_keyframe() {
}
],
properties_changed: vec![TransitionProperty::Width, TransitionProperty::Height],
+ vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs
index ed5696003b9..8005139e676 100644
--- a/tests/unit/style/stylesheets.rs
+++ b/tests/unit/style/stylesheets.rs
@@ -245,7 +245,8 @@ fn test_parse_stylesheet() {
Importance::Normal),
]))),
})),
- ]
+ ],
+ vendor_prefix: None,
})))
], &stylesheet.shared_lock),