mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-28 10:37:17 +02:00
Tests/LibWeb: Import WPT window named properties test
This used to crash before; let's lock this in place.
This commit is contained in:
committed by
Tim Flynn
parent
a095e4cd58
commit
ae0557a2cb
Notes:
github-actions[bot]
2026-03-15 13:04:35 +00:00
Author: https://github.com/gmta Commit: https://github.com/LadybirdBrowser/ladybird/commit/ae0557a2cbb Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8397 Reviewed-by: https://github.com/trflynn89 ✅
@@ -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://webidl.spec.whatwg.org/#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://webidl.spec.whatwg.org/#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);
|
||||
}, "[[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://webidl.spec.whatwg.org/#dfn-named-property-visibility
|
||||
assert_equals(wp.isPrototypeOf, w.Object.prototype.isPrototypeOf, supportedNonIndex);
|
||||
assert_equals(wp.dispatchEvent, w.EventTarget.prototype.dispatchEvent, supportedNonIndex);
|
||||
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>
|
||||
Reference in New Issue
Block a user