summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/content-security-policy/inheritance
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/content-security-policy/inheritance
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/content-security-policy/inheritance')
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/blob-inherits-from-meta-http-equiv-with-invalid-characters.html19
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-child-frame-self-navigate-inherits.sub.html17
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-main-window-self-navigate-inherits.sub.html23
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/blob-url-inherits-from-initiator.sub.html43
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/document-write-iframe.html65
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/frame-src-javascript-url.html40
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/history-iframe.sub.html178
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/history.sub.html195
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub.html102
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes.sub.html180
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-history-inheritance.html63
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-inheritance.html34
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/inheritance-from-initiator.sub.html173
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html49
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/javascript-url-open-in-main-window.html13
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/javascript-url-srcdoc-cross-origin-iframe-inheritance.html28
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/location-reload.html120
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html23
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html.sub.headers5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html21
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html.sub.headers5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/empty.html0
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/iframe-do.sub.html8
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/javascript-url-srcdoc-cross-origin-iframe-inheritance-helper.sub.html24
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/message-opener-and-navigate-back.html5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/message-top-and-navigate-back.html5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/navigate-parent-to-blob.html23
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html6
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html.sub.headers4
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-javascript.html12
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-opener.html4
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-top.html5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/support/srcdoc-child-frame.html19
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html22
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html.sub.headers5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html20
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html.sub.headers5
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/window-open-local-after-network-scheme.sub.html83
-rw-r--r--testing/web-platform/tests/content-security-policy/inheritance/window.html66
39 files changed, 1712 insertions, 0 deletions
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/blob-inherits-from-meta-http-equiv-with-invalid-characters.html b/testing/web-platform/tests/content-security-policy/inheritance/blob-inherits-from-meta-http-equiv-with-invalid-characters.html
new file mode 100644
index 0000000000..8463a2eaf1
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/blob-inherits-from-meta-http-equiv-with-invalid-characters.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Security-Policy" content="
+ default-src 'none';
+ script-src blob: 'nonce-abc'">
+<script nonce="abc" src="/resources/testharness.js"></script>
+<script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+<script nonce="abc">
+ async_test(t => {
+ var script = document.createElement("script");
+ script.onerror = () => assert_unreached("FAIL should not have fired error event.");
+ script.onload = () => t.done();
+ script.src = URL.createObjectURL(new Blob(["alert('PASS executed blob URL script.');"]));
+ document.head.appendChild(script);
+ }, "blob: URL inherits CSP from a meta tag whose contents have newline characters.");
+</script>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-child-frame-self-navigate-inherits.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-child-frame-self-navigate-inherits.sub.html
new file mode 100644
index 0000000000..f2b3d063e9
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-child-frame-self-navigate-inherits.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script nonce="abc" src="/resources/testharness.js"></script>
+ <script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+
+<!-- This tests that navigating a main window to a local scheme preserves the current CSP.
+ We need to test this in a main window with no parent/opener so we use
+ a link with target=_blank and rel=noopener. -->
+<body>
+ <iframe src="support/navigate-self-to-blob.html?csp=script-src%20%27nonce-abc%27&report_id={{$id:uuid()}}"></iframe>
+ <script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27nonce-abc%27&reportID={{$id}}'></script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-main-window-self-navigate-inherits.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-main-window-self-navigate-inherits.sub.html
new file mode 100644
index 0000000000..3b54528d56
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/blob-url-in-main-window-self-navigate-inherits.sub.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script nonce="abc" src="/resources/testharness.js"></script>
+ <script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+
+<!-- This tests that navigating a main window to a local scheme preserves the current CSP.
+ We need to test this in a main window with no parent/opener so we use
+ a link with target=_blank and rel=noopener. -->
+<body>
+ <script>
+ const a = document.createElement("a")
+ a.href = "support/navigate-self-to-blob.html?csp=script-src%20%27nonce-abc%27&report_id={{$id:uuid()}}";
+ a.target = "_blank"
+ a.rel = "noopener"
+ a.click()
+ </script>
+ <script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27nonce-abc%27&reportID={{$id}}'></script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/blob-url-inherits-from-initiator.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/blob-url-inherits-from-initiator.sub.html
new file mode 100644
index 0000000000..72d59325d1
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/blob-url-inherits-from-initiator.sub.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Blob URL inherits CSP from initiator.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ let testCases = [
+ {
+ initiator_origin: window.origin,
+ name: "Initiator is same-origin with target frame.",
+ },
+ {
+ initiator_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: "Initiator is cross-origin with target frame.",
+ },
+ ];
+
+ testCases.forEach(test => {
+ async_test(t => {
+ // Create a popup. At the beginning, the popup has no CSPs.
+ let target = window.open();
+ t.add_cleanup(() => target.close());
+
+ // Create a child frame in the popup. The child frame has
+ // Content-Security-Policy: script-src 'unsafe-inline'. The child frame
+ // will navigate the popup to a blob URL, which will try if eval is
+ // allowed and message back.
+ let initiator = target.document.createElement('iframe');
+ initiator.sandbox = "allow-scripts allow-same-origin allow-top-navigation";
+ initiator.src = test.initiator_origin +
+ "/content-security-policy/inheritance/support/navigate-parent-to-blob.html";
+
+ window.addEventListener("message", t.step_func(e => {
+ if (e.source !== target) return;
+ assert_equals(e.data, "eval blocked",
+ "Eval should be blocked by CSP in blob URL.");
+ t.done();
+ }));
+
+ target.document.body.appendChild(initiator);
+ }, test.name);
+ });
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/document-write-iframe.html b/testing/web-platform/tests/content-security-policy/inheritance/document-write-iframe.html
new file mode 100644
index 0000000000..d6ad88ddc9
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/document-write-iframe.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <title>document.open() does not change Content Security Policies</title>
+</head>
+<body>
+ <script>
+ let message_from = (w) => {
+ return new Promise(resolve => {
+ let listener = msg => {
+ if (msg.source != w)
+ return;
+ window.removeEventListener('message', listener);
+ resolve(msg.data);
+ };
+ window.addEventListener('message', listener);
+ });
+ };
+
+ var documentBody = function(should_load) {
+ let image = should_load ? "pass.png" : "fail.png";
+ return `
+ <script>
+ function loaded() {
+ window.top.postMessage("loaded", '*');
+ };
+ window.addEventListener('securitypolicyviolation', function(e) {
+ window.top.postMessage("blocked", '*');
+ });
+ </scr`+`ipt>
+ <img src='/content-security-policy/support/${image}' onload='loaded()'>`;
+ };
+
+ promise_test(async () => {
+ let iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+
+ let msg = message_from(iframe.contentWindow);
+ let doc = iframe.contentWindow.document;
+ doc.open();
+ doc.write("<html><body>" + documentBody(false) + "</body></html>");
+ doc.close();
+ assert_equals(await msg, "blocked");
+ }, "document.open() keeps inherited CSPs on empty iframe.");
+
+ promise_test(async () => {
+ let iframe = document.createElement('iframe');
+ let loaded = new Promise(resolve => iframe.onload = resolve);
+ iframe.src = "/common/blank.html";
+ document.body.appendChild(iframe);
+ await loaded;
+
+ let msg = message_from(iframe.contentWindow);
+ let doc = iframe.contentWindow.document;
+ doc.open();
+ doc.write("<html><body>" + documentBody(true) + "</body></html>");
+ doc.close();
+ assert_equals(await msg, "loaded");
+ }, "document.open() does not change delivered CSPs.");
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/frame-src-javascript-url.html b/testing/web-platform/tests/content-security-policy/inheritance/frame-src-javascript-url.html
new file mode 100644
index 0000000000..b08da85e87
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/frame-src-javascript-url.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta http-equiv="Content-Security-Policy" content="frame-src 'none'">
+
+<script>
+ const iframe_url = new URL("./support/empty.html", location.href);
+
+ // Regression test for: https://crbug.com/1064676
+ promise_test(async (t) => {
+ await new Promise(r => window.onload = r);
+
+ let url = `javascript:
+
+ window.addEventListener('securitypolicyviolation', e => {
+ parent.postMessage({
+ originalPolicy: e.originalPolicy,
+ blockedURI: e.blockedURI,
+ });
+ });
+
+ let iframe = document.createElement('iframe');
+ iframe.src = '${iframe_url}';
+ document.body.appendChild(iframe);
+
+ `;
+
+ let iframe = document.createElement('iframe');
+ iframe.src = encodeURI(url.replace(/\n/g, ""));
+
+ let violation = new Promise(r => window.addEventListener("message", r));
+ document.body.appendChild(iframe);
+ let {data} = await violation;
+
+ assert_equals(data.originalPolicy, "frame-src 'none'");
+ assert_equals(data.blockedURI, iframe_url.toString());
+
+ }, "<iframe src='javascript:...'>'s inherits policy (dynamically inserted <iframe> is blocked)");
+
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/history-iframe.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/history-iframe.sub.html
new file mode 100644
index 0000000000..412b3ac346
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/history-iframe.sub.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+<body>
+<script>
+ let message_from = (source_token, starts_with) => {
+ return new Promise(resolve => {
+ window.addEventListener('message', msg => {
+ if (msg.data.token === source_token) {
+ if (!starts_with || msg.data.msg.startsWith(starts_with))
+ resolve(msg.data.msg);
+ }
+ });
+ });
+ };
+
+ const img_url = window.origin + "/content-security-policy/support/fail.png";
+
+ const img_tag_string = img_token => `
+ <img src="${img_url}"
+ onload="top.postMessage(
+ {msg: 'img loaded', token: '${img_token}'}, '*');"
+ onerror="top.postMessage(
+ {msg: 'img blocked', token: '${img_token}'}, '*');"
+ >
+ `;
+
+ const html_test_payload = img_token => `
+ <!doctype html>
+ <script>
+ function add_image() {
+ let img = document.createElement('img');
+ img.onload = () => top.postMessage(
+ {msg: 'img loaded', token: '${img_token}'}, '*');
+ img.onerror = () => top.postMessage(
+ {msg: 'img blocked', token: '${img_token}'}, '*');
+ img.src = '${img_url}';
+ document.body.appendChild(img);
+ }
+ </scr`+`ipt>
+ <body onpageshow="add_image();"></body>
+ `;
+ let blob_url = blob_token => URL.createObjectURL(
+ new Blob([html_test_payload(blob_token)], { type: 'text/html' }));
+
+ let write_img_to_about_blank = async (t, iframe, img_token) => {
+ await t.step_wait(
+ condition = () => {
+ try {
+ return iframe.contentWindow.location.href == "about:blank";
+ } catch {}
+ return false;
+ },
+ description = "Wait for the iframe to navigate.",
+ timeout=6000,
+ interval=50);
+
+ let div = iframe.contentDocument.createElement('div');
+ div.innerHTML = img_tag_string(img_token);
+ iframe.contentDocument.body.appendChild(div);
+ };
+
+ let testCases = [
+ test_token => ({
+ token: test_token,
+ url: "about:blank",
+ add_img_function: (t, iframe) =>
+ write_img_to_about_blank(t, iframe, test_token),
+ other_origin: window.origin,
+ name: '"about:blank" document is navigated back from history same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: "about:blank",
+ add_img_function: (t, iframe) =>
+ write_img_to_about_blank(t, iframe, test_token),
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: '"about:blank" document is navigated back from history cross-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: blob_url(test_token),
+ other_origin: window.origin,
+ name: 'blob URL document is navigated back from history same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: blob_url(test_token),
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: 'blob URL document is navigated back from history cross-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: `data:text/html,${html_test_payload(test_token)}`,
+ other_origin: window.origin,
+ name: 'data URL document is navigated back from history same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: `data:text/html,${html_test_payload(test_token)}`,
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: 'data URL document is navigated back from history cross-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ srcdoc: `${html_test_payload(test_token)}`,
+ other_origin: window.origin,
+ name: 'srcdoc iframe is navigated back from history same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ srcdoc: `${html_test_payload(test_token)}`,
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: 'srcdoc iframe is navigated back from history cross-origin.',
+ }),
+ ].map(f => f(token()));
+
+ testCases.forEach(testCase => {
+ promise_test(async t => {
+ // Create an iframe.
+ let iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+
+ // Perform a real navigation in the iframe. This is needed because the
+ // initial empty document is not stored in history (so there is no way of
+ // navigating back to it and test history inheritance).
+ const token_1 = token();
+ let loaded_1 = message_from(token_1);
+ iframe.contentWindow.location = testCase.other_origin +
+ "/content-security-policy/inheritance/support" +
+ `/postmessage-top.html?token=${token_1}`;
+ assert_equals(await loaded_1, "ready",
+ "Could not navigate iframe.");
+
+ // Navigate to the local scheme document.
+ let message = message_from(testCase.token);
+ if (testCase.url)
+ iframe.contentWindow.location = testCase.url;
+ else
+ iframe.srcdoc = testCase.srcdoc;
+
+ // If the local scheme document is "about:blank", we need to write its
+ // content now.
+ if (testCase.add_img_function) {
+ testCase.add_img_function(t, iframe);
+ }
+
+ // Check that the local scheme document inherits CSP from the initiator.
+ assert_equals(await message, "img blocked",
+ "Image should be blocked by CSP inherited from navigation initiator.");
+
+ // Navigate to another page, which will navigate back.
+ const token_2 = token();
+ let loaded_2 = message_from(token_2, "ready");
+ let message_2 = message_from(testCase.token, "img");
+ iframe.contentWindow.location = testCase.other_origin +
+ "/content-security-policy/inheritance/support" +
+ `/message-top-and-navigate-back.html?token=${token_2}`;
+ assert_equals(await loaded_2, "ready",
+ "Could not navigate iframe.");
+
+ // If the local scheme document is "about:blank", we need to write its
+ // content again.
+ if (testCase.add_img_function) {
+ testCase.add_img_function(t, iframe);
+ }
+
+ // Check that the local scheme document reloaded from history still has
+ // the original CSPs.
+ assert_equals(await message_2, "img blocked",
+ "Image should be blocked by CSP reloaded from history.");
+ }, "History navigation in iframe: " + testCase.name);
+ });
+</script>
+</body>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/history.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/history.sub.html
new file mode 100644
index 0000000000..5ea6abe8fb
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/history.sub.html
@@ -0,0 +1,195 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+
+<script>
+ let message_from = (source_token, starts_with) => {
+ return new Promise(resolve => {
+ window.addEventListener('message', msg => {
+ if (msg.data.token === source_token) {
+ if (!starts_with || msg.data.msg.startsWith(starts_with))
+ resolve(msg.data.msg);
+ }
+ });
+ });
+ };
+
+ const img_url = window.origin + "/content-security-policy/support/fail.png";
+
+ const function_addImage_string = img_token => `
+ function addImage() {
+ let img = document.createElement('img');
+ img.src = '${img_url}';
+ img.onload = () => opener.postMessage(
+ {msg: 'img loaded', token: '${img_token}'}, '*');
+ img.onerror = () => opener.postMessage(
+ {msg: 'img blocked', token: '${img_token}'}, '*');
+ document.body.appendChild(img);
+ }
+ `;
+
+ const img_tag_string = img_token => `
+ <img src="${img_url}"
+ onload="opener.postMessage(
+ {msg: 'img loaded', token: '${img_token}'}, '*');"
+ onerror="opener.postMessage(
+ {msg: 'img blocked', token: '${img_token}'}, '*');"
+ >
+ `;
+
+ let write_img_to_popup = (popup, img_token) => {
+ let div = popup.document.createElement('div');
+ div.innerHTML = img_tag_string(img_token);
+ popup.document.body.appendChild(div);
+ };
+
+ // A beforeunload event listener disables bfcache (Firefox only).
+ //
+ // Note: Chrome enables bfcache only on HTTP/HTTPS documents, so a blob will
+ // never be put in the bfcache. Moreover with Chrome, bfcache needs a single
+ // top-level browsing context in the browsing context group. Since we are
+ // using window.open() below, the back-forward cache is not triggered.
+ const disable_bfcache = `
+ window.addEventListener('beforeunload', function(event) {
+ eval('1+1');
+ });
+ `;
+
+ const blob_payload = blob_token => `
+ <!doctype html>
+ <script>window.window_token = "${blob_token}";</scr`+`ipt>
+ <script>${function_addImage_string(`${blob_token}`)}</scr`+`ipt>
+ <body onpageshow="addImage();"></body>
+ `;
+ let blob_url = blob_token => URL.createObjectURL(
+ new Blob([blob_payload(blob_token)], { type: 'text/html' }));
+
+ const blob_payload_no_bfcache = blob_token => `
+ <!doctype html>
+ <script>window.window_token = "${blob_token}";</scr`+`ipt>
+ <script>${disable_bfcache}</scr`+`ipt>
+ <script>${function_addImage_string(`${blob_token}`)}</scr`+`ipt>
+ <body onpageshow="addImage();"></body>
+ `;
+ let blob_url_no_bfcache = blob_token => URL.createObjectURL(
+ new Blob([blob_payload_no_bfcache(blob_token)], { type: 'text/html' }));
+
+ let testCases = [
+ test_token => ({
+ token: test_token,
+ url: "about:blank",
+ add_img_function: popup => write_img_to_popup(popup, test_token),
+ other_origin: window.origin,
+ name: '"about:blank" document is navigated back from history same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: "about:blank",
+ add_img_function: popup => write_img_to_popup(popup, test_token),
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: '"about:blank" document is navigated back from history cross-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: blob_url(test_token),
+ other_origin: window.origin,
+ name: 'blob URL document is navigated back from history same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: blob_url(test_token),
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: 'blob URL document is navigated back from history cross-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: blob_url_no_bfcache(test_token),
+ other_origin: window.origin,
+ name: 'blob URL document is navigated back from history (without bfcache on Firefox) same-origin.',
+ }),
+ test_token => ({
+ token: test_token,
+ url: blob_url_no_bfcache(test_token),
+ other_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: 'blob URL document is navigated back from history (without bfcache on Firefox) cross-origin.',
+ }),
+ ].map(f => f(token()));
+
+ let async_promise_test = (promise, description) => {
+ async_test(test => {
+ promise(test)
+ .then(() => {test.done();})
+ .catch(test.step_func(error => { throw error; }));
+ }, description);
+ };
+
+ testCases.forEach(testCase => {
+ async_promise_test(async t => {
+ // Create a popup.
+ let popup = window.open();
+
+ // Closing fails sometimes on Firefox:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1698093
+ t.add_cleanup(() => { popup.close(); });
+
+ // Perform a real navigation in the popup. This is needed because the
+ // initial empty document is not stored in history (so there is no way of
+ // navigating back to it and test history inheritance).
+ const token_1 = token();
+ let loaded_1 = message_from(token_1);
+ popup.location = testCase.other_origin +
+ `/content-security-policy/inheritance/support` +
+ `/postmessage-opener.html?token=${token_1}`;
+ assert_equals(await loaded_1, "ready",
+ "Could not open and navigate popup.");
+
+ // Navigate to the local scheme document. We need to wait for the
+ // navigation to succeed.
+ let wait = () => t.step_wait(
+ condition = () => {
+ try {
+ return popup.location.href == testCase.url;
+ } catch {}
+ return false;
+ },
+ description = "Wait for the popup to navigate.",
+ timeout=3000,
+ interval=50);
+
+ let message = message_from(testCase.token);
+ popup.location = testCase.url;
+ await wait();
+ if (testCase.add_img_function) {
+ testCase.add_img_function(popup);
+ }
+
+ // Check that the local scheme document inherits CSP from the initiator.
+ assert_equals(await message, "img blocked",
+ "Image should be blocked by CSP inherited from navigation initiator.");
+
+ const token_2 = token();
+ let loaded_2 = message_from(token_2, "ready");
+ let message_2 = message_from(testCase.token, "img");
+ // Navigate to another page, which will navigate back.
+ popup.location = testCase.other_origin +
+ `/content-security-policy/inheritance/support` +
+ `/message-opener-and-navigate-back.html?token=${token_2}`;
+ assert_equals(await loaded_2, "ready",
+ "Could not navigate popup.");
+
+ // We need to wait for the history navigation to be performed.
+ await wait();
+
+ // Check that the "about:blank" document reloaded from history has the
+ // original CSPs.
+ if (testCase.add_img_function) {
+ testCase.add_img_function(popup);
+ }
+ assert_equals(await message_2, "img blocked",
+ "Image should be blocked by CSP reloaded from history.");
+ }, "History navigation: " + testCase.name);
+ });
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub.html
new file mode 100644
index 0000000000..73e974e51a
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes-inherit-self.sub.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'self'">
+
+<body>
+
+<script>
+ function wait_for_error_from_frame(frame, test) {
+ window.addEventListener('message', test.step_func(e => {
+ if (e.source != frame.contentWindow)
+ return;
+ assert_equals(e.data, "load");
+ frame.remove();
+ test.done();
+ }));
+ }
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ document.body.appendChild(i);
+
+ var img = document.createElement('img');
+ img.onload = t.step_func_done(_ => i.remove());
+ img.onerror = t.unreached_func();
+ i.contentDocument.body.appendChild(img);
+ img.src = "{{location[server]}}/images/red-16x16.png";
+ }, "<iframe>'s about:blank inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ i.srcdoc = `
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >
+ `;
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe srcdoc>'s inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ var b = new Blob(
+ [`
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >
+ `], {type:"text/html"});
+ i.src = URL.createObjectURL(b);
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='blob:...'>'s inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ i.src = `data:text/html,<img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >`;
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='data:...'>'s inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ i.src = `javascript:"<img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage(\\"load\\", \\"*\\");'
+ onerror='window.top.postMessage(\\"error\\", \\"*\\");'
+ >"`;
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='javascript:...'>'s inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ var b = new Blob(
+ [`
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >
+ `], {type:"text/html"});
+ i.src = URL.createObjectURL(b);
+ i.sandbox = 'allow-scripts';
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe sandbox src='blob:...'>'s inherits policy. (opaque origin sandbox)");
+
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes.sub.html
new file mode 100644
index 0000000000..4b787e0c18
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/iframe-all-local-schemes.sub.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+
+<body>
+
+<script>
+ function wait_for_error_from_frame(frame, test) {
+ window.addEventListener('message', test.step_func(e => {
+ if (e.source != frame.contentWindow)
+ return;
+ assert_equals(e.data, "error");
+ frame.remove();
+ test.done();
+ }));
+ }
+
+ function wait_for_error_from_window(opened_window, test) {
+ window.addEventListener('message', test.step_func(e => {
+ if (e.source != opened_window)
+ return;
+ assert_equals(e.data, "error");
+ opened_window.close();
+ test.done();
+ }));
+ }
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ document.body.appendChild(i);
+
+ var img = document.createElement('img');
+ img.onerror = t.step_func_done(_ => i.remove());
+ img.onload = t.unreached_func();
+ i.contentDocument.body.appendChild(img);
+ img.src = "{{location[server]}}/images/red-16x16.png";
+ }, "<iframe>'s about:blank inherits policy.");
+
+ async_test(t => {
+ var w = window.open("about:blank");
+
+ let then = t.step_func(() => {
+ then = () => {};
+ var img = w.document.createElement('img');
+ img.onerror = t.step_func_done(_ => w.close());
+ img.onload = t.unreached_func();
+ w.document.body.appendChild(img);
+ img.src = "{{location[server]}}/images/red-16x16.png";
+ });
+
+ // There are now interoperable way to wait for the initial about:blank
+ // document to load. Chrome loads it synchronously, hence we can't wait for
+ // w.onload. On the other side Firefox loads the initial empty document
+ // later and we can wait for the onload event.
+ w.onload = then;
+ setTimeout(then, 200);
+
+ // Navigations to about:blank happens synchronously. There is no need to
+ // wait for the document to load.
+ }, "window about:blank inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ i.srcdoc = `
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >
+ `;
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe srcdoc>'s inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ var b = new Blob(
+ [`
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >
+ `], {type:"text/html"});
+ i.src = URL.createObjectURL(b);
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='blob:...'>'s inherits policy.");
+
+ async_test(t => {
+ var b = new Blob(
+ [`
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.opener.postMessage("load", "*");'
+ onerror='window.opener.postMessage("error", "*");'
+ >
+ `], {type:"text/html"});
+ let url = URL.createObjectURL(b);
+ var w = window.open(url);
+ wait_for_error_from_window(w, t);
+ }, "window url='blob:...' inherits policy.");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ i.src = `data:text/html,<img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >`;
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='data:...'>'s inherits policy.");
+
+ // Opening a window toward a data-url isn't allowed anymore. Hence, it can't
+ // be tested.
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ i.src = `javascript:"<img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage(\\"load\\", \\"*\\");'
+ onerror='window.top.postMessage(\\"error\\", \\"*\\");'
+ >"`;
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='javascript:...'>'s inherits policy (static <img> is blocked)");
+
+ async_test(t => {
+ let url = `javascript:"<img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.opener.postMessage(\\"load\\", \\"*\\");'
+ onerror='window.opener.postMessage(\\"error\\", \\"*\\");'
+ >"`;
+
+ let w = window.open(url);
+ wait_for_error_from_window(w, t);
+ }, "window url='javascript:...'>'s inherits policy (static <img> is blocked)");
+
+ // Same as the previous javascript-URL test, but instead of loading the <img>
+ // from the new document, this one is created from the initial empty document,
+ // while evaluating the javascript-url.
+ // See https://crbug.com/1064676
+ async_test(t => {
+ let url = `javascript:
+ let img = document.createElement('img');
+ img.onload = () => window.top.postMessage('load', '*');
+ img.onerror = () => window.top.postMessage('error', '*');
+ img.src = '{{location[server]}}/images/red-16x16.png';
+ document.body.appendChild(img);
+ `;
+ var i = document.createElement('iframe');
+ i.src = encodeURI(url.replace(/\n/g, ""));
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe src='javascript:...'>'s inherits policy (dynamically inserted <img> is blocked)");
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ var b = new Blob(
+ [`
+ <img src='{{location[server]}}/images/red-16x16.png'
+ onload='window.top.postMessage("load", "*");'
+ onerror='window.top.postMessage("error", "*");'
+ >
+ `], {type:"text/html"});
+ i.src = URL.createObjectURL(b);
+ i.sandbox = 'allow-scripts';
+
+ wait_for_error_from_frame(i, t);
+
+ document.body.appendChild(i);
+ }, "<iframe sandbox src='blob:...'>'s inherits policy. (opaque origin sandbox)");
+
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-history-inheritance.html b/testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-history-inheritance.html
new file mode 100644
index 0000000000..907c88e813
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-history-inheritance.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+<iframe></iframe>
+<script>
+promise_test(async t => {
+ // Wait for the page to load + one task so that navigations from here on are
+ // not done in "replace" mode.
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+ const iframe = document.querySelector('iframe');
+
+ iframe.srcdoc = `
+ <h1>This is a dummy page that should not store the inherited policy
+ container in this history entry</h1>
+ `;
+
+ await new Promise(resolve => iframe.onload = () => t.step_timeout(resolve, 0));
+
+ // Navigate the iframe away.
+ iframe.contentWindow.location.href = "/common/blank.html";
+ await new Promise(resolve => iframe.onload = resolve);
+
+ // Tighten the outer page's security policy.
+ const meta = document.createElement("meta");
+ meta.setAttribute("http-equiv", "Content-Security-Policy");
+ meta.setAttribute("content", "img-src 'none'");
+ document.head.append(meta);
+
+ // Navigate the iframe back to the `about:srcdoc` page (this should work
+ // independent of whether the implementation stores the srcdoc contents in the
+ // history entry or reclaims it from the attribute).
+ iframe.contentWindow.history.back();
+ await new Promise(resolve => iframe.onload = resolve);
+
+ const img = iframe.contentDocument.createElement('img');
+
+ const promise = new Promise((resolve, reject) => {
+ img.onload = resolve;
+ // If the img is blocked because of Content Security Policy, a violation
+ // should be reported first, and the test will fail. If for some other
+ // reason the error event is fired without the violation being reported,
+ // something else went wrong, hence the test should fail.
+ img.error = e => {
+ reject(new Error("The srcdoc iframe's img failed to load but not due to " +
+ "a CSP violation"));
+ };
+ iframe.contentDocument.onsecuritypolicyviolation = e => {
+ reject(new Error("The srcdoc iframe's img has been blocked by the " +
+ "new CSP. It means it was different and wasn't restored from history"));
+ };
+ });
+ // The srcdoc iframe tries to load an image, which should succeed.
+ img.src = "/common/square.png";
+
+ return promise;
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-inheritance.html b/testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-inheritance.html
new file mode 100644
index 0000000000..e05150762f
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/iframe-srcdoc-inheritance.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="img-src 'self'">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script>
+ var t1 = async_test("First image should be blocked");
+ var t2 = async_test("Second image should be blocked");
+ window.onmessage = t1.step_func_done(function(e) {
+ if (e.data == "img blocked") {
+ frames[0].frames[0].frameElement.srcdoc =
+ `<script>
+ window.addEventListener('securitypolicyviolation', function(e) {
+ if (e.violatedDirective == 'img-src') {
+ top.postMessage('img blocked', '*');
+ }
+ })
+ </scr` + `ipt>
+ <img src='/content-security-policy/support/fail.png'
+ onload='top.postMessage("img loaded", "*")'/>`;
+ window.onmessage = t2.step_func_done(function(e) {
+ if (e.data != "img blocked")
+ assert_true(false, "The second image should have been blocked");
+ });
+ } else {
+ assert_true(false, "The first image should have been blocked");
+ }
+ });
+ </script>
+ <iframe src="support/srcdoc-child-frame.html"></iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/inheritance-from-initiator.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/inheritance-from-initiator.sub.html
new file mode 100644
index 0000000000..4621c57d45
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/inheritance-from-initiator.sub.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src http://{{hosts[][www]}}:{{ports[http][0]}}">
+<body>
+ <script>
+ let message_from = w => {
+ return new Promise(resolve => {
+ window.addEventListener('message', msg => {
+ if (msg.source == w) {
+ resolve(msg.data);
+ }
+ });
+ });
+ };
+
+ // `iframe_a` and `iframe_b` are two helper iframes with different
+ // CSPs.
+ let iframe_a, iframe_b;
+
+ // Setup `iframe_a` and `iframe_b`.
+ promise_setup(async () => {
+ iframe_a = document.createElement('iframe');
+ iframe_a.src = "./support/iframe-do.sub.html?" +
+ "img-src=http://{{hosts[][www1]}}:{{ports[http][0]}}";
+ document.body.appendChild(iframe_a);
+ await message_from(iframe_a.contentWindow);
+
+ iframe_b = document.createElement('iframe');
+ iframe_b.id = 'iframe_b';
+ iframe_b.src = "./support/iframe-do.sub.html?" +
+ "img-src=http://{{hosts[][www2]}}:{{ports[http][0]}}";
+ document.body.appendChild(iframe_b);
+ await message_from(iframe_b.contentWindow);
+ });
+
+ let test_iframe_id_counter = 0;
+
+ // Helper function to create the target iframe of a navigation.
+ let create_test_iframe = async () => {
+ let test_iframe = document.createElement('iframe');
+ test_iframe.id = "test_iframe_" + test_iframe_id_counter++;
+ test_iframe.name = test_iframe.id;
+ document.body.appendChild(test_iframe);
+ return test_iframe;
+ }
+
+ // The following code will try loading several images and check
+ // whether CSP has been inherited by the parent ("p"), `iframe_a`
+ // ("a") or `iframe_b` ("b"). It will post a message to the top
+ // with the result.
+ let data_payload = `
+ <body><script>
+ new Promise(async (resolve, reject) => {
+ const img_path = "/content-security-policy/support/pass.png";
+
+ let img_loaded = (origin) => new Promise(resolve => {
+ let img = document.createElement('img');
+ img.onerror = () => resolve(false);
+ img.onload = () => resolve(true);
+ img.src = origin + img_path;
+ document.body.appendChild(img);
+ });
+
+ inherited_from_p = await img_loaded(
+ "http://{{hosts[][www]}}:{{ports[http][0]}}");
+ inherited_from_a = await img_loaded(
+ "http://{{hosts[][www1]}}:{{ports[http][0]}}");
+ inherited_from_b = await img_loaded(
+ "http://{{hosts[][www2]}}:{{ports[http][0]}}");
+
+ if (inherited_from_a + inherited_from_b +
+ inherited_from_p !== 1) {
+ reject("Exactly one CSP should be inherited");
+ }
+ if (inherited_from_a) resolve("a");
+ if (inherited_from_b) resolve("b");
+ if (inherited_from_p) resolve("p");
+ }).then(from => top.postMessage(from, '*'),
+ error => top.postMessage(error, '*'));
+ </scr`+`ipt></body>
+ `;
+
+ let data_url = "data:text/html;base64," + btoa(data_payload);
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ iframe_a.contentWindow.postMessage(
+ `parent.document.getElementById('${test_iframe.id}').src = '${data_url}'`);
+
+ assert_equals(await message_from(test_iframe.contentWindow), "p");
+ }, "Setting src inherits from parent.");
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ iframe_a.contentWindow.postMessage(
+ `parent.document.getElementById('${test_iframe.id}').contentWindow.location = '${data_url}'`);
+
+ assert_equals(await message_from(test_iframe.contentWindow), "a");
+ }, "Changing contentWindow.location inherits from who changed it.");
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ window.navigate_test_iframe = () => {
+ test_iframe.contentWindow.location = data_url;
+ };
+ iframe_a.contentWindow.postMessage(`parent.navigate_test_iframe();`);
+ assert_equals(await message_from(test_iframe.contentWindow), "p");
+ }, "Changing contentWindow.location indirectly inherits from who changed it directly.");
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ iframe_a.contentWindow.postMessage(
+ `window.open('${data_url}', "${test_iframe.name}")`);
+
+ assert_equals(await message_from(test_iframe.contentWindow), "a");
+ }, "window.open() inherits from caller.");
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ let a = iframe_b.contentDocument.createElement('a');
+ a.id = 'a';
+ a.href = data_url;
+ a.target = test_iframe.name;
+ iframe_b.contentDocument.body.appendChild(a);
+
+ iframe_a.contentWindow.postMessage(
+ `parent.document.getElementById('iframe_b').contentDocument.getElementById('a').click();`);
+
+ assert_equals(await message_from(test_iframe.contentWindow), "b");
+ iframe_b.contentDocument.body.removeChild(a);
+ }, "Click on anchor inherits from owner of the anchor.");
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ let form = iframe_b.contentDocument.createElement('form');
+ form.id = 'form';
+ form.action = data_url;
+ form.target = test_iframe.name;
+ form.method = "POST";
+ iframe_b.contentDocument.body.appendChild(form);
+
+ iframe_a.contentWindow.postMessage(
+ `parent.document.getElementById('iframe_b').contentDocument.getElementById('form').submit();`);
+
+ assert_equals(await message_from(test_iframe.contentWindow), "b");
+ iframe_b.contentDocument.body.removeChild(form);
+ }, "Form submission through submit() inherits from owner of form.");
+
+ promise_test(async t => {
+ let test_iframe = await create_test_iframe();
+ let form = iframe_b.contentDocument.createElement('form');
+ form.id = 'form';
+ form.action = data_url;
+ form.target = test_iframe.name;
+ form.method = "POST";
+ iframe_b.contentDocument.body.appendChild(form);
+ let button = iframe_b.contentDocument.createElement('button');
+ button.type = "submit";
+ button.value = "submit";
+ button.id = "button";
+ form.appendChild(button);
+
+ iframe_a.contentWindow.postMessage(
+ `parent.document.getElementById('iframe_b').contentDocument.getElementById('button').click();`);
+
+ assert_equals(await message_from(test_iframe.contentWindow), "b");
+ iframe_b.contentDocument.body.removeChild(form);
+ }, "Form submission through button click inherits from owner of form.");
+
+ </script>
+</body>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html b/testing/web-platform/tests/content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html
new file mode 100644
index 0000000000..c473b3f426
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/inherited-csp-list-modifications-are-local.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline' 'self'">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Tests that mutations inside a context that inherits a copy of the CSP list
+ does not affect the parent context -->
+</head>
+<body>
+ <script>
+ var t1 = async_test("Test that parent document image loads");
+ var t2 = async_test("Test that embedded iframe document image does not load");
+ var t3 = async_test("Test that spv event is fired");
+
+ window.onmessage = function(e) {
+ if (e.data.type == 'spv') {
+ t3.step(function() {
+ assert_equals(e.data.violatedDirective, "img-src");
+ t3.done();
+ });
+ } else if (e.data.type == 'imgload') {
+ var img = document.createElement('img');
+ img.src = "../support/pass.png";
+ img.onload = function() { t1.done(); };
+ img.onerror = t1.unreached_func('Should have loaded the image');
+ document.body.appendChild(img);
+
+ t2.step(function() {
+ assert_false(e.data.loaded, "Should not have loaded image inside the frame because of its CSP");
+ t2.done();
+ });
+ }
+ }
+
+ var srcdoc = ['<meta http-equiv="Content-Security-Policy" content="img-src \'none\'">',
+ '<script>',
+ ' window.addEventListener("securitypolicyviolation", function(e) {',
+ ' window.top.postMessage({type: "spv", violatedDirective: e.violatedDirective}, "*");',
+ ' });',
+ '</scr' + 'ipt>',
+ '<img src="../support/fail.png"',
+ ' onload="window.top.postMessage({type: \'imgload\', loaded: true}, \'*\')"',
+ ' onerror="window.top.postMessage({type: \'imgload\', loaded: false}, \'*\')">'].join('\n');
+ var i = document.createElement('iframe');
+ i.srcdoc = srcdoc;
+ document.body.appendChild(i);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/javascript-url-open-in-main-window.html b/testing/web-platform/tests/content-security-policy/inheritance/javascript-url-open-in-main-window.html
new file mode 100644
index 0000000000..2366284fc5
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/javascript-url-open-in-main-window.html
@@ -0,0 +1,13 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ async_test(t => {
+ window.addEventListener("message", t.step_func_done(e => {
+ assert_equals(e.data, "img blocked",
+ "Img should be blocked by CSP img-src 'none'");
+ }));
+
+ w = window.open("./support/navigate-self-to-javascript.html");
+ t.add_cleanup(w.close);
+ }, "Executing Javascript URL keeps enforcing previous CSPs of the document.");
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/javascript-url-srcdoc-cross-origin-iframe-inheritance.html b/testing/web-platform/tests/content-security-policy/inheritance/javascript-url-srcdoc-cross-origin-iframe-inheritance.html
new file mode 100644
index 0000000000..81210fe30f
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/javascript-url-srcdoc-cross-origin-iframe-inheritance.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <title>Content Security Policy: nested inheritance</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script>
+ // This test creates a page with CSP: frame-src 'self'. The page is
+ // navigated to a javascript URL creating a cross-origin iframe inside a
+ // srcdoc iframe. If everything works correctly, the cross-origin iframe
+ // should be blocked.
+ //
+ // Note that most of the logic is performed by the iframe. This file is only
+ // for managing testharness assertions.
+ async_test(t => {
+ window.addEventListener("message", t.step_func(function(e) {
+ if (e.data === "frame allowed") {
+ assert_unreached("Frame should have been blocked.");
+ } else if (e.data === "frame blocked") {
+ t.done();
+ }
+ }));
+ }, "Nested cross-origin iframe should be blocked by frame-src 'self'.");
+ </script>
+ <iframe src="./support/javascript-url-srcdoc-cross-origin-iframe-inheritance-helper.sub.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/location-reload.html b/testing/web-platform/tests/content-security-policy/inheritance/location-reload.html
new file mode 100644
index 0000000000..5d68e381bc
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/location-reload.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+<body>
+<script>
+ let message_from = (w, starts_with) => {
+ return new Promise(resolve => {
+ window.addEventListener('message', msg => {
+ if (msg.source == w) {
+ if (!starts_with || msg.data.startsWith(starts_with))
+ resolve(msg.data);
+ }
+ });
+ });
+ };
+
+ const img_url = window.origin + "/content-security-policy/support/fail.png";
+ const img_tag_string = `
+ <img src="${img_url}"
+ onload="top.postMessage('img loaded', '*');"
+ onerror="top.postMessage('img blocked', '*');"
+ >
+ `;
+
+ const html_test_payload = `
+ <!doctype html>
+ <div>${img_tag_string}</div>
+ `;
+ let blob_url = URL.createObjectURL(
+ new Blob([html_test_payload], { type: 'text/html' }));
+
+ let write_img_to_iframe = (iframe) => {
+ let div = iframe.contentDocument.createElement('div');
+ div.innerHTML = img_tag_string;
+ iframe.contentDocument.body.appendChild(div);
+ };
+
+
+ // Test location.reload() for "about:blank".
+ promise_test(async t => {
+ // Create an empty iframe.
+ window.iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+
+ // Add an img.
+ let message = message_from(iframe.contentWindow);
+ write_img_to_iframe(iframe);
+
+ // Check that the empty document inherits CSP from the initiator.
+ assert_equals(await message, "img blocked",
+ "Image should be blocked by CSP inherited from the parent.");
+
+ // Now perform a reload.
+ let message_2 = message_from(iframe.contentWindow);
+ let loaded = new Promise(resolve => iframe.onload = resolve);
+ iframe.contentWindow.location.reload();
+ await loaded;
+
+ // Add an img.
+ write_img_to_iframe(iframe);
+
+ // Check that the empty document still has the right CSP after reload.
+ assert_equals(await message_2, "img blocked",
+ "Image should be blocked by CSP after reload.");
+ }, "location.reload() of empty iframe.");
+
+
+ // Test location.reload() for a blob URL.
+ promise_test(async t => {
+ // Create an iframe.
+ window.iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+
+ // Navigate to the blob URL.
+ let message = message_from(iframe.contentWindow);
+ iframe.contentWindow.location = blob_url;
+
+ // Check that the blob URL inherits CSP from the initiator.
+ assert_equals(await message, "img blocked",
+ "Image should be blocked by CSP inherited from navigation initiator.");
+
+ // Now perform a reload.
+ let message_2 = message_from(iframe.contentWindow);
+ let loaded = new Promise(resolve => iframe.onload = resolve);
+ iframe.contentWindow.location.reload();
+ await loaded;
+
+ // Check that the blob URL document still has the right CSP after reload.
+ assert_equals(await message_2, "img blocked",
+ "Image should be blocked by CSP after reload.");
+ }, "location.reload() of blob URL iframe.");
+
+
+ // Test location.reload() for a srcdoc iframe.
+ promise_test(async t => {
+ // Create a srcdoc iframe.
+ window.iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+
+ let message = message_from(iframe.contentWindow);
+ iframe.srcdoc = `${html_test_payload}`;
+
+ // Check that the srcdoc iframe inherits from the parent.
+ assert_equals(await message, "img blocked",
+ "Image should be blocked by CSP inherited from navigation initiator.");
+
+ // Now perform a reload.
+ let message_2 = message_from(iframe.contentWindow);
+ let loaded = new Promise(resolve => iframe.onload = resolve);
+ iframe.contentWindow.location.reload();
+ await loaded;
+
+ // Check that the srcdoc iframe still has the right CSP after reload.
+ assert_equals(await message_2, "img blocked",
+ "Image should be blocked by CSP after reload.");
+ }, "location.reload() of srcdoc iframe.");
+</script>
+</body>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html
new file mode 100644
index 0000000000..590fa7ec1a
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script nonce="abc" src="/resources/testharness.js"></script>
+ <script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script nonce='abc'>
+ var blob_string = "<script>alert(document.domain)<\/scr"+"ipt>";
+ var blob = new Blob([blob_string], {type : 'text/html'});
+ var url = URL.createObjectURL(blob);
+
+ var i = document.createElement('iframe');
+ i.src = url;
+ i.sandbox = "allow-scripts";
+ document.body.appendChild(i);
+ </script>
+ <script nonce='abc' async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27nonce-abc%27'></script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html.sub.headers b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html.sub.headers
new file mode 100644
index 0000000000..adc398d890
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-blob-scheme.html.sub.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Pragma: no-cache
+Set-Cookie: sandboxed-blob-scheme={{$id:uuid()}}; Path=/content-security-policy/inheritance/
+Content-Security-Policy: script-src 'nonce-abc'; report-uri http://{{host}}:{{ports[http][0]}}/reporting/resources/report.py?op=put&reportID={{$id}}
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html
new file mode 100644
index 0000000000..b97bfb0c05
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script nonce="abc" src="/resources/testharness.js"></script>
+ <script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script nonce='abc'>
+ var url = "data:text/html,<script>alert(document.domain)<\/scr"+"ipt>";
+
+ var i = document.createElement('iframe');
+ i.src = url;
+ i.sandbox = "allow-scripts";
+ document.body.appendChild(i);
+ </script>
+ <script nonce='abc' async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27nonce-abc%27'></script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html.sub.headers b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html.sub.headers
new file mode 100644
index 0000000000..96da6514b8
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/sandboxed-data-scheme.html.sub.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Pragma: no-cache
+Set-Cookie: sandboxed-data-scheme={{$id:uuid()}}; Path=/content-security-policy/inheritance/
+Content-Security-Policy: script-src 'nonce-abc'; report-uri http://{{host}}:{{ports[http][0]}}/reporting/resources/report.py?op=put&reportID={{$id}}
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/empty.html b/testing/web-platform/tests/content-security-policy/inheritance/support/empty.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/empty.html
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/iframe-do.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/support/iframe-do.sub.html
new file mode 100644
index 0000000000..effc1adcdd
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/iframe-do.sub.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta http-equiv="Content-Security-Policy" content="img-src {{GET[img-src]}}">
+<script>
+ window.addEventListener('message', function(e) {
+ eval(e.data);
+ });
+ top.postMessage('ready', '*');
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/javascript-url-srcdoc-cross-origin-iframe-inheritance-helper.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/support/javascript-url-srcdoc-cross-origin-iframe-inheritance-helper.sub.html
new file mode 100644
index 0000000000..afe4753cf9
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/javascript-url-srcdoc-cross-origin-iframe-inheritance-helper.sub.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="frame-src 'self'">
+ <script>
+ // The following is the content of a srcdoc iframe. It contains:
+ // - a script that catches the frame-src securitypolicyviolation event and
+ // forwards the information to the parent,
+ // - a cross-origin iframe.
+ let doc = `
+ <script>
+ window.addEventListener("securitypolicyviolation", e => {
+ if (e.violatedDirective === "frame-src") {
+ window.top.postMessage("frame blocked", "*");
+ }
+ });
+ </scr` + `ipt>
+ <iframe src="http://{{hosts[alt][]}}:{{ports[http][0]}}/content-security-policy/inheritance/support/postmessage-top.html"></iframe>`;
+ doc = doc.replaceAll('"', "\\\'");
+
+ const js_url = "javascript:'<iframe srcdoc=\""+ doc +"\">'";
+ window.open(js_url, "_self");
+ </script>
+</head>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/message-opener-and-navigate-back.html b/testing/web-platform/tests/content-security-policy/inheritance/support/message-opener-and-navigate-back.html
new file mode 100644
index 0000000000..75ee5bee7c
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/message-opener-and-navigate-back.html
@@ -0,0 +1,5 @@
+<script>
+ const params = new URLSearchParams(window.location.search);
+ opener.postMessage({msg: "ready", token: params.get("token")}, "*");
+ window.history.back();
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/message-top-and-navigate-back.html b/testing/web-platform/tests/content-security-policy/inheritance/support/message-top-and-navigate-back.html
new file mode 100644
index 0000000000..53d5a18cb3
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/message-top-and-navigate-back.html
@@ -0,0 +1,5 @@
+<script>
+ const params = new URLSearchParams(window.location.search);
+ top.postMessage({msg: "ready", token: params.get("token")}, "*");
+ window.history.back();
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-parent-to-blob.html b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-parent-to-blob.html
new file mode 100644
index 0000000000..df4a443893
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-parent-to-blob.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'">
+ </head>
+ <body>
+ <script>
+ const blob_payload = `
+ <!doctype html>
+ <script>
+ var i = false;
+ try {
+ eval('i = true');
+ } catch {}
+ opener.postMessage(i ? "eval allowed" : "eval blocked", '*');
+ </scr` + `ipt>
+ `;
+ var blob_url = URL.createObjectURL(
+ new Blob([blob_payload], { type: 'text/html' }));
+ parent.location = blob_url;
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html
new file mode 100644
index 0000000000..9ea069969c
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html
@@ -0,0 +1,6 @@
+<script nonce="abc">
+ var blob_string = "<script>alert(document.domain)<\/script>";
+ var blob = new Blob([blob_string], {type : 'text/html'});
+ var url = URL.createObjectURL(blob);
+ location.href=url;
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html.sub.headers b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html.sub.headers
new file mode 100644
index 0000000000..2642b0fa06
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-blob.html.sub.headers
@@ -0,0 +1,4 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Pragma: no-cache
+Content-Security-Policy: {{GET[csp]}}; report-uri http://{{host}}:{{ports[http][0]}}/reporting/resources/report.py?op=put&reportID={{GET[report_id]}}
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-javascript.html b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-javascript.html
new file mode 100644
index 0000000000..86ea60c283
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/navigate-self-to-javascript.html
@@ -0,0 +1,12 @@
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'"/>
+<script>
+ const js_payload = `
+ <div>
+ <img src="${window.origin}/content-security-policy/support/fail.png"
+ onload="opener.postMessage(\\\'img loaded\\\', \\\'*\\\');"
+ onerror="opener.postMessage(\\\'img blocked\\\', \\\'*\\\');"
+ >
+ </div>
+ `;
+ open(`javascript:'${js_payload}'`,"_self");
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-opener.html b/testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-opener.html
new file mode 100644
index 0000000000..7ee11bc78d
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-opener.html
@@ -0,0 +1,4 @@
+<script>
+ const params = new URLSearchParams(window.location.search);
+ opener.postMessage({msg: "ready", token: params.get("token")}, "*");
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-top.html b/testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-top.html
new file mode 100644
index 0000000000..242063a80e
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/postmessage-top.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ const params = new URLSearchParams(window.location.search);
+ top.postMessage({msg: "ready", token: params.get("token")}, "*");
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/support/srcdoc-child-frame.html b/testing/web-platform/tests/content-security-policy/inheritance/support/srcdoc-child-frame.html
new file mode 100644
index 0000000000..9148be203d
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/support/srcdoc-child-frame.html
@@ -0,0 +1,19 @@
+<head>
+ <meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+</head>
+<body>
+ <script>
+ var i = document.createElement('iframe');
+ i.srcdoc=`<script>
+ window.addEventListener('securitypolicyviolation', function(e) {
+ if (e.violatedDirective == 'img-src') {
+ top.postMessage('img blocked', '*');
+ }
+ })
+ </scr` + `ipt>
+ <img src='/content-security-policy/support/fail.png'
+ onload='top.postMessage("img loaded", "*")'/>`;
+ i.id = "srcdoc-frame";
+ document.body.appendChild(i);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html
new file mode 100644
index 0000000000..cab192f836
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script nonce="abc" src="/resources/testharness.js"></script>
+ <script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script nonce='abc'>
+ var blob_string = "<script>alert(document.domain)<\/scr"+"ipt>";
+ var blob = new Blob([blob_string], {type : 'text/html'});
+ var url = URL.createObjectURL(blob);
+
+ var i = document.createElement('iframe');
+ i.src = url;
+ document.body.appendChild(i);
+ </script>
+ <script nonce='abc' async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27nonce-abc%27'></script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html.sub.headers b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html.sub.headers
new file mode 100644
index 0000000000..b1054d3506
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-blob-scheme.html.sub.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Pragma: no-cache
+Set-Cookie: unsandboxed-blob-scheme={{$id:uuid()}}; Path=/content-security-policy/inheritance/
+Content-Security-Policy: script-src 'nonce-abc'; report-uri http://{{host}}:{{ports[http][0]}}/reporting/resources/report.py?op=put&reportID={{$id}}
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html
new file mode 100644
index 0000000000..a9d8e207dc
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <script nonce="abc" src="/resources/testharness.js"></script>
+ <script nonce="abc" src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+ <script nonce='abc'>
+ var url = "data:text/html,<script>alert(document.domain)<\/scri"+"pt>";
+
+ var i = document.createElement('iframe');
+ i.src = url;
+ document.body.appendChild(i);
+ </script>
+ <script nonce='abc' async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27nonce-abc%27'></script>
+</body>
+
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html.sub.headers b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html.sub.headers
new file mode 100644
index 0000000000..f4a6088578
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/unsandboxed-data-scheme.html.sub.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Pragma: no-cache
+Set-Cookie: unsandboxed-data-scheme={{$id:uuid()}}; Path=/content-security-policy/inheritance/
+Content-Security-Policy: script-src 'nonce-abc'; report-uri http://{{host}}:{{ports[http][0]}}/reporting/resources/report.py?op=put&reportID={{$id}}
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/window-open-local-after-network-scheme.sub.html b/testing/web-platform/tests/content-security-policy/inheritance/window-open-local-after-network-scheme.sub.html
new file mode 100644
index 0000000000..0cdc03ce92
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/window-open-local-after-network-scheme.sub.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+<title>about:blank in popup inherits CSPs from the navigation initiator</title>
+<body>
+
+<script>
+ const message_from = (source_token, w) => {
+ return new Promise(resolve => {
+ window.addEventListener('message', msg => {
+ if (msg.data.token === source_token)
+ resolve(msg.data.msg);
+ });
+ });
+ };
+
+ const testCases = [
+ {
+ previous_origin: window.origin,
+ name: "Popup being navigated to about:blank was same-origin.",
+ },
+ {
+ previous_origin: "http://{{hosts[alt][]}}:{{ports[http][0]}}",
+ name: "Popup being navigated to about:blank was cross-origin.",
+ },
+ ];
+
+ testCases.forEach(testCase => {
+ promise_test(async t => {
+ // Create a popup and navigate it.
+ const popup_token = token();
+ // const popup = window.open("about:blank", testCase.name);
+ const loaded = message_from(popup_token);
+ const popup = window.open(
+ testCase.previous_origin +
+ "/content-security-policy/inheritance/support" +
+ `/postmessage-opener.html?token=${popup_token}`,
+ testCase.name);
+ t.add_cleanup(() => popup.close());
+
+ assert_equals(await loaded, "ready");
+
+ // Navigate the popup to "about:blank".
+ window.open("about:blank", testCase.name);
+ await t.step_wait(
+ condition = () => {
+ try {
+ return popup.location.href == "about:blank";
+ } catch {}
+ return false;
+ },
+ description = "Wait for the popup to navigate.",
+ timeout=3000,
+ interval=50);
+
+ // Now create an img in the popup and check if it is blocked by CSPs.
+ const script = popup.document.createElement('script');
+ script.innerText = `
+ function messageBack(msg) {
+ opener.postMessage(msg ,"*");
+ }
+ `;
+ popup.document.head.appendChild(script);
+ const div = popup.document.createElement('div');
+
+ const img_token = token();
+ const img_url = window.origin + "/content-security-policy/support/fail.png";
+ div.innerHTML = `
+ <img src="${img_url}"
+ onload="messageBack({msg: 'img loaded', token: '${img_token}'});"
+ onerror="messageBack({msg: 'img blocked', token: '${img_token}'});"
+ >
+ `;
+
+ const msg = message_from(img_token);
+ popup.document.body.appendChild(div);
+ assert_equals(await msg, "img blocked");
+ }, testCase.name);
+ });
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/inheritance/window.html b/testing/web-platform/tests/content-security-policy/inheritance/window.html
new file mode 100644
index 0000000000..73def60ceb
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/inheritance/window.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="img-src 'none'">
+
+<body>
+
+<script>
+ function wait_for_error_from_window(w, test) {
+ window.addEventListener('message', test.step_func(e => {
+ if (e.source != w)
+ return;
+ assert_equals(e.data, "error");
+ w.close();
+ test.done();
+ }));
+ }
+
+ async_test(t => {
+ var w = window.open();
+
+ var img = document.createElement('img');
+ img.onerror = t.step_func_done(_ => w.close());
+ img.onload = t.unreached_func();
+ img.src = "/images/red-16x16.png";
+ w.document.body.appendChild(img);
+ }, "window.open() inherits policy.");
+
+ async_test(t => {
+ var w = window.open();
+
+ wait_for_error_from_window(w, t);
+
+ w.document.write(`
+ <img src='/images/red-16x16.png'
+ onload='window.opener.postMessage("load", "*");'
+ onerror='window.opener.postMessage("error", "*");'
+ >
+ `);
+ }, "`document.write` into `window.open()` inherits policy.");
+
+ async_test(t => {
+ var b = new Blob(
+ [`
+ <img src='${window.origin}/images/red-16x16.png'
+ onload='window.opener.postMessage("load", "*");'
+ onerror='window.opener.postMessage("error", "*");'
+ >
+ `], {type:"text/html"});
+
+ wait_for_error_from_window(window.open(URL.createObjectURL(b)), t);
+ }, "window.open('blob:...') inherits policy.");
+
+ // Navigation to top-level `data:` is blocked.
+
+ async_test(t => {
+ var url =
+ `javascript:"<img src='${window.origin}/images/red-16x16.png'
+ onload='window.opener.postMessage(\\"load\\", \\"*\\");'
+ onerror='window.opener.postMessage(\\"error\\", \\"*\\");'
+ >"`;
+
+ wait_for_error_from_window(window.open(url), t);
+ }, "window.open('javascript:...') inherits policy.");
+</script>