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 1 commit
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.

282 changes: 282 additions & 0 deletions WebIDL/ecmascript-binding/window-named-properties-object.html
@@ -0,0 +1,282 @@
<!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 looseModeSet(base, key, value) { base[key] = value; }
shvaikalesh marked this conversation as resolved.
Show resolved Hide resolved
</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);
shvaikalesh marked this conversation as resolved.
Show resolved Hide resolved

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] = {};

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;

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) {
looseModeSet(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 @@ -10,8 +10,6 @@
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>
<iframe name="bar"></iframe>
<iframe name="baz"></iframe>
<iframe name="baz"></iframe>
<iframe name="constructor"></iframe>
<iframe id="quux"></iframe>
<script>
Expand All @@ -30,7 +28,7 @@
test(function() {
assert_true("quux" in window, "quux not in window");
assert_equals(window["quux"],
document.getElementsByTagName("iframe")[4]);
document.getElementsByTagName("iframe")[2]);
}, "Static id");

test(function() {
Expand Down Expand Up @@ -58,12 +56,6 @@
assert_false(gsp.hasOwnProperty("constructor"), "gsp.hasOwnProperty(\"constructor\")");
assert_equals(Object.getOwnPropertyDescriptor(gsp, "constructor"), undefined);
}, "constructor");
test(function() {
var gsp = Object.getPrototypeOf(Object.getPrototypeOf(window));
var names = Object.getOwnPropertyNames(gsp);
assert_equals(names.filter((name) => name == "baz").length, 1);

}, "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")
t.step(function() {
Expand Down