.css() splits a declaration when a data URI's semicolon is followed by a colon
.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 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 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.
Source: cheeriojs/cheerio