.css() still splits a declaration when the part after a semicolon contains a colon, and rewriting an unrelated property then corrupts the style attribute.
Tested on cheerio 1.2.0 (and current main).
Reproduction
const cheerio = require('cheerio');
const style = `background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>")`;
const $ = cheerio.load(`<div style='${style}'></div>`);
const $div = $('div');
console.log($div.css());
$div.css('color', 'red');
console.log($div.attr('style'));
Expected
{ background: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>")` }
// background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>"); color: red;
Actual
{
background: 'url("data:image/svg+xml',
"utf8,<svg xmlns='http": "//www.w3.org/2000/svg'/>\")"
}
// background: url("data:image/svg+xml; utf8,<svg xmlns='http: //www.w3.org/2000/svg'/>"); color: red;
Two separate problems:
- The declaration is split at the
; inside the data URI, producing a bogus second property.
- Because
.css(prop, val) re-serialises everything it parsed, setting an unrelated property writes the mangled value back to the element. The URL now contains ; and : , so the image silently stops loading. Nothing in the caller's code touched background.
Why the #1134 fix doesn't cover this
#1134 (and #907 before it) fixed the same split for url(data:image/png;base64,…). The fix keeps a fragment that has no colon as a continuation of the previous value, which works for base64,…. But a data URI whose text after the ; does contain a colon takes the other branch and becomes a new property. xmlns='http://www.w3.org/2000/svg' is the common case here, since inline SVG data URIs almost always carry that attribute.
The same applies to any quoted value holding a ; and a ::
cheerio.load(`<li style="content: 'a;b:c'">`)('li').css();
// { content: "'a", b: "c'" } expected: { content: "'a;b:c'" }
Per CSS syntax, a ; or : inside a string or inside parentheses is part of the value and does not separate declarations. Checking the same inputs through a spec-compliant parser (postcss) returns a single declaration in each case.
Fix
Skipping over quoted sections and parentheses when scanning for the separating ; and : handles all of these, and subsumes the base64 case that motivated #1134 rather than special-casing it. Happy to open a PR — I have the change and tests ready.
.css()still splits a declaration when the part after a semicolon contains a colon, and rewriting an unrelated property then corrupts thestyleattribute.Tested on cheerio 1.2.0 (and current
main).Reproduction
Expected
Actual
Two separate problems:
;inside the data URI, producing a bogus second property..css(prop, val)re-serialises everything it parsed, setting an unrelated property writes the mangled value back to the element. The URL now contains;and:, so the image silently stops loading. Nothing in the caller's code touchedbackground.Why the #1134 fix doesn't cover this
#1134 (and #907 before it) fixed the same split for
url(data:image/png;base64,…). The fix keeps a fragment that has no colon as a continuation of the previous value, which works forbase64,…. But a data URI whose text after the;does contain a colon takes the other branch and becomes a new property.xmlns='http://www.w3.org/2000/svg'is the common case here, since inline SVG data URIs almost always carry that attribute.The same applies to any quoted value holding a
;and a::Per CSS syntax, a
;or:inside a string or inside parentheses is part of the value and does not separate declarations. Checking the same inputs through a spec-compliant parser (postcss) returns a single declaration in each case.Fix
Skipping over quoted sections and parentheses when scanning for the separating
;and:handles all of these, and subsumes thebase64case that motivated #1134 rather than special-casing it. Happy to open a PR — I have the change and tests ready.