Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 6 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
function readableURL(url) {
return url.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
}
// For each of the following tests, we'll inject a frame containing the HTML we'd like to poke at
// as a `srcdoc` attribute. Because we're injecting markup via `srcdoc`, we need to entity-escape
// the content we'd like to treat as "raw" (e.g. `\n` => ` `, `<` => `<`), and
// double-escape the "escaped" content.
var rawBrace = "<";
var escapedBrace = "&lt;";
var doubleEscapedBrace = "&amp;lt;";
var rawNewline = " ";
var escapedNewline = "&#10;";
// doubleEscapedNewline is used inside a data URI, and so must have its '#' escaped.
var doubleEscapedNewline = "&amp;%2310;";
function appendFrameAndGetElement(test, frame) {
return new Promise((resolve, reject) => {
frame.onload = test.step_func(_ => {
frame.onload = null;
resolve(frame.contentDocument.querySelector('#dangling'));
});
document.body.appendChild(frame);
});
}
function assert_img_loaded(test, frame) {
appendFrameAndGetElement(test, frame)
.then(test.step_func_done(img => {
assert_equals(img.naturalHeight, 1, "Height");
frame.remove();
}));
}
function assert_img_not_loaded(test, frame) {
appendFrameAndGetElement(test, frame)
.then(test.step_func_done(img => {
assert_equals(img.naturalHeight, 0, "Height");
assert_equals(img.naturalWidth, 0, "Width");
}));
}
function assert_nested_img_not_loaded(test, frame) {
window.addEventListener('message', test.step_func(e => {
if (e.source != frame.contentWindow)
return;
assert_equals(e.data, 'error');
test.done();
}));
appendFrameAndGetElement(test, frame);
}
function assert_nested_img_loaded(test, frame) {
window.addEventListener('message', test.step_func(e => {
if (e.source != frame.contentWindow)
return;
assert_equals(e.data, 'loaded');
test.done();
}));
appendFrameAndGetElement(test, frame);
}
function createFrame(markup) {
var i = document.createElement('iframe');
i.srcdoc = `${markup}sekrit`;
return i;
}
// Subresource requests:
[
// Data URLs don't themselves trigger blocking:
`<img id="dangling" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">`,
`<img id="dangling" src="data:image/png;base64,${rawNewline}iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">`,
`<img id="dangling" src="data:image/png;base64,i${rawNewline}VBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">`,
// Data URLs with visual structure don't trigger blocking
`<img id="dangling" src="data:image/svg+xml;utf8,
<rect width='100%' height='100%' fill='rebeccapurple'/>
<rect x='10%' y='10%' width='80%' height='80%' fill='lightgreen'/>
</svg>">`
].forEach(markup => {
async_test(t => {
var i = createFrame(`${markup} <element attr="" another=''>`);
assert_img_loaded(t, i);
}, readableURL(markup));
});
// Nested subresource requests:
//
// The following tests load a given HTML string into `<iframe srcdoc="...">`, so we'll
// end up with a frame with an ID of `dangling` inside the srcdoc frame. That frame's
// `src` is a `data:` URL that resolves to an HTML document containing an `<img>`. The
// error/load handlers on that image are piped back up to the top-level document to
// determine whether the tests' expectations were met. *phew*
// Allowed:
[
// Just a newline:
`<iframe id="dangling"
src="data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
// Just a brace:
`<iframe id="dangling"
src="data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
// Newline and escaped brace.
`<iframe id="dangling"
src="data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
src='http://{{host}}:{{ports[http][0]}}/images/gr${rawNewline}een-256x256.png?${doubleEscapedBrace}'>
">
</iframe>`,
// Brace and escaped newline:
`<iframe id="dangling"
src="data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
src='http://{{host}}:{{ports[http][0]}}/images/green-256x256.png?${doubleEscapedNewline}${rawBrace}'>
">
</iframe>`,
].forEach(markup => {
async_test(t => {
var i = createFrame(`
<script>
// Repeat the message so that the parent can track this frame as the source.
window.onmessage = e => window.parent.postMessage(e.data, '*');
</scr`+`ipt>
${markup}
`);
assert_nested_img_loaded(t, i);
}, readableURL(markup));
});
// Nested requests that should fail:
[
// Newline and brace:
`<iframe id="dangling"
src="data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
// Leading whitespace:
`<iframe id="dangling"
src=" data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
// Leading newline:
`<iframe id="dangling"
src="\ndata:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
`<iframe id="dangling"
src="${rawNewline}data:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
// Leading tab:
`<iframe id="dangling"
src="\tdata:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
// Leading carrige return:
`<iframe id="dangling"
src="\rdata:text/html,
<img
onload='window.parent.postMessage("loaded", "*");'
onerror='window.parent.postMessage("error", "*");'
">
</iframe>`,
].forEach(markup => {
async_test(t => {
var i = createFrame(`
<script>
// Repeat the message so that the parent can track this frame as the source.
window.onmessage = e => window.parent.postMessage(e.data, '*');
</scr`+`ipt>
${markup}
`);
assert_nested_img_not_loaded(t, i);
}, readableURL(markup));
});
</script>