diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b866d44..df65d1fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ - Update `selectors` to `0.25`. - Bump MSRV to `1.65`. +### Fixed + +- Replace double quotes in all property values. + +### Performance + +- Avoid allocation when replacing double quotes in property values. + ## [0.11.0] - 2023-09-26 ### Added diff --git a/benchmarks/benchmarks.json b/benchmarks/benchmarks.json index 21b32243..267d0e68 100644 --- a/benchmarks/benchmarks.json +++ b/benchmarks/benchmarks.json @@ -7,6 +7,10 @@ "name": "merging", "html": "

Big Text

Solid

Foot notes

" }, + { + "name": "double_quotes", + "html": "

Big Text

Big Text

Big Text

Big Text

Big Text

Big Text

Big Text

Big Text

Big Text

" + }, { "name": "big_email_1", "html": "Newsletter
\"\"
\"\"
\"Thanks
\"\"
\"\"
Dear peter,
You are scheduled for a Genius Bar appointment.
Topic: iPhone
Date: Wednesday, Aug 26, 2009
Time: 11:10AM
Location: Apple Store, Regent Street
Apple Store,
Regent Street
If you are no longer able to attend this session, please cancel or reschedule your reservation.

We look forward to seeing you.
Your Apple Store team,
Regent Street
\"\"
\"\"
TM and copyright © 2008 Apple Inc. 1 Infinite Loop, MS 303-3DM, Cupertino, CA 95014.
" diff --git a/bindings/python/CHANGELOG.md b/bindings/python/CHANGELOG.md index 6914d6f1..018420ec 100644 --- a/bindings/python/CHANGELOG.md +++ b/bindings/python/CHANGELOG.md @@ -9,6 +9,14 @@ - Update `selectors` to `0.25`. - Bump MSRV to `1.65`. +### Fixed + +- Replace double quotes in all property values. + +### Performance + +- Avoid allocation when replacing double quotes in property values. + ## [0.11.0] - 2023-09-26 ### Added diff --git a/bindings/ruby/CHANGELOG.md b/bindings/ruby/CHANGELOG.md index 1af77a60..e17eaabe 100644 --- a/bindings/ruby/CHANGELOG.md +++ b/bindings/ruby/CHANGELOG.md @@ -9,6 +9,14 @@ - Update `selectors` to `0.25`. - Bump MSRV to `1.65`. +### Fixed + +- Replace double quotes in all property values. + +### Performance + +- Avoid allocation when replacing double quotes in property values. + ## [0.11.0] - 2023-09-26 ### Added diff --git a/bindings/wasm/CHANGELOG.md b/bindings/wasm/CHANGELOG.md index d91fcee0..af0868db 100644 --- a/bindings/wasm/CHANGELOG.md +++ b/bindings/wasm/CHANGELOG.md @@ -2,10 +2,6 @@ ## [Unreleased] -### Fixed - -- `getrandom` dependency features. - ### Changed - Update `indexmap` to `2.1`. @@ -13,6 +9,15 @@ - Update `selectors` to `0.25`. - Bump MSRV to `1.65`. +### Fixed + +- `getrandom` dependency features. +- Replace double quotes in all property values. + +### Performance + +- Avoid allocation when replacing double quotes in property values. + ## [0.11.0] - 2023-09-26 ### Added diff --git a/css-inline/src/html/serializer.rs b/css-inline/src/html/serializer.rs index 24b3e2e2..87a93556 100644 --- a/css-inline/src/html/serializer.rs +++ b/css-inline/src/html/serializer.rs @@ -366,20 +366,7 @@ impl<'a, W: Write> HtmlSerializer<'a, W> { Ok(()) } } -/// Replace double quotes in property values. -/// -/// This implementation is deliberately simplistic and covers only `font-family`, but escaping -/// might be needed in other properties that accept strings. -macro_rules! replace_double_quotes { - ($writer:expr, $name:expr, $value:expr) => { - // Avoid allocation if there is no double quote in the input string - if $name.starts_with("font-family") && $value.as_bytes().contains(&b'"') { - $writer.write_all(&$value.replace('"', "\'").as_bytes())? - } else { - $writer.write_all($value.as_bytes())? - }; - }; -} + const STYLE_SEPARATOR: &[u8] = b": "; #[inline] @@ -390,7 +377,29 @@ fn write_declaration( ) -> Result<(), InlineError> { writer.write_all(name.as_bytes())?; writer.write_all(STYLE_SEPARATOR)?; - replace_double_quotes!(writer, name, value.trim()); + let value = value.trim(); + if value.as_bytes().contains(&b'"') { + // Roughly based on `str::replace` + let mut last_end = 0; + for (start, part) in value.match_indices('"') { + writer.write_all( + value + .get(last_end..start) + .expect("Invalid substring") + .as_bytes(), + )?; + writer.write_all(b"'")?; + last_end = start.checked_add(part.len()).expect("Size overflow"); + } + writer.write_all( + value + .get(last_end..value.len()) + .expect("Invalid substring") + .as_bytes(), + )?; + } else { + writer.write_all(value.as_bytes())?; + }; Ok(()) } diff --git a/css-inline/tests/test_inlining.rs b/css-inline/tests/test_inlining.rs index e35fcc69..18072a0a 100644 --- a/css-inline/tests/test_inlining.rs +++ b/css-inline/tests/test_inlining.rs @@ -223,6 +223,17 @@ fn font_family_quoted() { ) } +#[test] +fn other_property_quoted() { + // When property value contains double quotes + assert_inlined!( + style = r#"h1 { --bs-font-sant-serif: system-ui,-applie-system,"helvetica neue"; }"#, + body = r#"

Hello world!

"#, + // Then it should be replaced with single quotes + expected = r#"

Hello world!

"# + ) +} + #[test] fn href_attribute_unchanged() { // All HTML attributes should be serialized as is