Skip to content

Commit

Permalink
fix: CSS-wide keywords and none in @keyframes cannot remove quotes (
Browse files Browse the repository at this point in the history
  • Loading branch information
yisibl authored Sep 7, 2022
1 parent 4485c3d commit aa13001
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 26 deletions.
68 changes: 54 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5396,37 +5396,77 @@ mod tests {
fn test_keyframes() {
minify_test(
r#"
@keyframes test {
from {
background: green;
}

50% {
background: red;
}

@keyframes "test" {
100% {
background: blue
}
}
"#,
"@keyframes test{0%{background:green}50%{background:red}to{background:#00f}}",
"@keyframes test{to{background:#00f}}",
);
minify_test(
r#"
@keyframes test {
100% {
background: blue
}
}
"#,
"@keyframes test{to{background:#00f}}",
);

// CSS-wide keywords and `none` cannot remove quotes.
minify_test(
r#"
@keyframes "revert" {
from {
background: green;
background-color: red;
}
}
"#,
"@keyframes \"revert\"{0%{background:green}}",
);

100% {
background: blue
minify_test(
r#"
@keyframes "none" {
from {
background: green;
}
}
"#,
"@keyframes test{0%{background:red}to{background:#00f}}",
"@keyframes \"none\"{0%{background:green}}",
);

// CSS-wide keywords without quotes throws an error.
error_test(
r#"
@keyframes revert {}
"#,
ParserError::UnexpectedToken(Token::Ident("revert".into())),
);

error_test(
r#"
@keyframes revert-layer {}
"#,
ParserError::UnexpectedToken(Token::Ident("revert-layer".into())),
);

error_test(
r#"
@keyframes none {}
"#,
ParserError::UnexpectedToken(Token::Ident("none".into())),
);

error_test(
r#"
@keyframes NONE {}
"#,
ParserError::UnexpectedToken(Token::Ident("NONE".into())),
);

minify_test(
r#"
@-webkit-keyframes test {
Expand Down
14 changes: 4 additions & 10 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::rules::{
document::MozDocumentRule,
font_face::{FontFaceDeclarationParser, FontFaceRule},
import::ImportRule,
keyframes::{KeyframeListParser, KeyframesRule},
keyframes::{KeyframeListParser, KeyframesName, KeyframesRule},
layer::LayerName,
media::MediaRule,
namespace::NamespaceRule,
Expand Down Expand Up @@ -122,7 +122,7 @@ pub enum AtRulePrelude<'i> {
/// A @viewport rule prelude.
Viewport(VendorPrefix),
/// A @keyframes rule, with its animation name and vendor prefix if exists.
Keyframes(CustomIdent<'i>, VendorPrefix),
Keyframes(KeyframesName<'i>, VendorPrefix),
/// A @page rule prelude.
Page(Vec<PageSelector<'i>>),
/// A @-moz-document rule.
Expand Down Expand Up @@ -425,14 +425,8 @@ impl<'a, 'o, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'o, 'i> {
VendorPrefix::None
};

let location = input.current_source_location();
let name = match *input.next()? {
Token::Ident(ref s) => s.into(),
Token::QuotedString(ref s) => s.into(),
ref t => return Err(location.new_unexpected_token_error(t.clone())),
};

Ok(AtRulePrelude::Keyframes(CustomIdent(name), prefix))
let name = input.try_parse(KeyframesName::parse)?;
Ok(AtRulePrelude::Keyframes(name, prefix))
},
"page" => {
let selectors = input.try_parse(|input| input.parse_comma_separated(PageSelector::parse)).unwrap_or_default();
Expand Down
63 changes: 62 additions & 1 deletion src/rules/keyframes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::traits::{Parse, ToCss};
use crate::values::color::ColorFallbackKind;
use crate::values::ident::CustomIdent;
use crate::values::percentage::Percentage;
use crate::values::string::CowArcStr;
use crate::vendor_prefix::VendorPrefix;
use cssparser::*;

Expand All @@ -23,8 +24,9 @@ use cssparser::*;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KeyframesRule<'i> {
/// The animation name.
/// <keyframes-name> = <custom-ident> | <string>
#[cfg_attr(feature = "serde", serde(borrow))]
pub name: CustomIdent<'i>,
pub name: KeyframesName<'i>,
/// A list of keyframes in the animation.
pub keyframes: Vec<Keyframe<'i>>,
/// A vendor prefix for the rule, e.g. `@-webkit-keyframes`.
Expand All @@ -33,6 +35,65 @@ pub struct KeyframesRule<'i> {
pub loc: Location,
}

/// KeyframesName
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum KeyframesName<'i> {
/// `<custom-ident>` of a `@keyframes` name.
#[cfg_attr(feature = "serde", serde(borrow))]
Ident(CustomIdent<'i>),

/// `<string>` of a `@keyframes` name.
#[cfg_attr(feature = "serde", serde(borrow))]
Custom(CowArcStr<'i>),
}

impl<'i> Parse<'i> for KeyframesName<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
match input.next()?.clone() {
Token::Ident(ref s) => {
// CSS-wide keywords without quotes throws an error.
match_ignore_ascii_case! { &*s,
"none" | "initial" | "inherit" | "unset" | "default" | "revert" | "revert-layer" => {
Err(input.new_unexpected_token_error(Token::Ident(s.clone())))
},
_ => {
Ok(KeyframesName::Ident(CustomIdent(s.into())))
}
}
}

Token::QuotedString(ref s) => Ok(KeyframesName::Custom(s.into())),
t => return Err(input.new_unexpected_token_error(t.clone())),
}
}
}

impl<'i> ToCss for KeyframesName<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
KeyframesName::Ident(ident) => {
dest.write_ident(ident.0.as_ref())?;
}
KeyframesName::Custom(s) => {
// CSS-wide keywords and `none` cannot remove quotes.
match_ignore_ascii_case! { &*s,
"none" | "initial" | "inherit" | "unset" | "default" | "revert" | "revert-layer" => {
serialize_string(&s, dest)?;
},
_ => {
dest.write_ident(s.as_ref())?;
}
}
}
}
Ok(())
}
}

impl<'i> KeyframesRule<'i> {
pub(crate) fn minify(&mut self, context: &mut MinifyContext<'_, 'i>) {
context.handler_context.context = DeclarationContext::Keyframes;
Expand Down
6 changes: 5 additions & 1 deletion src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ use nesting::NestingRule;
use page::PageRule;
use serde::Serialize;
use std::collections::{HashMap, HashSet};
use std::default::Default;
use style::StyleRule;
use supports::SupportsRule;
use unknown::UnknownAtRule;
Expand Down Expand Up @@ -259,7 +260,10 @@ impl<'i> CssRuleList<'i> {
for mut rule in self.0.drain(..) {
match &mut rule {
CssRule::Keyframes(keyframes) => {
if context.unused_symbols.contains(keyframes.name.0.as_ref()) {
if context
.unused_symbols
.contains(&keyframes.name.to_css_string(Default::default()).unwrap())
{
continue;
}
keyframes.minify(context);
Expand Down

0 comments on commit aa13001

Please sign in to comment.