mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
style: Part 1: Implement @scroll-timeline in style system
Define the data structure for @scroll-timeline rule, the parsing code, and the serialization. Differential Revision: https://phabricator.services.mozilla.com/D125764
This commit is contained in:
parent
3e251f50fc
commit
111c8d616f
3 changed files with 345 additions and 17 deletions
|
@ -20,6 +20,7 @@ mod page_rule;
|
||||||
mod rule_list;
|
mod rule_list;
|
||||||
mod rule_parser;
|
mod rule_parser;
|
||||||
mod rules_iterator;
|
mod rules_iterator;
|
||||||
|
pub mod scroll_timeline_rule;
|
||||||
mod style_rule;
|
mod style_rule;
|
||||||
mod stylesheet;
|
mod stylesheet;
|
||||||
pub mod supports_rule;
|
pub mod supports_rule;
|
||||||
|
|
315
components/style/stylesheets/scroll_timeline_rule.rs
Normal file
315
components/style/stylesheets/scroll_timeline_rule.rs
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! scroll-timeline-at-rule: https://drafts.csswg.org/scroll-animations/#scroll-timeline-at-rule
|
||||||
|
|
||||||
|
use crate::parser::{Parse, ParserContext};
|
||||||
|
use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
|
||||||
|
use crate::str::CssStringWriter;
|
||||||
|
use crate::values::specified::{LengthPercentage, Number};
|
||||||
|
use crate::values::{AtomIdent, TimelineName};
|
||||||
|
use cssparser::{AtRuleParser, CowRcStr, DeclarationParser, Parser, SourceLocation, Token};
|
||||||
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
|
use std::fmt::{self, Debug, Write};
|
||||||
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
|
/// A [`@scroll-timeline`][descriptors] rule.
|
||||||
|
///
|
||||||
|
/// [descriptors] https://drafts.csswg.org/scroll-animations/#scroll-timeline-descriptors
|
||||||
|
#[derive(Clone, Debug, ToShmem)]
|
||||||
|
pub struct ScrollTimelineRule {
|
||||||
|
/// The name of the current scroll timeline.
|
||||||
|
pub name: TimelineName,
|
||||||
|
/// The descriptors.
|
||||||
|
pub descriptors: ScrollTimelineDescriptors,
|
||||||
|
/// The line and column of the rule's source code.
|
||||||
|
pub source_location: SourceLocation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCssWithGuard for ScrollTimelineRule {
|
||||||
|
fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
|
||||||
|
let mut dest = CssWriter::new(dest);
|
||||||
|
dest.write_str("@scroll-timeline ")?;
|
||||||
|
self.name.to_css(&mut dest)?;
|
||||||
|
dest.write_str(" { ")?;
|
||||||
|
self.descriptors.to_css(&mut dest)?;
|
||||||
|
dest.write_str("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The descriptors of @scroll-timeline.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#scroll-timeline-descriptors
|
||||||
|
#[derive(Clone, Debug, Default, ToShmem)]
|
||||||
|
pub struct ScrollTimelineDescriptors {
|
||||||
|
/// The source of the current scroll timeline.
|
||||||
|
pub source: Option<Source>,
|
||||||
|
/// The orientation of the current scroll timeline.
|
||||||
|
pub orientation: Option<Orientation>,
|
||||||
|
/// The scroll timeline's scrollOffsets.
|
||||||
|
pub offsets: Option<ScrollOffsets>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ScrollTimelineDescriptors {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
use crate::cssparser::DeclarationListParser;
|
||||||
|
use crate::error_reporting::ContextualParseError;
|
||||||
|
|
||||||
|
let mut descriptors = ScrollTimelineDescriptors::default();
|
||||||
|
let parser = ScrollTimelineDescriptorsParser {
|
||||||
|
context,
|
||||||
|
descriptors: &mut descriptors,
|
||||||
|
};
|
||||||
|
let mut iter = DeclarationListParser::new(input, parser);
|
||||||
|
while let Some(declaration) = iter.next() {
|
||||||
|
if let Err((error, slice)) = declaration {
|
||||||
|
let location = error.location;
|
||||||
|
let error = ContextualParseError::UnsupportedRule(slice, error);
|
||||||
|
context.log_css_error(location, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(descriptors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basically, this is used for the serialization of CSSScrollTimelineRule, so we follow the
|
||||||
|
// instructions in https://drafts.csswg.org/scroll-animations-1/#serialize-a-cssscrolltimelinerule.
|
||||||
|
impl ToCss for ScrollTimelineDescriptors {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
if let Some(ref value) = self.source {
|
||||||
|
dest.write_str("source: ")?;
|
||||||
|
value.to_css(dest)?;
|
||||||
|
dest.write_str("; ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref value) = self.orientation {
|
||||||
|
dest.write_str("orientation: ")?;
|
||||||
|
value.to_css(dest)?;
|
||||||
|
dest.write_str("; ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/w3c/csswg-drafts/issues/6617
|
||||||
|
if let Some(ref value) = self.offsets {
|
||||||
|
dest.write_str("scroll-offsets: ")?;
|
||||||
|
value.to_css(dest)?;
|
||||||
|
dest.write_str("; ")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScrollTimelineDescriptorsParser<'a, 'b: 'a> {
|
||||||
|
context: &'a ParserContext<'b>,
|
||||||
|
descriptors: &'a mut ScrollTimelineDescriptors,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'i> AtRuleParser<'i> for ScrollTimelineDescriptorsParser<'a, 'b> {
|
||||||
|
type Prelude = ();
|
||||||
|
type AtRule = ();
|
||||||
|
type Error = StyleParseErrorKind<'i>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'i> DeclarationParser<'i> for ScrollTimelineDescriptorsParser<'a, 'b> {
|
||||||
|
type Declaration = ();
|
||||||
|
type Error = StyleParseErrorKind<'i>;
|
||||||
|
|
||||||
|
fn parse_value<'t>(
|
||||||
|
&mut self,
|
||||||
|
name: CowRcStr<'i>,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<(), ParseError<'i>> {
|
||||||
|
macro_rules! parse_descriptor {
|
||||||
|
(
|
||||||
|
$( $name: tt / $ident: ident, )*
|
||||||
|
) => {
|
||||||
|
match_ignore_ascii_case! { &*name,
|
||||||
|
$(
|
||||||
|
$name => {
|
||||||
|
let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
|
||||||
|
self.descriptors.$ident = Some(value)
|
||||||
|
},
|
||||||
|
)*
|
||||||
|
_ => {
|
||||||
|
return Err(input.new_custom_error(
|
||||||
|
SelectorParseErrorKind::UnexpectedIdent(name.clone()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_descriptor! {
|
||||||
|
"source" / source,
|
||||||
|
"orientation" / orientation,
|
||||||
|
"scroll-offsets" / offsets,
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scroll-timeline source.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#descdef-scroll-timeline-source
|
||||||
|
#[derive(Clone, Debug, Parse, PartialEq, ToCss, ToShmem)]
|
||||||
|
pub enum Source {
|
||||||
|
/// The scroll container.
|
||||||
|
Selector(ScrollTimelineSelector),
|
||||||
|
/// The initial value. The scrollingElement of the Document associated with the Window that is
|
||||||
|
/// the current global object.
|
||||||
|
Auto,
|
||||||
|
/// Null. However, it's not clear what is the expected behavior of this. See the spec issue:
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#issue-0d1e73bd
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scroll-timeline orientation.
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#descdef-scroll-timeline-orientation
|
||||||
|
///
|
||||||
|
/// Note: the initial orientation is auto, and we will treat it as block, the same as the
|
||||||
|
/// definition of ScrollTimelineOptions (WebIDL API).
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#dom-scrolltimelineoptions-orientation
|
||||||
|
#[derive(Clone, Copy, Debug, MallocSizeOf, Eq, Parse, PartialEq, PartialOrd, ToCss, ToShmem)]
|
||||||
|
pub enum Orientation {
|
||||||
|
/// The initial value.
|
||||||
|
Auto,
|
||||||
|
/// The direction along the block axis. This is the default value.
|
||||||
|
Block,
|
||||||
|
/// The direction along the inline axis
|
||||||
|
Inline,
|
||||||
|
/// The physical horizontal direction.
|
||||||
|
Horizontal,
|
||||||
|
/// The physical vertical direction.
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll-timeline offsets. We treat None as an empty vector.
|
||||||
|
/// value: none | <scroll-timeline-offset>#
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#descdef-scroll-timeline-scroll-offsets
|
||||||
|
#[derive(Clone, Debug, ToCss, ToShmem)]
|
||||||
|
#[css(comma)]
|
||||||
|
pub struct ScrollOffsets(#[css(if_empty = "none", iterable)] Box<[ScrollTimelineOffset]>);
|
||||||
|
|
||||||
|
impl Parse for ScrollOffsets {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
|
||||||
|
return Ok(ScrollOffsets(Box::new([])));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ScrollOffsets(
|
||||||
|
input
|
||||||
|
.parse_comma_separated(|i| ScrollTimelineOffset::parse(context, i))?
|
||||||
|
.into_boxed_slice(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A <scroll-timeline-offset>.
|
||||||
|
/// value: auto | <length-percentage> | <element-offset>
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/scroll-animations/#typedef-scroll-timeline-offset
|
||||||
|
#[derive(Clone, Debug, Parse, PartialEq, ToCss, ToShmem)]
|
||||||
|
pub enum ScrollTimelineOffset {
|
||||||
|
/// The initial value. A container-based offset.
|
||||||
|
Auto,
|
||||||
|
/// A container-based offset with the distance indicated by the value along source's scroll
|
||||||
|
/// range in orientation.
|
||||||
|
LengthPercentage(LengthPercentage),
|
||||||
|
/// An element-based offset.
|
||||||
|
ElementOffset(ElementOffset),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An <element-offset-edge>.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/scroll-animations-1/#typedef-element-offset-edge
|
||||||
|
#[derive(Clone, Copy, Debug, MallocSizeOf, Eq, Parse, PartialEq, PartialOrd, ToCss, ToShmem)]
|
||||||
|
pub enum ElementOffsetEdge {
|
||||||
|
/// Start edge
|
||||||
|
Start,
|
||||||
|
/// End edge.
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An <element-offset>.
|
||||||
|
/// value: selector( <id-selector> ) [<element-offset-edge> || <number>]?
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/scroll-animations-1/#typedef-element-offset
|
||||||
|
#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
|
||||||
|
pub struct ElementOffset {
|
||||||
|
/// The target whose intersection with source's scrolling box determines the concrete scroll
|
||||||
|
/// offset.
|
||||||
|
target: ScrollTimelineSelector,
|
||||||
|
/// An optional value of <element-offset-edge>. If not provided, the default value is start.
|
||||||
|
edge: Option<ElementOffsetEdge>,
|
||||||
|
/// An optional value of threshold. If not provided, the default value is 0.
|
||||||
|
threshold: Option<Number>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ElementOffset {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
let target = ScrollTimelineSelector::parse(context, input)?;
|
||||||
|
|
||||||
|
// Parse `[<element-offset-edge> || <number>]?`
|
||||||
|
let mut edge = input.try_parse(ElementOffsetEdge::parse).ok();
|
||||||
|
let threshold = input.try_parse(|i| Number::parse(context, i)).ok();
|
||||||
|
if edge.is_none() {
|
||||||
|
edge = input.try_parse(ElementOffsetEdge::parse).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ElementOffset {
|
||||||
|
target,
|
||||||
|
edge,
|
||||||
|
threshold,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of the selector ID.
|
||||||
|
#[derive(Clone, Eq, PartialEq, ToShmem)]
|
||||||
|
pub struct ScrollTimelineSelector(AtomIdent);
|
||||||
|
|
||||||
|
impl Parse for ScrollTimelineSelector {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
_context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
// Parse `selector(<id-selector>)`.
|
||||||
|
input.expect_function_matching("selector")?;
|
||||||
|
input.parse_nested_block(|i| match i.next()? {
|
||||||
|
Token::IDHash(id) => Ok(ScrollTimelineSelector(id.as_ref().into())),
|
||||||
|
_ => Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for ScrollTimelineSelector {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
use crate::cssparser::ToCss as CssparserToCss;
|
||||||
|
dest.write_str("selector(")?;
|
||||||
|
dest.write_char('#')?;
|
||||||
|
self.0.to_css(dest)?;
|
||||||
|
dest.write_char(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ScrollTimelineSelector {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.to_css(&mut CssWriter::new(f))
|
||||||
|
}
|
||||||
|
}
|
|
@ -464,29 +464,33 @@ impl ToCss for CustomIdent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The <timeline-name> or <keyframes-name>.
|
||||||
|
/// The definition of these two names are the same, so we use the same type for them.
|
||||||
|
///
|
||||||
|
/// <https://drafts.csswg.org/css-animations-2/#typedef-timeline-name>
|
||||||
/// <https://drafts.csswg.org/css-animations/#typedef-keyframes-name>
|
/// <https://drafts.csswg.org/css-animations/#typedef-keyframes-name>
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
|
Clone, Debug, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
|
||||||
)]
|
)]
|
||||||
pub enum KeyframesName {
|
pub enum TimelineOrKeyframesName {
|
||||||
/// <custom-ident>
|
/// <custom-ident>
|
||||||
Ident(CustomIdent),
|
Ident(CustomIdent),
|
||||||
/// <string>
|
/// <string>
|
||||||
QuotedString(Atom),
|
QuotedString(Atom),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyframesName {
|
impl TimelineOrKeyframesName {
|
||||||
/// <https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name>
|
/// <https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name>
|
||||||
pub fn from_ident(value: &str) -> Self {
|
pub fn from_ident(value: &str) -> Self {
|
||||||
let location = SourceLocation { line: 0, column: 0 };
|
let location = SourceLocation { line: 0, column: 0 };
|
||||||
let custom_ident = CustomIdent::from_ident(location, &value.into(), &["none"]).ok();
|
let custom_ident = CustomIdent::from_ident(location, &value.into(), &["none"]).ok();
|
||||||
match custom_ident {
|
match custom_ident {
|
||||||
Some(ident) => KeyframesName::Ident(ident),
|
Some(ident) => Self::Ident(ident),
|
||||||
None => KeyframesName::QuotedString(value.into()),
|
None => Self::QuotedString(value.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new KeyframesName from Atom.
|
/// Create a new TimelineOrKeyframesName from Atom.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn from_atom(atom: Atom) -> Self {
|
pub fn from_atom(atom: Atom) -> Self {
|
||||||
debug_assert_ne!(atom, atom!(""));
|
debug_assert_ne!(atom, atom!(""));
|
||||||
|
@ -494,19 +498,19 @@ impl KeyframesName {
|
||||||
// FIXME: We might want to preserve <string>, but currently Gecko
|
// FIXME: We might want to preserve <string>, but currently Gecko
|
||||||
// stores both of <custom-ident> and <string> into nsAtom, so
|
// stores both of <custom-ident> and <string> into nsAtom, so
|
||||||
// we can't tell it.
|
// we can't tell it.
|
||||||
KeyframesName::Ident(CustomIdent(atom))
|
Self::Ident(CustomIdent(atom))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The name as an Atom
|
/// The name as an Atom
|
||||||
pub fn as_atom(&self) -> &Atom {
|
pub fn as_atom(&self) -> &Atom {
|
||||||
match *self {
|
match *self {
|
||||||
KeyframesName::Ident(ref ident) => &ident.0,
|
Self::Ident(ref ident) => &ident.0,
|
||||||
KeyframesName::QuotedString(ref atom) => atom,
|
Self::QuotedString(ref atom) => atom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for KeyframesName {}
|
impl Eq for TimelineOrKeyframesName {}
|
||||||
|
|
||||||
/// A trait that returns whether a given type is the `auto` value or not. So far
|
/// A trait that returns whether a given type is the `auto` value or not. So far
|
||||||
/// only needed for background-size serialization, which special-cases `auto`.
|
/// only needed for background-size serialization, which special-cases `auto`.
|
||||||
|
@ -515,13 +519,13 @@ pub trait IsAuto {
|
||||||
fn is_auto(&self) -> bool;
|
fn is_auto(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for KeyframesName {
|
impl PartialEq for TimelineOrKeyframesName {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.as_atom() == other.as_atom()
|
self.as_atom() == other.as_atom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl hash::Hash for KeyframesName {
|
impl hash::Hash for TimelineOrKeyframesName {
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where
|
where
|
||||||
H: hash::Hasher,
|
H: hash::Hasher,
|
||||||
|
@ -530,32 +534,40 @@ impl hash::Hash for KeyframesName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for KeyframesName {
|
impl Parse for TimelineOrKeyframesName {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
_context: &ParserContext,
|
_context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
match *input.next()? {
|
match *input.next()? {
|
||||||
Token::Ident(ref s) => Ok(KeyframesName::Ident(CustomIdent::from_ident(
|
Token::Ident(ref s) => Ok(Self::Ident(CustomIdent::from_ident(
|
||||||
location,
|
location,
|
||||||
s,
|
s,
|
||||||
&["none"],
|
&["none"],
|
||||||
)?)),
|
)?)),
|
||||||
Token::QuotedString(ref s) => Ok(KeyframesName::QuotedString(Atom::from(s.as_ref()))),
|
Token::QuotedString(ref s) => {
|
||||||
|
Ok(Self::QuotedString(Atom::from(s.as_ref())))
|
||||||
|
},
|
||||||
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for KeyframesName {
|
impl ToCss for TimelineOrKeyframesName {
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
KeyframesName::Ident(ref ident) => ident.to_css(dest),
|
Self::Ident(ref ident) => ident.to_css(dest),
|
||||||
KeyframesName::QuotedString(ref atom) => atom.to_string().to_css(dest),
|
Self::QuotedString(ref atom) => atom.to_string().to_css(dest),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The typedef of <timeline-name>.
|
||||||
|
pub type TimelineName = TimelineOrKeyframesName;
|
||||||
|
|
||||||
|
/// The typedef of <keyframes-name>.
|
||||||
|
pub type KeyframesName = TimelineOrKeyframesName;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue