| <!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> |