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
"
},
+ {
+ "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 | | 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