style: Fix parsing and add generated keyframes

This commit is contained in:
Emilio Cobos Álvarez 2016-06-27 08:52:33 -07:00
parent 46eec45886
commit 2d566ef0ef
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
10 changed files with 189 additions and 94 deletions

View file

@ -2,33 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::{Parser, Delimiter};
use parser::ParserContext;
use cssparser::{AtRuleParser, Delimiter, Parser, QualifiedRuleParser, RuleListParser};
use parser::{ParserContext, log_css_error};
use properties::animated_properties::TransitionProperty;
use properties::{PropertyDeclaration, parse_property_declaration_list};
use std::sync::Arc;
/// Parses a keyframes list, like:
/// 0%, 50% {
/// width: 50%;
/// }
///
/// 40%, 60%, 100% {
/// width: 100%;
/// }
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Result<Vec<Keyframe>, ()> {
let mut keyframes = vec![];
while !input.is_exhausted() {
keyframes.push(try!(Keyframe::parse(context, input)));
}
if keyframes.len() < 2 {
return Err(())
}
Ok(keyframes)
}
/// A number from 1 to 100, indicating the percentage of the animation where
/// this keyframe should run.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, HeapSizeOf)]
@ -57,7 +36,11 @@ impl KeyframePercentage {
} else if input.try(|input| input.expect_ident_matching("to")).is_ok() {
KeyframePercentage::new(1.)
} else {
KeyframePercentage::new(try!(input.expect_percentage()))
let percentage = try!(input.expect_percentage());
if percentage > 1. || percentage < 0. {
return Err(());
}
KeyframePercentage::new(percentage)
};
Ok(percentage)
@ -100,8 +83,7 @@ impl Keyframe {
Ok(parse_property_declaration_list(context, input))
}).unwrap();
// NB: Other browsers seem to ignore important declarations in keyframe
// animations too.
// NB: Important declarations are explicitely ignored in the spec.
Ok(Keyframe {
selector: selector,
declarations: declarations.normal,
@ -109,22 +91,33 @@ impl Keyframe {
}
}
/// A keyframes step value. This can be a synthetised keyframes animation, that
/// is, one autogenerated from the current computed values, or a list of
/// declarations to apply.
// TODO: Find a better name for this?
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub enum KeyframesStepValue {
Declarations(Arc<Vec<PropertyDeclaration>>),
ComputedValues,
}
/// A single step from a keyframe animation.
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct KeyframesStep {
/// The percentage of the animation duration when this step starts.
pub start_percentage: KeyframePercentage,
/// Declarations that will determine the final style during the step.
pub declarations: Arc<Vec<PropertyDeclaration>>,
/// Declarations that will determine the final style during the step, or
/// `ComputedValues` if this is an autogenerated step.
pub value: KeyframesStepValue,
}
impl KeyframesStep {
#[inline]
fn new(percentage: KeyframePercentage,
declarations: Arc<Vec<PropertyDeclaration>>) -> Self {
value: KeyframesStepValue) -> Self {
KeyframesStep {
start_percentage: percentage,
declarations: declarations,
value: value,
}
}
}
@ -161,24 +154,34 @@ fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
impl KeyframesAnimation {
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<Self> {
debug_assert!(keyframes.len() > 1);
let mut steps = vec![];
let animated_properties = get_animated_properties(&keyframes[0]);
if animated_properties.is_empty() {
if keyframes.is_empty() || animated_properties.is_empty() {
return None;
}
let mut steps = vec![];
for keyframe in keyframes {
for percentage in keyframe.selector.0.iter() {
steps.push(KeyframesStep::new(*percentage,
keyframe.declarations.clone()));
KeyframesStepValue::Declarations(keyframe.declarations.clone())));
}
}
// Sort by the start percentage, so we can easily find a frame.
steps.sort_by_key(|step| step.start_percentage);
// Prepend autogenerated keyframes if appropriate.
if steps[0].start_percentage.0 != 0. {
steps.insert(0, KeyframesStep::new(KeyframePercentage::new(0.),
KeyframesStepValue::ComputedValues));
}
if steps.last().unwrap().start_percentage.0 != 1. {
steps.push(KeyframesStep::new(KeyframePercentage::new(0.),
KeyframesStepValue::ComputedValues));
}
Some(KeyframesAnimation {
steps: steps,
properties_changed: animated_properties,
@ -186,3 +189,54 @@ impl KeyframesAnimation {
}
}
/// Parses a keyframes list, like:
/// 0%, 50% {
/// width: 50%;
/// }
///
/// 40%, 60%, 100% {
/// width: 100%;
/// }
struct KeyframeListParser<'a> {
context: &'a ParserContext<'a>,
}
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<Keyframe> {
RuleListParser::new_for_nested_rule(input, KeyframeListParser { context: context })
.filter_map(Result::ok)
.collect()
}
enum Void {}
impl<'a> AtRuleParser for KeyframeListParser<'a> {
type Prelude = Void;
type AtRule = Keyframe;
}
impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
type Prelude = KeyframeSelector;
type QualifiedRule = Keyframe;
fn parse_prelude(&self, input: &mut Parser) -> Result<Self::Prelude, ()> {
let start = input.position();
match input.parse_comma_separated(|input| KeyframePercentage::parse(input)) {
Ok(percentages) => Ok(KeyframeSelector(percentages)),
Err(()) => {
let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start));
log_css_error(input, start, &message, self.context);
Err(())
}
}
}
fn parse_block(&self, prelude: Self::Prelude, input: &mut Parser)
-> Result<Self::QualifiedRule, ()> {
Ok(Keyframe {
selector: prelude,
// FIXME: needs parsing different from parse_property_declaration_list:
// https://drafts.csswg.org/css-animations/#keyframes
// Paragraph "The <declaration-list> inside of <keyframe-block> ..."
declarations: parse_property_declaration_list(self.context, input).normal,
})
}
}