use crate::error_reporting::ContextualParseError;
use crate::parser::ParserContext;
use crate::properties::{
longhands::{
animation_composition::single_value::SpecifiedValue as SpecifiedComposition,
transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction,
},
parse_property_declaration_list, LonghandId, PropertyDeclaration, PropertyDeclarationBlock,
PropertyDeclarationId, PropertyDeclarationIdSet,
};
use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
use crate::shared_lock::{Locked, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::rule_parser::VendorPrefix;
use crate::stylesheets::{CssRuleType, StylesheetContents};
use crate::values::{serialize_percentage, KeyframesName};
use cssparser::{
parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserInput, ParserState,
QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
};
use servo_arc::Arc;
use std::borrow::Cow;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
#[derive(Debug, ToShmem)]
pub struct KeyframesRule {
pub name: KeyframesName,
pub keyframes: Vec<Arc<Locked<Keyframe>>>,
pub vendor_prefix: Option<VendorPrefix>,
pub source_location: SourceLocation,
}
impl ToCssWithGuard for KeyframesRule {
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@keyframes ")?;
self.name.to_css(&mut CssWriter::new(dest))?;
dest.write_str(" {")?;
let iter = self.keyframes.iter();
for lock in iter {
dest.write_str("\n")?;
let keyframe = lock.read_with(&guard);
keyframe.to_css(guard, dest)?;
}
dest.write_str("\n}")
}
}
impl KeyframesRule {
pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
let mut input = ParserInput::new(selector);
if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
if keyframe.read_with(guard).selector == selector {
return Some(i);
}
}
}
None
}
}
impl DeepCloneWithLock for KeyframesRule {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
) -> Self {
KeyframesRule {
name: self.name.clone(),
keyframes: self
.keyframes
.iter()
.map(|x| {
Arc::new(
lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard)),
)
})
.collect(),
vendor_prefix: self.vendor_prefix.clone(),
source_location: self.source_location.clone(),
}
}
}
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
pub struct KeyframePercentage(pub f32);
impl ::std::cmp::Ord for KeyframePercentage {
#[inline]
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
impl ::std::cmp::Eq for KeyframePercentage {}
impl ToCss for KeyframePercentage {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
serialize_percentage(self.0, dest)
}
}
impl KeyframePercentage {
#[inline]
pub fn new(value: f32) -> KeyframePercentage {
debug_assert!(value >= 0. && value <= 1.);
KeyframePercentage(value)
}
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> {
let token = input.next()?.clone();
match token {
Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => {
Ok(KeyframePercentage::new(0.))
},
Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => {
Ok(KeyframePercentage::new(1.))
},
Token::Percentage {
unit_value: percentage,
..
} if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)),
_ => Err(input.new_unexpected_token_error(token)),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
#[css(comma)]
pub struct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>);
impl KeyframeSelector {
#[inline]
pub fn percentages(&self) -> &[KeyframePercentage] {
&self.0
}
pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
KeyframeSelector(percentages)
}
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
input
.parse_comma_separated(KeyframePercentage::parse)
.map(KeyframeSelector)
}
}
#[derive(Debug, ToShmem)]
pub struct Keyframe {
pub selector: KeyframeSelector,
pub block: Arc<Locked<PropertyDeclarationBlock>>,
pub source_location: SourceLocation,
}
impl ToCssWithGuard for Keyframe {
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
self.selector.to_css(&mut CssWriter::new(dest))?;
dest.write_str(" { ")?;
self.block.read_with(guard).to_css(dest)?;
dest.write_str(" }")?;
Ok(())
}
}
impl Keyframe {
pub fn parse<'i>(
css: &'i str,
parent_stylesheet_contents: &StylesheetContents,
lock: &SharedRwLock,
) -> Result<Arc<Locked<Self>>, ParseError<'i>> {
let url_data = parent_stylesheet_contents.url_data.read();
let namespaces = parent_stylesheet_contents.namespaces.read();
let mut context = ParserContext::new(
parent_stylesheet_contents.origin,
&url_data,
Some(CssRuleType::Keyframe),
ParsingMode::DEFAULT,
parent_stylesheet_contents.quirks_mode,
Cow::Borrowed(&*namespaces),
None,
None,
);
let mut input = ParserInput::new(css);
let mut input = Parser::new(&mut input);
let mut rule_parser = KeyframeListParser {
context: &mut context,
shared_lock: &lock,
};
parse_one_rule(&mut input, &mut rule_parser)
}
}
impl DeepCloneWithLock for Keyframe {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
) -> Keyframe {
Keyframe {
selector: self.selector.clone(),
block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
source_location: self.source_location.clone(),
}
}
}
#[derive(Clone, Debug, MallocSizeOf)]
pub enum KeyframesStepValue {
Declarations {
#[cfg_attr(
feature = "gecko",
ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile"
)]
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
block: Arc<Locked<PropertyDeclarationBlock>>,
},
ComputedValues,
}
#[derive(Clone, Debug, MallocSizeOf)]
pub struct KeyframesStep {
pub start_percentage: KeyframePercentage,
pub value: KeyframesStepValue,
pub declared_timing_function: bool,
pub declared_composition: bool,
}
impl KeyframesStep {
#[inline]
fn new(
start_percentage: KeyframePercentage,
value: KeyframesStepValue,
guard: &SharedRwLockReadGuard,
) -> Self {
let mut declared_timing_function = false;
let mut declared_composition = false;
if let KeyframesStepValue::Declarations { ref block } = value {
for prop_decl in block.read_with(guard).declarations().iter() {
match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => {
declared_timing_function = true;
},
PropertyDeclaration::AnimationComposition(..) => {
declared_composition = true;
},
_ => continue,
}
if declared_timing_function && declared_composition {
break;
}
}
}
KeyframesStep {
start_percentage,
value,
declared_timing_function,
declared_composition,
}
}
#[inline]
fn get_declared_property<'a>(
&'a self,
guard: &'a SharedRwLockReadGuard,
property: LonghandId,
) -> Option<&'a PropertyDeclaration> {
match self.value {
KeyframesStepValue::Declarations { ref block } => {
let guard = block.read_with(guard);
let (declaration, _) = guard
.get(PropertyDeclarationId::Longhand(property))
.unwrap();
match *declaration {
PropertyDeclaration::CSSWideKeyword(..) => None,
PropertyDeclaration::WithVariables(..) => None,
_ => Some(declaration),
}
},
KeyframesStepValue::ComputedValues => {
panic!("Shouldn't happen to set this property in missing keyframes")
},
}
}
pub fn get_animation_timing_function(
&self,
guard: &SharedRwLockReadGuard,
) -> Option<SpecifiedTimingFunction> {
if !self.declared_timing_function {
return None;
}
self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
.map(|decl| {
match *decl {
PropertyDeclaration::AnimationTimingFunction(ref value) => {
value.0[0].clone()
},
_ => unreachable!("Unexpected PropertyDeclaration"),
}
})
}
pub fn get_animation_composition(
&self,
guard: &SharedRwLockReadGuard,
) -> Option<SpecifiedComposition> {
if !self.declared_composition {
return None;
}
self.get_declared_property(guard, LonghandId::AnimationComposition)
.map(|decl| {
match *decl {
PropertyDeclaration::AnimationComposition(ref value) => {
value.0[0].clone()
},
_ => unreachable!("Unexpected PropertyDeclaration"),
}
})
}
}
#[derive(Clone, Debug, MallocSizeOf)]
pub struct KeyframesAnimation {
pub steps: Vec<KeyframesStep>,
pub properties_changed: PropertyDeclarationIdSet,
pub vendor_prefix: Option<VendorPrefix>,
}
fn get_animated_properties(
keyframes: &[Arc<Locked<Keyframe>>],
guard: &SharedRwLockReadGuard,
) -> PropertyDeclarationIdSet {
let mut ret = PropertyDeclarationIdSet::default();
for keyframe in keyframes {
let keyframe = keyframe.read_with(&guard);
let block = keyframe.block.read_with(guard);
for declaration in block.normal_declaration_iter() {
let declaration_id = declaration.id();
if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display) {
continue;
}
if !declaration_id.is_animatable() {
continue;
}
ret.insert(declaration_id);
}
}
ret
}
impl KeyframesAnimation {
pub fn from_keyframes(
keyframes: &[Arc<Locked<Keyframe>>],
vendor_prefix: Option<VendorPrefix>,
guard: &SharedRwLockReadGuard,
) -> Self {
let mut result = KeyframesAnimation {
steps: vec![],
properties_changed: PropertyDeclarationIdSet::default(),
vendor_prefix,
};
if keyframes.is_empty() {
return result;
}
result.properties_changed = get_animated_properties(keyframes, guard);
if result.properties_changed.is_empty() {
return result;
}
for keyframe in keyframes {
let keyframe = keyframe.read_with(&guard);
for percentage in keyframe.selector.0.iter() {
result.steps.push(KeyframesStep::new(
*percentage,
KeyframesStepValue::Declarations {
block: keyframe.block.clone(),
},
guard,
));
}
}
result.steps.sort_by_key(|step| step.start_percentage);
if result.steps[0].start_percentage.0 != 0. {
result.steps.insert(
0,
KeyframesStep::new(
KeyframePercentage::new(0.),
KeyframesStepValue::ComputedValues,
guard,
),
);
}
if result.steps.last().unwrap().start_percentage.0 != 1. {
result.steps.push(KeyframesStep::new(
KeyframePercentage::new(1.),
KeyframesStepValue::ComputedValues,
guard,
));
}
result
}
}
struct KeyframeListParser<'a, 'b> {
context: &'a mut ParserContext<'b>,
shared_lock: &'a SharedRwLock,
}
pub fn parse_keyframe_list<'a>(
context: &mut ParserContext<'a>,
input: &mut Parser,
shared_lock: &SharedRwLock,
) -> Vec<Arc<Locked<Keyframe>>> {
let mut parser = KeyframeListParser {
context,
shared_lock,
};
RuleBodyParser::new(input, &mut parser)
.filter_map(Result::ok)
.collect()
}
impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {
type Prelude = ();
type AtRule = Arc<Locked<Keyframe>>;
type Error = StyleParseErrorKind<'i>;
}
impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
type Declaration = Arc<Locked<Keyframe>>;
type Error = StyleParseErrorKind<'i>;
}
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {
type Prelude = KeyframeSelector;
type QualifiedRule = Arc<Locked<Keyframe>>;
type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>(
&mut self,
input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i>> {
let start_position = input.position();
KeyframeSelector::parse(input).map_err(|e| {
let location = e.location;
let error = ContextualParseError::InvalidKeyframeRule(
input.slice_from(start_position),
e.clone(),
);
self.context.log_css_error(location, error);
e
})
}
fn parse_block<'t>(
&mut self,
selector: Self::Prelude,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::QualifiedRule, ParseError<'i>> {
let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| {
parse_property_declaration_list(&p, input, &[])
});
Ok(Arc::new(self.shared_lock.wrap(Keyframe {
selector,
block: Arc::new(self.shared_lock.wrap(block)),
source_location: start.source_location(),
})))
}
}
impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>>
for KeyframeListParser<'a, 'b>
{
fn parse_qualified(&self) -> bool {
true
}
fn parse_declarations(&self) -> bool {
false
}
}