/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
usecrate::error_reporting::ContextualParseError; usecrate::parser::ParserContext; usecrate::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,
}; usecrate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard}; usecrate::shared_lock::{Locked, ToCssWithGuard}; usecrate::str::CssStringWriter; usecrate::stylesheets::rule_parser::VendorPrefix; usecrate::stylesheets::{CssRuleType, StylesheetContents}; usecrate::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};
/// A [`@keyframes`][keyframes] rule. /// /// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes #[derive(Debug, ToShmem)] pubstruct KeyframesRule { /// The name of the current animation. pub name: KeyframesName, /// The keyframes specified for this CSS rule. pub keyframes: Vec<Arc<Locked<Keyframe>>>, /// Vendor prefix type the @keyframes has. pub vendor_prefix: Option<VendorPrefix>, /// The line and column of the rule's source code. pub source_location: SourceLocation,
}
impl ToCssWithGuard for KeyframesRule { // Serialization of KeyframesRule is not specced. 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 { /// Returns the index of the last keyframe that matches the given selector. /// If the selector is not valid, or no keyframe is found, returns None. /// /// Related spec: /// <https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule> pubfn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> { letmut input = ParserInput::new(selector); iflet Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) { for (i, keyframe) inself.keyframes.iter().enumerate().rev() { if keyframe.read_with(guard).selector == selector { return Some(i);
}
}
}
None
}
}
/// A number from 0 to 1, indicating the percentage of the animation when this /// keyframe should run. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)] pubstruct 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 ToCss for KeyframePercentage { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where
W: Write,
{
serialize_percentage(self.0, dest)
}
}
impl KeyframePercentage { /// Trivially constructs a new `KeyframePercentage`. #[inline] pubfn new(value: f32) -> KeyframePercentage {
debug_assert!(value >= 0. && value <= 1.);
KeyframePercentage(value)
}
/// A keyframes selector is a list of percentages or from/to symbols, which are /// converted at parse time to percentages. #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] #[css(comma)] pubstruct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>);
impl KeyframeSelector { /// Return the list of percentages this selector contains. #[inline] pubfn percentages(&self) -> &[KeyframePercentage] {
&self.0
}
/// A dummy public function so we can write a unit test for this. pubfn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
KeyframeSelector(percentages)
}
/// A keyframe. #[derive(Debug, ToShmem)] pubstruct Keyframe { /// The selector this keyframe was specified from. pub selector: KeyframeSelector,
/// The declaration block that was declared inside this keyframe. /// /// Note that `!important` rules in keyframes don't apply, but we keep this /// `Arc` just for convenience. pub block: Arc<Locked<PropertyDeclarationBlock>>,
/// The line and column of the rule's source code. pub source_location: SourceLocation,
}
/// 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(Clone, Debug, MallocSizeOf)] pubenum KeyframesStepValue { /// A step formed by a declaration block specified by the CSS.
Declarations { /// The declaration block per se. #[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>>,
}, /// A synthetic step computed from the current computed values at the time /// of the animation.
ComputedValues,
}
/// A single step from a keyframe animation. #[derive(Clone, Debug, MallocSizeOf)] pubstruct 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, /// Whether an 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, /// Whether an animation-composition declaration exists in the list of /// declarations. /// /// This is used to know when to override the keyframe animation style. pub declared_composition: bool,
}
impl KeyframesStep { #[inline] fn new(
start_percentage: KeyframePercentage,
value: KeyframesStepValue,
guard: &SharedRwLockReadGuard,
) -> Self { letmut declared_timing_function = false; letmut declared_composition = false; iflet 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,
} // Don't need to continue the loop if both are found. if declared_timing_function && declared_composition { break;
}
}
}
/// Return specified PropertyDeclaration. #[inline] fn get_declared_property<'a>(
&'a self,
guard: &'a SharedRwLockReadGuard,
property: LonghandId,
) -> Option<&'a PropertyDeclaration> { matchself.value {
KeyframesStepValue::Declarations { ref block } => { let guard = block.read_with(guard); let (declaration, _) = guard
.get(PropertyDeclarationId::Longhand(property))
.unwrap(); match *declaration {
PropertyDeclaration::CSSWideKeyword(..) => None, // FIXME: Bug 1710735: Support css variable in @keyframes rule.
PropertyDeclaration::WithVariables(..) => None,
_ => Some(declaration),
}
},
KeyframesStepValue::ComputedValues => {
panic!("Shouldn't happen to set this property in missing keyframes")
},
}
}
/// Return specified TransitionTimingFunction if this KeyframesSteps has /// 'animation-timing-function'. pubfn 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) => { // Use the first value
value.0[0].clone()
},
_ => unreachable!("Unexpected PropertyDeclaration"),
}
})
}
/// Return CompositeOperation if this KeyframesSteps has 'animation-composition'. pubfn 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) => { // Use the first value
value.0[0].clone()
},
_ => unreachable!("Unexpected PropertyDeclaration"),
}
})
}
}
/// This structure represents a list of animation steps computed from the list /// of keyframes, in order. /// /// It only takes into account animable properties. #[derive(Clone, Debug, MallocSizeOf)] pubstruct KeyframesAnimation { /// The difference steps of the animation. pub steps: Vec<KeyframesStep>, /// The properties that change in this animation. pub properties_changed: PropertyDeclarationIdSet, /// Vendor prefix type the @keyframes has. pub vendor_prefix: Option<VendorPrefix>,
}
/// Get all the animated properties in a keyframes animation. fn get_animated_properties(
keyframes: &[Arc<Locked<Keyframe>>],
guard: &SharedRwLockReadGuard,
) -> PropertyDeclarationIdSet { letmut ret = PropertyDeclarationIdSet::default(); // NB: declarations are already deduplicated, so we don't have to check for // it here. for keyframe in keyframes { let keyframe = keyframe.read_with(&guard); let block = keyframe.block.read_with(guard); // CSS Animations spec clearly defines that properties with !important // in keyframe rules are invalid and ignored, but it's still ambiguous // whether we should drop the !important properties or retain the // properties when they are set via CSSOM. So we assume there might // be properties with !important in keyframe rules here. // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824 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 { /// Create a keyframes animation from a given list of keyframes. /// /// This will return a keyframe animation with empty steps and /// properties_changed if the list of keyframes is empty, or there are no /// animated properties obtained from the keyframes. /// /// Otherwise, this will compute and sort the steps used for the animation, /// and return the animation object. pubfn from_keyframes(
keyframes: &[Arc<Locked<Keyframe>>],
vendor_prefix: Option<VendorPrefix>,
guard: &SharedRwLockReadGuard,
) -> Self { letmut 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,
));
}
}
// Sort by the start percentage, so we can easily find a frame.
result.steps.sort_by_key(|step| step.start_percentage);
// Prepend autogenerated keyframes if appropriate. if result.steps[0].start_percentage.0 != 0. {
result.steps.insert( 0,
KeyframesStep::new(
KeyframePercentage::new(0.),
KeyframesStepValue::ComputedValues,
guard,
),
);
}
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>(
&mutself,
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
})
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.