Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Firefox CSP foreignObject doesn't render
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Content-Security-Policy" content="style-src 'self' 'nonce-foobar==';">
<!-- Replace CSP with next line to make svg render correctly -->
<!--<meta http-equiv="Content-Security-Policy" content="style-src 'self' 'unsafe-inline';">-->
<title>Firefox foreignObject CSP</title>
<style nonce="foobar==">
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#dst, #expected {
border: 1px solid black;
}
#text {
display: block;
width: 100%;
height: 200px;
}
</style>
</head>
<body>
<h4>Svg source:</h4>
<textarea id="text" readonly></textarea>
<h4>Svg image:</h4>
<img id="dst">
<h4>Expected:</h4>
<img id="expected">
<h4>Actual (Firefox 52.0.2):</h4>
onload fired. Image is blank. CSP errors reported to console.
<script>
document.body.onload = async () => {
try {
await main();
} catch (e) {
console.error(e);
}
};
async function main() {
const image = document.querySelector('#image'),
imageUrl = image.textContent.trim(),
{width, height} = image.dataset;
const expected = document.querySelector('#expected');
expected.style.width = width;
expected.style.height = height;
expected.src = imageUrl;
let html;
if (location.search == '?style') {
html = `
<div xmlns="http://www.w3.org/1999/xhtml">
<style nonce="foobar==">
.x1 { position: relative; }
.x2 { z-index: 10; background: url(${imageUrl}), white; width: ${width}px; height: ${height}px; }
</style>
<div class="x1"><div class="x2"></div></div>
</div>
`.replace(/^\n/, '');
} else {
html = `
<div xmlns="http://www.w3.org/1999/xhtml" style="position: relative;">
<div style="z-index: 10; background: url(${imageUrl}), white; width: ${width}px; height: ${height}px;"></div>
</div>
`.replace(/^\n/, '');
}
let svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<foreignObject width="100%" height="100%">
${html}
</foreignObject>
</svg>
`.replace(/^\n/, '');
const spaces = svg.match(/^\s+/)[0],
stripSpacesRe = new RegExp(`^${spaces}`);
svg = svg.split('\n').map(x => x.replace(stripSpacesRe, '')).join('\n');
document.querySelector('#text').value = svg;
const svgUrl = URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
const dst = document.querySelector('#dst');
dst.style.width = width;
dst.style.height = height;
try {
await new Promise((resolve, reject) => {
dst.onload = resolve;
dst.onerror = reject;
dst.src = svgUrl;
});
} finally {
URL.revokeObjectURL(svgUrl);
}
};
</script>
<script id="image" type="text/plain" data-width="32" data-height="32">
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAIAAACsiDHgAAAABGdBTUEAAYagMeiWXwAAAOVJREFUeJzVlsEKgzAQRKfgQX/Lfrf9rfaWHgYDkoYmZpPMehiGReQ91qCPEEIAPi/gmu9kcnN+GD0nM1/O4vNad7cC6850KHCiM5fz7fJwXdEBYPOygV/o7PICeXSmsMA/dKbkGShD51xsAzXo7DIC9ehMAYG76MypZ6ANnfNJG7BAZx8uYIfOHChgjR4F+MfuDx0AtmfnDfREZ+8m0B+9m8Ao9Chg9x0Yi877jTYwA529WWAeerPAbPQoUH8GNNA5r9yAEjp7sYAeerGAKnoUyJ8BbXTOMxvwgM6eCPhBTwS8oTO/5kL+Xge7xOwAAAAASUVORK5CYII=
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment