mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
style: Fix parsing and add generated keyframes
This commit is contained in:
parent
46eec45886
commit
2d566ef0ef
10 changed files with 189 additions and 94 deletions
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue