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

Add comprehensive coverage for WindowProperties object #27970

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -16,17 +16,6 @@ test(() => {
assert_equals(Object.prototype.toString.call(namedPropertiesObject), "[object WindowProperties]");
}, "Object.prototype.toString");

test(t => {
assert_own_property(namedPropertiesObject, Symbol.toStringTag, "Precondition for this test: @@toStringTag exists");

t.add_cleanup(() => {
Object.defineProperty(namedPropertiesObject, Symbol.toStringTag, { value: "WindowProperties" });
});

Object.defineProperty(namedPropertiesObject, Symbol.toStringTag, { value: "NotWindowProperties" });
assert_equals(Object.prototype.toString.call(namedPropertiesObject), "[object NotWindowProperties]");
}, "Object.prototype.toString applied after modifying @@toStringTag");

// Chrome had a bug (https://bugs.chromium.org/p/chromium/issues/detail?id=793406) where if there
// was no @@toStringTag, it would fall back to a magic class string. Tests for this are present in
// the sibling class-string*.any.js tests. However, the named properties object always fails calls
Expand Down
33 changes: 0 additions & 33 deletions WebIDL/ecmascript-binding/named-properties-object.html

This file was deleted.

284 changes: 284 additions & 0 deletions WebIDL/ecmascript-binding/window-named-properties-object.html
@@ -0,0 +1,284 @@
<!doctype html>
<meta charset="utf-8">
<title>Internal methods of Window's named properties object</title>
<link rel="help" href="https://heycam.github.io/webidl/#named-properties-object">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
function sloppyModeSet(base, key, value) { base[key] = value; }
</script>
<script>
"use strict";

const supportedNonIndex = "supported non-index property name";
const supportedIndex = "supported indexed property name";
const unsupportedNonIndex = "unsupported non-index property name";
const unsupportedIndex = "unsupported indexed property name";
const existingSymbol = "existing symbol property name";
const nonExistingSymbol = "non-existing symbol property name";

test(t => {
const { w, wp } = createWindowProperties(t);

Object.setPrototypeOf(wp, w.EventTarget.prototype); // Setting current [[Prototype]] value shouldn't throw

assert_throws_js(TypeError, () => { Object.setPrototypeOf(wp, {}); });
assert_throws_js(w.TypeError, () => { wp.__proto__ = null; });
assert_false(Reflect.setPrototypeOf(wp, w.Object.prototype));

assert_equals(Object.getPrototypeOf(wp), w.EventTarget.prototype);
}, "[[SetPrototypeOf]] and [[GetPrototypeOf]]");

test(t => {
const { wp } = createWindowProperties(t);

assert_throws_js(TypeError, () => { Object.preventExtensions(wp); });
assert_false(Reflect.preventExtensions(wp));

assert_true(Object.isExtensible(wp));
}, "[[PreventExtensions]] and [[IsExtensible]]");

test(t => {
const { w, wp } = createWindowProperties(t);

const elA = appendElementWithId(w, "a");
const el0 = appendIframeWithName(w, 0);

assert_prop_desc(Object.getOwnPropertyDescriptor(wp, "a"), elA, supportedNonIndex);
assert_prop_desc(Reflect.getOwnPropertyDescriptor(wp, 0), el0, supportedIndex);
assert_equals(Reflect.getOwnPropertyDescriptor(wp, "b"), undefined, unsupportedNonIndex);
assert_equals(Object.getOwnPropertyDescriptor(wp, 1), undefined, unsupportedIndex);
}, "[[GetOwnProperty]]");

test(t => {
const { w, wp } = createWindowProperties(t);

appendIframeWithName(w, "hasOwnProperty");
appendFormWithName(w, "addEventListener");
appendElementWithId(w, "a");
appendIframeWithName(w, 0);

w.Object.prototype.a = {};
w.EventTarget.prototype[0] = {};

// These are shadowed by properties higher in [[Prototype]] chain. See https://heycam.github.io/webidl/#dfn-named-property-visibility
assert_equals(Object.getOwnPropertyDescriptor(wp, "hasOwnProperty"), undefined, supportedNonIndex);
assert_equals(Reflect.getOwnPropertyDescriptor(wp, "addEventListener"), undefined, supportedNonIndex);
assert_equals(Object.getOwnPropertyDescriptor(wp, "a"), undefined, supportedNonIndex);
assert_equals(Reflect.getOwnPropertyDescriptor(wp, 0), undefined, supportedIndex);
shvaikalesh marked this conversation as resolved.
Show resolved Hide resolved
}, "[[GetOwnProperty]] (named property visibility algorithm)");

test(t => {
const { w, wp } = createWindowProperties(t);

appendElementWithId(w, "a");
appendFormWithName(w, 0);

assert_define_own_property_fails(wp, "a", {}, supportedNonIndex);
assert_define_own_property_fails(wp, 0, {}, supportedIndex);
assert_define_own_property_fails(wp, "b", {}, unsupportedNonIndex);
assert_define_own_property_fails(wp, 1, {}, unsupportedIndex);
assert_define_own_property_fails(wp, Symbol.toStringTag, {}, existingSymbol);
assert_define_own_property_fails(wp, Symbol(), {}, nonExistingSymbol);
}, "[[DefineOwnProperty]]");

test(t => {
const { w, wp } = createWindowProperties(t);

appendFormWithName(w, "a");
appendElementWithId(w, 0);

assert_true("a" in wp, supportedNonIndex);
assert_true(Reflect.has(wp, "a"), supportedNonIndex);
assert_true(0 in wp, supportedIndex);
assert_true(Reflect.has(wp, 0), supportedIndex);

assert_false("b" in wp, unsupportedNonIndex);
assert_false(Reflect.has(wp, 1), unsupportedIndex);
}, "[[HasProperty]]");

test(t => {
const { w, wp } = createWindowProperties(t);
const elA = appendFormWithName(w, "a");
const el0 = appendIframeWithName(w, 0);

assert_equals(wp.a, elA, supportedNonIndex);
assert_equals(wp[0], el0, supportedIndex);
assert_equals(wp[Symbol.toStringTag], "WindowProperties", existingSymbol);

assert_equals(wp.b, undefined, unsupportedNonIndex);
assert_equals(wp[1], undefined, unsupportedIndex);
assert_equals(wp[Symbol.iterator], undefined, nonExistingSymbol);
}, "[[Get]]");

test(t => {
const { w, wp } = createWindowProperties(t);

appendIframeWithName(w, "isPrototypeOf");
appendFormWithName(w, "dispatchEvent");
appendElementWithId(w, "a");
appendElementWithId(w, 0);

w.EventTarget.prototype.a = 10;
w.Object.prototype[0] = 20;

// These are shadowed by properties higher in [[Prototype]] chain. See https://heycam.github.io/webidl/#dfn-named-property-visibility
assert_equals(wp.isPrototypeOf, w.Object.prototype.isPrototypeOf, supportedNonIndex);
assert_equals(wp.dispatchEvent, w.EventTarget.prototype.dispatchEvent, supportedNonIndex);
shvaikalesh marked this conversation as resolved.
Show resolved Hide resolved
assert_equals(wp.a, 10, supportedNonIndex);
assert_equals(wp[0], 20, supportedIndex);
}, "[[Get]] (named property visibility algorithm)");

test(t => {
const { w, wp } = createWindowProperties(t);
const elA = appendIframeWithName(w, "a");
const el0 = appendFormWithName(w, 0);

assert_set_fails(wp, "a", supportedNonIndex);
assert_set_fails(wp, "b", unsupportedNonIndex);
assert_set_fails(wp, 0, supportedIndex);
assert_set_fails(wp, 1, unsupportedIndex);
assert_set_fails(wp, Symbol.toStringTag, existingSymbol);
assert_set_fails(wp, Symbol(), nonExistingSymbol);

assert_equals(wp.a, elA, supportedNonIndex);
assert_equals(wp[0], el0, supportedIndex);
assert_equals(wp.b, undefined, unsupportedNonIndex);
assert_equals(wp[1], undefined, unsupportedIndex);
}, "[[Set]] (direct)");

test(t => {
const { w, wp } = createWindowProperties(t);
const receiver = Object.create(wp);

appendIframeWithName(w, "a");
appendElementWithId(w, 0);

let setterThisValue;
Object.defineProperty(w.Object.prototype, 1, { set() { setterThisValue = this; } });
Object.defineProperty(w.EventTarget.prototype, "b", { writable: false });

receiver.a = 10;
assert_throws_js(TypeError, () => { receiver.b = {}; }, unsupportedNonIndex);
receiver[0] = 20;
receiver[1] = {};

assert_equals(receiver.a, 10, supportedNonIndex);
assert_equals(receiver[0], 20, supportedIndex);
assert_false(receiver.hasOwnProperty("b"), unsupportedNonIndex);
assert_false(receiver.hasOwnProperty(1), unsupportedIndex);
assert_equals(setterThisValue, receiver, "setter |this| value is receiver");
}, "[[Set]] (prototype chain)");

test(t => {
const { w, wp } = createWindowProperties(t);
const receiver = {};

appendFormWithName(w, "a");
appendIframeWithName(w, 0);

let setterThisValue;
Object.defineProperty(w.Object.prototype, "b", { set() { setterThisValue = this; } });
Object.defineProperty(w.EventTarget.prototype, 1, { writable: false });

assert_true(Reflect.set(wp, "a", 10, receiver), supportedNonIndex);
assert_true(Reflect.set(wp, 0, 20, receiver), supportedIndex);
assert_true(Reflect.set(wp, "b", {}, receiver), unsupportedNonIndex);
assert_false(Reflect.set(wp, 1, {}, receiver), unsupportedIndex);

assert_equals(receiver.a, 10, supportedNonIndex);
assert_equals(receiver[0], 20, supportedIndex);
assert_false(receiver.hasOwnProperty("b"), unsupportedNonIndex);
assert_equals(setterThisValue, receiver, "setter |this| value is receiver");
assert_false(receiver.hasOwnProperty(1), unsupportedIndex);
}, "[[Set]] (Reflect.set)");

test(t => {
const { w, wp } = createWindowProperties(t);
const elA = appendFormWithName(w, "a");
const el0 = appendElementWithId(w, 0);

assert_delete_fails(wp, "a", supportedNonIndex);
assert_delete_fails(wp, 0, supportedIndex);
assert_delete_fails(wp, "b", unsupportedNonIndex);
assert_delete_fails(wp, 1, unsupportedIndex);
assert_delete_fails(wp, Symbol.toStringTag, existingSymbol);
assert_delete_fails(wp, Symbol("foo"), nonExistingSymbol);

assert_equals(wp.a, elA, supportedNonIndex);
assert_equals(wp[0], el0, supportedIndex);
assert_equals(wp[Symbol.toStringTag], "WindowProperties", existingSymbol);
}, "[[Delete]]");

test(t => {
const { w, wp } = createWindowProperties(t);

appendIframeWithName(w, "a");
appendElementWithId(w, 0);
appendFormWithName(w, "b");

const forInKeys = [];
for (const key in wp)
forInKeys.push(key);

assert_array_equals(forInKeys, Object.keys(w.EventTarget.prototype));
assert_array_equals(Object.getOwnPropertyNames(wp), []);
assert_array_equals(Reflect.ownKeys(wp), [Symbol.toStringTag]);
}, "[[OwnPropertyKeys]]");

function createWindowProperties(t) {
const iframe = document.createElement("iframe");
document.body.append(iframe);
t.add_cleanup(() => { iframe.remove(); });

const w = iframe.contentWindow;
const wp = Object.getPrototypeOf(w.Window.prototype);
return { w, wp };
}

function appendIframeWithName(w, name) {
const el = w.document.createElement("iframe");
el.name = name;
w.document.body.append(el);
return el.contentWindow;
}

function appendFormWithName(w, name) {
const el = w.document.createElement("form");
el.name = name;
w.document.body.append(el);
return el;
}

function appendElementWithId(w, id) {
const el = w.document.createElement("div");
el.id = id;
w.document.body.append(el);
return el;
}

function assert_prop_desc(desc, value, testInfo) {
assert_equals(typeof desc, "object", `${testInfo} typeof desc`);
assert_equals(desc.value, value, `${testInfo} [[Value]]`);
assert_true(desc.writable, `${testInfo} [[Writable]]`);
assert_false(desc.enumerable, `${testInfo} [[Enumerable]]`);
assert_true(desc.configurable, `${testInfo} [[Configurable]]`);
}

function assert_define_own_property_fails(object, key, desc, testInfo) {
assert_throws_js(TypeError, () => { Object.defineProperty(object, key, desc); }, testInfo);
assert_false(Reflect.defineProperty(object, key, desc), testInfo);
}

function assert_set_fails(object, key, value, testInfo) {
sloppyModeSet(object, key, value);
assert_throws_js(TypeError, () => { object[key] = value; }, testInfo);
assert_false(Reflect.set(object, key, value), testInfo);
}

function assert_delete_fails(object, key, testInfo) {
assert_throws_js(TypeError, () => { delete object[key]; }, testInfo);
assert_false(Reflect.deleteProperty(object, key), testInfo);
}
</script>
Expand Up @@ -232,7 +232,10 @@
}
for (var prop of windowAllowlists.namedFrames) {
win[prop]; // Shouldn't throw.
Object.getOwnPropertyDescriptor(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) {
Expand Down
Expand Up @@ -60,9 +60,7 @@
}, "constructor");
test(function() {
var gsp = Object.getPrototypeOf(Object.getPrototypeOf(window));
var names = Object.getOwnPropertyNames(gsp);
assert_equals(names.filter((name) => name == "baz").length, 1);

assert_equals(gsp.baz, document.getElementsByTagName("iframe")[1]);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this is wrong. #29063

}, "duplicate property names")
shvaikalesh marked this conversation as resolved.
Show resolved Hide resolved
var t = async_test("Dynamic name")
var t2 = async_test("Ghost name")
Expand Down