Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebKit export of https://bugs.webkit.org/show_bug.cgi?id=225997 #29767

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Cross-realm getter / setter / operation doesn't use lexical global object if |this| value is incompatible object / null / undefined</title>
<link rel="help" href="https://heycam.github.io/webidl/#dfn-attribute-getter">
<link rel="help" href="https://heycam.github.io/webidl/#dfn-attribute-setter">
<link rel="help" href="https://heycam.github.io/webidl/#dfn-create-operation-function">

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/create-realm.js"></script>

<body>
<script>
promise_test(async t => {
const other = await createRealm(t);
const notWindow = Object.create(Object.getPrototypeOf(other));

assert_throws_js(other.TypeError, () => { Object.create(other).window; });
assert_throws_js(other.TypeError, () => { Object.getOwnPropertyDescriptor(other, "history").get.call(notWindow); });
assert_throws_js(other.TypeError, () => { Reflect.get(other, "screen", notWindow); });
assert_throws_js(other.TypeError, () => { new Proxy(other, {}).onclick; });
}, "Cross-realm global object's getter throws when called on incompatible object");

promise_test(async t => {
const other = await createRealm(t);
const notWindow = Object.create(Object.getPrototypeOf(other));

assert_throws_js(other.TypeError, () => { Object.create(other).name = "dummy"; });
assert_throws_js(other.TypeError, () => { Object.getOwnPropertyDescriptor(other, "status").set.call(notWindow, other.status); });
// parent is [Replaceable]
assert_throws_js(other.TypeError, () => { Reflect.set(other, "parent", window, notWindow); });
assert_throws_js(other.TypeError, () => { new Proxy(other, {}).location = location; });
}, "Cross-realm global object's setter throws when called on incompatible object");

promise_test(async t => {
const other = await createRealm(t);
const notWindow = Object.create(Object.getPrototypeOf(other));

assert_throws_js(other.TypeError, () => { Object.create(other).focus(); });
assert_throws_js(other.TypeError, () => { other.clearInterval.call(notWindow, 0); });
assert_throws_js(other.TypeError, () => { Reflect.apply(other.blur, notWindow, []); });
assert_throws_js(other.TypeError, () => { new Proxy(other, {}).removeEventListener("foo", () => {}); });
}, "Cross-realm global object's operation throws when called on incompatible object");

promise_test(async t => {
const other = await createRealm(t);
const otherNameGetter = Object.getOwnPropertyDescriptor(other, "name").get;

assert_equals(Reflect.get(other, "self", null), other);
assert_equals(Reflect.get(other, "document", undefined), other.document);
assert_equals(otherNameGetter.call(null), "dummy");
// An engine might have different code path for calling a function from outer scope to implement step 1.b.iii of https://tc39.es/ecma262/#sec-evaluatecall
assert_equals((() => otherNameGetter())(), "dummy");
}, "Cross-realm global object's getter called on null / undefined");

promise_test(async t => {
const other = await createRealm(t);
const otherLocationSetter = Object.getOwnPropertyDescriptor(other, "location").set;
const otherHref = other.location.href;
const newSelf = {};

// self is [Replaceable]
assert_true(Reflect.set(other, "self", newSelf, null));
assert_true(Reflect.set(other, "name", "newName", undefined));

otherLocationSetter.call(null, `${otherHref}#foo`);
assert_equals(other.location.hash, "#foo");
// An engine might have different code path for calling a function from outer scope to implement step 1.b.iii of https://tc39.es/ecma262/#sec-evaluatecall
(() => { otherLocationSetter(`${otherHref}#bar`); })();
assert_equals(other.location.hash, "#bar");

// Check these after calling "location" setter make sure no navigation has occurred
assert_equals(other.self, newSelf);
assert_equals(other.name, "newName");
}, "Cross-realm global object's setter called on null / undefined");

promise_test(async t => {
const other = await createRealm(t);
const otherFocus = other.focus;
const otherDispatchEvent = other.dispatchEvent;

assert_equals(document.activeElement, document.body);
// An engine might have different code path for calling a function from outer scope to implement step 1.b.iii of https://tc39.es/ecma262/#sec-evaluatecall
(() => { otherFocus(); })();
assert_equals(document.activeElement.contentWindow, other);

let caughtEvent;
other.addEventListener.call(null, "foo", event => { caughtEvent = event; });
const dispatchedEvent = new other.Event("foo");
assert_true(otherDispatchEvent(dispatchedEvent));
assert_equals(caughtEvent, dispatchedEvent);

const messagePromise = new EventWatcher(t, other, "message").wait_for("message");
other.postMessage.call(null, "foo");
assert_equals((await messagePromise).data, "foo");
}, "Cross-realm global object's operation called on null / undefined");
</script>
45 changes: 45 additions & 0 deletions WebIDL/ecmascript-binding/invalid-this-value-cross-realm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Cross-realm getter / setter / operation doesn't use lexical global object to throw an error for incompatible |this| value</title>
<link rel="help" href="https://heycam.github.io/webidl/#dfn-attribute-getter">
<link rel="help" href="https://heycam.github.io/webidl/#dfn-attribute-setter">
<link rel="help" href="https://heycam.github.io/webidl/#dfn-create-operation-function">

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/create-realm.js"></script>

<body>
<script>
promise_test(async t => {
const other = await createRealm(t);
const notElement = Object.create(other.HTMLElement.prototype);

assert_throws_js(other.TypeError, () => { Object.create(other.document).head; });
assert_throws_js(other.TypeError, () => { Object.getOwnPropertyDescriptor(other.HTMLElement.prototype, "title").get.call(notElement); });
assert_throws_js(other.TypeError, () => { Reflect.get(other.document.createElement("div"), "hidden", notElement); });
assert_throws_js(other.TypeError, () => { new Proxy(other.text, {}).nodeType; });
}, "Cross-realm getter throws when called on incompatible object");

promise_test(async t => {
const other = await createRealm(t);
const notElement = Object.create(other.HTMLElement.prototype);
const notText = Object.create(other.Text.prototype);

assert_throws_js(other.TypeError, () => { Object.create(other.element).innerHTML = ""; });
assert_throws_js(other.TypeError, () => { Object.getOwnPropertyDescriptor(other.HTMLElement.prototype, "onclick").set.call(notElement, null); });
assert_throws_js(other.TypeError, () => { Reflect.set(new other.Text("foo"), "data", "foo", notText); });
assert_throws_js(other.TypeError, () => { new Proxy(other.document, {}).title = ""; });
}, "Cross-realm setter throws when called on incompatible object");

promise_test(async t => {
const other = await createRealm(t);
const notDocument = Object.create(other.HTMLDocument.prototype);
const notText = Object.create(other.Text.prototype);

assert_throws_js(other.TypeError, () => { Object.create(other.element).click(); });
assert_throws_js(other.TypeError, () => { other.document.querySelector.call(notDocument, "*"); });
assert_throws_js(other.TypeError, () => { Reflect.apply(other.text.remove, notText, []); });
assert_throws_js(other.TypeError, () => { new Proxy(other.document.createElement("a"), {}).addEventListener("foo", () => {}); });
}, "Cross-realm operation throws when called on incompatible object");
</script>
12 changes: 12 additions & 0 deletions WebIDL/ecmascript-binding/support/create-realm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use strict";

function createRealm(t) {
return new Promise(resolve => {
const iframe = document.createElement("iframe");
t.add_cleanup(() => { iframe.remove(); });
iframe.onload = () => { resolve(iframe.contentWindow); };
iframe.name = "dummy";
iframe.src = "support/dummy-iframe.html";
document.body.append(iframe);
});
}
7 changes: 7 additions & 0 deletions WebIDL/ecmascript-binding/support/dummy-iframe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<meta charset="utf-8">
<body>foo
<button id="element"></button>
<script>
window.text = document.body.firstChild;
</script>
Original file line number Diff line number Diff line change
@@ -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://heycam.github.io/webidl/#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>
18 changes: 18 additions & 0 deletions performance-timeline/supportedEntryTypes-cross-realm-access.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Cross-realm access of supportedEntryTypes returns Array of another realm</title>
<link rel="help" href="https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
test(t => {
const iframe = document.createElement("iframe");
t.add_cleanup(() => { iframe.remove(); });
iframe.onload = t.step_func_done(() => {
const otherWindow = iframe.contentWindow;
assert_true(otherWindow.PerformanceObserver.supportedEntryTypes instanceof otherWindow.Array);
});
document.body.append(iframe);
});
</script>
3 changes: 2 additions & 1 deletion webrtc-encoded-transform/sframe-transform-readable.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
<iframe src="." id="frame"></iframe>
<script>
promise_test(async (test) => {
const frameDOMException = frame.contentWindow.DOMException;
const transform = new frame.contentWindow.SFrameTransform;
frame.remove();
assert_throws_dom("InvalidStateError", () => transform.readable);
assert_throws_dom("InvalidStateError", frameDOMException, () => transform.readable);
});
</script>
</body>
Expand Down