mirror of
https://github.com/servo/servo.git
synced 2025-06-17 04:44:28 +00:00
288 lines
9.8 KiB
Rust
288 lines
9.8 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* 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::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
|
|
use cssparser::{DeclarationListParser, DeclarationParser};
|
|
use parser::{ParserContext, log_css_error};
|
|
use properties::PropertyDeclaration;
|
|
use properties::PropertyDeclarationParseResult;
|
|
use properties::animated_properties::TransitionProperty;
|
|
use std::sync::Arc;
|
|
|
|
/// A number from 1 to 100, indicating the percentage of the animation where
|
|
/// this keyframe should run.
|
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub struct KeyframePercentage(pub f32);
|
|
|
|
impl ::std::cmp::Ord for KeyframePercentage {
|
|
#[inline]
|
|
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
|
|
// We know we have a number from 0 to 1, so unwrap() here is safe.
|
|
self.0.partial_cmp(&other.0).unwrap()
|
|
}
|
|
}
|
|
|
|
impl ::std::cmp::Eq for KeyframePercentage { }
|
|
|
|
impl KeyframePercentage {
|
|
#[inline]
|
|
pub fn new(value: f32) -> KeyframePercentage {
|
|
debug_assert!(value >= 0. && value <= 1.);
|
|
KeyframePercentage(value)
|
|
}
|
|
|
|
fn parse(input: &mut Parser) -> Result<KeyframePercentage, ()> {
|
|
let percentage = if input.try(|input| input.expect_ident_matching("from")).is_ok() {
|
|
KeyframePercentage::new(0.)
|
|
} else if input.try(|input| input.expect_ident_matching("to")).is_ok() {
|
|
KeyframePercentage::new(1.)
|
|
} else {
|
|
let percentage = try!(input.expect_percentage());
|
|
if percentage > 1. || percentage < 0. {
|
|
return Err(());
|
|
}
|
|
KeyframePercentage::new(percentage)
|
|
};
|
|
|
|
Ok(percentage)
|
|
}
|
|
}
|
|
|
|
/// A keyframes selector is a list of percentages or from/to symbols, which are
|
|
/// converted at parse time to percentages.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub struct KeyframeSelector(Vec<KeyframePercentage>);
|
|
impl KeyframeSelector {
|
|
#[inline]
|
|
pub fn percentages(&self) -> &[KeyframePercentage] {
|
|
&self.0
|
|
}
|
|
|
|
/// A dummy public function so we can write a unit test for this.
|
|
pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
|
|
KeyframeSelector(percentages)
|
|
}
|
|
}
|
|
|
|
/// A keyframe.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub struct Keyframe {
|
|
pub selector: KeyframeSelector,
|
|
pub declarations: Arc<Vec<PropertyDeclaration>>,
|
|
}
|
|
|
|
/// 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)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub enum KeyframesStepValue {
|
|
Declarations(Arc<Vec<PropertyDeclaration>>),
|
|
ComputedValues,
|
|
}
|
|
|
|
/// A single step from a keyframe animation.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(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, or
|
|
/// `ComputedValues` if this is an autogenerated step.
|
|
pub value: KeyframesStepValue,
|
|
/// Wether a animation-timing-function declaration exists in the list of
|
|
/// declarations.
|
|
///
|
|
/// This is used to know when to override the keyframe animation style.
|
|
pub declared_timing_function: bool,
|
|
}
|
|
|
|
impl KeyframesStep {
|
|
#[inline]
|
|
fn new(percentage: KeyframePercentage,
|
|
value: KeyframesStepValue) -> Self {
|
|
let declared_timing_function = match value {
|
|
KeyframesStepValue::Declarations(ref declarations) => {
|
|
declarations.iter().any(|prop_decl| {
|
|
match *prop_decl {
|
|
PropertyDeclaration::AnimationTimingFunction(..) => true,
|
|
_ => false,
|
|
}
|
|
})
|
|
}
|
|
_ => false,
|
|
};
|
|
|
|
KeyframesStep {
|
|
start_percentage: percentage,
|
|
value: value,
|
|
declared_timing_function: declared_timing_function,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This structure represents a list of animation steps computed from the list
|
|
/// of keyframes, in order.
|
|
///
|
|
/// It only takes into account animable properties.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
pub struct KeyframesAnimation {
|
|
pub steps: Vec<KeyframesStep>,
|
|
/// The properties that change in this animation.
|
|
pub properties_changed: Vec<TransitionProperty>,
|
|
}
|
|
|
|
/// Get all the animated properties in a keyframes animation. Note that it's not
|
|
/// defined what happens when a property is not on a keyframe, so we only peek
|
|
/// the props of the first one.
|
|
///
|
|
/// In practice, browsers seem to try to do their best job at it, so we might
|
|
/// want to go through all the actual keyframes and deduplicate properties.
|
|
fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
|
|
let mut ret = vec![];
|
|
// NB: declarations are already deduplicated, so we don't have to check for
|
|
// it here.
|
|
for declaration in keyframe.declarations.iter() {
|
|
if let Some(property) = TransitionProperty::from_declaration(&declaration) {
|
|
ret.push(property);
|
|
}
|
|
}
|
|
|
|
ret
|
|
}
|
|
|
|
impl KeyframesAnimation {
|
|
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<Self> {
|
|
if keyframes.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
let animated_properties = get_animated_properties(&keyframes[0]);
|
|
if 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,
|
|
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,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// 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, ()> {
|
|
let mut declarations = Vec::new();
|
|
let parser = KeyframeDeclarationParser {
|
|
context: self.context,
|
|
};
|
|
let mut iter = DeclarationListParser::new(input, parser);
|
|
while let Some(declaration) = iter.next() {
|
|
match declaration {
|
|
Ok(d) => declarations.extend(d),
|
|
Err(range) => {
|
|
let pos = range.start;
|
|
let message = format!("Unsupported keyframe property declaration: '{}'",
|
|
iter.input.slice(range));
|
|
log_css_error(iter.input, pos, &*message, self.context);
|
|
}
|
|
}
|
|
// `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
|
|
}
|
|
Ok(Keyframe {
|
|
selector: prelude,
|
|
declarations: Arc::new(declarations),
|
|
})
|
|
}
|
|
}
|
|
|
|
struct KeyframeDeclarationParser<'a, 'b: 'a> {
|
|
context: &'a ParserContext<'b>,
|
|
}
|
|
|
|
/// Default methods reject all at rules.
|
|
impl<'a, 'b> AtRuleParser for KeyframeDeclarationParser<'a, 'b> {
|
|
type Prelude = ();
|
|
type AtRule = Vec<PropertyDeclaration>;
|
|
}
|
|
|
|
impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> {
|
|
type Declaration = Vec<PropertyDeclaration>;
|
|
|
|
fn parse_value(&self, name: &str, input: &mut Parser) -> Result<Vec<PropertyDeclaration>, ()> {
|
|
let mut results = Vec::new();
|
|
match PropertyDeclaration::parse(name, self.context, input, &mut results, true) {
|
|
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}
|
|
_ => return Err(())
|
|
}
|
|
Ok(results)
|
|
}
|
|
}
|