From 4765111c002e247c9c9e2cccefd239ddd5c46b38 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 5 Jun 2024 23:56:56 +0100 Subject: [PATCH 01/21] [unicode-range] Avoid integer overflow (panic in debug builds) If a long string of hex digits are passed to unicode-range, the code tries to parse them into a number before checking whether there are more than the 6 digits allowed by the syntax, and this may lead to integer overflow. To avoid this, check the number of digits and error out earlier if there are too many to possibly be valid. (See https://bugzilla.mozilla.org/show_bug.cgi?id=1900403) --- src/css-parsing-tests/urange.json | 5 +++++ src/unicode_range.rs | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/css-parsing-tests/urange.json b/src/css-parsing-tests/urange.json index 857d1d62..4dcb529c 100644 --- a/src/css-parsing-tests/urange.json +++ b/src/css-parsing-tests/urange.json @@ -84,6 +84,11 @@ null, null, null +], + +"U+26F9200D2640, U+10000-26F9200D2640", [ + null, + null ] ] diff --git a/src/unicode_range.rs b/src/unicode_range.rs index ce6bb3b5..a4130ef0 100644 --- a/src/unicode_range.rs +++ b/src/unicode_range.rs @@ -104,7 +104,7 @@ fn parse_concatenated(text: &[u8]) -> Result { Some((&b'+', text)) => text, _ => return Err(()), }; - let (first_hex_value, hex_digit_count) = consume_hex(&mut text); + let (first_hex_value, hex_digit_count) = consume_hex(&mut text, 6)?; let question_marks = consume_question_marks(&mut text); let consumed = hex_digit_count + question_marks; if consumed == 0 || consumed > 6 { @@ -124,7 +124,7 @@ fn parse_concatenated(text: &[u8]) -> Result { end: first_hex_value, }); } else if let Some((&b'-', mut text)) = text.split_first() { - let (second_hex_value, hex_digit_count) = consume_hex(&mut text); + let (second_hex_value, hex_digit_count) = consume_hex(&mut text, 6)?; if hex_digit_count > 0 && hex_digit_count <= 6 && text.is_empty() { return Ok(UnicodeRange { start: first_hex_value, @@ -135,19 +135,23 @@ fn parse_concatenated(text: &[u8]) -> Result { Err(()) } -fn consume_hex(text: &mut &[u8]) -> (u32, usize) { +// Consume hex digits, but return an error if more than digit_limit are found. +fn consume_hex(text: &mut &[u8], digit_limit: usize) -> Result<(u32, usize), ()> { let mut value = 0; let mut digits = 0; while let Some((&byte, rest)) = text.split_first() { if let Some(digit_value) = (byte as char).to_digit(16) { + if digits == digit_limit { + return Err(()); + } value = value * 0x10 + digit_value; digits += 1; - *text = rest + *text = rest; } else { break; } } - (value, digits) + Ok((value, digits)) } fn consume_question_marks(text: &mut &[u8]) -> usize { From bb071313db0d23a1cea87d1589e42e4ff388e51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 7 Nov 2024 18:25:09 +0100 Subject: [PATCH 02/21] Fix clippy lints. --- src/cow_rc_str.rs | 28 ++++++++++++++-------------- src/parser.rs | 8 ++++---- src/rules_and_declarations.rs | 4 ++-- src/serializer.rs | 6 +++--- src/tests.rs | 4 ++-- src/tokenizer.rs | 14 +++++--------- 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/cow_rc_str.rs b/src/cow_rc_str.rs index 26508481..03631f47 100644 --- a/src/cow_rc_str.rs +++ b/src/cow_rc_str.rs @@ -51,7 +51,7 @@ impl<'a> From<&'a str> for CowRcStr<'a> { } } -impl<'a> From for CowRcStr<'a> { +impl From for CowRcStr<'_> { #[inline] fn from(s: String) -> Self { CowRcStr::from_rc(Rc::new(s)) @@ -84,7 +84,7 @@ impl<'a> CowRcStr<'a> { } } -impl<'a> Clone for CowRcStr<'a> { +impl Clone for CowRcStr<'_> { #[inline] fn clone(&self) -> Self { match self.unpack() { @@ -99,7 +99,7 @@ impl<'a> Clone for CowRcStr<'a> { } } -impl<'a> Drop for CowRcStr<'a> { +impl Drop for CowRcStr<'_> { #[inline] fn drop(&mut self) { if let Err(ptr) = self.unpack() { @@ -108,7 +108,7 @@ impl<'a> Drop for CowRcStr<'a> { } } -impl<'a> ops::Deref for CowRcStr<'a> { +impl ops::Deref for CowRcStr<'_> { type Target = str; #[inline] @@ -119,65 +119,65 @@ impl<'a> ops::Deref for CowRcStr<'a> { // Boilerplate / trivial impls below. -impl<'a> AsRef for CowRcStr<'a> { +impl AsRef for CowRcStr<'_> { #[inline] fn as_ref(&self) -> &str { self } } -impl<'a> Borrow for CowRcStr<'a> { +impl Borrow for CowRcStr<'_> { #[inline] fn borrow(&self) -> &str { self } } -impl<'a> Default for CowRcStr<'a> { +impl Default for CowRcStr<'_> { #[inline] fn default() -> Self { Self::from("") } } -impl<'a> hash::Hash for CowRcStr<'a> { +impl hash::Hash for CowRcStr<'_> { #[inline] fn hash(&self, hasher: &mut H) { str::hash(self, hasher) } } -impl<'a, T: AsRef> PartialEq for CowRcStr<'a> { +impl> PartialEq for CowRcStr<'_> { #[inline] fn eq(&self, other: &T) -> bool { str::eq(self, other.as_ref()) } } -impl<'a, T: AsRef> PartialOrd for CowRcStr<'a> { +impl> PartialOrd for CowRcStr<'_> { #[inline] fn partial_cmp(&self, other: &T) -> Option { str::partial_cmp(self, other.as_ref()) } } -impl<'a> Eq for CowRcStr<'a> {} +impl Eq for CowRcStr<'_> {} -impl<'a> Ord for CowRcStr<'a> { +impl Ord for CowRcStr<'_> { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { str::cmp(self, other) } } -impl<'a> fmt::Display for CowRcStr<'a> { +impl fmt::Display for CowRcStr<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { str::fmt(self, formatter) } } -impl<'a> fmt::Debug for CowRcStr<'a> { +impl fmt::Debug for CowRcStr<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { str::fmt(self, formatter) diff --git a/src/parser.rs b/src/parser.rs index dd35fc50..0a432912 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -76,7 +76,7 @@ pub enum BasicParseErrorKind<'i> { QualifiedRuleInvalid, } -impl<'i> fmt::Display for BasicParseErrorKind<'i> { +impl fmt::Display for BasicParseErrorKind<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BasicParseErrorKind::UnexpectedToken(token) => { @@ -176,7 +176,7 @@ impl<'i, T> ParseErrorKind<'i, T> { } } -impl<'i, E: fmt::Display> fmt::Display for ParseErrorKind<'i, E> { +impl fmt::Display for ParseErrorKind<'_, E> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ParseErrorKind::Basic(ref basic) => basic.fmt(f), @@ -218,13 +218,13 @@ impl<'i, T> ParseError<'i, T> { } } -impl<'i, E: fmt::Display> fmt::Display for ParseError<'i, E> { +impl fmt::Display for ParseError<'_, E> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.kind.fmt(f) } } -impl<'i, E: fmt::Display + fmt::Debug> std::error::Error for ParseError<'i, E> {} +impl std::error::Error for ParseError<'_, E> {} /// The owned input for a parser. pub struct ParserInput<'i> { diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 48da02b5..188e354e 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -241,7 +241,7 @@ impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { } /// https://drafts.csswg.org/css-syntax/#consume-a-blocks-contents -impl<'i, 't, 'a, I, P, E: 'i> Iterator for RuleBodyParser<'i, 't, 'a, P, I, E> +impl<'i, I, P, E: 'i> Iterator for RuleBodyParser<'i, '_, '_, P, I, E> where P: RuleBodyItemParser<'i, I, E>, { @@ -349,7 +349,7 @@ where } /// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. -impl<'i, 't, 'a, R, P, E: 'i> Iterator for StyleSheetParser<'i, 't, 'a, P> +impl<'i, R, P, E: 'i> Iterator for StyleSheetParser<'i, '_, '_, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> + AtRuleParser<'i, AtRule = R, Error = E>, diff --git a/src/serializer.rs b/src/serializer.rs index 3c6e31cb..5df73954 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -55,7 +55,7 @@ where Ok(()) } -impl<'a> ToCss for Token<'a> { +impl ToCss for Token<'_> { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, @@ -311,7 +311,7 @@ where } } -impl<'a, W> fmt::Write for CssStringWriter<'a, W> +impl fmt::Write for CssStringWriter<'_, W> where W: fmt::Write, { @@ -539,7 +539,7 @@ impl TokenSerializationType { } } -impl<'a> Token<'a> { +impl Token<'_> { /// Categorize a token into a type that determines when `/**/` needs to be inserted /// between two tokens when serialized next to each other without whitespace in between. /// diff --git a/src/tests.rs b/src/tests.rs index 7389664d..ec0fc517 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -773,7 +773,7 @@ where } } -impl<'a> ToJson for CowRcStr<'a> { +impl ToJson for CowRcStr<'_> { fn to_json(&self) -> Value { let s: &str = self; s.to_json() @@ -946,7 +946,7 @@ impl<'i> QualifiedRuleParser<'i> for JsonParser { } } -impl<'i> RuleBodyItemParser<'i, Value, ()> for JsonParser { +impl RuleBodyItemParser<'_, Value, ()> for JsonParser { fn parse_qualified(&self) -> bool { true } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index ea173a5e..2e86c66a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -190,7 +190,7 @@ pub enum Token<'a> { CloseCurlyBracket, } -impl<'a> Token<'a> { +impl Token<'_> { /// Return whether this token represents a parse error. /// /// `BadUrl` and `BadString` are tokenizer-level parse errors. @@ -324,11 +324,11 @@ impl<'a> Tokenizer<'a> { let current = self.position(); let start = self .slice(SourcePosition(0)..current) - .rfind(|c| matches!(c, '\r' | '\n' | '\x0C')) + .rfind(['\r', '\n', '\x0C']) .map_or(0, |start| start + 1); let end = self .slice(current..SourcePosition(self.input.len())) - .find(|c| matches!(c, '\r' | '\n' | '\x0C')) + .find(['\r', '\n', '\x0C']) .map_or(self.input.len(), |end| current.0 + end); self.slice(SourcePosition(start)..SourcePosition(end)) } @@ -720,9 +720,7 @@ fn check_for_source_map<'a>(tokenizer: &mut Tokenizer<'a>, contents: &'a str) { // If there is a source map directive, extract the URL. if contents.starts_with(directive) || contents.starts_with(directive_old) { let contents = &contents[directive.len()..]; - tokenizer.source_map_url = contents - .split(|c| c == ' ' || c == '\t' || c == '\x0C' || c == '\r' || c == '\n') - .next() + tokenizer.source_map_url = contents.split([' ', '\t', '\x0C', '\r', '\n']).next(); } let directive = "# sourceURL="; @@ -731,9 +729,7 @@ fn check_for_source_map<'a>(tokenizer: &mut Tokenizer<'a>, contents: &'a str) { // If there is a source map directive, extract the URL. if contents.starts_with(directive) || contents.starts_with(directive_old) { let contents = &contents[directive.len()..]; - tokenizer.source_url = contents - .split(|c| c == ' ' || c == '\t' || c == '\x0C' || c == '\r' || c == '\n') - .next() + tokenizer.source_url = contents.split([' ', '\t', '\x0C', '\r', '\n']).next() } } From 6fb149bc852a019b08958eac199720404c6b4932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 7 Nov 2024 19:17:56 +0100 Subject: [PATCH 03/21] Bump cssparser_color version. (#395) --- color/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/color/Cargo.toml b/color/Cargo.toml index 47544815..d5cde913 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser-color" -version = "0.1.0" +version = "0.2.0" authors = ["Emilio Cobos Álvarez "] description = "Color implementation based on cssparser" documentation = "https://docs.rs/cssparser-color/" @@ -12,7 +12,7 @@ edition = "2021" path = "lib.rs" [dependencies] -cssparser = { path = ".." } +cssparser = { path = "..", version = "0.34" } serde = { version = "1.0", features = ["derive"], optional = true } [features] From fa6f5eb23f058c6fce444ac781b0b380003fdb59 Mon Sep 17 00:00:00 2001 From: AlaskanEmily <31194934+AlaskanEmily@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:07:38 -0800 Subject: [PATCH 04/21] Minor optimization of Delimiters::from_byte to avoid an extra branch. (#397) Co-authored-by: AlaskanEmily --- src/parser.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 0a432912..aabeea6a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -360,10 +360,8 @@ impl Delimiters { table }; - match byte { - None => Delimiter::None, - Some(b) => TABLE[b as usize], - } + assert_eq!(TABLE[0], Delimiter::None); + TABLE[byte.unwrap_or(0) as usize] } } From 008a91c3cea7da09baf092c9be8b77c4d385141a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 21 Feb 2025 11:30:43 +0100 Subject: [PATCH 05/21] Fix clippy lints (#401) --- src/from_bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/from_bytes.rs b/src/from_bytes.rs index 78a56d3e..7d9d2c76 100644 --- a/src/from_bytes.rs +++ b/src/from_bytes.rs @@ -24,9 +24,9 @@ pub trait EncodingSupport { /// /// * `css_bytes`: A byte string. /// * `protocol_encoding`: The encoding label, if any, defined by HTTP or equivalent protocol. -/// (e.g. via the `charset` parameter of the `Content-Type` header.) +/// (e.g. via the `charset` parameter of the `Content-Type` header.) /// * `environment_encoding`: An optional `Encoding` object for the [environment encoding] -/// (https://drafts.csswg.org/css-syntax/#environment-encoding), if any. +/// (https://drafts.csswg.org/css-syntax/#environment-encoding), if any. /// /// Returns the encoding to use. pub fn stylesheet_encoding( From 5e477ab8195f64ed9b22ab1cf7b08685347f3879 Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Fri, 21 Feb 2025 11:30:02 +0100 Subject: [PATCH 06/21] Add declaration_start parameter to DeclarationParser parse_value (#400) --- src/rules_and_declarations.rs | 6 ++++-- src/tests.rs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 188e354e..bdaef077 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -49,6 +49,7 @@ pub trait DeclarationParser<'i> { &mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>, + _declaration_start: &ParserState, ) -> Result> { Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name)))) } @@ -279,7 +280,7 @@ where error_behavior, |input| { input.expect_colon()?; - parser.parse_value(name, input) + parser.parse_value(name, input, &start) }, ) }; @@ -408,12 +409,13 @@ pub fn parse_one_declaration<'i, 't, P, E>( where P: DeclarationParser<'i, Error = E>, { + let start = input.state(); let start_position = input.position(); input .parse_entirely(|input| { let name = input.expect_ident()?.clone(); input.expect_colon()?; - parser.parse_value(name, input) + parser.parse_value(name, input, &start) }) .map_err(|e| (e, input.slice_from(start_position))) } diff --git a/src/tests.rs b/src/tests.rs index ec0fc517..3c122f0d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -849,6 +849,7 @@ impl<'i> DeclarationParser<'i> for JsonParser { &mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>, + _declaration_start: &ParserState, ) -> Result> { let mut value = vec![]; let mut important = false; From 0d800216303aa6586b1469cb948859fb1dbd6a2f Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Fri, 21 Feb 2025 23:31:11 +1300 Subject: [PATCH 07/21] Implement `MallocSizeOf` for cssparser types (#399) * Implement MallocSizeOf for cssparser types * Fix clippy lints Signed-off-by: Nico Burns --------- Signed-off-by: Nico Burns --- .github/workflows/main.yml | 1 + Cargo.toml | 3 ++- src/serializer.rs | 3 +++ src/tokenizer.rs | 6 ++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b08ddef7..a7be6aec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,6 +22,7 @@ jobs: features: - - --features dummy_match_byte + - --features malloc_size_of include: - toolchain: nightly features: --features bench diff --git a/Cargo.toml b/Cargo.toml index f783f19f..f4e980fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.34.0" +version = "0.34.1" authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" @@ -25,6 +25,7 @@ dtoa-short = "0.3" itoa = "1.0" phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0", features = ["derive"], optional = true } +malloc_size_of = { version = "0.1", default-features = false, optional = true } smallvec = "1.0" [profile.profiling] diff --git a/src/serializer.rs b/src/serializer.rs index 5df73954..6696a622 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -466,6 +466,9 @@ pub enum TokenSerializationType { Other, } +#[cfg(feature = "malloc_size_of")] +malloc_size_of::malloc_size_of_is_0!(TokenSerializationType); + impl TokenSerializationType { /// Return a value that represents the absence of a token, e.g. before the start of the input. #[deprecated( diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 2e86c66a..892d10ae 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -533,6 +533,9 @@ impl<'a> Tokenizer<'a> { #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub struct SourcePosition(pub(crate) usize); +#[cfg(feature = "malloc_size_of")] +malloc_size_of::malloc_size_of_is_0!(SourcePosition); + impl SourcePosition { /// Returns the current byte index in the original input. #[inline] @@ -552,6 +555,9 @@ pub struct SourceLocation { pub column: u32, } +#[cfg(feature = "malloc_size_of")] +malloc_size_of::malloc_size_of_is_0!(SourceLocation); + fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, ()> { if tokenizer.is_eof() { return Err(()); From 958a3f098acb92ddacdce18a7ef2c4a87ac3326f Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Fri, 21 Feb 2025 14:28:55 +0100 Subject: [PATCH 08/21] Derive Default for SourceLocation (#402) --- src/tokenizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 892d10ae..1c85e10a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -545,7 +545,7 @@ impl SourcePosition { } /// The line and column number for a given position within the input. -#[derive(PartialEq, Eq, Debug, Clone, Copy)] +#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)] pub struct SourceLocation { /// The line number, starting at 0 for the first line. pub line: u32, From 3dd4f63639925438d9932dafc0d81395847f9be4 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Fri, 21 Mar 2025 07:11:19 +1300 Subject: [PATCH 09/21] Bump version 0.35.0 (#404) Signed-off-by: Nico Burns --- Cargo.toml | 2 +- color/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f4e980fd..6604f20e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.34.1" +version = "0.35.0" authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" diff --git a/color/Cargo.toml b/color/Cargo.toml index d5cde913..5dd6aac7 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser-color" -version = "0.2.0" +version = "0.3.0" authors = ["Emilio Cobos Álvarez "] description = "Color implementation based on cssparser" documentation = "https://docs.rs/cssparser-color/" @@ -12,7 +12,7 @@ edition = "2021" path = "lib.rs" [dependencies] -cssparser = { path = "..", version = "0.34" } +cssparser = { path = "..", version = "0.35" } serde = { version = "1.0", features = ["derive"], optional = true } [features] From 37fe2cd6276fdeb52951235c213231e1a749cbb6 Mon Sep 17 00:00:00 2001 From: Cheng Xu <3105373+xu-cheng@users.noreply.github.com> Date: Wed, 27 Aug 2025 04:55:28 -0700 Subject: [PATCH 10/21] chore(deps): update phf to 0.13 (#406) * fix: fix clippy warnings * fix: fix invalid rustdoc code block attributes * chore(deps)!: update phf to 0.13 Also raises MSRV to 1.66, as it is required by phf 0.13. --- .github/workflows/main.yml | 2 +- Cargo.toml | 4 ++-- macros/lib.rs | 4 ++-- src/lib.rs | 4 ++-- src/parser.rs | 6 +++--- src/serializer.rs | 2 +- src/tests.rs | 2 +- src/tokenizer.rs | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a7be6aec..d0641a84 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: - nightly - beta - stable - - 1.63.0 + - 1.66.0 features: - - --features dummy_match_byte diff --git a/Cargo.toml b/Cargo.toml index 6604f20e..90e0b573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" keywords = ["css", "syntax", "parser"] license = "MPL-2.0" edition = "2018" -rust-version = "1.63" +rust-version = "1.66" exclude = ["src/css-parsing-tests/**", "src/big-data-url.css"] @@ -23,7 +23,7 @@ encoding_rs = "0.8" cssparser-macros = { path = "./macros", version = "0.6.1" } dtoa-short = "0.3" itoa = "1.0" -phf = { version = "0.11.2", features = ["macros"] } +phf = { version = "0.13.1", features = ["macros"] } serde = { version = "1.0", features = ["derive"], optional = true } malloc_size_of = { version = "0.1", default-features = false, optional = true } smallvec = "1.0" diff --git a/macros/lib.rs b/macros/lib.rs index 8b40bd4c..dc7b36e3 100644 --- a/macros/lib.rs +++ b/macros/lib.rs @@ -162,7 +162,7 @@ pub fn match_byte(input: TokenStream) -> TokenStream { for (i, ref arm) in arms.iter().enumerate() { let case_id = i + 1; let index = case_id as isize; - let name = syn::Ident::new(&format!("Case{}", case_id), arm.span()); + let name = syn::Ident::new(&format!("Case{case_id}"), arm.span()); let pat = &arm.pat; parse_pat_to_table(pat, case_id as u8, &mut wildcard, &mut table); @@ -177,7 +177,7 @@ pub fn match_byte(input: TokenStream) -> TokenStream { let mut table_content = Vec::new(); for entry in table.iter() { - let name: syn::Path = syn::parse_str(&format!("Case::Case{}", entry)).unwrap(); + let name: syn::Path = syn::parse_str(&format!("Case::Case{entry}")).unwrap(); table_content.push(name); } let table = quote::quote!(static __CASES: [Case; 256] = [#(#table_content),*];); diff --git a/src/lib.rs b/src/lib.rs index dc44fb74..a7460a43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ As a consequence, when calling another parsing function, either: Examples: -```{rust,ignore} +```rust,ignore // 'none' | fn parse_background_image(context: &ParserContext, input: &mut Parser) -> Result, ()> { @@ -53,7 +53,7 @@ fn parse_background_image(context: &ParserContext, input: &mut Parser) } ``` -```{rust,ignore} +```rust,ignore // [ | ] [ | ]? fn parse_border_spacing(_context: &ParserContext, input: &mut Parser) -> Result<(LengthOrPercentage, LengthOrPercentage), ()> { diff --git a/src/parser.rs b/src/parser.rs index aabeea6a..82ec4b00 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -80,11 +80,11 @@ impl fmt::Display for BasicParseErrorKind<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BasicParseErrorKind::UnexpectedToken(token) => { - write!(f, "unexpected token: {:?}", token) + write!(f, "unexpected token: {token:?}") } BasicParseErrorKind::EndOfInput => write!(f, "unexpected end of input"), BasicParseErrorKind::AtRuleInvalid(rule) => { - write!(f, "invalid @ rule encountered: '@{}'", rule) + write!(f, "invalid @ rule encountered: '@{rule}'") } BasicParseErrorKind::AtRuleBodyInvalid => write!(f, "invalid @ rule body encountered"), BasicParseErrorKind::QualifiedRuleInvalid => { @@ -295,7 +295,7 @@ impl BlockType { /// /// The union of two sets can be obtained with the `|` operator. Example: /// -/// ```{rust,ignore} +/// ```rust,ignore /// input.parse_until_before(Delimiter::CurlyBracketBlock | Delimiter::Semicolon) /// ``` #[derive(Copy, Clone, PartialEq, Eq, Debug)] diff --git a/src/serializer.rs b/src/serializer.rs index 6696a622..ed325fc9 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -286,7 +286,7 @@ where /// /// Typical usage: /// -/// ```{rust,ignore} +/// ```rust,ignore /// fn write_foo(foo: &Foo, dest: &mut W) -> fmt::Result where W: fmt::Write { /// dest.write_str("\"")?; /// { diff --git a/src/tests.rs b/src/tests.rs index 3c122f0d..d7b4833e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -794,7 +794,7 @@ fn delimiter_from_byte(b: &mut Bencher) { } #[cfg(feature = "bench")] -const BACKGROUND_IMAGE: &'static str = include_str!("big-data-url.css"); +const BACKGROUND_IMAGE: &str = include_str!("big-data-url.css"); #[cfg(feature = "bench")] #[bench] diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 1c85e10a..1e2bb737 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -229,7 +229,7 @@ enum SeenStatus { impl<'a> Tokenizer<'a> { #[inline] - pub fn new(input: &str) -> Tokenizer { + pub fn new(input: &'a str) -> Self { Tokenizer { input, position: 0, From 03f982ed70ce1de67bef02de63b2e11e08a9e111 Mon Sep 17 00:00:00 2001 From: noriaki watanabe Date: Fri, 24 Oct 2025 17:46:09 +0900 Subject: [PATCH 11/21] Fix is_ident_start to accept immutable reference (#409) --- src/tokenizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 1e2bb737..68c1b10f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -891,7 +891,7 @@ fn consume_quoted_string<'a>( } #[inline] -fn is_ident_start(tokenizer: &mut Tokenizer) -> bool { +fn is_ident_start(tokenizer: &Tokenizer) -> bool { !tokenizer.is_eof() && match_byte! { tokenizer.next_byte_unchecked(), b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'\0' => true, From 71b7cfe6f1cd85427ca905a41be31ca9f6af29a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 25 Oct 2025 10:50:34 +0200 Subject: [PATCH 12/21] Bug 1996318 - Add display-p3-linear color-space to css color(). (#411) * Bug 1996318 - Add display-p3-linear color-space to css color(). r=#style,tlouw It's display-p3 without gamma encoding. cssparser change will obviously go upstream. Differential Revision: https://phabricator.services.mozilla.com/D270012 * Bump MSRV to 1.68 to deal with syn. --- .github/workflows/main.yml | 2 +- Cargo.toml | 2 +- src/color.rs | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d0641a84..3f669833 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: - nightly - beta - stable - - 1.66.0 + - 1.68.0 features: - - --features dummy_match_byte diff --git a/Cargo.toml b/Cargo.toml index 90e0b573..35b91a60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" keywords = ["css", "syntax", "parser"] license = "MPL-2.0" edition = "2018" -rust-version = "1.66" +rust-version = "1.68" exclude = ["src/css-parsing-tests/**", "src/big-data-url.css"] diff --git a/src/color.rs b/src/color.rs index 978936e0..472c6478 100644 --- a/src/color.rs +++ b/src/color.rs @@ -85,6 +85,8 @@ pub enum PredefinedColorSpace { SrgbLinear, /// DisplayP3, + /// + DisplayP3Linear, /// A98Rgb, /// @@ -107,6 +109,7 @@ impl PredefinedColorSpace { "srgb" => Self::Srgb, "srgb-linear" => Self::SrgbLinear, "display-p3" => Self::DisplayP3, + "display-p3-linear" => Self::DisplayP3Linear, "a98-rgb" => Self::A98Rgb, "prophoto-rgb" => Self::ProphotoRgb, "rec2020" => Self::Rec2020, @@ -126,6 +129,7 @@ impl ToCss for PredefinedColorSpace { Self::Srgb => "srgb", Self::SrgbLinear => "srgb-linear", Self::DisplayP3 => "display-p3", + Self::DisplayP3Linear => "display-p3-linear", Self::A98Rgb => "a98-rgb", Self::ProphotoRgb => "prophoto-rgb", Self::Rec2020 => "rec2020", From 11d2d01eb0ca343c927add8f99d6c7f7653d8025 Mon Sep 17 00:00:00 2001 From: noriaki watanabe Date: Thu, 30 Oct 2025 20:17:53 +0900 Subject: [PATCH 13/21] Fix outdated parser names in comments (#412) * Correct outdated references from RuleListParser to StyleSheetParser in comments * Correct outdated references from DeclarationListParser to RuleBodyParser in comments --- src/rules_and_declarations.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index bdaef077..7f268c59 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -31,7 +31,7 @@ pub trait DeclarationParser<'i> { /// Parse the value of a declaration with the given `name`. /// /// Return the finished representation for the declaration - /// as returned by `DeclarationListParser::next`, + /// as returned by `RuleBodyParser::next`, /// or an `Err(..)` to ignore the entire declaration as invalid. /// /// Declaration name matching should be case-insensitive in the ASCII range. @@ -63,7 +63,7 @@ pub trait DeclarationParser<'i> { /// /// Default implementations that reject all at-rules are provided, /// so that `impl AtRuleParser<(), ()> for ... {}` can be used -/// for using `DeclarationListParser` to parse a declarations list with only qualified rules. +/// for using `RuleBodyParser` to parse a declarations list with only qualified rules. pub trait AtRuleParser<'i> { /// The intermediate representation of prelude of an at-rule. type Prelude; @@ -121,7 +121,7 @@ pub trait AtRuleParser<'i> { /// The location passed in is source location of the start of the prelude. /// /// Return the finished representation of the at-rule - /// as returned by `RuleListParser::next` or `DeclarationListParser::next`, + /// as returned by `StyleSheetParser::next` or `RuleBodyParser::next`, /// or an `Err(..)` to ignore the entire at-rule as invalid. /// /// This is only called when `parse_prelude` returned `WithBlock`, and a block @@ -146,7 +146,7 @@ pub trait AtRuleParser<'i> { /// /// Default implementations that reject all qualified rules are provided, so that /// `impl QualifiedRuleParser<(), ()> for ... {}` can be used for example for using -/// `RuleListParser` to parse a rule list with only at-rules (such as inside +/// `StyleSheetParser` to parse a rule list with only at-rules (such as inside /// `@font-feature-values`). pub trait QualifiedRuleParser<'i> { /// The intermediate representation of a qualified rule prelude. @@ -179,7 +179,7 @@ pub trait QualifiedRuleParser<'i> { /// The location passed in is source location of the start of the prelude. /// /// Return the finished representation of the qualified rule - /// as returned by `RuleListParser::next`, + /// as returned by `StyleSheetParser::next`, /// or an `Err(..)` to ignore the entire at-rule as invalid. fn parse_block<'t>( &mut self, @@ -197,7 +197,7 @@ pub trait QualifiedRuleParser<'i> { pub struct RuleBodyParser<'i, 't, 'a, P, I, E> { /// The input given to the parser. pub input: &'a mut Parser<'i, 't>, - /// The parser given to `DeclarationListParser::new` + /// The parser given to `RuleBodyParser::new` pub parser: &'a mut P, _phantom: std::marker::PhantomData<(I, E)>, @@ -218,7 +218,7 @@ pub trait RuleBodyItemParser<'i, DeclOrRule, Error: 'i>: } impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { - /// Create a new `DeclarationListParser` for the given `input` and `parser`. + /// Create a new `RuleBodyParser` for the given `input` and `parser`. /// /// Note that all CSS declaration lists can on principle contain at-rules. /// Even if no such valid at-rule exists (yet), @@ -230,7 +230,7 @@ impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { /// since `AtRuleParser` provides default implementations of its methods. /// /// The return type for finished declarations and at-rules also needs to be the same, - /// since `::next` can return either. + /// since `::next` can return either. /// It could be a custom enum. pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self { Self { @@ -339,7 +339,7 @@ where /// implementations of their methods. /// /// The return type for finished qualified rules and at-rules also needs to be the same, - /// since `::next` can return either. It could be a custom enum. + /// since `::next` can return either. It could be a custom enum. pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self { Self { input, @@ -349,7 +349,7 @@ where } } -/// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. +/// `StyleSheetParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. impl<'i, R, P, E: 'i> Iterator for StyleSheetParser<'i, '_, '_, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> From 88c9a12dac33c9158586f76c34a4c0504a57ba7d Mon Sep 17 00:00:00 2001 From: noriaki watanabe Date: Mon, 3 Nov 2025 19:45:52 +0900 Subject: [PATCH 14/21] Remove temporary binding and return match result directly in almost_equals (#413) --- src/tests.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tests.rs b/src/tests.rs index d7b4833e..339b5ac3 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -24,7 +24,7 @@ macro_rules! JArray { } fn almost_equals(a: &Value, b: &Value) -> bool { - let var_name = match (a, b) { + match (a, b) { (Value::Number(a), Value::Number(b)) => { let a = a.as_f64().unwrap(); let b = b.as_f64().unwrap(); @@ -39,8 +39,7 @@ fn almost_equals(a: &Value, b: &Value) -> bool { (&Value::Object(_), &Value::Object(_)) => panic!("Not implemented"), (&Value::Null, &Value::Null) => true, _ => false, - }; - var_name + } } fn normalize(json: &mut Value) { From 60d05a59d0c88121252bf2b4b2055bc7169ac9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Kj=C3=A4ll?= Date: Sat, 8 Nov 2025 12:12:13 +0100 Subject: [PATCH 15/21] disable the 'size_of_tests' on 32-bit architectures (#408) --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a7460a43..0ae55f12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ mod parser; mod serializer; mod unicode_range; -#[cfg(test)] +#[cfg(all(test,target_pointer_width = "64"))] mod size_of_tests; #[cfg(test)] mod tests; From 1de9d4a0e14b18c72c9941907e69378e0cb6d3e8 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Mon, 10 Nov 2025 14:31:30 +0900 Subject: [PATCH 16/21] Fix lints (#418) --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0ae55f12..3968eea0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ mod parser; mod serializer; mod unicode_range; -#[cfg(all(test,target_pointer_width = "64"))] +#[cfg(all(test, target_pointer_width = "64"))] mod size_of_tests; #[cfg(test)] mod tests; From ff98fbe886e1f527b28b4ed42a146d8991b0608c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 10 Nov 2025 14:39:10 +0900 Subject: [PATCH 17/21] parser: Make list of arbitrary substitution functions configurable. (#416) This prevents needing cssparser changes every time we add one, and makes them configurable at runtime. --- src/parser.rs | 26 +++++++++++++++++--------- src/size_of_tests.rs | 4 ++-- src/tests.rs | 14 +++++++++++--- src/tokenizer.rs | 31 +++++++++++++++++-------------- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 82ec4b00..a7cab1f2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -380,6 +380,10 @@ macro_rules! expect { } } +/// A list of arbitrary substitution functions. Should be lowercase ascii. +/// See https://drafts.csswg.org/css-values-5/#arbitrary-substitution +pub type ArbitrarySubstitutionFunctions<'a> = &'a [&'static str]; + impl<'i: 't, 't> Parser<'i, 't> { /// Create a new parser #[inline] @@ -546,19 +550,23 @@ impl<'i: 't, 't> Parser<'i, 't> { self.at_start_of = state.at_start_of; } - /// Start looking for `var()` / `env()` functions. (See the - /// `.seen_var_or_env_functions()` method.) + /// Start looking for arbitrary substitution functions like `var()` / `env()` functions. + /// (See the `.seen_arbitrary_substitution_functions()` method.) #[inline] - pub fn look_for_var_or_env_functions(&mut self) { - self.input.tokenizer.look_for_var_or_env_functions() + pub fn look_for_arbitrary_substitution_functions( + &mut self, + fns: ArbitrarySubstitutionFunctions<'i>, + ) { + self.input + .tokenizer + .look_for_arbitrary_substitution_functions(fns) } - /// Return whether a `var()` or `env()` function has been seen by the - /// tokenizer since either `look_for_var_or_env_functions` was called, and - /// stop looking. + /// Return whether a relevant function has been seen by the tokenizer since + /// `look_for_arbitrary_substitution_functions` was called, and stop looking. #[inline] - pub fn seen_var_or_env_functions(&mut self) -> bool { - self.input.tokenizer.seen_var_or_env_functions() + pub fn seen_arbitrary_substitution_functions(&mut self) -> bool { + self.input.tokenizer.seen_arbitrary_substitution_functions() } /// The old name of `try_parse`, which requires raw identifiers in the Rust 2018 edition. diff --git a/src/size_of_tests.rs b/src/size_of_tests.rs index edd2b439..7f4b85fa 100644 --- a/src/size_of_tests.rs +++ b/src/size_of_tests.rs @@ -42,8 +42,8 @@ size_of_test!(token, Token, 32); size_of_test!(std_cow_str, std::borrow::Cow<'static, str>, 24, 32); size_of_test!(cow_rc_str, CowRcStr, 16); -size_of_test!(tokenizer, crate::tokenizer::Tokenizer, 72); -size_of_test!(parser_input, crate::parser::ParserInput, 136); +size_of_test!(tokenizer, crate::tokenizer::Tokenizer, 96); +size_of_test!(parser_input, crate::parser::ParserInput, 160); size_of_test!(parser, crate::parser::Parser, 16); size_of_test!(source_position, crate::SourcePosition, 8); size_of_test!(parser_state, crate::ParserState, 24); diff --git a/src/tests.rs b/src/tests.rs index 339b5ac3..0c2acbe8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -7,6 +7,9 @@ extern crate test; use serde_json::{json, Map, Value}; +#[cfg(feature = "bench")] +use crate::parser::ArbitrarySubstitutionFunctions; + #[cfg(feature = "bench")] use self::test::Bencher; @@ -795,20 +798,25 @@ fn delimiter_from_byte(b: &mut Bencher) { #[cfg(feature = "bench")] const BACKGROUND_IMAGE: &str = include_str!("big-data-url.css"); +#[cfg(feature = "bench")] +const ARBITRARY_SUBSTITUTION_FUNCTIONS: ArbitrarySubstitutionFunctions = &["var", "env"]; + #[cfg(feature = "bench")] #[bench] fn unquoted_url(b: &mut Bencher) { b.iter(|| { let mut input = ParserInput::new(BACKGROUND_IMAGE); let mut input = Parser::new(&mut input); - input.look_for_var_or_env_functions(); + input.look_for_arbitrary_substitution_functions(ARBITRARY_SUBSTITUTION_FUNCTIONS); let result = input.try_parse(|input| input.expect_url()); assert!(result.is_ok()); - input.seen_var_or_env_functions(); - (result.is_ok(), input.seen_var_or_env_functions()) + ( + result.is_ok(), + input.seen_arbitrary_substitution_functions(), + ) }) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 68c1b10f..65562766 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -6,7 +6,7 @@ use self::Token::*; use crate::cow_rc_str::CowRcStr; -use crate::parser::ParserState; +use crate::parser::{ArbitrarySubstitutionFunctions, ParserState}; use std::char; use std::ops::Range; @@ -215,15 +215,15 @@ pub struct Tokenizer<'a> { /// of UTF-16 characters. current_line_start_position: usize, current_line_number: u32, - var_or_env_functions: SeenStatus, + arbitrary_substitution_functions: SeenStatus<'a>, source_map_url: Option<&'a str>, source_url: Option<&'a str>, } #[derive(Copy, Clone, PartialEq, Eq)] -enum SeenStatus { +enum SeenStatus<'a> { DontCare, - LookingForThem, + LookingForThem(ArbitrarySubstitutionFunctions<'a>), SeenAtLeastOne, } @@ -235,30 +235,33 @@ impl<'a> Tokenizer<'a> { position: 0, current_line_start_position: 0, current_line_number: 0, - var_or_env_functions: SeenStatus::DontCare, + arbitrary_substitution_functions: SeenStatus::DontCare, source_map_url: None, source_url: None, } } #[inline] - pub fn look_for_var_or_env_functions(&mut self) { - self.var_or_env_functions = SeenStatus::LookingForThem; + pub fn look_for_arbitrary_substitution_functions( + &mut self, + fns: ArbitrarySubstitutionFunctions<'a>, + ) { + self.arbitrary_substitution_functions = SeenStatus::LookingForThem(fns); } #[inline] - pub fn seen_var_or_env_functions(&mut self) -> bool { - let seen = self.var_or_env_functions == SeenStatus::SeenAtLeastOne; - self.var_or_env_functions = SeenStatus::DontCare; + pub fn seen_arbitrary_substitution_functions(&mut self) -> bool { + let seen = self.arbitrary_substitution_functions == SeenStatus::SeenAtLeastOne; + self.arbitrary_substitution_functions = SeenStatus::DontCare; seen } #[inline] pub fn see_function(&mut self, name: &str) { - if self.var_or_env_functions == SeenStatus::LookingForThem - && (name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env")) - { - self.var_or_env_functions = SeenStatus::SeenAtLeastOne; + if let SeenStatus::LookingForThem(fns) = self.arbitrary_substitution_functions { + if fns.iter().any(|a| name.eq_ignore_ascii_case(a)) { + self.arbitrary_substitution_functions = SeenStatus::SeenAtLeastOne; + } } } From 9339ddd71443463dfb85d1185ad50cd98b34bc8f Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Mon, 10 Nov 2025 14:53:33 +0900 Subject: [PATCH 18/21] Bump version 0.36.0 (#417) Signed-off-by: Oriol Brufau --- Cargo.toml | 2 +- color/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 35b91a60..41b6bb53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.35.0" +version = "0.36.0" authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" diff --git a/color/Cargo.toml b/color/Cargo.toml index 5dd6aac7..48a539f0 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser-color" -version = "0.3.0" +version = "0.4.0" authors = ["Emilio Cobos Álvarez "] description = "Color implementation based on cssparser" documentation = "https://docs.rs/cssparser-color/" @@ -12,7 +12,7 @@ edition = "2021" path = "lib.rs" [dependencies] -cssparser = { path = "..", version = "0.35" } +cssparser = { path = "..", version = "0.36" } serde = { version = "1.0", features = ["derive"], optional = true } [features] From 6ccdec4d3c7e32b0f2bfc95e43c41b033bb3fcaa Mon Sep 17 00:00:00 2001 From: noriaki watanabe Date: Sat, 29 Nov 2025 20:32:24 +0900 Subject: [PATCH 19/21] Remove duplicated test case from declaration_list.json (#414) --- src/css-parsing-tests/declaration_list.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/css-parsing-tests/declaration_list.json b/src/css-parsing-tests/declaration_list.json index abd23042..cafc3c1b 100644 --- a/src/css-parsing-tests/declaration_list.json +++ b/src/css-parsing-tests/declaration_list.json @@ -51,8 +51,6 @@ ], ["declaration", "a", [["ident", "b"]], false], ["at-rule", "media", [" ", ["ident", "print"]], [["ident", "div"], ["{}"]]] -], - -"", [] +] ] From 452e6affea4652ff4b480dae57e250393dfe8bdc Mon Sep 17 00:00:00 2001 From: edenw97 <75601835+edenw97@users.noreply.github.com> Date: Sat, 29 Nov 2025 03:40:24 -0800 Subject: [PATCH 20/21] fix issue that integer 1000001 is written as 1000000 (#410) * fix issue that interger 1000001 is written as 1000000 * lint --------- Co-authored-by: Yidong Wei --- src/css-parsing-tests/component_value_list.json | 5 +++-- src/serializer.rs | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/css-parsing-tests/component_value_list.json b/src/css-parsing-tests/component_value_list.json index ed26e125..6d4bcddd 100644 --- a/src/css-parsing-tests/component_value_list.json +++ b/src/css-parsing-tests/component_value_list.json @@ -223,7 +223,7 @@ ["error", "bad-url"] ], -"12 +34 -45 .67 +.89 -.01 2.3 +45.0 -0.67", [ +"12 +34 -45 .67 +.89 -.01 2.3 +45.0 -0.67 1000001", [ ["number", "12", 12, "integer"], " ", ["number", "+34", 34, "integer"], " ", ["number", "-45", -45, "integer"], " ", @@ -232,7 +232,8 @@ ["number", "-0.01", -0.01, "number"], " ", ["number", "2.3", 2.3, "number"], " ", ["number", "+45.0", 45, "number"], " ", - ["number", "-0.67", -0.67, "number"] + ["number", "-0.67", -0.67, "number"], " ", + ["number", "1000001", 1000001, "integer"] ], "12e2 +34e+1 -45E-0 .68e+3 +.79e-1 -.01E2 2.3E+1 +45.0e6 -0.67e0", [ diff --git a/src/serializer.rs b/src/serializer.rs index ed325fc9..78175925 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -44,6 +44,12 @@ where decimal_point: false, scientific: false, } + } else if let Some(int_val) = int_value { + write!(dest, "{}", int_val)?; + Notation { + decimal_point: false, + scientific: false, + } } else { dtoa_short::write(dest, value)? }; From 6967a85e06c4f92d91ccab9ccfcfc359f6469b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 29 Nov 2025 17:14:06 +0100 Subject: [PATCH 21/21] serializer: Simplify a bit number serialization. (#421) Follow-up to #410 --- src/serializer.rs | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/serializer.rs b/src/serializer.rs index 78175925..ca6eda81 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::match_byte; -use dtoa_short::Notation; use std::fmt::{self, Write}; use std::str; @@ -32,30 +31,21 @@ fn write_numeric(value: f32, int_value: Option, has_sign: bool, dest: &m where W: fmt::Write, { - // `value.value >= 0` is true for negative 0. - if has_sign && value.is_sign_positive() { + if value == 0.0 && value.is_sign_negative() { + // Negative zero. Work around #20596. + return dest.write_str("-0"); + } + // NOTE: `value.value >= 0` is true for negative 0 but we've dealt with it above. + if has_sign && value >= 0.0 { dest.write_str("+")?; } - let notation = if value == 0.0 && value.is_sign_negative() { - // Negative zero. Work around #20596. - dest.write_str("-0")?; - Notation { - decimal_point: false, - scientific: false, - } - } else if let Some(int_val) = int_value { - write!(dest, "{}", int_val)?; - Notation { - decimal_point: false, - scientific: false, - } - } else { - dtoa_short::write(dest, value)? - }; + if let Some(v) = int_value { + return write!(dest, "{}", v); + } - if int_value.is_none() && value.fract() == 0. && !notation.decimal_point && !notation.scientific - { + let notation = dtoa_short::write(dest, value)?; + if value.fract() == 0. && !notation.decimal_point && !notation.scientific { dest.write_str(".0")?; } Ok(())