diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/html/browsers/origin | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/browsers/origin')
150 files changed, 4819 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html new file mode 100644 index 0000000000..425374faec --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-due-to-document-domain-only.html @@ -0,0 +1,33 @@ +<!doctype html> +<title>Cross-origin due to document.domain</title> +<meta charset=utf-8> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<iframe src=resources/cross-origin-due-to-document-domain-only-helper.html></iframe> +<script> +async_test(t => { + onload = t.step_func_done(() => { + const frame = document.querySelector("iframe"); + const innerSelf = self[0]; + const innerLocation = innerSelf.location; + const innerDocument = innerSelf.document; + assert_equals(innerLocation.host, location.host); + assert_true(innerSelf.expandosForever); + assert_true(innerLocation.expandosForever); + assert_equals(frame.contentWindow, innerSelf); + assert_equals(frame.contentDocument, innerDocument); + innerSelf.setDocumentDomain(); + assert_throws_dom("SecurityError", () => innerSelf.expandosForever); + assert_throws_dom("SecurityError", () => innerLocation.expandosForever); + assert_throws_dom("SecurityError", () => innerLocation.host); + assert_equals(innerSelf.parent, self); + assert_throws_dom("SecurityError", () => innerSelf.frameElement); + assert_throws_dom("SecurityError", () => innerLocation.reload()); + assert_equals(frame.contentWindow, innerSelf); + assert_equals(frame.contentDocument, null); + // Cross-origin Document object obtained before it became cross-origin has no protections + assert_equals(innerDocument.URL, frame.src); + }); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html new file mode 100644 index 0000000000..a8af18d106 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-caching.html @@ -0,0 +1,49 @@ +<!doctype html> +<meta charset=utf-8> +<title>Cross-origin methods and accessors are cached per Realm via[[CrossOriginPropertyDescriptorMap]]</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="cross-origin-objects-function-common.js"></script> +<div id=log></div> +<script> +"use strict"; + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + for (const {key} of crossOriginWindowMethods) { + assert_equals(w[key], w[key], `w.${key} via [[Get]]`); + const desc1 = Object.getOwnPropertyDescriptor(w, key); + const desc2 = Object.getOwnPropertyDescriptor(w, key); + assert_equals(desc1.value, desc2.value, `w.${key} via [[GetOwnProperty]]`); + } +}, "Cross-origin Window methods are cached"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + for (const {key} of crossOriginWindowAccessors) { + const desc1 = Object.getOwnPropertyDescriptor(w, key); + const desc2 = Object.getOwnPropertyDescriptor(w, key); + assert_equals(desc1.get, desc2.get, `w.${key} getter`); + if (key === "location") { + assert_equals(desc1.set, desc2.set, `w.${key} setter`); + } + } +}, "Cross-origin Window accessors are cached"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + assert_equals(w.location.replace, w.location.replace, "via [[Get]]"); + const desc1 = Object.getOwnPropertyDescriptor(w.location, "replace"); + const desc2 = Object.getOwnPropertyDescriptor(w.location, "replace"); + assert_equals(desc1.value, desc2.value, "via [[GetOwnProperty]]"); +}, "Cross-origin Location `replace` method is cached"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + const desc1 = Object.getOwnPropertyDescriptor(w.location, "href"); + const desc2 = Object.getOwnPropertyDescriptor(w.location, "href"); + assert_equals(desc1.set, desc2.set); +}, "Cross-origin Location `href` setter is cached"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js new file mode 100644 index 0000000000..3b93b498a2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-common.js @@ -0,0 +1,34 @@ +"use strict"; + +const crossOriginWindowMethods = [ + {key: "close", length: 0}, + {key: "focus", length: 0}, + {key: "blur", length: 0}, + {key: "postMessage", length: 1}, +]; + +const crossOriginWindowAccessors = [ + "window", + "self", + "location", + "closed", + "frames", + "length", + "top", + "opener", + "parent", +].map(key => ({key})); + +const makeCrossOriginWindow = t => { + const iframe = document.createElement("iframe"); + const path = location.pathname.slice(0, location.pathname.lastIndexOf("/")) + "/frame.html"; + iframe.src = get_host_info().HTTP_REMOTE_ORIGIN + path; + + return new Promise((resolve, reject) => { + iframe.onload = () => { resolve(iframe.contentWindow); }; + iframe.onerror = reject; + + document.body.append(iframe); + t.add_cleanup(() => { iframe.remove(); }); + }); +}; diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html new file mode 100644 index 0000000000..466915a461 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-length.html @@ -0,0 +1,45 @@ +<!doctype html> +<meta charset=utf-8> +<title>Cross-origin methods and accessors are created with correct 'length' property</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="cross-origin-objects-function-common.js"></script> +<div id=log></div> +<script> +"use strict"; + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + for (const {key, length} of crossOriginWindowMethods) { + assert_equals(w[key].length, length, `w.${key} via [[Get]]`); + const desc = Object.getOwnPropertyDescriptor(w, key); + assert_equals(desc.value.length, length, `w.${key} via [[GetOwnProperty]]`); + } +}, "Cross-origin Window methods have correct 'length'"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + for (const {key} of crossOriginWindowAccessors) { + const desc = Object.getOwnPropertyDescriptor(w, key); + assert_equals(desc.get.length, 0, `w.${key}`); + if (key === "location") { + assert_equals(desc.set.length, 1, `w.${key}`); + } + } +}, "Cross-origin Window accessors have correct 'length'"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + assert_equals(w.location.replace.length, 1); + const desc = Object.getOwnPropertyDescriptor(w.location, "replace"); + assert_equals(desc.value.length, 1); +}, "Cross-origin Location `replace` method has correct 'length'"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + const desc = Object.getOwnPropertyDescriptor(w.location, "href"); + assert_equals(desc.set.length, 1); +}, "Cross-origin Location `href` setter has correct 'length'"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html new file mode 100644 index 0000000000..167c30e8f3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-function-name.html @@ -0,0 +1,45 @@ +<!doctype html> +<meta charset=utf-8> +<title>Cross-origin methods and accessors are created with correct 'name' property</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="cross-origin-objects-function-common.js"></script> +<div id=log></div> +<script> +"use strict"; + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + for (const {key} of crossOriginWindowMethods) { + assert_equals(w[key].name, key, `w.${key} via [[Get]]`); + const desc = Object.getOwnPropertyDescriptor(w, key); + assert_equals(desc.value.name, key, `w.${key} via [[GetOwnProperty]]`); + } +}, "Cross-origin Window methods have correct 'name'"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + for (const {key} of crossOriginWindowAccessors) { + const desc = Object.getOwnPropertyDescriptor(w, key); + assert_equals(desc.get.name, `get ${key}`); + if (key === "location") { + assert_equals(desc.set.name, `set ${key}`); + } + } +}, "Cross-origin Window accessors have correct 'name'"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + assert_equals(w.location.replace.name, "replace"); + const desc = Object.getOwnPropertyDescriptor(w.location, "replace"); + assert_equals(desc.value.name, "replace"); +}, "Cross-origin Location `replace` method has correct 'name'"); + +promise_test(async t => { + const w = await makeCrossOriginWindow(t); + const desc = Object.getOwnPropertyDescriptor(w.location, "href"); + assert_equals(desc.set.name, "set href"); +}, "Cross-origin Location `href` setter has correct 'name'"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html new file mode 100644 index 0000000000..3ad0de6a3a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html @@ -0,0 +1,25 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>Cross-origin behavior of Window and Location on new Window</title> +<link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-window"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<script> +setup({explicit_done: true}); + +window.addEventListener('message', function onmessage(evt) { + window.removeEventListener('message', onmessage); + test(function() { + var results = evt.data; + assert_true(results.length > 0, 'Need results'); + results.forEach(function(r) { assert_true(r.pass, r.message); }); + }, "Cross-origin object identity preserved across document.domain"); + win.close(); + done(); +}); +var win = window.open('win-documentdomain.sub.html'); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html new file mode 100644 index 0000000000..d1b6cabc0f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html @@ -0,0 +1,718 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>Cross-origin behavior of Window and Location</title> +<link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-window"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<div id=log></div> +<iframe id="B"></iframe> +<iframe id="C"></iframe> +<iframe id="D"></iframe> +<iframe id="E"></iframe> +<iframe id="F"></iframe> +<iframe id="G"></iframe> +<iframe id="H"></iframe> +<script> + +/* + * Setup boilerplate. This gives us a same-origin window "B", cross-origin + * windows "C" and "D", initially same-origin but then changing document.domain + * windows "E" and "F", and not-same-site (also cross-origin, of course) windows + * "G" and "H". + */ +var host_info = get_host_info(); + +setup({explicit_done: true}); +path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame.html'; +pathWithThen = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame-with-then.html'; +var B = document.getElementById('B').contentWindow; +var C = document.getElementById('C').contentWindow; +var D = document.getElementById('D').contentWindow; +var E = document.getElementById('E').contentWindow; +var F = document.getElementById('F').contentWindow; +var G = document.getElementById('G').contentWindow; +var H = document.getElementById('H').contentWindow; +B.frameElement.uriToLoad = path; +C.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + path; +D.frameElement.uriToLoad = get_host_info().HTTP_REMOTE_ORIGIN + pathWithThen; +E.frameElement.uriToLoad = path + "?setdomain"; +F.frameElement.uriToLoad = pathWithThen + "?setdomain"; +G.frameElement.uriToLoad = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path; +H.frameElement.uriToLoad = get_host_info().HTTP_NOTSAMESITE_ORIGIN + pathWithThen; + +function winName(win) { + var iframes = document.getElementsByTagName('iframe'); + iframes.find = Array.prototype.find; + var found = iframes.find(function (ifr) { + return ifr.contentWindow == win; + }); + if (found) { + return found.id; + } + return "UNKNOWN"; +} + +function reloadSubframes(cb) { + var iframes = document.getElementsByTagName('iframe'); + iframes.forEach = Array.prototype.forEach; + var count = 0; + function frameLoaded() { + this.onload = null; + if (++count == iframes.length) + cb(); + } + iframes.forEach(function(ifr) { ifr.onload = frameLoaded; ifr.setAttribute('src', ifr.uriToLoad); }); +} +function isObject(x) { return Object(x) === x; } + +/* + * Note: we eschew assert_equals in a lot of these tests, since the harness ends + * up throwing when it tries to format a message involving a cross-origin object. + */ + +/* + * List of tests. Each test is actually a pair: an array of tests to run and a + * boolean for whether these are promise tests. We reload all the subframes in + * between running each toplevel test. This is done to avoid having to reload + * all the subframes for every single test, which is overkill: some of these + * tests are known to touch only one subframe. And doing it makes the test + * really slow. + */ +var testList = []; +function addTest(func, desc) { + testList.push( + {tests: [ + {func: func.bind(null, C), + desc: desc + " (cross-origin)"}, + {func: func.bind(null, E), + desc: desc + " (same-origin + document.domain)"}, + {func: func.bind(null, G), + desc: desc + " (cross-site)"}], + promiseTest: false}); +} + +function addPromiseTest(func, desc) { + testList.push( + {tests: [ + {func: func.bind(null, C), + desc: desc + " (cross-origin)"}, + {func: func.bind(null, E), + desc: desc + " (same-origin + document.domain)"}, + {func: func.bind(null, G), + desc: desc + " (cross-site)"}], + promiseTest: true}); +} + +/** + * Similar helpers, but for the subframes that load frame-with-then.html + */ +function addThenTest(func, desc) { + testList.push( + {tests: [ + {func: func.bind(null, D), + desc: desc + " (cross-origin)"}, + {func: func.bind(null, F), + desc: desc + " (same-origin + document.domain)"}, + {func: func.bind(null, H), + desc: desc + " (cross-site)"}], + promiseTest: false}); +} + +function addPromiseThenTest(func, desc) { + testList.push( + {tests: [ + {func: func.bind(null, D), + desc: desc + " (cross-origin)"}, + {func: func.bind(null, F), + desc: desc + " (same-origin + document.domain)"}, + {func: func.bind(null, H), + desc: desc + " (cross-site)"}], + promiseTest: true}); +} + +/* + * Basic smoke tests for same-origin and cross-origin behaviors. + */ + +addTest(function(win) { + // Note: we do not check location.host as its default port semantics are hard to reflect statically + assert_equals(location.hostname, host_info.ORIGINAL_HOST, 'Need to run the top-level test from domain ' + host_info.ORIGINAL_HOST); + assert_equals(get_port(location), host_info.HTTP_PORT, 'Need to run the top-level test from port ' + host_info.HTTP_PORT); + assert_equals(B.parent, window, "window.parent works same-origin"); + assert_equals(win.parent, window, "window.parent works cross-origin"); + assert_equals(B.location.pathname, path, "location.href works same-origin"); + assert_throws_dom("SecurityError", function() { win.location.pathname; }, "location.pathname throws cross-origin"); + assert_equals(B.frames, 'override', "Overrides visible in the same-origin case"); + assert_equals(win.frames, win, "Overrides invisible in the cross-origin case"); + assert_equals(B.focus, 'override', "Overrides visible in the same-origin case"); + checkFunction(win.focus, Function.prototype); + assert_not_equals(win.focus, focus, "Overrides invisible in the cross-origin case"); +}, "Basic sanity-checking"); + +/* + * Tests regarding which properties are allowed cross-origin. + * + * Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior. + */ + +var allowedSymbols = [Symbol.toStringTag, Symbol.hasInstance, + Symbol.isConcatSpreadable]; +var windowAllowlists = { + namedFrames: ['donotleakme'], + indices: ['0', '1'], + getters: ['location', 'window', 'frames', 'self', 'top', 'parent', + 'opener', 'closed', 'length'], + setters: ['location'], + methods: ['postMessage', 'close', 'blur', 'focus'], + // These are methods which return promises and, therefore, when called with a + // cross-origin `this` object, do not throw immediately, but instead return a + // Promise which rejects with the same SecurityError that they would + // otherwise throw. They are not, however, cross-origin accessible. + promiseMethods: ['createImageBitmap', 'fetch'], +} +windowAllowlists.propNames = Array.from(new Set([...windowAllowlists.indices, + ...windowAllowlists.getters, + ...windowAllowlists.setters, + ...windowAllowlists.methods, + 'then'])).sort(); +windowAllowlists.props = windowAllowlists.propNames.concat(allowedSymbols); + +var locationAllowlists = { + getters: [], + setters: ['href'], + methods: ['replace'], + promiseMethods: [], +} +locationAllowlists.propNames = Array.from(new Set([...locationAllowlists.getters, + ...locationAllowlists.setters, + ...locationAllowlists.methods, + 'then'])).sort(); + +// Define various sets of arguments to call cross-origin methods with. Arguments +// for any cross-origin-callable method must be valid, and should aim to have no +// side-effects. Any method without an entry in this list will be called with +// an empty arguments list. +var methodArgs = new Map(Object.entries({ + // As a basic smoke test, we call one cross-origin-inaccessible method with + // both valid and invalid arguments to make sure that it rejects with the + // same SecurityError regardless. + assign: [ + [], + ["javascript:undefined"], + ], + // Note: If we post a message to frame.html with a matching origin, its + // "onmessage" handler will change its `document.domain`, and potentially + // invalidate subsequent tests, so be sure to only pass non-matching origins. + postMessage: [ + ["foo", "http://does-not.exist/"], + ["foo", {}], + ], + replace: [["javascript:undefined"]], +})); + +addTest(function(win) { + for (var prop in window) { + if (windowAllowlists.props.indexOf(prop) != -1) { + win[prop]; // Shouldn't throw. + Object.getOwnPropertyDescriptor(win, prop); // Shouldn't throw. + assert_true(Object.prototype.hasOwnProperty.call(win, prop), "hasOwnProperty for " + String(prop)); + } else { + assert_throws_dom("SecurityError", function() { win[prop]; }, "Should throw when accessing " + String(prop) + " on Window"); + assert_throws_dom("SecurityError", function() { Object.getOwnPropertyDescriptor(win, prop); }, + "Should throw when accessing property descriptor for " + prop + " on Window"); + assert_throws_dom("SecurityError", function() { Object.prototype.hasOwnProperty.call(win, prop); }, + "Should throw when invoking hasOwnProperty for " + prop + " on Window"); + } + if (prop != 'location') + assert_throws_dom("SecurityError", function() { win[prop] = undefined; }, "Should throw when writing to " + prop + " on Window"); + } + for (var prop of windowAllowlists.namedFrames) { + win[prop]; // Shouldn't throw. + var desc = Object.getOwnPropertyDescriptor(win, prop); + assert_false(desc.writable, "[[Writable]] for named frame " + String(prop)); + assert_false(desc.enumerable, "[[Enumerable]] for named frame " + String(prop)); + assert_true(desc.configurable, "[[Configurable]] for named frame " + String(prop)); + assert_true(Object.prototype.hasOwnProperty.call(win, prop), "hasOwnProperty for " + String(prop)); + } + for (var prop in location) { + if (prop == 'replace') { + win.location[prop]; // Shouldn't throw. + Object.getOwnPropertyDescriptor(win.location, prop); // Shouldn't throw. + assert_true(Object.prototype.hasOwnProperty.call(win.location, prop), "hasOwnProperty for " + prop); + assert_throws_dom("SecurityError", function() { win.location[prop] = undefined; }, "Should throw when writing to " + prop + " on Location"); + } + else if (prop == 'href') { + Object.getOwnPropertyDescriptor(win.location, prop); // Shouldn't throw. + assert_true(Object.prototype.hasOwnProperty.call(win.location, prop), "hasOwnProperty for " + prop); + assert_throws_dom("SecurityError", function() { win.location[prop] }, + "Should throw reading href on Location"); + } + else { + assert_throws_dom("SecurityError", function() { win.location[prop]; }, "Should throw when accessing " + prop + " on Location"); + assert_throws_dom("SecurityError", function() { Object.getOwnPropertyDescriptor(win.location, prop); }, + "Should throw when accessing property descriptor for " + prop + " on Location"); + assert_throws_dom("SecurityError", function() { Object.prototype.hasOwnProperty.call(win.location, prop); }, + "Should throw when invoking hasOwnProperty for " + prop + " on Location"); + assert_throws_dom("SecurityError", function() { win.location[prop] = undefined; }, "Should throw when writing to " + prop + " on Location"); + } + } +}, "Only certain properties are accessible cross-origin"); + +addPromiseTest(async function(win, test_obj) { + async function checkProperties(objName, allowedlists) { + var localObj = window[objName]; + var otherObj = win[objName]; + + for (var prop in localObj) { + let desc; + for (let obj = localObj; !desc; obj = Object.getPrototypeOf(obj)) { + desc = Object.getOwnPropertyDescriptor(obj, prop); + + } + + if ("value" in desc) { + if (typeof desc.value === "function" && String(desc.value).includes("[native code]")) { + if (allowedlists.promiseMethods.includes(prop)) { + await promise_rejects_dom(test_obj, "SecurityError", desc.value.call(otherObj), + `Should throw when calling ${objName}.${prop} with cross-origin this object`); + } else if (!allowedlists.methods.includes(prop)) { + for (let args of methodArgs.get(prop) || [[]]) { + assert_throws_dom("SecurityError", desc.value.bind(otherObj, ...args), + `Should throw when calling ${objName}.${prop} with cross-origin this object`); + } + + } else { + for (let args of methodArgs.get(prop) || [[]]) { + desc.value.apply(otherObj, args); // Shouldn't throw. + } + } + } + } else { + if (desc.get) { + if (allowedlists.getters.includes(prop)) { + desc.get.call(otherObj); // Shouldn't throw. + } else { + assert_throws_dom("SecurityError", desc.get.bind(otherObj), + `Should throw when calling ${objName}.${prop} getter with cross-origin this object`); + } + } + if (desc.set) { + if (allowedlists.setters.includes(prop)) { + desc.set.call(otherObj, "javascript:undefined"); // Shouldn't throw. + } else { + assert_throws_dom("SecurityError", desc.set.bind(otherObj, "foo"), + `Should throw when calling ${objName}.${prop} setter with cross-origin this object`); + } + } + } + } + } + + await checkProperties("location", locationAllowlists); + await checkProperties("window", windowAllowlists); +}, "Only certain properties are usable as cross-origin this objects"); + +/* + * ES Internal Methods. + */ + +/* + * [[GetPrototypeOf]] + */ +addTest(function(win) { + assert_equals(Object.getPrototypeOf(win), null, "cross-origin Window proto is null"); + assert_equals(Object.getPrototypeOf(win.location), null, "cross-origin Location proto is null (__proto__)"); + var protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').get; + assert_equals(protoGetter.call(win), null, "cross-origin Window proto is null"); + assert_equals(protoGetter.call(win.location), null, "cross-origin Location proto is null (__proto__)"); + assert_throws_dom("SecurityError", function() { win.__proto__; }, "__proto__ property not available cross-origin"); + assert_throws_dom("SecurityError", function() { win.location.__proto__; }, "__proto__ property not available cross-origin"); + +}, "[[GetPrototypeOf]] should return null"); + +/* + * [[SetPrototypeOf]] + */ +addTest(function(win) { + assert_throws_dom("SecurityError", function() { win.__proto__ = new Object(); }, "proto set on cross-origin Window"); + assert_throws_dom("SecurityError", function() { win.location.__proto__ = new Object(); }, "proto set on cross-origin Location"); + var setters = [Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set]; + if (Object.setPrototypeOf) + setters.push(function(p) { Object.setPrototypeOf(this, p); }); + setters.forEach(function(protoSetter) { + assert_throws_js(TypeError, function() { protoSetter.call(win, new Object()); }, "proto setter |call| on cross-origin Window"); + assert_throws_js(TypeError, function() { protoSetter.call(win.location, new Object()); }, "proto setter |call| on cross-origin Location"); + }); + // Hack to avoid "duplicate test name" harness issues. + setters.forEach(function(protoSetter) { + test(function() { protoSetter.call(win, null); }, + "proto setter |call| on cross-origin Window with null (" + protoSetter + ", " + winName(win) + ")"); + test(function() { protoSetter.call(win.location, null); }, + "proto setter |call| on cross-origin Location with null (" + protoSetter + ", " + winName(win) + ")"); + }); + if (Reflect.setPrototypeOf) { + assert_false(Reflect.setPrototypeOf(win, new Object()), + "Reflect.setPrototypeOf on cross-origin Window"); + assert_true(Reflect.setPrototypeOf(win, null), + "Reflect.setPrototypeOf on cross-origin Window with null"); + assert_false(Reflect.setPrototypeOf(win.location, new Object()), + "Reflect.setPrototypeOf on cross-origin Location"); + assert_true(Reflect.setPrototypeOf(win.location, null), + "Reflect.setPrototypeOf on cross-origin Location with null"); + } +}, "[[SetPrototypeOf]] should return false"); + +/* + * [[IsExtensible]] + */ +addTest(function(win) { + assert_true(Object.isExtensible(win), "cross-origin Window should be extensible"); + assert_true(Object.isExtensible(win.location), "cross-origin Location should be extensible"); +}, "[[IsExtensible]] should return true for cross-origin objects"); + +/* + * [[PreventExtensions]] + */ +addTest(function(win) { + assert_throws_js(TypeError, function() { Object.preventExtensions(win) }, + "preventExtensions on cross-origin Window should throw"); + assert_throws_js(TypeError, function() { Object.preventExtensions(win.location) }, + "preventExtensions on cross-origin Location should throw"); + assert_false(Reflect.preventExtensions(win), + "Reflect.preventExtensions on cross-origin Window"); + assert_false(Reflect.preventExtensions(win.location), + "Reflect.preventExtensions on cross-origin Location"); +}, "[[PreventExtensions]] should return false cross-origin objects"); + +/* + * [[GetOwnProperty]] + */ + +addTest(function(win) { + assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'close')), "win.close is |own|"); + assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'top')), "win.top is |own|"); + assert_true(isObject(Object.getOwnPropertyDescriptor(win.location, 'href')), "win.location.href is |own|"); + assert_true(isObject(Object.getOwnPropertyDescriptor(win.location, 'replace')), "win.location.replace is |own|"); +}, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |own|"); + +function checkPropertyDescriptor(desc, propName, expectWritable) { + const isSymbol = typeof(propName) === "symbol"; + const isArrayIndexPropertyName = !isSymbol && !isNaN(parseInt(propName, 10)); + propName = String(propName); + assert_true(isObject(desc), "property descriptor for " + propName + " should exist"); + assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable"); + if (!isArrayIndexPropertyName) { + assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should not be enumerable"); + if (isSymbol || propName == "then") { + assert_true("value" in desc, + "property descriptor for " + propName + " should be a value descriptor"); + assert_equals(desc.value, undefined, + "symbol-named cross-origin visible prop " + propName + + " should come back as undefined"); + } + } else { + assert_equals(desc.enumerable, true, "property descriptor for " + propName + " should be enumerable"); + } + if ('value' in desc) + assert_equals(desc.writable, expectWritable, "property descriptor for " + propName + " should have writable: " + expectWritable); + else + assert_equals(typeof desc.set != 'undefined', expectWritable, + "property descriptor for " + propName + " should " + (expectWritable ? "" : "not ") + "have setter"); +} + +addTest(function(win) { + windowAllowlists.props.forEach(function(prop) { + var desc = Object.getOwnPropertyDescriptor(win, prop); + checkPropertyDescriptor(desc, prop, prop == 'location'); + }); + checkPropertyDescriptor(Object.getOwnPropertyDescriptor(win.location, 'replace'), 'replace', false); + checkPropertyDescriptor(Object.getOwnPropertyDescriptor(win.location, 'href'), 'href', true); + assert_equals(typeof Object.getOwnPropertyDescriptor(win.location, 'href').get, 'undefined', "Cross-origin location should have no href getter"); + allowedSymbols.forEach(function(prop) { + var desc = Object.getOwnPropertyDescriptor(win.location, prop); + checkPropertyDescriptor(desc, prop, false); + }); +}, "[[GetOwnProperty]] - Property descriptors for cross-origin properties should be set up correctly"); + +addThenTest(function(win) { + assert_equals(typeof win.then, "object"); +}, "[[GetOwnProperty]] - Subframe named 'then' should shadow the default 'then' value"); + +addThenTest(function(win) { + assert_equals(typeof win.close, "function"); + assert_equals(typeof win.open, "object"); +}, "[[GetOwnProperty]] - Subframes should be visible cross-origin only if their names don't match the names of cross-origin-exposed IDL properties"); + +addTest(function(win) { + assert_equals(typeof Object.getOwnPropertyDescriptor(win, '0').value, "object"); + assert_equals(typeof Object.getOwnPropertyDescriptor(win, '1').value, "object"); + assert_throws_dom("SecurityError", function() { + Object.getOwnPropertyDescriptor(win, '2'); + }); +}, "[[GetOwnProperty]] - Should be able to get a property descriptor for an indexed property only if it corresponds to a child window."); + +/* + * [[Delete]] + */ +addTest(function(win) { + assert_throws_dom("SecurityError", function() { delete win[0]; }, "Can't delete cross-origin indexed property"); + assert_throws_dom("SecurityError", function() { delete win[100]; }, "Can't delete cross-origin indexed property"); + assert_throws_dom("SecurityError", function() { delete win.location; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.parent; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.length; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.document; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.foopy; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.location.href; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.location.replace; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.location.port; }, "Can't delete cross-origin property"); + assert_throws_dom("SecurityError", function() { delete win.location.foopy; }, "Can't delete cross-origin property"); +}, "[[Delete]] Should throw on cross-origin objects"); + +/* + * [[DefineOwnProperty]] + */ +function checkDefine(obj, prop) { + var valueDesc = { configurable: true, enumerable: false, writable: false, value: 2 }; + var accessorDesc = { configurable: true, enumerable: false, get: function() {} }; + assert_throws_dom("SecurityError", function() { Object.defineProperty(obj, prop, valueDesc); }, "Can't define cross-origin value property " + prop); + assert_throws_dom("SecurityError", function() { Object.defineProperty(obj, prop, accessorDesc); }, "Can't define cross-origin accessor property " + prop); +} +addTest(function(win) { + checkDefine(win, 'length'); + checkDefine(win, 'parent'); + checkDefine(win, 'location'); + checkDefine(win, 'document'); + checkDefine(win, 'foopy'); + checkDefine(win.location, 'href'); + checkDefine(win.location, 'replace'); + checkDefine(win.location, 'port'); + checkDefine(win.location, 'foopy'); +}, "[[DefineOwnProperty]] Should throw for cross-origin objects"); + +/* + * EnumerateObjectProperties (backed by [[OwnPropertyKeys]]) + */ + +addTest(function(win) { + let i = 0; + for (var prop in win) { + i++; + assert_true(windowAllowlists.indices.includes(prop), prop + " is not safelisted for a cross-origin Window"); + } + assert_equals(i, windowAllowlists.indices.length, "Enumerate all enumerable safelisted cross-origin Window properties"); + i = 0; + for (var prop in win.location) { + i++; + } + assert_equals(i, 0, "There's nothing to enumerate for cross-origin Location properties"); +}, "Can only enumerate safelisted enumerable properties"); + +/* + * [[OwnPropertyKeys]] + */ + +addTest(function(win) { + assert_array_equals(Object.getOwnPropertyNames(win).sort(), + windowAllowlists.propNames, + "Object.getOwnPropertyNames() gives the right answer for cross-origin Window"); + assert_array_equals(Object.keys(win).sort(), + windowAllowlists.indices, + "Object.keys() gives the right answer for cross-origin Window"); + assert_array_equals(Object.getOwnPropertyNames(win.location).sort(), + locationAllowlists.propNames, + "Object.getOwnPropertyNames() gives the right answer for cross-origin Location"); + assert_equals(Object.keys(win.location).length, 0, + "Object.keys() gives the right answer for cross-origin Location"); +}, "[[OwnPropertyKeys]] should return all properties from cross-origin objects"); + +addTest(function(win) { + assert_array_equals(Object.getOwnPropertySymbols(win), allowedSymbols, + "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Window"); + assert_array_equals(Object.getOwnPropertySymbols(win.location), + allowedSymbols, + "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Location"); +}, "[[OwnPropertyKeys]] should return the right symbol-named properties for cross-origin objects"); + +addTest(function(win) { + var allWindowProps = Reflect.ownKeys(win); + indexedWindowProps = allWindowProps.slice(0, windowAllowlists.indices.length); + stringWindowProps = allWindowProps.slice(0, -1 * allowedSymbols.length); + symbolWindowProps = allWindowProps.slice(-1 * allowedSymbols.length); + // stringWindowProps should have "then" last in this case. Do this + // check before we call stringWindowProps.sort() below. + assert_equals(stringWindowProps[stringWindowProps.length - 1], "then", + "'then' property should be added to the end of the string list if not there"); + assert_array_equals(indexedWindowProps, windowAllowlists.indices, + "Reflect.ownKeys should start with the indices exposed on the cross-origin window."); + assert_array_equals(stringWindowProps.sort(), windowAllowlists.propNames, + "Reflect.ownKeys should continue with the cross-origin window properties for a cross-origin Window."); + assert_array_equals(symbolWindowProps, allowedSymbols, + "Reflect.ownKeys should end with the cross-origin symbols for a cross-origin Window."); + + var allLocationProps = Reflect.ownKeys(win.location); + stringLocationProps = allLocationProps.slice(0, -1 * allowedSymbols.length); + symbolLocationProps = allLocationProps.slice(-1 * allowedSymbols.length); + assert_array_equals(stringLocationProps.sort(), locationAllowlists.propNames, + "Reflect.ownKeys should start with the cross-origin window properties for a cross-origin Location.") + assert_array_equals(symbolLocationProps, allowedSymbols, + "Reflect.ownKeys should end with the cross-origin symbols for a cross-origin Location.") +}, "[[OwnPropertyKeys]] should place the symbols after the property names after the subframe indices"); + +addThenTest(function(win) { + var stringProps = Object.getOwnPropertyNames(win); + // Named frames are not exposed via [[OwnPropertyKeys]]. + assert_equals(stringProps.indexOf("a"), -1); + assert_equals(stringProps.indexOf("b"), -1); + assert_equals(typeof win.a, "object"); + assert_equals(typeof win.b, "object"); + assert_equals(stringProps[stringProps.length - 1], "then"); + assert_equals(stringProps.indexOf("then"), stringProps.lastIndexOf("then")); +}, "[[OwnPropertyKeys]] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then'"); + +addTest(function(win) { + assert_equals(B.eval('parent.' + winName(win)), win, "A and B observe the same identity for C's Window"); + assert_equals(B.eval('parent.' + winName(win) + '.location'), win.location, "A and B observe the same identity for C's Location"); +}, "A and B jointly observe the same identity for cross-origin Window and Location"); + +function checkFunction(f, proto) { + var name = f.name || '<missing name>'; + assert_equals(typeof f, 'function', name + " is a function"); + assert_equals(Object.getPrototypeOf(f), proto, f.name + " has the right prototype"); +} + +addTest(function(win) { + checkFunction(win.close, Function.prototype); + checkFunction(win.location.replace, Function.prototype); +}, "Cross-origin functions get local Function.prototype"); + +addTest(function(win) { + assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'parent')), + "Need to be able to use Object.getOwnPropertyDescriptor do this test"); + checkFunction(Object.getOwnPropertyDescriptor(win, 'parent').get, Function.prototype); + checkFunction(Object.getOwnPropertyDescriptor(win.location, 'href').set, Function.prototype); +}, "Cross-origin Window accessors get local Function.prototype"); + +addTest(function(win) { + checkFunction(close, Function.prototype); + assert_not_equals(close, B.close, 'same-origin Window functions get their own object'); + assert_not_equals(close, win.close, 'cross-origin Window functions get their own object'); + var close_B = B.eval('parent.' + winName(win) + '.close'); + assert_not_equals(close, close_B, 'close_B is unique when viewed by the parent'); + assert_not_equals(close_B, win.close, 'different Window functions per-incumbent script settings object'); + checkFunction(close_B, B.Function.prototype); + + checkFunction(location.replace, Function.prototype); + assert_not_equals(location.replace, win.location.replace, "cross-origin Location functions get their own object"); + var replace_B = B.eval('parent.' + winName(win) + '.location.replace'); + assert_not_equals(replace_B, win.location.replace, 'different Location functions per-incumbent script settings object'); + checkFunction(replace_B, B.Function.prototype); +}, "Same-origin observers get different functions for cross-origin objects"); + +addTest(function(win) { + assert_true(isObject(Object.getOwnPropertyDescriptor(win, 'parent')), + "Need to be able to use Object.getOwnPropertyDescriptor do this test"); + var get_self_parent = Object.getOwnPropertyDescriptor(window, 'parent').get; + var get_parent_A = Object.getOwnPropertyDescriptor(win, 'parent').get; + var get_parent_B = B.eval('Object.getOwnPropertyDescriptor(parent.' + winName(win) + ', "parent").get'); + assert_not_equals(get_self_parent, get_parent_A, 'different Window accessors per-incumbent script settings object'); + assert_not_equals(get_parent_A, get_parent_B, 'different Window accessors per-incumbent script settings object'); + checkFunction(get_self_parent, Function.prototype); + checkFunction(get_parent_A, Function.prototype); + checkFunction(get_parent_B, B.Function.prototype); +}, "Same-origin observers get different accessors for cross-origin Window"); + +addTest(function(win) { + var set_self_href = Object.getOwnPropertyDescriptor(window.location, 'href').set; + var set_href_A = Object.getOwnPropertyDescriptor(win.location, 'href').set; + var set_href_B = B.eval('Object.getOwnPropertyDescriptor(parent.' + winName(win) + '.location, "href").set'); + assert_not_equals(set_self_href, set_href_A, 'different Location accessors per-incumbent script settings object'); + assert_not_equals(set_href_A, set_href_B, 'different Location accessors per-incumbent script settings object'); + checkFunction(set_self_href, Function.prototype); + checkFunction(set_href_A, Function.prototype); + checkFunction(set_href_B, B.Function.prototype); +}, "Same-origin observers get different accessors for cross-origin Location"); + +addTest(function(win) { + assert_equals({}.toString.call(win), "[object Object]"); + assert_equals({}.toString.call(win.location), "[object Object]"); +}, "{}.toString.call() does the right thing on cross-origin objects"); + +addPromiseTest(function(win) { + return Promise.resolve(win).then((arg) => { + assert_equals(arg, win); + }); +}, "Resolving a promise with a cross-origin window without a 'then' subframe should work"); + +addPromiseThenTest(function(win) { + return Promise.resolve(win).then((arg) => { + assert_equals(arg, win); + }); +}, "Resolving a promise with a cross-origin window with a 'then' subframe should work"); + +addPromiseThenTest(function(win) { + return Promise.resolve(win.location).then((arg) => { + assert_equals(arg, win.location); + }); +}, "Resolving a promise with a cross-origin location should work"); + +addTest(function(win) { + var desc = Object.getOwnPropertyDescriptor(window, "onmouseenter"); + var f = () => {}; + + // Check that it has [LegacyLenientThis] behavior + assert_equals(desc.get.call({}), undefined, "getter should return undefined"); + desc.set.call({}, f); // Should not throw. + + // Check that we can apply it to a same-origin window. + assert_equals(desc.get.call(B), null, "Should be able to read the value"); + desc.set.call(B, f); + assert_equals(desc.get.call(B), f, "Value should have updated"); + // And reset it for our next test + desc.set.call(B, null); + assert_equals(desc.get.call(B), null, "Should have been reset"); + + // Check that applying it to a cross-origin window throws instead of doing + // the [LegacyLenientThis] behavior. + assert_throws_dom("SecurityError", () => { + desc.get.call(win); + }, "Should throw when getting cross-origin"); + assert_throws_dom("SecurityError", () => { + desc.set.call(win, f); + }, "Should throw when setting cross-origin"); +}, "LegacyLenientThis behavior"); + +// We do a fresh load of the subframes for each test to minimize side-effects. +// It would be nice to reload ourselves as well, but we can't do that without +// disrupting the test harness. +function testDone() { + if (testList.length != 0) { + reloadSubframes(runNextTest); + } else { + done(); + } +} + +async function runNextTest() { + var entry = testList.shift(); + if (entry.promiseTest) { + for (let t of entry.tests) { + await new Promise(resolve => { + promise_test(test_obj => { + return new Promise(res => res(t.func(test_obj))).finally(resolve); + }, t.desc); + }); + } + } else { + for (let t of entry.tests) { + test(t.func, t.desc); + } + } + testDone(); +} +reloadSubframes(runNextTest); + +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html new file mode 100644 index 0000000000..3eedeca38a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame-with-then.html @@ -0,0 +1,19 @@ +<!doctype html> +<html> + <script> + if (location.search == "?setdomain") { + document.domain = document.domain; + } + </script> + <body> + <!--- Some frames to test ordering --> + <iframe name="a"></iframe> + <!-- A subframe to test "then" behavior --> + <iframe name="then"></iframe> + <iframe name="b"></iframe> + <!-- Two subframes with names corresponding to IDL-defined properties; one + a cross-origin-exposed property and one not exposed cross-origin --> + <iframe name="close"></iframe> + <iframe name="open"></iframe> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html new file mode 100644 index 0000000000..ca2dd8ebf8 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html @@ -0,0 +1,51 @@ +<!doctype html> +<html> +<head> +<script> + if (location.search == "?setdomain") { + document.domain = document.domain; + } + + // Override the |frames| and |focus| property to test that such overrides are + // properly ignored cross-origin. + window.frames = "override"; + window.focus = "override"; + + // Also add a |then| property to test that it doesn't get exposed. + window.then = "something"; + window.location.then = "something-else"; + + // If we get a postMessage, we grab references to everything and set + // document.domain to trim off our topmost subdomain. + window.onmessage = function(evt) { + window.windowReferences = []; + window.locationReferences = []; + for (var i = 0; i < parent.length; ++i) { + windowReferences.push(parent[i]); + locationReferences.push(parent[i].location); + } + try { + document.domain = document.domain.substring(document.domain.indexOf('.') + 1); + evt.source.postMessage('PASS', '*'); + } catch (e) { + evt.source.postMessage('FAIL: cannot trim off document.domain: ' + e, '*'); + } + } + + function checkWindowReferences() { + for (var i = 0; i < parent.length; ++i) { + if (windowReferences[i] != parent[i]) + throw new Error("Window references don't match for " + i + " after document.domain"); + if (locationReferences[i] != parent[i].location) + throw new Error("Location references don't match for " + i + " after document.domain"); + } + return true; + } +</script> +</head> +<body> + <!-- Two subframes to give us some indexed properties --> + <iframe></iframe> + <iframe name=donotleakme></iframe><!-- "donotleakme" is excluded as cross-origin named property due to [[HideFromKeys]] --> +</body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.html new file mode 100644 index 0000000000..1d1c1136a6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title> + Verify that certain location interface properties are protected cross-origin. +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> + +<body></body> <!-- Needed to append the iframe --> + +<script> + +const iframeToken = token(); +const responseToken = token(); +const iframeUrl = + get_host_info().REMOTE_ORIGIN + + "/common/dispatcher/executor.html?uuid=" + + iframeToken; + +const iframe = document.createElement("iframe"); +iframe.src = iframeUrl; +document.body.appendChild(iframe); + +[ + "assign", + "customproperty", + "hash", + "host", + "hostname", + "pathname", + "port", + "protocol", + "reload", + "search", + "toString", + "valueOf", +].forEach(property => { + promise_test(async t => { + // Make sure the cross-origin document is loaded in the iframe. + send(iframeToken, `send("${responseToken}", "Responsive");`); + assert_equals(await receive(responseToken), "Responsive"); + + assert_throws_dom("SecurityError", () => { + const unused = iframe.contentWindow.location[property]; + }, "Cross origin get of a location property should throw a security error"); + + assert_throws_dom("SecurityError", () => { + iframe.contentWindow.location[property] = "Random string"; + }, "Cross origin set of a location property should throw a security error"); + + // Verify that the property was indeed not modified. + send(iframeToken, `send("${responseToken}", location["${property}"])`); + assert_true(await receive(responseToken) != "Random string"); + + assert_throws_dom("SecurityError", () => { + const unused = Object.getOwnPropertyDescriptor( + iframe.contentWindow.location, property); + }, "Cross origin get of descriptors should throw a security error"); + }, `Verifying that cross-origin access of '${property}' is restricted`); +}); + +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html new file mode 100644 index 0000000000..10ac8ece0e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/resources/cross-origin-due-to-document-domain-only-helper.html @@ -0,0 +1,9 @@ +<!doctype html> +<meta charset=utf-8> +<script> +self.expandosForever = true +self.location.expandosForever = true +function setDocumentDomain() { + document.domain = document.domain +} +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html new file mode 100644 index 0000000000..a315e21208 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/win-documentdomain.sub.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/common/get-host-info.sub.js"></script> + <script> + function loadFrames() { + window.A = document.getElementById('A').contentWindow; + window.B = document.getElementById('B').contentWindow; + window.C = document.getElementById('C').contentWindow; + window.D = document.getElementById('D').contentWindow; + + var path = location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/frame.html'; + A.location = 'frame.html'; + B.location = '//{{domains[www2]}}:' + get_port(location) + path; + C.location = '//{{domains[www2]}}:' + get_port(location) + path; + D.location = '//{{domains[www1]}}:' + get_port(location) + path; + + var loadCount = 0; + function frameLoaded() { + if (++loadCount == 4) + go(); + } + var iframes = document.getElementsByTagName('iframe'); + for (var i = 0; i < iframes.length; i++) { + iframes[i].onload = frameLoaded; + } + } + + var results = []; + function assert(cond, msg) { + results.push({pass: !!cond, message: msg}); + } + + function go() { + window.onmessage = function(evt) { + try { + assert(evt.data == "PASS", "frame.html processing should be PASS but got " + evt.data); + assert(B.checkWindowReferences(), "B's Window references are still self-consistent after document.domain"); + for (var i = 0; i < window.length; ++i) { + assert(window[i] === B.windowReferences[i], + "Window reference " + i + " consistent between globals after document.domain"); + assert(window[i].location === B.locationReferences[i], + "Location reference " + i + " consistent between globals after document.domain"); + } + } catch(e) { + assert(false, "Should not receive exception: " + e); + } + opener.postMessage(results, '*'); + }; + A.document.domain = A.document.domain; + document.domain = document.domain; + B.postMessage('', '*'); + } + + </script> +</head> +<body onload="loadFrames()"> + <iframe id="A"></iframe> + <iframe id="B"></iframe> + <iframe id="C"></iframe> + <iframe id="D"></iframe> +</body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html new file mode 100644 index 0000000000..f03550a141 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/window-location-and-location-href-cross-realm-set.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Cross-realm [[Set]] to window.location and location.href throws an error of correct realm</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#window"> +<link rel="help" href="https://webidl.spec.whatwg.org/#Unforgeable"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<body> +<script> +const URL_SAME_ORIGIN = get_host_info().ORIGINAL_HOST; +const URL_CROSS_ORIGIN = get_host_info().HTTP_REMOTE_ORIGIN; +const URL_VALID = "#foo"; +const URL_INVALID = "http://#"; + +const { get: locationGet, set: locationSet } = Object.getOwnPropertyDescriptor(window, "location"); +const { get: hrefGet, set: hrefSet } = Object.getOwnPropertyDescriptor(location, "href"); + + +promise_test(async t => { + const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN); + assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow).location; }); + assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.get(sameOriginWindow, "location", {}); }); + assert_throws_js(TypeError, () => { locationGet.call({}); }); +}, "Same-origin window.location getter throws TypeError in holder's realm on invalid |this| value"); + +promise_test(async t => { + const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN); + assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow.location).href; }); + assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.get(sameOriginWindow.location, "href", {}); }); + assert_throws_js(TypeError, () => { hrefGet(); }); +}, "Same-origin location.href getter throws TypeError in holder's realm on invalid |this| value"); + +promise_test(async t => { + const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN); + assert_throws_dom("SECURITY_ERR", () => { crossOriginWindow.location.href; }); + assert_throws_dom("SECURITY_ERR", () => { hrefGet.call(crossOriginWindow.location); }); + assert_equals(Object.getOwnPropertyDescriptor(crossOriginWindow.location, "href").get, undefined); +}, "Cross-origin location.href getter throws SecurityError in lexical realm"); + + +promise_test(async t => { + const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN); + assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow).location = URL_VALID; }); + assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.set(sameOriginWindow, "location", URL_VALID, {}); }); + assert_throws_js(TypeError, () => { locationSet.call(() => {}, URL_VALID); }); +}, "Same-origin window.location setter throws TypeError in holder's realm on invalid |this| value"); + +promise_test(async t => { + const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN); + assert_throws_js(sameOriginWindow.TypeError, () => { Object.create(sameOriginWindow.location).href = URL_VALID; }); + assert_throws_js(sameOriginWindow.TypeError, () => { Reflect.set(sameOriginWindow.location, "href", URL_VALID, {}); }); + assert_throws_js(TypeError, () => { hrefSet.call(undefined, URL_VALID); }); +}, "Same-origin location.href setter throws TypeError in holder's realm on invalid |this| value"); + +promise_test(async t => { + const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN); + assert_throws_js(TypeError, () => { Object.create(crossOriginWindow).location = URL_VALID; }); + assert_throws_js(TypeError, () => { Reflect.set(crossOriginWindow, "location", URL_VALID, {}); }); + assert_throws_js(TypeError, () => { locationSet.call([], URL_VALID); }); +}, "Cross-origin window.location setter throws TypeError in lexical realm on invalid |this| value"); + +promise_test(async t => { + const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN); + assert_throws_js(TypeError, () => { Object.create(crossOriginWindow.location).href = URL_VALID; }); + assert_throws_js(TypeError, () => { Reflect.set(crossOriginWindow.location, "href", URL_VALID, {}); }); + assert_throws_js(TypeError, () => { hrefSet.call(null, URL_VALID); }); +}, "Cross-origin location.href setter throws TypeError in lexical realm on invalid |this| value"); + + +promise_test(async t => { + const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN); + assert_throws_js(sameOriginWindow.TypeError, () => { sameOriginWindow.location = Symbol(); }); + + // The error originates in sameOriginWindow.location.href setter, hence it's not in realm of locationSet. + assert_throws_js(sameOriginWindow.TypeError, () => { locationSet.call(sameOriginWindow, Symbol()); }); +}, "Same-origin window.location` setter throws TypeError in holder's realm on non-coercible URL argument"); + +promise_test(async t => { + const sameOriginWindow = await makeWindow(t, URL_SAME_ORIGIN); + assert_throws_js(sameOriginWindow.TypeError, () => { sameOriginWindow.location.href = Symbol(); }); + assert_throws_js(TypeError, () => { hrefSet.call(sameOriginWindow.location, Symbol()); }); +}, "Same-origin location.href setter throws TypeError in holder's realm on non-coercible URL argument"); + +promise_test(async t => { + const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN); + assert_throws_js(TypeError, () => { crossOriginWindow.location = Symbol(); }); + assert_throws_js(TypeError, () => { locationSet.call(crossOriginWindow, Symbol()); }); +}, "Cross-origin window.location setter throws TypeError in lexical realm on non-coercible URL argument"); + +promise_test(async t => { + const crossOriginWindow = await makeWindow(t, URL_CROSS_ORIGIN); + assert_throws_js(TypeError, () => { crossOriginWindow.location.href = Symbol(); }); + assert_throws_js(TypeError, () => { hrefSet.call(crossOriginWindow.location, Symbol()); }); +}, "Cross-origin location.href setter throws TypeError in lexical realm on non-coercible URL argument"); + +function makeWindow(t, src) { + return new Promise(resolve => { + const iframe = document.createElement("iframe"); + t.add_cleanup(() => { iframe.remove(); }); + iframe.onload = () => { resolve(iframe.contentWindow); }; + iframe.src = src; + document.body.append(iframe); + }); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html new file mode 100644 index 0000000000..fabde327a1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-iframe.html @@ -0,0 +1,28 @@ +<!doctype html> +<html> + <head> + <title>about:blank in child browsing context aliases security origin</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(() => { + let iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + // Should not throw: srcdoc should always be same-origin. + iframe.contentWindow.document.body.innerHTML = '<p>Hello world!</p>'; + + // Explicitly set `domain` component of origin: any other same-origin + // browsing contexts are now cross-origin unless they also explicitly + // set document.domain to the same value. + document.domain = document.domain; + // Should not throw: the origin should be aliased, so setting + // document.domain in one Document should affect both Documents. + assert_equals( + iframe.contentWindow.document.body.textContent, + 'Hello world!'); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html new file mode 100644 index 0000000000..cc3177f943 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/inheritance/about-blank-window.html @@ -0,0 +1,25 @@ +<!doctype html> +<html> + <head> + <title>about:blank in auxiliary browsing context aliases security origin</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(() => { + let newWindow = window.open(); + // Should not throw: the newly-opened window should be same-origin. + newWindow.document.body.innerHTML = '<p>Hello world!</p>'; + + // Explicitly set `domain` component of origin: any other same-origin + // browsing contexts are now cross-origin unless they also explicitly + // set document.domain to the same value. + document.domain = document.domain; + // Should not throw: the origin should be aliased, so setting + // document.domain in one Document should affect both Documents. + assert_equals(newWindow.document.body.textContent, 'Hello world!'); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html b/testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html new file mode 100644 index 0000000000..971811ee66 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/inheritance/about-srcdoc.html @@ -0,0 +1,29 @@ +<!doctype html> +<html> + <head> + <title>about:srcdoc aliases security origin</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(() => { + let iframe = document.createElement('iframe'); + iframe.srcdoc = '<body></body>'; + document.body.appendChild(iframe); + // Should not throw: srcdoc should always be same-origin. + iframe.contentWindow.document.body.innerHTML = '<p>Hello world!</p>'; + + // Explicitly set `domain` component of origin: any other same-origin + // browsing contexts are now cross-origin unless they also explicitly + // set document.domain to the same value. + document.domain = document.domain; + // Should not throw: the origin should be aliased, so setting + // document.domain in one Document should affect both Documents. + assert_equals( + iframe.contentWindow.document.body.textContent, + 'Hello world!'); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html b/testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html new file mode 100644 index 0000000000..7dfb1130ce --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/inheritance/javascript-url.html @@ -0,0 +1,33 @@ +<!doctype html> +<html> + <head> + <title>javascript: aliases security origin</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + promise_test(t => { + let iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + // Should not throw: srcdoc should always be same-origin. + iframe.contentDocument; + + iframe.contentWindow.location = 'javascript:"Hello world!"'; + return new Promise(resolve => { + iframe.addEventListener('load', resolve); + }).then(() => { + // Explicitly set `domain` component of origin: any other same-origin + // browsing contexts are now cross-origin unless they also explicitly + // set document.domain to the same value. + document.domain = document.domain; + // Should not throw: the origin should be aliased, so setting + // document.domain in one Document should affect both Documents. + assert_equals( + iframe.contentWindow.document.body.textContent, + 'Hello world!'); + }); + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html new file mode 100644 index 0000000000..3a45ee6d6a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-bad-subdomain.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, child attempts to origin-key but uses a bad header value, child is a subdomain of the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let frameIndex = 0; +for (const badValue of ["", "?0", "true", "\"?1\"", "1", "?2", "(?1)"]) { + promise_test(async () => { + await insertIframe("{{hosts[][www]}}", badValue); + }, `"${badValue}": frame insertion`); + + // Since the header values are bad they should be site-keyed. + testSameAgentCluster([self, frameIndex], `"${badValue}"`); + testGetter(frameIndex, false, `"${badValue}"`); + ++frameIndex; +} +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html new file mode 100644 index 0000000000..85fb1f64e0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, child is origin-keyed, child is different-origin to the parent because of a port mismatch</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][]}}:{{ports[https][1]}}", "?1"); +}); + +// Since they're different-origin, the child's request is respected, so the +// parent ends up in the site-keyed agent cluster and the child in the +// origin-keyed one. +testDifferentAgentClusters([self, 0]); + +testGetter(self, false, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html new file mode 100644 index 0000000000..7ece02c81a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-same.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, child is origin-keyed, child is same-origin to the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][]}}", "?1"); +}); + +// Since they're same-origin, and the parent loaded in the site-keyed agent +// cluster, the child's request for origin-keying gets ignored, and both end up +// site-keyed. +testSameAgentCluster([self, 0]); + +testGetter(self, false, "parent"); +testGetter(0, false, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html new file mode 100644 index 0000000000..994f80876d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain-with-redirect.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, child is reached via a redirect response with no header, child final response does have the header</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][www]}}", "?1", { redirectFirst: true }); +}); + +// Since they're different-origin, the child's request is respected, so the +// parent ends up in the site-keyed agent cluster and the child in the +// origin-keyed one. +testDifferentAgentClusters([self, 0]); + +testGetter(self, false, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..5fc2fa29f3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-subdomain.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, child is origin-keyed, child is a subdomain of the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][www]}}", "?1"); +}); + +// Since they're different-origin, the child's request is respected, so the +// parent ends up in the site-keyed agent cluster and the child in the +// origin-keyed one. +testDifferentAgentClusters([self, 0]); + +testGetter(self, false, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html new file mode 100644 index 0000000000..3e7c8419b3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yeswithparams-subdomain.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, child is origin-keyed using parameters on its structured header, child is a subdomain of the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][www]}}", "?1;param1;param2=value2"); +}); + +// Since they're different-origin, the child's request is respected, so the +// parent ends up in the site-keyed agent cluster and the child in the +// origin-keyed one. +testDifferentAgentClusters([self, 0]); + +testGetter(self, false, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html new file mode 100644 index 0000000000..f00814cfbf --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child is site-keyed, child is is different-origin to the parent because of a port mismatch</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][]}}:{{ports[https][1]}}"); +}); + +// Since they're different-origin, the parent's request is respected, as is the +// child's non-request. So the parent ends up in the origin-keyed agent cluster +// and the child ends up in the site-keyed one. +testDifferentAgentClusters([self, 0]); + +testGetter(self, true, "parent"); +testGetter(0, false, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-port.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html new file mode 100644 index 0000000000..307f8c48d7 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child is site-keyed, child is same-origin to the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][]}}"); +}); + +// Since they're same-origin, and the parent loaded with origin-keying, the +// child's non-request gets ignored, and both end up origin-keyed. +testSameAgentCluster([self, 0]); + +testGetter(self, true, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-same.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html new file mode 100644 index 0000000000..8c823fa36f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child is site-keyed, child is a subdomain of the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][www]}}"); +}); + +// Since they're different-origin, the parent's request is respected, as is the +// child's non-request. So the parent ends up in the origin-keyed agent cluster +// and the child ends up in the site-keyed one. +testDifferentAgentClusters([self, 0]); + +testGetter(self, true, "parent"); +testGetter(0, false, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-no-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html new file mode 100644 index 0000000000..5e431e6e41 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child is site-keyed, child is different-origin to the parent because of a port mismatch</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][]}}:{{ports[https][1]}}", "?1"); +}); + +// Both request origin-keying, so the parent ends up in one origin-keyed agent +// cluster (the default port's origin), and the child ends up in a different +// origin-keyed agent cluster (the other port's origin). +testDifferentAgentClusters([self, 0]); + +testGetter(self, true, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-port.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html new file mode 100644 index 0000000000..3b8c214a61 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child is site-keyed, child is same-origin to the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][]}}", "?1"); +}); + +// Both request origin-keying, and they're same-origin, so they both end up in +// the same origin-keyed agent cluster. +testSameAgentCluster([self, 0]); + +testGetter(self, true, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-same.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..136a3a0bba --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child is site-keyed, child is a subdomain of the parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][www]}}", "?1"); +}); + +// Both request origin-keying, so the parent ends up in one origin-keyed agent +// cluster (the base domain's origin), and the child ends up in a different +// origin-keyed agent cluster (the www subdomain's origin). +testDifferentAgentClusters([self, 0]); + +testGetter(self, true, "parent"); +testGetter(0, true, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-yes-child-yes-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..1bb252f0ab --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomain.sub.https.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, subdomain child 1 is site-keyed, same-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Must be sequential, not parallel: the site-keyed frame must load first. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www]}}", "?1"); +}); + + +// Since they're different-origin, the parent's non-request is respected, as is +// child 1's non-request. child 2 requests origin-keying but is ignored, since +// child 1 is in the same browsing context group. +// +// So, everyone ends up in the site-keyed agent cluster. +testSameAgentCluster([self, 0], "Parent to child1"); +testSameAgentCluster([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, false, "parent"); +testGetter(0, false, "child1"); +testGetter(1, false, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html new file mode 100644 index 0000000000..5b80c528f0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, subdomain child 1 is site-keyed, different-port-same-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www]}}:{{ports[https][1]}}", "?1"); +}); + +// Since everybody is different-origin, everyone's requests/non-requests get +// respected. +// +// So, the parent and child 1 end up in the site-keyed agent cluster, and child +// 2 ends up in its own origin-keyed agent cluster. +testSameAgentCluster([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, false, "parent"); +testGetter(0, false, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html new file mode 100644 index 0000000000..bba13b82a4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain1-child2-yes-subdomain2.sub.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, subdomain child 1 is site-keyed, different-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www1]}}", "?1"); +}); + +// Since everybody is different-origin, everyone's requests/non-requests get +// respected. +// +// So, the parent and child 1 end up in the site-keyed agent cluster, and child +// 2 ends up in its own origin-keyed agent cluster. +testSameAgentCluster([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, false, "parent"); +testGetter(0, false, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html new file mode 100644 index 0000000000..d01d180213 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-port.sub.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, subdomain child 1 is origin-keyed, non-subdomain but different-port child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][]}}:{{ports[https][1]}}"); +}); + +// Everyone is different-origin, so everyone's request/non-request is +// respected. +// +// So, the parent and child2 end up in the site-keyed agent cluster, and child1 +// ends up in an origin-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testSameAgentCluster([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, false, "parent"); +testGetter(0, true, "child1"); +testGetter(1, false, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html new file mode 100644 index 0000000000..9a245b3ace --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, subdomain child 1 is origin-keyed, same-subdomain child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Must be sequential, not parallel: the origin-keyed frame must load first. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][www]}}"); +}); + + +// Since they're different-origin, the parent's non-request is respected, as is +// child 1's request. child 2's non-request is ignored, since child 1 is in the +// same browsing context group. +// +// So, the parent ends up in the site-keyed agent cluster, and both children end +// up in an origin-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, false, "parent"); +testGetter(0, true, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html new file mode 100644 index 0000000000..c308b9a17a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is site-keyed, same-subdomain child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www]}}"); +}); + + +// Since everybody is different-origin, everyone's requests/non-requests get +// respected. +// +// So, the parent ends up in its origin-keyed agent cluster, and child 1 and +// child 2 both end up in the site-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, false, "child1"); +testGetter(1, false, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html new file mode 100644 index 0000000000..767d908c21 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is site-keyed, different-subdomain child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www1]}}"); +}); + + +// Since everybody is different-origin, everyone's requests/non-requests get +// respected. +// +// So, the parent ends up in its origin-keyed agent cluster, and child 1 and +// child 2 both end up in the site-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, false, "child1"); +testGetter(1, false, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-no-subdomain2.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..45047f3ae1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is site-keyed, same-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Must be sequential, not parallel: the non-isolaed frame must load first. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www]}}", "?1"); +}); + + +// Since they're different-origin, the parent's request is respected, as is +// child 1's non-request. child 2 requests origin-keying but is ignored, since +// child 1 is in the same browsing context group. +// +// So, the parent ends up in the origin-keyed agent cluster, and both children +// ends up in the site-keyed one. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, false, "child1"); +testGetter(1, false, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html new file mode 100644 index 0000000000..202b916767 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is site-keyed, different-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www1]}}", "?1"); +}); + + +// Since everybody is different-origin, everyone's requests/non-requests get +// respected. +// +// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in +// the site-keyed agent cluster, and child 2 ends up in a different origin-keyed +// agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, false, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomain2.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html new file mode 100644 index 0000000000..a1316731ac --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is site-keyed, different-port-same-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}"); + await insertIframe("{{hosts[][www]}}:{{ports[https][1]}}", "?1"); +}); + + +// Since everybody is different-origin, everyone's requests/non-requests get +// respected. +// +// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in +// the site-keyed agent cluster, and child 2 ends up in a different origin-keyed +// agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, false, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-no-subdomain-child2-yes-subdomainport.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html new file mode 100644 index 0000000000..46bef4b9a9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, non-subdomain but different-port child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][]}}:{{ports[https][1]}}"); +}); + +// Everyone is different-origin, so everyone's request/non-request is +// respected. +// +// So, child2 ends up in the site-keyed agent cluster, and the parent and child1 +// end up in two separate origin-keyed agent clusters. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, true, "child1"); +testGetter(1, false, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-port.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html new file mode 100644 index 0000000000..39dcfc04b0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, same-subdomain child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Must be sequential, not parallel: the origin-keyed frame must load first. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][www]}}"); +}); + + +// Since they're different-origin, the parent's request is respected, as is +// child 1's request. child 2's non-request is ignored, since child 1 is in the +// same browsing context group. +// +// So, the parent ends up in the origin-keyed agent cluster, and both children +// ends up in a different origin-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, true, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-no-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..b6daf91b54 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, same-subdomain child 2 is site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][www]}}", "?1"); +}); + + +// Since they're different-origin, the parent's request is respected, as is +// child 1's request. child 2's request is redundant, since child 1 is in the +// same browsing context group. +// +// So, the parent ends up in the origin-keyed agent cluster, and both children +// ends up in a different origin-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, true, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html new file mode 100644 index 0000000000..b94f9392d4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, different-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][www1]}}", "?1"); +}); + + +// Since everybody is different-origin, everyone's requests get +// respected. +// +// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in +// a second origin-keyed agent cluster, and child 2 ends up in a third +// origin-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, true, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomain2.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html new file mode 100644 index 0000000000..fb3fda1bf2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, subdomain child 1 is origin-keyed, different-port-same-subdomain child 2 is origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +promise_setup(async () => { + // Order of loading should not matter, but we make it sequential to ensure the + // tests are deterministic. + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][www]}}:{{ports[https][1]}}", "?1"); +}); + + +// Since everybody is different-origin, everyone's requests get +// respected. +// +// So, the parent ends up in its origin-keyed agent cluster, child 1 ends up in +// a second origin-keyed agent cluster, and child 2 ends up in a third +// origin-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child1"); +testDifferentAgentClusters([self, 1], "Parent to child2"); +testDifferentAgentClusters([0, 1], "child1 to child2"); +testDifferentAgentClusters([1, 0], "child2 to child1"); + +testGetter(self, true, "parent"); +testGetter(0, true, "child1"); +testGetter(1, true, "child2"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml new file mode 100644 index 0000000000..f21ce69f6a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/META.yml @@ -0,0 +1,5 @@ +spec: https://html.spec.whatwg.org/multipage/#origin-keyed-agent-clusters +suggested_reviewers: + - domenic + - annevk + - wjmaclean diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md new file mode 100644 index 0000000000..85ba3bce7f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/README.md @@ -0,0 +1,30 @@ +# Origin-keyed agent clusters tests + +These are tests for the [origin-keyed agent clusters](https://html.spec.whatwg.org/multipage/origin.html#origin-keyed-agent-clusters) +feature. + +## Test filenames + +The tests in `2-iframes` follow the file naming pattern + +``` +parent-[yes|no]-child1-[yes|no]-[designator]-child2-[yes|no]-[designator] +``` + +Here: + +* `yes` or `no` refers to whether the `Origin-Agent-Cluster` header is set or + unset. +* `designator` explains how the child differs from the parent: e.g. by being a + subdomain, or having a different port, or both. There's also `same` if it's + same-origin. + +Other directories have variations on this, e.g. `1-iframe/` does the same thing +but for a single `child` instead of `child1` and `child2`, and `navigation/` +uses `1` and `2` to represent the two different locations the single iframe will +be navigated to. + +## Coverage + +Header parsing is covered by a few tests in the `1-iframe/` subdirectory, and +not duplicated to all other scenarios. diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html new file mode 100644 index 0000000000..556d528aa0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>The initial about:blank respects origin isolation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "./resources/helpers.mjs"; + +promise_setup(async () => { + await insertAboutBlankIframe(); + await insertIframe("{{hosts[][www]}}"); +}); + +// Since the initial about:blank inherits its origin from its parent, it is +// same-origin with the parent, and thus cross-origin with child2. +testSameAgentCluster([self, 0], "parent to about:blank"); +testDifferentAgentClusters([0, 1], "about:blank to child2"); +testDifferentAgentClusters([1, 0], "child2 to about:blank"); + +testGetter(self, true, "parent"); +testGetter(0, true, "about:blank"); +testGetter(1, false, "child2"); + +async function insertAboutBlankIframe() { + const iframe = await createBlankIframe(); + + // Now create and add the script, but don't navigate anywhere (since we want + // to stay on the initial about:blank). + // We need to absolutize the URL to since about:blank doesn't have a base URL. + const scriptURL = (new URL("./resources/send-header-page-script.mjs", import.meta.url)).href; + const script = iframe.contentDocument.createElement("script"); + script.type = "module"; + script.src = scriptURL; + + await new Promise((resolve, reject) => { + script.onload = resolve; + script.onerror = () => reject( + new Error("Could not load the child frame script into the about:blank page") + ); + iframe.contentDocument.body.append(script); + }); + + await setBothDocumentDomains(iframe.contentWindow); +} + +function createBlankIframe() { + const iframe = document.createElement("iframe"); + const promise = new Promise(resolve => { + iframe.addEventListener("load", resolve); + }); + document.body.append(iframe); + return promise; +} +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/about-blank.https.sub.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html new file mode 100644 index 0000000000..b4535d9e54 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Setting document.domain does not change same-originness when origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<!-- + Other tests check that using document.domain doesn't allow cross-origin + access. This test ensures a different, more subtle property: that + origin-keying makes document.domain into a no-op in other ways. +--> + +<iframe src="resources/frame.html"></iframe> +<iframe src="//{{domains[www1]}}:{{location[port]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html"></iframe> + +<script type="module"> +setup({ explicit_done: true }); + +window.onload = () => { + test(() => { + // Normally, setting document.domain to itself would change the domain + // component of the origin. Since the iframe does *not* set document.domain, + // the two would then be considered cross-origin. + document.domain = document.domain; + + // However, because we're origin-keyed, this shouldn't have any impact. The + // test fails if this throws, and passes if it succeeds. + frames[0].document; + }, "Setting document.domain must not change same-originness"); + + test(() => { + assert_throws_dom("SecurityError", () => { + document.domain = "{{hosts[][nonexistent]}}"; + }); + }, "The registrable domain suffix check must happen before the bail-out"); + + async_test(t => { + frames[1].postMessage({ + type: "set document.domain", + newValue: "{{host}}" + }, "*"); + + window.onmessage = t.step_func_done(e => { + assert_equals(e.data.type, "new document.domain"); + assert_equals(e.data.result, "{{domains[www1]}}"); + }); + }, "Having an origin-keyed subdomain child try to set document.domain " + + "must not change the document.domain value it sees"); + + done(); +}; +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/document-domain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html new file mode 100644 index 0000000000..a521934cc9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster must be implied by cross-origin isolation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<iframe src="//{{domains[www1]}}:{{location[port]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html"></iframe> + +<div id="log"></div> + +<script type="module"> +import { testGetter } from "../resources/helpers.mjs"; + +setup({ explicit_done: true }); + +window.onload = () => { + // Cross-origin isolated pages are always origin-keyed. + testGetter(self, true, "self"); + + // Child frames of cross-origin isolated pages must also be cross-origin + // isolated, and thus also origin-keyed. Make sure the implementation doesn't + // treat them specially in some weird way, for the purposes of this + // implication. + testGetter(0, true, "child"); + + done(); +}; +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers new file mode 100644 index 0000000000..5f8621ef83 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/cross-origin-isolated.sub.https.html.headers @@ -0,0 +1,2 @@ +Cross-Origin-Embedder-Policy: require-corp +Cross-Origin-Opener-Policy: same-origin diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html new file mode 100644 index 0000000000..e0b5f92376 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a top-level frame sandboxed by CSP with no Origin-Agent-Cluster header</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { testGetter } from "../resources/helpers.mjs"; + +// Even without the header, sandboxing makes this page have an opaque origin, +// so it is origin-keyed. +testGetter(self, true); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers new file mode 100644 index 0000000000..4705ce9ded --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-no.https.html.headers @@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-scripts; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html new file mode 100644 index 0000000000..a2220c5acc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a top-level frame sandboxed by CSP with an Origin-Agent-Cluster header</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { testGetter } from "../resources/helpers.mjs"; + +// We're definitely origin-keyed: both the CSP sandboxing and the +// Origin-Agent-Cluster header should ensure this. +testGetter(self, true); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers new file mode 100644 index 0000000000..a52bf50900 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/csp-sandbox-yes.https.html.headers @@ -0,0 +1,2 @@ +Content-Security-Policy: sandbox allow-scripts; +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html new file mode 100644 index 0000000000..06149cda8a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-no.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a javascript: URL navigated to from a data: URL on a site-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/data-to-javascript-test.mjs"; +runTest(); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html new file mode 100644 index 0000000000..af6fea0ad9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a javascript: URL navigated to from a data: URL on an origin-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/data-to-javascript-test.mjs"; +runTest(); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-to-javascript-yes.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html new file mode 100644 index 0000000000..8ae564a072 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-no.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a data: URL on a site-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/data-url-test.mjs"; +runTest(); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html new file mode 100644 index 0000000000..bcbf098a66 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a data: URL on an origin-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/data-url-test.mjs"; +runTest(); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/data-url-yes.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html new file mode 100644 index 0000000000..1b54ad42a4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-no.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a javascript: URL on a site-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/data-url-test.mjs"; +runTest({ expected: false }); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html new file mode 100644 index 0000000000..e2b7730dd2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a javascript: URL on an origin-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/data-url-test.mjs"; +runTest({ expected: true }); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/javascript-url-yes.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html new file mode 100644 index 0000000000..fcf5068908 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a removed frame</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { navigateIframe } from "../resources/helpers.mjs"; + +promise_test(async () => { + // We cannot use insertIframe because it sets both `document.domain`s. That + // shouldn't matter, but Chrome has a bug (https://crbug.com/1095145), so + // let's avoid making the test needlessly fail because of that bug. + const iframe = document.createElement("iframe"); + const navigatePromise = navigateIframe(iframe, "{{hosts[][]}}", "?1"); + document.body.append(iframe); + await navigatePromise; + + const frameWindow = iframe.contentWindow; + + assert_equals(frameWindow.originAgentCluster, true, "before"); + iframe.remove(); + assert_equals(frameWindow.originAgentCluster, true, "after"); +}, "Removing the iframe does not change originAgentCluster"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/removed-iframe.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs new file mode 100644 index 0000000000..3a88253ee3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-to-javascript-test.mjs @@ -0,0 +1,33 @@ +import { insertCustomIframe, testSupportScript } from "./helpers.mjs"; +import { waitForIframe, testGetter } from "../../resources/helpers.mjs"; + +const testSupportScriptSuitableForNesting = + testSupportScript.replace('</script>', '</scri` + `pt>'); + +export default () => { + promise_setup(async () => { + const jsURL = `javascript:'${testSupportScript}'`; + const iframe = await insertCustomIframe(`data:text/html, + Start page + <script> + window.onmessage = () => { + location.href = \`javascript:'End page${testSupportScriptSuitableForNesting}'\`; + }; + </script> + `); + + const waitPromise = waitForIframe(iframe, "javascript: URL"); + + // Kick off the navigation. We can't do it directly because only same-origin + // pages can navigate to a javascript: URL, and we're not same-origin with + // a data: URL. + iframe.contentWindow.postMessage(undefined, "*"); + + await waitPromise; + }); + + // The javascript: URL iframe inherits its origin from the previous occupant + // of the iframe, which is a data: URL, so it should always be true. + + testGetter(0, true); +}; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs new file mode 100644 index 0000000000..1a9b3be47f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/data-url-test.mjs @@ -0,0 +1,13 @@ +import { insertCustomIframe, testSupportScript } from "./helpers.mjs"; +import { testGetter } from "../../resources/helpers.mjs"; + +export default () => { + promise_setup(() => { + return insertCustomIframe(`data:text/html,${testSupportScript}`); + }); + + // The data: URL iframe has an opaque origin, so it should return true, since + // for them site === origin so they are always origin-keyed. + + testGetter(0, true, "data: URL child"); +}; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs new file mode 100644 index 0000000000..4610ffcad0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/helpers.mjs @@ -0,0 +1,28 @@ +import { waitForIframe } from "../../resources/helpers.mjs"; + +/** + * Inserts an iframe, not specialized for origin-keyed agent cluster testing, + * pointing to a custom URL. This is just a wrapper to remove some boilerplate. + * @param {string} src - The src="" value for the iframe + */ +export async function insertCustomIframe(src) { + const iframe = document.createElement("iframe"); + iframe.src = src; + + const waitPromise = waitForIframe(iframe); + document.body.append(iframe); + await waitPromise; + + return iframe; +} + +/** + * This is the part of send-oac-header.py that allows us to reuse testGetter. + */ +export const testSupportScript = ` + <script> + window.onmessage = () => { + parent.postMessage(self.originAgentCluster, "*"); + }; + </script> +`; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs new file mode 100644 index 0000000000..de474d8caf --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/javascript-url-test.mjs @@ -0,0 +1,14 @@ +import { insertCustomIframe, testSupportScript } from "./helpers.mjs"; +import { testGetter } from "../../resources/helpers.mjs"; + +export default ({ expected }) => { + promise_setup(() => { + return insertCustomIframe(`javascript:'${testSupportScript}'`); + }); + + // The javascript: URL iframe inherits its origin from the previous occupant + // of the iframe, which is about:blank, which in turn inherits from the + // parent. So, the caller needs to tell us what to expect. + + testGetter(0, expected); +}; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs new file mode 100644 index 0000000000..9357df00c5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-iframe-test.sub.mjs @@ -0,0 +1,20 @@ +import { + navigateIframe, + testGetter +} from "../../resources/helpers.mjs"; + +export default () => { + // We do this manually instead of using insertIframe because we want to add a + // sandbox="" attribute and we don't want to set both document.domains. + promise_setup(() => { + const iframe = document.createElement("iframe"); + iframe.sandbox = "allow-scripts"; + const navigatePromise = navigateIframe(iframe, "{{hosts[][]}}", "?1"); + document.body.append(iframe); + return navigatePromise; + }); + + // Sandboxed iframes have an opaque origin, so it should return true, since + // for them site === origin so they are always origin-keyed. + testGetter(0, true); +}; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs new file mode 100644 index 0000000000..272f805870 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/resources/sandboxed-same-origin-iframe-test.sub.mjs @@ -0,0 +1,20 @@ +import { + navigateIframe, + testGetter +} from "../../resources/helpers.mjs"; + +export default ({ expected }) => { + // We do this manually instead of using insertIframe because we want to add a + // sandbox="" attribute and we don't want to set both document.domains. + promise_setup(() => { + const iframe = document.createElement("iframe"); + iframe.sandbox = "allow-scripts allow-same-origin"; + const navigatePromise = navigateIframe(iframe, "{{hosts[][]}}", "?1"); + document.body.append(iframe); + return navigatePromise; + }); + + // Since the allow-same-origin token is set, this should behave like a normal + // iframe, and follow the embedder. + testGetter(0, expected); +}; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html new file mode 100644 index 0000000000..29758a17b8 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-no.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a sandboxed iframe on a site-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/sandboxed-iframe-test.sub.mjs"; +runTest(); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html new file mode 100644 index 0000000000..5eb5d08d10 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a sandboxed iframe on an origin-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/sandboxed-iframe-test.sub.mjs"; +runTest(); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-iframe-yes.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html new file mode 100644 index 0000000000..3ed4096f39 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-no.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a sandboxed, but same-origin, iframe on a site-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/sandboxed-same-origin-iframe-test.sub.mjs"; +runTest({ expected: false }); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html new file mode 100644 index 0000000000..c7ea5f0693 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>window.originAgentCluster for a sandboxed, but same-origin, iframe on an origin-keyed page</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import runTest from "./resources/sandboxed-same-origin-iframe-test.sub.mjs"; +runTest({ expected: true }); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/getter-special-cases/sandboxed-same-origin-iframe-yes.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html new file mode 100644 index 0000000000..a593619ea6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, child1 is site-keyed, child1 navigates to a different site, child2 gets inserted and is origin-keyed, child1 navigates back</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + waitForIframe, + setBothDocumentDomains, + testDifferentAgentClusters, + testSameAgentCluster, +} from "./resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][www]}}"); +}); + +// Since they're different-origin, the parent's origin-keying request is +// respected, as is the child's non-request. So the parent ends up in the +// origin-keyed agent cluster and the child ends up in the site-keyed one. +testDifferentAgentClusters([self, 0], "Before navigation: parent to child1"); + +// Navigate the iframe to a different site. These, of course, must not be in the +// same agent cluster. + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[alt][]}}"); +}, "Navigation"); + +// Now insert a second iframe, pointing to the same place as the first one +// originally did, but this time with origin-keying requested. Because of the +// historical map of agent cluster keys for the browsing context group, the new +// iframe should still end up in the site-keyed agent cluster. + +promise_test(async () => { + await insertIframe("{{hosts[][www]}}", "?1"); +}, "Inserting a second iframe"); + +testDifferentAgentClusters([self, 1], "After navigation: parent to child2"); + +// Now navigate the first iframe back. The resulting Document should be put in +// the site-keyed agent cluster, together with the second iframe's Document. + +promise_test(async () => { + const waitPromise = waitForIframe(frame1); + history.back(); + await waitPromise; + + await setBothDocumentDomains(frames[0]); +}, "Going back in history (navigating back the first iframe)"); + +testDifferentAgentClusters([self, 0], "After back: parent to child1"); +testDifferentAgentClusters([self, 1], "After back: parent to child2"); +testSameAgentCluster([0, 1], "child1 to child2"); +testSameAgentCluster([1, 0], "child2 to child1"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html new file mode 100644 index 0000000000..8237f2f23f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-port.sub.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, navigate a frame from same-origin site-keyed to different-origin (different-port) origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][]}}"); +}); + +// Nobody requested origin-keying yet. + +testSameAgentCluster([self, 0], "Before: parent to child"); +testGetter(self, false, "before parent"); +testGetter(0, false, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][]}}:{{ports[https][1]}}", "?1"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Since the new page is different-origin, its origin-keying request should be +// respected. + +testDifferentAgentClusters([self, 0], "After: parent to child"); +testGetter(self, false, "after parent"); +testGetter(0, true, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..00d8c3164a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-same-2-yes-subdomain.sub.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, navigate a frame from same-origin site-keyed to different-origin (subdomain) origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][]}}"); +}); + +// Nobody requested origin-keying yet. + +testSameAgentCluster([self, 0], "Before: parent to child"); +testGetter(self, false, "before parent"); +testGetter(0, false, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][www]}}", "?1"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Since the new page is different-origin, its origin-keying request should be +// respected. + +testDifferentAgentClusters([self, 0], "After: parent to child"); +testGetter(self, false, "after parent"); +testGetter(0, true, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..803e684e1c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain.sub.https.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, navigate a frame from a subdomain site-keyed to the same subdomain origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][www]}}"); +}); + +// Nobody requested origin-keying yet. + +testSameAgentCluster([self, 0], "Before: parent to child"); +testGetter(self, false, "before parent"); +testGetter(0, false, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][www]}}", "?1"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Because this subdomain was previously site-keyed, the second load's +// origin-keying request is ignored; instead we continue with site-keying. + +testSameAgentCluster([self, 0], "After: parent to child"); +testGetter(self, false, "after parent"); +testGetter(0, false, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html new file mode 100644 index 0000000000..b96d10afd1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-no-subdomain-2-yes-subdomain2.sub.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, navigate a frame from a subdomain site-keyed to a second-subdomain origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][www]}}"); +}); + +// Nobody requested origin-keying yet. + +testSameAgentCluster([self, 0], "Before: parent to child"); +testGetter(self, false, "before parent"); +testGetter(0, false, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][www1]}}", "?1"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Because we're going to a different subdomain (and thus different origin), the +// origin-keying request is respected. + +testDifferentAgentClusters([self, 0], "After: parent to child"); +testGetter(self, false, "after parent"); +testGetter(0, true, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html new file mode 100644 index 0000000000..a70ed56670 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-subdomain-yes-2-subdomain2-no.sub.https.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, navigate a frame from a subdomain origin-keyed to a second-subdomain site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][www]}}", "?1"); +}); + +// Since they are different-origin, the child's origin-keying request is +// respected. + +testDifferentAgentClusters([self, 0], "Before: parent to child"); +testGetter(self, false, "before parent"); +testGetter(0, true, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][www1]}}"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Make sure that the different-subdomain page (which doesn't request +// origin-keying) doesn't somehow get origin-keyed just because its predecessor +// was. + +testSameAgentCluster([self, 0], "After: parent to child"); +testGetter(self, false, "after parent"); +testGetter(0, false, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html new file mode 100644 index 0000000000..38e2630128 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-no-1-yes-subdomain-2-no-subdomain.sub.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is site-keyed, navigate a frame from a subdomain origin-keyed to the same subdomain site-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][www]}}", "?1"); +}); + +// Since they are different-origin, the child's origin-keying request is +// respected. + +testDifferentAgentClusters([self, 0], "Before: parent to child"); +testGetter(self, false, "before parent"); +testGetter(0, true, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][www]}}"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Because this subdomain was previously origin-keyed, the second load's +// non-request is ignored; instead we continue origin-keying. + +testDifferentAgentClusters([self, 0], "After: parent to child"); +testGetter(self, false, "after parent"); +testGetter(0, true, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html new file mode 100644 index 0000000000..6211845be1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, navigate a frame from same-origin site-keyed to different-origin (different-port) origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][]}}"); +}); + +// Since the parent is origin-keyed, the same-origin child's non-request is +// ignored, so it gets origin-keyed too. + +testSameAgentCluster([self, 0], "Before: parent to child"); +testGetter(self, true, "before parent"); +testGetter(0, true, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][]}}:{{ports[https][1]}}"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Since the new page is different-origin, its non-request should be respected. + +testDifferentAgentClusters([self, 0], "After: parent to child"); +testGetter(self, true, "after parent"); +testGetter(0, false, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-port.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html new file mode 100644 index 0000000000..ead56754a7 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent is origin-keyed, navigate a frame from same-origin site-keyed to different-origin (subdomain) origin-keyed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + navigateIframe, + setBothDocumentDomains, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "../resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][]}}"); +}); + +// Since the parent is origin-keyed, the same-origin child's non-request is +// ignored, so it gets origin-keyed too. + +testSameAgentCluster([self, 0], "Before: parent to child"); +testGetter(self, true, "before parent"); +testGetter(0, true, "before child"); + +promise_test(async () => { + await navigateIframe(frame1, "{{hosts[][www]}}"); + await setBothDocumentDomains(frames[0]); +}, "Navigation"); + +// Since the new page is different-origin, its non-request should be respected. + +testDifferentAgentClusters([self, 0], "After: parent to child"); +testGetter(self, true, "after parent"); +testGetter(0, false, "after child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/iframe-navigation/parent-yes-1-no-same-2-no-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html new file mode 100644 index 0000000000..6f9e5d8b73 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Parent requests origin-keying, child requests origin-keying, child is a subdomain of the parent, but all over insecure HTTP</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testGetter +} from "./resources/helpers.mjs"; + +promise_setup(async () => { + await insertIframe("{{hosts[][www]}}", "?1"); +}); + +// All origin-keying requests are ignored, since this is over insecure HTTP. +// So both end up in the site-keyed agent cluster. +testSameAgentCluster([self, 0]); + +testGetter(self, false, "parent"); +testGetter(0, false, "child"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/insecure-http.sub.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html new file mode 100644 index 0000000000..dcfb5eb277 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Crash test for https://crbug.com/1099718</title> + +<div id="log"></div> + +<script> +window.open("resources/crashy-popup.sub.html", "windowName1", "noopener"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html new file mode 100644 index 0000000000..a0bf569b12 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-port.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is site-keyed, openee is origin-keyed, openee is different-origin to the opener because of a port mismatch</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInADifferentAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}", "?1"); +}); + +// Since they're different-origin, the openee's origin-keying request is +// respected, so the opener ends up in the site-keyed agent cluster and the +// openee in the origin-keyed one. +testOpenedWindowIsInADifferentAgentCluster(() => openee); + +testGetter(self, false, "opener"); +testGetter(() => openee, true, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html new file mode 100644 index 0000000000..196dff1449 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-same.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is site-keyed, openee is origin-keyed, openee is same-origin to the opener</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][]}}", "?1"); +}); + +// Since they're same-origin, and the opener loaded with site-keying, the +// child's request for origin-keying gets ignored, and both end up site-keyed. +testOpenedWindowIsInSameAgentCluster(() => openee); + +testGetter(self, false, "opener"); +testGetter(() => openee, false, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..f96d2273d5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-no-openee-yes-subdomain.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is site-keyed, openee is origin-keyed, openee is a subdomain of the opener</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInADifferentAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][www]}}", "?1"); +}); + +// Since they're different-origin, the openee's origin-keying request is +// respected, so the opener ends up in the site-keyed agent cluster and the +// openee in the origin-keyed one. +testOpenedWindowIsInADifferentAgentCluster(() => openee); + +testGetter(self, false, "opener"); +testGetter(() => openee, true, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html new file mode 100644 index 0000000000..51c5a208c5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is origin-keyed, openee is site-keyed, openee is different-origin to the opener because of a port mismatch</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInADifferentAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}"); +}); + +// Since they're different-origin, the openee's non-request is respected, so the +// opener ends up in the origin-keyed agent cluster and the openee in the +// site-keyed one. +testOpenedWindowIsInADifferentAgentCluster(() => openee); + +testGetter(self, true, "opener"); +testGetter(() => openee, false, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-port.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html new file mode 100644 index 0000000000..562ab40c68 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is origin-keyed, openee is site-keyed, openee is same-origin to the opener</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][]}}"); +}); + +// Since they're same-origin, the openee's non-request is ignored, so both end +// up in the origin-keyed agent cluster. +testOpenedWindowIsInSameAgentCluster(() => openee); + +testGetter(self, true, "opener"); +testGetter(() => openee, true, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-same.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html new file mode 100644 index 0000000000..d7d4791459 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is origin-keyed, openee is site-keyed, openee is a subdomain of the opener</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInADifferentAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][www]}}"); +}); + +// Since they're different-origin, the openee's non-request is respected, so the +// opener ends up in the origin-keyed agent cluster and the openee in the +// site-keyed one. +testOpenedWindowIsInADifferentAgentCluster(() => openee); + +testGetter(self, true, "opener"); +testGetter(() => openee, false, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-no-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html new file mode 100644 index 0000000000..32a5066d2e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is origin-keyed, openee is origin-keyed, openee is different-origin to the opener because of a port mismatch</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInADifferentAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}", "?1"); +}); + +// Both request origin-keying, so the opener ends up in one origin-keyed agent +// cluster (the default port's origin), and the openee ends up in a different +// origin-keyed agent cluster (the other port's origin). +testOpenedWindowIsInADifferentAgentCluster(() => openee); + +testGetter(self, true, "opener"); +testGetter(() => openee, true, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-port.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html new file mode 100644 index 0000000000..a85decac3c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is origin-keyed, openee is origin-keyed, openee is same-origin to the opener</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInSameAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][]}}", "?1"); +}); + +// Both request origin-keying, and they're same-origin, so they both end up in +// the same origin-keyed agent cluster. +testOpenedWindowIsInSameAgentCluster(() => openee); + +testGetter(self, true, "opener"); +testGetter(() => openee, true, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-same.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html new file mode 100644 index 0000000000..148b39af23 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Opener is origin-keyed, openee is origin-keyed, openee is a subdomain of the opener</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + openWindow, + testOpenedWindowIsInADifferentAgentCluster, + testGetter +} from "../resources/helpers.mjs"; + +let openee; +promise_setup(async () => { + openee = await openWindow("{{hosts[][www]}}", "?1"); +}); + +// Both request origin-keyed, so the opener ends up in one origin-keyed agent +// cluster (the base domain's origin), and the openee ends up in a different +// origin-keyed agent cluster (the www subdomain's origin). +testOpenedWindowIsInADifferentAgentCluster(() => openee); + +testGetter(self, true, "opener"); +testGetter(() => openee, true, "openee"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/popups/opener-yes-openee-yes-subdomain.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html new file mode 100644 index 0000000000..d0b09f335d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/regression-1399759.https.sub.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<html> +<head> +<meta name="variant" content="?pipe=header(Origin-Agent-Cluster,%3F0)"> +<meta name="variant" content="?pipe=header(Origin-Agent-Cluster,%3F1)"> +<title>Origin-Isolation after navigating about:blank.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> +</head> +<body> +</body> +<script> +// Regression test for crbug.com/1399759. This is mainly based on +// html/infrastructure/urls/terminology-0/document-base-url-initiated-grand-parent.https.window.html, +// but restricts itself to the exact error condition. +// +// This test is run in two variants which differ in the Origin-Agent-Cluster +// http header values, ?0 and ?1. The test should pass in either case, but the +// regression we're testing for involves inconsistent clustering decisions, +// which requires clustering to be enabled in the first place. +promise_test(async test => { + // Create a cross-origin iframe. Use the executor.html, so we can ask it + // to execute scripts for us. + const child_token = token(); + const iframe = document.createElement("iframe"); + iframe.src = get_host_info().HTTPS_REMOTE_ORIGIN + + `/common/dispatcher/executor.html?uuid=${child_token}`; + document.body.appendChild(iframe); + + // The child creates a grand child in an iframe. + const reply_token = token(); + send(child_token, ` + const iframe = document.createElement("iframe"); + iframe.src = "/common/blank.html"; + iframe.onload = () => { + send("${reply_token}", "grand child loaded"); + }; + document.body.appendChild(iframe); + `); + assert_equals(await receive(reply_token), "grand child loaded"); + const grandchild = iframe.contentWindow[0]; + + // Navigate the grand-child toward about:blank. + grandchild.location = "about:blank"; + assert_equals(await receive(reply_token), "grand child loaded"); + + // This document and grandchild are same-origin, because about:blank + // inherits its origin from the initiator of the navigation, which is us. + // This access should not throw. + grandchild.document; +}, "Check the baseURL of an about:blank document cross-origin with its parent"); + +promise_test(async test => { + // This tests the same setup as above, but with about:srcdoc. Since one + // cannot just navigate to about:srcdoc, we'll have to include an extra + // step: Create an iframe with srcdoc attribute; navigate away; then + // navigate to about:srcdoc. + // srcdoc does not inherit the origin from the initiator - unlike + // about:blank - and so in this case the grandchild.document access should + // throw. + + // Create a cross-origin iframe. Use the executor.html, so we can ask it + // to execute scripts for us. + const child_token = token(); + const iframe = document.createElement("iframe"); + iframe.src = get_host_info().HTTPS_REMOTE_ORIGIN + + `/common/dispatcher/executor.html?uuid=${child_token}`; + document.body.appendChild(iframe); + + // The child creates a grand child in an iframe, using the srcdoc attribute. + const reply_token = token(); + send(child_token, ` + const iframe = document.createElement("iframe"); + iframe.onload = () => { + send("${reply_token}", "grand child loaded"); + }; + iframe.srcdoc = "nothing interesting"; + document.body.appendChild(iframe); + `); + assert_equals(await receive(reply_token), "grand child loaded"); + const grandchild = iframe.contentWindow[0]; + + // Navigate the grand child toward a regular URL. + grandchild.location = get_host_info().HTTPS_REMOTE_ORIGIN + "/common/blank.html"; + assert_equals(await receive(reply_token), "grand child loaded"); + + // Navigate the grand-child back, to about:srcdoc. + grandchild.location = "about:srcdoc"; + assert_equals(await receive(reply_token), "grand child loaded"); + + // This document and grandchild are cross-origin. about:srcdoc does not + // inherits its origin from the initiator of the navigation. This access + // should throw: + assert_throws_dom("SecurityError", () => { grandchild.document; }); +}, "Check that about:srcdoc navigation does not follow about:blank rules."); +</script> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html new file mode 100644 index 0000000000..b83aa9f5be --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>A site-keyed child at a given origin causes future children to also be site-keyed even after the iframe is removed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id="log"></div> + +<script type="module"> +import { + insertIframe, + testSameAgentCluster, + testDifferentAgentClusters, + testGetter +} from "./resources/helpers.mjs"; + +let frame1; +promise_setup(async () => { + frame1 = await insertIframe("{{hosts[][www]}}"); +}); + +// Since they're different-origin, the parent's origin-keying request is +// respected, as is the child's non-request. So the parent ends up in the +// origin-keyed agent cluster and the child ends up in the site-keyed one. +testDifferentAgentClusters([self, 0], "Before"); +testGetter(self, true, "parent"); +testGetter(0, false, "child1"); + +promise_test(async () => { + frame1.remove(); + + await insertIframe("{{hosts[][www]}}", "?1"); + await insertIframe("{{hosts[][www1]}}"); +}, "Remove the iframe and insert new ones"); + +// Because of the historical presence of a site-keyed {{hosts[][www]}} iframe, +// the origin-keying request for child 2 will be ignored. So, +// child 2 and child 3 both end up in the site-keyed agent cluster. +testDifferentAgentClusters([self, 0], "Parent to child2"); +testDifferentAgentClusters([self, 1], "Parent to child3"); +testSameAgentCluster([0, 1], "child2 to child3"); +testSameAgentCluster([1, 0], "child3 to child2"); + +testGetter(0, false, "child2"); +testGetter(1, false, "child3"); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/removing-iframes.sub.https.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md new file mode 100644 index 0000000000..9292fe3894 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/README.md @@ -0,0 +1,6 @@ +Why are there `.headers` files here for the `.mjs` scripts? + +Because `../getter-special-cases/sandboxed-iframe.sub.https.html` is testing an +opaque origin, which is cross-origin with these scripts. Since +`<script type="module">` respects the same-origin policy, we need CORS headers +to allow them to be accessed. diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html new file mode 100644 index 0000000000..7cbd89b943 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>A page with COEP set that will respond when asked</title> + +<script type="module" src="send-header-page-script.mjs"></script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers new file mode 100644 index 0000000000..4e798cd9f5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html.headers @@ -0,0 +1,2 @@ +Cross-Origin-Embedder-Policy: require-corp +Cross-Origin-Resource-Policy: cross-origin diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html new file mode 100644 index 0000000000..45c8d5074d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>This page helps exhibit a crash bug when window.open()ed (see ../popups-crash.https.html)</title> + +<iframe src="https://{{hosts[][www]}}:{{ports[https][0]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py"></iframe> +<iframe src="https://{{hosts[][www]}}:{{ports[https][0]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py?header=?1"></iframe> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/crashy-popup.sub.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html new file mode 100644 index 0000000000..537de07b14 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>A frame included by a test page</title> + +<script> +window.onmessage = e => { + if (e.data.type === "set document.domain") { + document.domain = e.data.newValue; + e.source.postMessage({ type: "new document.domain", result: document.domain }, "*"); + } +}; +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers new file mode 100644 index 0000000000..79a20f30fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/frame.html.headers @@ -0,0 +1 @@ +Origin-Agent-Cluster: ?1 diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs new file mode 100644 index 0000000000..6bad76e3d9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs @@ -0,0 +1,390 @@ +/** + * Inserts an iframe usable for origin-keyed agent cluster testing, and returns + * a promise fulfilled when the iframe is loaded and its document.domain is set. + * The iframe will point to the send-oac-header.py file, on the designated + * host. + * @param {string} host - The host used to calculate the iframe's src="" + * @param {string=} header - The value of the Origin-Agent-Cluster header that + * the iframe will set. Omit this to set no header. + * @param {object=} options - Rarely-used options. + * @param {boolean=} options.redirectFirst - Whether to do a 302 redirect first + * before arriving at the page that sets the header. The redirecting page will + * not set the Origin-Agent-Cluster header. + * @returns {HTMLIFrameElement} The created iframe element + */ +export async function insertIframe(host, header, { redirectFirst = false } = {}) { + const iframe = document.createElement("iframe"); + const navigatePromise = navigateIframe(iframe, host, header, { redirectFirst }); + document.body.append(iframe); + await navigatePromise; + await setBothDocumentDomains(iframe.contentWindow); + return iframe; +} + +/** + * Navigates an iframe to a page for origin-keyed agent cluster testing, similar + * to insertIframe but operating on an existing iframe. + * @param {HTMLIFrameElement} iframeEl - The <iframe> element to navigate + * @param {string} host - The host to calculate the iframe's new src="" + * @param {string=} header - The value of the Origin-Agent-Cluster header that + * the newly-navigated-to page will set. Omit this to set no header. + * @param {object=} options - Rarely-used options. + * @param {boolean=} options.redirectFirst - Whether to do a 302 redirect first + * before arriving at the page that sets the header. The redirecting page will + * not set the Origin-Agent-Cluster header. + * @returns {Promise} a promise fulfilled when the load event fires, or rejected + * if the error event fires + */ +export function navigateIframe(iframeEl, host, header, { redirectFirst = false } = {}) { + const url = getSendHeaderURL(host, header, { redirectFirst }); + + const waitPromise = waitForIframe(iframeEl, url); + iframeEl.src = url; + return waitPromise; +} + +/** + * Returns a promise that is fulfilled when an iframe's load event fires, or + * rejected when its error event fires. + * @param {HTMLIFrameElement} iframeEl - The <iframe> element to wait on + * @param {string} destinationForErrorMessage - A string used in the promise + * rejection error message, if the error event fires + * @returns {Promise} a promise fulfilled when the load event fires, or rejected + * if the error event fires + */ +export function waitForIframe(iframeEl, destinationForErrorMessage) { + return new Promise((resolve, reject) => { + iframeEl.addEventListener("load", () => resolve()); + iframeEl.addEventListener( + "error", + () => reject(new Error(`Could not navigate to ${destinationForErrorMessage}`)) + ); + }); +} + +/** + * Opens a new window usable for origin-keyed agent cluster testing, and returns + * a promise fulfilled when the window is loaded and its document.domain is set. + * The window will point to the send-oac-header.py file, on the designated host. + * + * The opened window will be automatically closed when all the tests complete. + * @param {string} host - The host used to calculate the window's URL + * @param {string=} header - The value of the Origin-Agent-Cluster header that + * the opened window's page will set. Omit this to set no header. + * @returns {WindowProxy} The created window + */ +export async function openWindow(host, header) { + const url = getSendHeaderURL(host, header, { sendLoadedMessage: true }); + const openedWindow = window.open(url); + + add_completion_callback(() => openedWindow.close()); + + const whatHappened = await waitForMessage(openedWindow); + assert_equals(whatHappened, "loaded"); + + await setBothDocumentDomains(openedWindow); + + return openedWindow; +} + +/** + * Expands into a pair of promise_test() calls to ensure that two Windows are in + * the same agent cluster, by checking both that we can send a + * WebAssembly.Module, and that we can synchronously access the DOM. + * @param {Array} testFrames - An array of either the form [self, frameIndex] or + * [frameIndex1, frameIndex2], indicating the two Windows under test. E.g. + * [self, 0] or [0, 1]. + * @param {string=} testLabelPrefix - A prefix used in the test names. This can + * be omitted if testSameAgentCluster is only used once in a test file. + */ +export function testSameAgentCluster(testFrames, testLabelPrefix) { + const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `; + + if (testFrames[0] === self) { + // Between parent and a child at the index given by testFrames[1] + + promise_test(async () => { + const frameWindow = frames[testFrames[1]]; + const frameElement = document.querySelectorAll("iframe")[testFrames[1]]; + + // Must not throw + frameWindow.document; + + // Must not throw + frameWindow.location.href; + + assert_not_equals(frameElement.contentDocument, null, "contentDocument"); + + const whatHappened = await accessFrameElement(frameWindow); + assert_equals(whatHappened, "frameElement accessed successfully"); + }, `${prefix}setting document.domain must give sync access`); + } else { + // Between the two children at the index given by testFrames[0] and + // testFrames[1] + + promise_test(async () => { + const whatHappened1 = await accessDocumentBetween(testFrames); + assert_equals(whatHappened1, "accessed document successfully"); + + const whatHappened2 = await accessLocationHrefBetween(testFrames); + assert_equals(whatHappened2, "accessed location.href successfully"); + + // We don't test contentDocument/frameElement for these because accessing + // those via siblings has to go through the parent anyway. + }, `${prefix}setting document.domain must give sync access`); + } +} + +/** + * Expands into a pair of promise_test() calls to ensure that two Windows are in + * different agent clusters, by checking both that we cannot send a + * WebAssembly.Module, and that we cannot synchronously access the DOM. + * @param {Array} testFrames - An array of either the form [self, frameIndex] or + * [frameIndex1, frameIndex2], indicating the two Windows under test. E.g. + * [self, 0] or [0, 1]. + * @param {string=} testLabelPrefix - A prefix used in the test names. This can + * be omitted if testDifferentAgentClusters is only used once in a test file. + */ +export function testDifferentAgentClusters(testFrames, testLabelPrefix) { + const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `; + + if (testFrames[0] === self) { + // Between parent and a child at the index given by testFrames[1] + + promise_test(async () => { + // In general, cross-origin sharing of WebAssembly.Module is prohibited, + // so if we're in different agent clusters, it's definitely prohibited. + // Basic tests for this cross-origin prohibition are elsewhere; we include + // these here as an extra check to make sure there's no weird interactions + // with Origin-Agent-Cluster. + const frameWindow = frames[testFrames[1]]; + const whatHappened = await sendWasmModule(frameWindow); + + assert_equals(whatHappened, "messageerror"); + }, `${prefix}messageerror event must occur`); + + promise_test(async () => { + const frameWindow = frames[testFrames[1]]; + const frameElement = document.querySelectorAll("iframe")[testFrames[1]]; + + assert_throws_dom("SecurityError", DOMException, () => { + frameWindow.document; + }); + + assert_throws_dom("SecurityError", DOMException, () => { + frameWindow.location.href; + }); + + assert_equals(frameElement.contentDocument, null, "contentDocument"); + + const whatHappened = await accessFrameElement(frameWindow); + assert_equals(whatHappened, "null"); + }, `${prefix}setting document.domain must not give sync access`); + } else { + // Between the two children at the index given by testFrames[0] and + // testFrames[1] + + promise_test(async () => { + const whatHappened = await sendWasmModuleBetween(testFrames); + assert_equals(whatHappened, "messageerror"); + }, `${prefix}messageerror event must occur`); + + promise_test(async () => { + const whatHappened1 = await accessDocumentBetween(testFrames); + assert_equals(whatHappened1, "SecurityError"); + + const whatHappened2 = await accessLocationHrefBetween(testFrames); + assert_equals(whatHappened2, "SecurityError"); + + // We don't test contentDocument/frameElement for these because accessing + // those via siblings has to go through the parent anyway. + }, `${prefix}setting document.domain must not give sync access`); + } +} + +/** + * Expands into a pair of promise_test() calls to ensure that the given window, + * opened by window.open(), is in a different agent cluster from the current + * (opener) window. + * @param {function} openedWindowGetter - A function that returns the opened + * window + */ +export function testOpenedWindowIsInADifferentAgentCluster(openedWindowGetter) { + promise_test(async () => { + const whatHappened = await sendWasmModule(openedWindowGetter()); + + assert_equals(whatHappened, "messageerror"); + }, `messageerror event must occur`); + + promise_test(async () => { + assert_throws_dom("SecurityError", DOMException, () => { + openedWindowGetter().document; + }); + + assert_throws_dom("SecurityError", DOMException, () => { + openedWindowGetter().location.href; + }); + }, `setting document.domain must not give sync access`); +} + +/** + * Expands into a pair of promise_test() calls to ensure that the given window, + * opened by window.open(), is in the same agent cluster as the current + * (opener) window. + * @param {function} openedWindowGetter - A function that returns the opened + * window + */ +export function testOpenedWindowIsInSameAgentCluster(openedWindowGetter) { + promise_test(async () => { + const whatHappened = await sendWasmModule(openedWindowGetter()); + + assert_equals(whatHappened, "WebAssembly.Module message received"); + }, `message event must occur`); + + promise_test(async () => { + // Must not throw + openedWindowGetter().document; + + // Must not throw + openedWindowGetter().location.href; + }, `setting document.domain must give sync access`); +} + +/** + * Creates a promise_test() to check the value of the originAgentCluster getter + * in the given testFrame. + * @param {Window|number|function} testFrame - Either self, or a frame index to + test, or a function that returns a Window to test. + * @param {boolean} expected - The expected value for originAgentCluster. + * @param {string=} testLabelPrefix - A prefix used in the test names. This can + * be omitted if the function is only used once in a test file. + */ +export function testGetter(testFrame, expected, testLabelPrefix) { + const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `; + + promise_test(async () => { + if (testFrame === self) { + assert_equals(self.originAgentCluster, expected); + } else if (typeof testFrame === "number") { + const frameWindow = frames[testFrame]; + const result = await accessOriginAgentCluster(frameWindow); + assert_equals(result, expected); + } else { + assert_equals(typeof testFrame, "function", + "testFrame argument must be self, a number, or a function"); + const result = await accessOriginAgentCluster(testFrame()); + assert_equals(result, expected); + } + }, `${prefix}originAgentCluster must equal ${expected}`); +} + +/** + * Sends a WebAssembly.Module instance to the given Window, and waits for it to + * send back a message indicating whether it got the module or got a + * messageerror event. (This relies on the given Window being derived from + * insertIframe or navigateIframe.) + * @param {Window} frameWindow - The destination Window + * @returns {Promise} A promise which will be fulfilled with either + * "WebAssembly.Module message received" or "messageerror" + */ +export async function sendWasmModule(frameWindow) { + // This function is coupled to ./send-oac-header.py, which ensures that + // sending such a message will result in a message back. + frameWindow.postMessage(await createWasmModule(), "*"); + return waitForMessage(frameWindow); +} + +/** + * Sets document.domain (to itself) for both the current Window and the given + * Window. The latter relies on the given Window being derived from insertIframe + * or navigateIframe. + * @param frameWindow - The other Window whose document.domain is to be set + * @returns {Promise} A promise which will be fulfilled after both + * document.domains are set + */ +export async function setBothDocumentDomains(frameWindow) { + // By setting both this page's document.domain and the iframe's + // document.domain to the same value, we ensure that they can synchronously + // access each other, unless they are origin-keyed. + // NOTE: document.domain being unset is different than it being set to its + // current value. It is a terrible API. + document.domain = document.domain; + + // This function is coupled to ./send-oac-header.py, which ensures that + // sending such a message will result in a message back. + frameWindow.postMessage({ command: "set document.domain", newDocumentDomain: document.domain }, "*"); + const whatHappened = await waitForMessage(frameWindow); + assert_equals(whatHappened, "document.domain is set"); +} + +async function accessOriginAgentCluster(frameWindow) { + // This function is coupled to ./send-oac-header.py, which ensures that + // sending such a message will result in a message back. + frameWindow.postMessage({ command: "get originAgentCluster" }, "*"); + return waitForMessage(frameWindow); +} + +function getSendHeaderURL(host, header, { sendLoadedMessage = false, redirectFirst = false } = {}) { + const url = new URL("send-oac-header.py", import.meta.url); + url.host = host; + if (header !== undefined) { + url.searchParams.set("header", header); + } + if (sendLoadedMessage) { + url.searchParams.set("send-loaded-message", ""); + } + if (redirectFirst) { + url.searchParams.set("redirect-first", ""); + } + + return url.href; +} + +async function sendWasmModuleBetween(testFrames) { + const sourceFrame = frames[testFrames[0]]; + const indexIntoParentFrameOfDestination = testFrames[1]; + + sourceFrame.postMessage({ command: "send WASM module", indexIntoParentFrameOfDestination }, "*"); + return waitForMessage(sourceFrame); +} + +async function accessDocumentBetween(testFrames) { + const sourceFrame = frames[testFrames[0]]; + const indexIntoParentFrameOfDestination = testFrames[1]; + + sourceFrame.postMessage({ command: "access document", indexIntoParentFrameOfDestination }, "*"); + return waitForMessage(sourceFrame); +} + +async function accessLocationHrefBetween(testFrames) { + const sourceFrame = frames[testFrames[0]]; + const indexIntoParentFrameOfDestination = testFrames[1]; + + sourceFrame.postMessage({ command: "access location.href", indexIntoParentFrameOfDestination }, "*"); + return waitForMessage(sourceFrame); +} + +async function accessFrameElement(frameWindow) { + frameWindow.postMessage({ command: "access frameElement" }, "*"); + return waitForMessage(frameWindow); +} + +function waitForMessage(expectedSource) { + return new Promise(resolve => { + const handler = e => { + if (e.source === expectedSource) { + resolve(e.data); + window.removeEventListener("message", handler); + } + }; + window.addEventListener("message", handler); + }); +} + +// Any WebAssembly.Module will work fine for our tests; we just want to find out +// if it gives message or messageerror. So, we reuse one from the /wasm/ tests. +async function createWasmModule() { + const response = await fetch("/wasm/serialization/module/resources/incrementer.wasm"); + const ab = await response.arrayBuffer(); + return WebAssembly.compile(ab); +} diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers new file mode 100644 index 0000000000..cb762eff80 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/helpers.mjs.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs new file mode 100644 index 0000000000..17b00684d6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs @@ -0,0 +1,63 @@ +import { sendWasmModule } from "./helpers.mjs"; + +// This is done for the window.open() case. For <iframe>s we use the +// <iframe> element's load event instead. +const usp = new URLSearchParams(location.search); +if (usp.has("send-loaded-message")) { + opener.postMessage("loaded", "*"); +} + +window.onmessage = async (e) => { + // These could come from the parent, opener, or siblings. + if (e.data.constructor === WebAssembly.Module) { + e.source.postMessage("WebAssembly.Module message received", "*"); + } + + // These could come from the parent or opener. + if (e.data.command === "set document.domain") { + document.domain = e.data.newDocumentDomain; + e.source.postMessage("document.domain is set", "*"); + } else if (e.data.command === "get originAgentCluster") { + e.source.postMessage(self.originAgentCluster, "*"); + } + + // These only come from the parent. + if (e.data.command === "send WASM module") { + const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination]; + const whatHappened = await sendWasmModule(destinationFrameWindow); + parent.postMessage(whatHappened, "*"); + } else if (e.data.command === "access document") { + const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination]; + try { + destinationFrameWindow.document; + parent.postMessage("accessed document successfully", "*"); + } catch (e) { + parent.postMessage(e.name, "*"); + } + } else if (e.data.command === "access location.href") { + const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination]; + try { + destinationFrameWindow.location.href; + parent.postMessage("accessed location.href successfully", "*"); + } catch (e) { + parent.postMessage(e.name, "*"); + } + } else if (e.data.command === "access frameElement") { + if (frameElement === null) { + parent.postMessage("null", "*"); + } else if (frameElement?.constructor?.name === "HTMLIFrameElement") { + parent.postMessage("frameElement accessed successfully", "*"); + } else { + parent.postMessage("something wierd happened", "*"); + } + } + + // We could also receive e.data === "WebAssembly.Module message received", + // but that's handled by await sendWasmModule() above. +}; + +window.onmessageerror = e => { + e.source.postMessage("messageerror", "*"); +}; + +document.body.textContent = location.href; diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers new file mode 100644 index 0000000000..cb762eff80 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-header-page-script.mjs.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py new file mode 100644 index 0000000000..cc8860fe75 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py @@ -0,0 +1,43 @@ +def main(request, response): + """Send a response with the Origin-Agent-Cluster header given in the + "header" query parameter, or no header if that is not provided. Other query + parameters (only their presence/absence matters) are "send-loaded-message" + and "redirect-first", which modify the behavior a bit. + + In either case, the response will listen for various messages posted and + coordinate with the sender. See ./helpers.mjs for how these handlers are + used. + """ + + if b"redirect-first" in request.GET: + # Create a new query string, which is the same as the one we're given but + # with the redirect-first component stripped out. This allows tests to use + # any value (or no value) for the other query params, in combination with + # redirect-first. + query_string_pieces = [] + if b"header" in request.GET: + query_string_pieces.append(b"header=" + request.GET.first(b"header")) + if b"send-loaded-message" in request.GET: + query_string_pieces.append(b"send-loaded-message") + query_string = b"?" + b"&".join(query_string_pieces) + + return ( + 302, + [(b"Location", b"/html/browsers/origin/origin-keyed-agent-clusters/resources/send-oac-header.py" + query_string)], + u"" + ) + + if b"header" in request.GET: + header = request.GET.first(b"header") + response.headers.set(b"Origin-Agent-Cluster", header) + + response.headers.set(b"Content-Type", b"text/html") + + return u""" + <!DOCTYPE html> + <meta charset="utf-8"> + <title>Helper page for origin-keyed agent cluster tests</title> + + <body> + <script type="module" src="send-header-page-script.mjs"></script> + """ diff --git a/testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html b/testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html new file mode 100644 index 0000000000..448f47fa24 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/origin-of-data-document.html @@ -0,0 +1,30 @@ +<!doctype html> +<html> + <head> + <meta charset=utf-8> + <title>Origin of document produced from a 'data:' URL</title> + <link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#origin"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + async_test(function (t) { + var i = document.createElement('iframe'); + i.src = "data:text/html,<script>" + + " window.parent.postMessage('Hello!', '*');" + + "</scr" + "ipt>"; + + window.addEventListener("message", t.step_func_done(function (e) { + assert_equals(e.origin, "null", "Messages sent from a 'data:' URL should have an opaque origin (which serializes to 'null')."); + assert_throws_dom("SecurityError", function () { + var couldAccessCrossOriginProperty = e.source.location.href; + }, "The 'data:' frame should be cross-origin: 'window.location.href'"); + assert_equals(i.contentDocument, null, "The 'data:' iframe should be unable to access its contentDocument."); + })); + + document.body.appendChild(i); + }, "The origin of a 'data:' document in a frame is opaque."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html new file mode 100644 index 0000000000..d3af35c6d7 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain.html @@ -0,0 +1,35 @@ +<!doctype html> +<html> + <head> + <title>document.domain's getter</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + test(function() { + assert_equals(typeof document.domain, "string", "document.domain is a string"); + assert_not_equals(document.domain, "", "document.domain is not empty"); + }, "basics"); + + test(function() { + assert_equals(document.domain, window.location.hostname, "equals location.hostname"); + }, "current document"); + + test(function() { + var doc = new Document(); + assert_equals(doc.domain, window.location.hostname, "equals location.hostname"); + }, "new Document()"); + + async_test(t => { + const client = new XMLHttpRequest(); + client.open("GET", "/common/blank.html"); + client.responseType = "document" + client.send(); + client.onload = t.step_func_done(() => { + assert_equals(client.response.domain, window.location.hostname); + }); + }, "XMLHttpRequest's response document"); + </script> + </head> + <body> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html new file mode 100644 index 0000000000..eb02c96f1d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_access_details.sub.html @@ -0,0 +1,305 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/document_domain_frame.sub.js"></script> +<body> +<script> +promise_test(async (t) => { + let frame1 = await createFrame(t, "control1-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "control1-2", "{{domains[www1]}}"); + let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "control1-2" }); + assert_equals(result.data, "omg!"); +}, "Access allowed if same-origin with no 'document.domain' modification. (Sanity check)"); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "control2-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "control2-2", "{{domains[www2]}}"); + let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "control2-2" }); + assert_equals(result.data, "SecurityError"); +}, "Access not allowed if different-origin with no 'document.domain' modification. (Sanity check)"); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "one-set-one-not-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "one-set-one-not-2", "{{domains[www1]}}"); + await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + + let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "one-set-one-not-2" }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "one-set-one-not-1" }); + assert_equals(result.data, "SecurityError"); +}, "Access disallowed if same-origin but only one sets document.domain."); + +promise_test(async (t) => { + var frame1 = await createFrame(t, "both-set-to-existing-1", "{{domains[www1]}}"); + var frame2 = await createFrame(t, "both-set-to-existing-2", "{{domains[www1]}}"); + let result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, { 'poke-at-sibling': "both-set-to-existing-2" }); + assert_equals(result.data, "omg!"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "both-set-to-existing-1" }); + assert_equals(result.data, "omg!"); +}, "Access allowed if same-origin and both set document.domain to existing value."); + +promise_test(async (t) => { + var frame1 = await createFrame(t, "both-set-to-parent-1", "{{domains[www1]}}"); + var frame2 = await createFrame(t, "both-set-to-parent-2", "{{domains[www2]}}"); + let result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame2, { domain: "{{domains[]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, { 'poke-at-sibling': "both-set-to-parent-2" }); + assert_equals(result.data, "omg!"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "both-set-to-parent-1" }); + assert_equals(result.data, "omg!"); +}, "Access allowed if different-origin but both set document.domain to parent domain."); + +promise_test(async (t) => { + var frame1 = await createFrame(t, "allow-then-revoke-1", "{{domains[www1]}}"); + var frame2 = await createFrame(t, "allow-then-revoke-2", "{{domains[www1]}}"); + let result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, { 'poke-at-sibling': "allow-then-revoke-2" }); + assert_equals(result.data, "omg!"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "allow-then-revoke-1" }); + assert_equals(result.data, "omg!"); + + result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, { 'poke-at-sibling': "allow-then-revoke-2" }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "allow-then-revoke-1" }); + assert_equals(result.data, "SecurityError"); +}, "Access disallowed again if same-origin, both set document-domain to existing value, then one sets to parent."); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "revoke-Window-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "revoke-Window-2", "{{domains[www1]}}"); + + let result = await postMessageToFrame(frame1, { cache: ["parent", "revoke-Window-2"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 1"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "revoke-Window-1"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 1"); + + result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "SecurityError"); +}, "Access is revoked to Window object when we stop being same effective script origin due to document.domain."); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "revoke-Location-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "revoke-Location-2", "{{domains[www1]}}"); + + let result = await postMessageToFrame(frame1, { cache: ["parent", "revoke-Location-2", "location"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 3"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "revoke-Location-1", "location"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 3"); + + result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "SecurityError"); +}, "Access is revoked to Location object when we stop being same effective script origin due to document.domain."); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "no-revoke-Document-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "no-revoke-Document-2", "{{domains[www1]}}"); + + let result = await postMessageToFrame(frame1, { cache: ["parent", "no-revoke-Document-2", "document"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 4"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "no-revoke-Document-1", "document"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "Reachable 4"); + + result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 4"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "Reachable 4"); +}, "Access is not revoked to Document object when we stop being same effective script origin due to document.domain."); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "no-revoke-object-1", "{{domains[www1]}}"); + let frame2 = await createFrame(t, "no-revoke-object-2", "{{domains[www1]}}"); + + let result = await postMessageToFrame(frame1, { cache: ["parent", "no-revoke-object-2", "bar"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 2"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "no-revoke-object-1", "bar"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 2"); + + result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 2"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "Reachable 2"); +}, "Access is not revoked to random object when we stop being same effective script origin due to document.domain."); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "join-and-diverge-1", "{{domains[www2.www1]}}"); + let frame2 = await createFrame(t, "join-and-diverge-2", "{{domains[www1.www1]}}"); + + // Make sure we can't touch each other. + let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-2" }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-1" }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-2", "bar"] }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-1", "document"] }); + assert_equals(result.data, "SecurityError"); + + // Let's join up now. + result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + // Now we should be able to touch each other. + result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-2" }); + assert_equals(result.data, "omg!"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-1" }); + assert_equals(result.data, "omg!"); + + // Cache a random object and a document. + result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-2", "bar"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 2"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-1", "document"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "Reachable 4"); + + // OK, now let's diverge + result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" }); + assert_equals(result.data, "Done"); + + // We should still be able to touch our cached things. + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 2"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "Reachable 4"); +}, "Access evolves correctly for non-cross-origin objects when we join up via document.domain and then diverge again."); + +promise_test(async (t) => { + let frame1 = await createFrame(t, "join-and-diverge-cross-origin-1", "{{domains[www2.www1]}}"); + let frame2 = await createFrame(t, "join-and-diverge-cross-origin-2", "{{domains[www1.www1]}}"); + + // Make sure we can't touch each other. + let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-cross-origin-2" }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-cross-origin-1" }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-2", "bar"] }); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-1", "document"] }); + assert_equals(result.data, "SecurityError"); + + // Let's join up now. + result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" }); + assert_equals(result.data, "Done"); + + // Now we should be able to touch each other. + result = await postMessageToFrame(frame1, { 'poke-at-sibling': "join-and-diverge-cross-origin-2" }); + assert_equals(result.data, "omg!"); + + result = await postMessageToFrame(frame2, { 'poke-at-sibling': "join-and-diverge-cross-origin-1" }); + assert_equals(result.data, "omg!"); + + // Cache a window and a location + result = await postMessageToFrame(frame1, { cache: ["parent", "join-and-diverge-cross-origin-2"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "Reachable 1"); + + result = await postMessageToFrame(frame2, { cache: ["parent", "join-and-diverge-cross-origin-1", "location"] }); + assert_equals(result.data, "cached"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "Reachable 3"); + + // OK, now let's diverge + result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" }); + assert_equals(result.data, "Done"); + + // Now our cross-origin objects should start denying access. + result = await postMessageToFrame(frame1, 'touch-cached'); + assert_equals(result.data, "SecurityError"); + + result = await postMessageToFrame(frame2, 'touch-cached'); + assert_equals(result.data, "SecurityError"); +}, "Access evolves correctly for cross-origin objects when we join up via document.domain and then diverge again."); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html new file mode 100644 index 0000000000..2539528341 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter.html @@ -0,0 +1,76 @@ +<!doctype html> +<html> + <head> + <title>document.domain's setter</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/common/get-host-info.sub.js"></script> + </head> + <body> + <iframe id="iframe"></iframe> + <script> + var host_info = get_host_info(); + var HTTP_PORT = host_info.HTTP_PORT; + var ORIGINAL_HOST = host_info.ORIGINAL_HOST; + var SUFFIX_HOST = ORIGINAL_HOST.substring(ORIGINAL_HOST.lastIndexOf('.') + 1); // e.g. "test" + var REMOTE_HOST = host_info.REMOTE_HOST; + var iframe = document.getElementById("iframe"); + var iframe_url = new URL("support/document_domain_setter_iframe.html", document.location); + iframe_url.hostname = REMOTE_HOST; + iframe.src = iframe_url; + + test(function() { + assert_throws_dom("SecurityError", function() { document.domain = SUFFIX_HOST; }); + assert_throws_dom("SecurityError", function() { document.domain = "." + SUFFIX_HOST; }); + assert_throws_dom("SecurityError", function() { document.domain = REMOTE_HOST; }); + assert_throws_dom("SecurityError", function() { document.domain = "example.com"; }); + }, "failed setting of document.domain"); + + async_test(function(t) { + iframe.addEventListener("load", t.step_func_done(function() { + // Before setting document.domain, the iframe is not + // same-origin-domain, so security checks fail. + assert_equals(iframe.contentDocument, null); + assert_throws_dom("SecurityError", () => iframe.contentWindow.frameElement); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.origin; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.href; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.protocol; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.host; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.port; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.hostname; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.pathname; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.hash; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.search; }); + assert_throws_dom("SecurityError", function() { iframe.contentWindow.location.toString(); }); + // Set document.domain + document.domain = ORIGINAL_HOST; + // After setting document.domain, the iframe is + // same-origin-domain, so security checks pass. + assert_equals(iframe.contentDocument.domain, document.domain); + assert_equals(iframe.contentWindow.frameElement, iframe); + assert_equals(iframe.contentWindow.origin, iframe_url.origin); + assert_equals(iframe.contentWindow.location.href, iframe_url.href); + assert_equals(iframe.contentWindow.location.protocol, iframe_url.protocol); + assert_equals(iframe.contentWindow.location.host, iframe_url.host); + assert_equals(iframe.contentWindow.location.port, iframe_url.port); + assert_equals(iframe.contentWindow.location.hostname, iframe_url.hostname); + assert_equals(iframe.contentWindow.location.pathname, iframe_url.pathname); + assert_equals(iframe.contentWindow.location.hash, iframe_url.hash); + assert_equals(iframe.contentWindow.location.search, iframe_url.search); + assert_equals(iframe.contentWindow.location.search, iframe_url.search); + assert_equals(iframe.contentWindow.location.toString(), iframe_url.toString()); + // document.open checks for same-origin, not same-origin-domain, + // https://github.com/whatwg/html/issues/2282 + assert_throws_dom("SecurityError", iframe.contentWindow.DOMException, + function() { iframe.contentDocument.open(); }); + })); + }, "same-origin-domain iframe"); + + test(() => { + assert_throws_dom("SecurityError", () => { (new Document).domain = document.domain }); + assert_throws_dom("SecurityError", () => { document.implementation.createHTMLDocument().domain = document.domain }); + assert_throws_dom("SecurityError", () => { document.implementation.createDocument(null, "").domain = document.domain }); + }, "failed setting of document.domain for documents without browsing context"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html new file mode 100644 index 0000000000..65a7f5c898 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<!-- SEKRITS! --> +<input id="sekrit" value="omg!"> + +<script> + function postMessageToFrame(frame, message) { + return new Promise(resolve => { + var c = new MessageChannel(); + c.port1.onmessage = e => { + resolve({ data: e.data, frame: frame }) + }; + frame.contentWindow.postMessage(message, '*', [c.port2]); + }); + } + + function createFrame() { + return new Promise(resolve => { + var i = document.createElement('iframe'); + i.srcdoc = ` + <script> + window.addEventListener('message', e => { + if (e.data.domain !== undefined) { + try { + document.domain = e.data.domain; + e.ports[0].postMessage('Done'); + } catch(error) { + e.ports[0].postMessage(error.name); + } + } else if (e.data == 'poke-at-parent') { + try { + var sekrit = window.parent.document.body.querySelector('#sekrit').value; + e.ports[0].postMessage(sekrit); + } catch(error) { + e.ports[0].postMessage(error.name); + } + } + }); + window.parent.postMessage('Hi!', '*'); + </scr` + `ipt>`; + window.addEventListener('message', m => { + if (m.source == i.contentWindow) + resolve(i); + }); + document.body.appendChild(i); + }); + } + + promise_test(t => { + return createFrame() + .then(f => postMessageToFrame(f, 'poke-at-parent')) + .then(result => { + assert_equals(result.data, document.querySelector('#sekrit').value); + result.frame.remove(); + }); + }, "srcdoc can access with no 'document.domain' modification."); + + promise_test(t => { + return createFrame() + .then(f => postMessageToFrame(f, { domain: window.location.hostname })) + .then(result => { + assert_equals(result.data, 'Done'); + return postMessageToFrame(result.frame, 'poke-at-parent') + .then(result => { + assert_equals(result.data, document.querySelector('#sekrit').value); + result.frame.remove(); + }); + }); + }, "srcdoc can access with valid 'document.domain'."); + + promise_test(t => { + return createFrame() + .then(f => { + document.domain = window.location.hostname; + return postMessageToFrame(f, 'poke-at-parent'); + }) + .then(result => { + assert_equals(result.data, document.querySelector('#sekrit').value); + result.frame.remove(); + }); + }, "srcdoc can access when parent modifies 'document.domain'."); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html new file mode 100644 index 0000000000..ae1a0ccd56 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html @@ -0,0 +1,21 @@ +<!doctype html> +<title>Sandboxed document.domain</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + test(() => { + assert_throws_dom("SecurityError", () => { document.domain = document.domain }); + }); + test(() => { + assert_throws_dom("SecurityError", () => { (new Document).domain = document.domain }); + }); + test(() => { + assert_throws_dom("SecurityError", () => { document.implementation.createHTMLDocument().domain = document.domain }); + }); + test(() => { + assert_throws_dom("SecurityError", () => { document.implementation.createDocument(null, "").domain = document.domain }); + }); + test(() => { + assert_throws_dom("SecurityError", () => { document.createElement("template").content.ownerDocument.domain = document.domain }); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers new file mode 100644 index 0000000000..82e8023d0b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/sandboxed-document_domain.html.headers @@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-scripts allow-same-origin diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html new file mode 100644 index 0000000000..61f54af359 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<script> + let cache = window; + // "foo" needs to be a var so it's a property on the global. + var foo = 'Reachable 1'; + // "bar" needs to be a var so it's a property on the global. + var bar = { foo: 'Reachable 2' }; + location.foo = 'Reachable 3'; + document.foo = 'Reachable 4'; + window.addEventListener('message', e => { + if (e.data.domain !== undefined) { + try { + document.domain = e.data.domain; + e.ports[0].postMessage('Done'); + } catch(error) { + e.ports[0].postMessage(error.name); + } + } else if (e.data['poke-at-sibling'] !== undefined) { + try { + var sekrit = parent[e.data['poke-at-sibling']].document.body.querySelector('#sekrit').value; + e.ports[0].postMessage(sekrit); + } catch(error) { + e.ports[0].postMessage(error.name); + } + } else if (e.data.cache != undefined) { + let path = e.data.cache; + try { + while (path.length != 0) { + cache = cache[path.shift()]; + } + e.ports[0].postMessage('cached'); + } catch (error) { + e.ports[0].postMessage(error.name); + } + } else if (e.data == 'touch-cached') { + try { + e.ports[0].postMessage(cache.foo); + } catch (error) { + e.ports[0].postMessage(error.name); + } + } else if (e.data == 'poke-at-parent') { + try { + var sekrit = window.parent.document.body.querySelector('#sekrit').value; + e.ports[0].postMessage(sekrit); + } catch(error) { + e.ports[0].postMessage(error.name); + } + } + }); + window.parent.postMessage('Hi!', '*'); +</script> +<input id="sekrit" value="omg!"> diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js new file mode 100644 index 0000000000..b6631ea4f1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.sub.js @@ -0,0 +1,65 @@ +/** + * Utilities to be used with document_domain_frame.html. + */ + +/** + * Send a message to the frame and resolve a promise when a response is received. + * + * Supported messages: + * + * 1) { domain: something }. Has the subframe try to set document.domain to the + * given value, and message back 'Done' if that succeeds or an error name if it + * fails. + * + * 2) 'poke-at-parent'. Has the subframe try to synchronously attempt to access + * the parent's DOM, read out a string value, and message it back to the parent. + * Again, sends back the error name if that fails. + * + * 3) { 'poke-at-sibling': name }. Has the subframe try to synchronously + * attempt to access the DOM of the sibling with the given name, read out a + * string value, and message it back to the parent. + */ +function postMessageToFrame(frame, message) { + return new Promise(resolve => { + var c = new MessageChannel(); + c.port1.onmessage = e => { + resolve({ data: e.data, frame: frame }) + }; + frame.contentWindow.postMessage(message, '*', [c.port2]); + }); +} + +/** + * Create a frame that loads document_domain_frame.html and resolves a promise + * when the frame is loaded enough to be sending and receiving messages. + * + * If a "name" argument is provided, that name is used for the iframe, so + * + * If a "hostname" argument is provided, that hostname is used for the load, to + * allow testing details of the behavior when different sorts of hostnames are + * used. + */ +function createFrame(t, name, hostname) { + return new Promise(resolve => { + var i = document.createElement('iframe'); + if (hostname) { + i.src = `//${hostname}:{{location[port]}}/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html`; + } else { + i.src = "support/document_domain_frame.html"; + } + if (name) { + i.name = name; + } + var listener = m => { + if (m.source == i.contentWindow) + resolve(i); + } + window.addEventListener('message', listener); + t.add_cleanup(() => { + i.remove(); + window.removeEventListener('message', listener); + }); + document.body.appendChild(i); + }); +} + diff --git a/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html new file mode 100644 index 0000000000..d3d5260af3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_setter_iframe.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> + <head> + <title></title> + <script src="/common/get-host-info.sub.js"></script> + <script> + document.domain = get_host_info().ORIGINAL_HOST; + </script> + </head> + <body> + </body> +</html> |