window-named-properties-object.html (10825B)
1 <!doctype html> 2 <meta charset="utf-8"> 3 <title>Internal methods of Window's named properties object</title> 4 <link rel="help" href="https://webidl.spec.whatwg.org/#named-properties-object"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <body> 8 <script> 9 function sloppyModeSet(base, key, value) { base[key] = value; } 10 </script> 11 <script> 12 "use strict"; 13 14 const supportedNonIndex = "supported non-index property name"; 15 const supportedIndex = "supported indexed property name"; 16 const unsupportedNonIndex = "unsupported non-index property name"; 17 const unsupportedIndex = "unsupported indexed property name"; 18 const existingSymbol = "existing symbol property name"; 19 const nonExistingSymbol = "non-existing symbol property name"; 20 21 test(t => { 22 const { w, wp } = createWindowProperties(t); 23 24 Object.setPrototypeOf(wp, w.EventTarget.prototype); // Setting current [[Prototype]] value shouldn't throw 25 26 assert_throws_js(TypeError, () => { Object.setPrototypeOf(wp, {}); }); 27 assert_throws_js(w.TypeError, () => { wp.__proto__ = null; }); 28 assert_false(Reflect.setPrototypeOf(wp, w.Object.prototype)); 29 30 assert_equals(Object.getPrototypeOf(wp), w.EventTarget.prototype); 31 }, "[[SetPrototypeOf]] and [[GetPrototypeOf]]"); 32 33 test(t => { 34 const { wp } = createWindowProperties(t); 35 36 assert_throws_js(TypeError, () => { Object.preventExtensions(wp); }); 37 assert_false(Reflect.preventExtensions(wp)); 38 39 assert_true(Object.isExtensible(wp)); 40 }, "[[PreventExtensions]] and [[IsExtensible]]"); 41 42 test(t => { 43 const { w, wp } = createWindowProperties(t); 44 45 const elA = appendElementWithId(w, "a"); 46 const el0 = appendIframeWithName(w, 0); 47 48 assert_prop_desc(Object.getOwnPropertyDescriptor(wp, "a"), elA, supportedNonIndex); 49 assert_prop_desc(Reflect.getOwnPropertyDescriptor(wp, 0), el0, supportedIndex); 50 assert_equals(Reflect.getOwnPropertyDescriptor(wp, "b"), undefined, unsupportedNonIndex); 51 assert_equals(Object.getOwnPropertyDescriptor(wp, 1), undefined, unsupportedIndex); 52 }, "[[GetOwnProperty]]"); 53 54 test(t => { 55 const { w, wp } = createWindowProperties(t); 56 57 appendIframeWithName(w, "hasOwnProperty"); 58 appendFormWithName(w, "addEventListener"); 59 appendElementWithId(w, "a"); 60 appendIframeWithName(w, 0); 61 62 w.Object.prototype.a = {}; 63 w.EventTarget.prototype[0] = {}; 64 65 // These are shadowed by properties higher in [[Prototype]] chain. See https://webidl.spec.whatwg.org/#dfn-named-property-visibility 66 assert_equals(Object.getOwnPropertyDescriptor(wp, "hasOwnProperty"), undefined, supportedNonIndex); 67 assert_equals(Reflect.getOwnPropertyDescriptor(wp, "addEventListener"), undefined, supportedNonIndex); 68 assert_equals(Object.getOwnPropertyDescriptor(wp, "a"), undefined, supportedNonIndex); 69 assert_equals(Reflect.getOwnPropertyDescriptor(wp, 0), undefined, supportedIndex); 70 }, "[[GetOwnProperty]] (named property visibility algorithm)"); 71 72 test(t => { 73 const { w, wp } = createWindowProperties(t); 74 75 appendElementWithId(w, "a"); 76 appendFormWithName(w, 0); 77 78 assert_define_own_property_fails(wp, "a", {}, supportedNonIndex); 79 assert_define_own_property_fails(wp, 0, {}, supportedIndex); 80 assert_define_own_property_fails(wp, "b", {}, unsupportedNonIndex); 81 assert_define_own_property_fails(wp, 1, {}, unsupportedIndex); 82 assert_define_own_property_fails(wp, Symbol.toStringTag, {}, existingSymbol); 83 assert_define_own_property_fails(wp, Symbol(), {}, nonExistingSymbol); 84 }, "[[DefineOwnProperty]]"); 85 86 test(t => { 87 const { w, wp } = createWindowProperties(t); 88 89 appendFormWithName(w, "a"); 90 appendElementWithId(w, 0); 91 92 assert_true("a" in wp, supportedNonIndex); 93 assert_true(Reflect.has(wp, "a"), supportedNonIndex); 94 assert_true(0 in wp, supportedIndex); 95 assert_true(Reflect.has(wp, 0), supportedIndex); 96 97 assert_false("b" in wp, unsupportedNonIndex); 98 assert_false(Reflect.has(wp, 1), unsupportedIndex); 99 }, "[[HasProperty]]"); 100 101 test(t => { 102 const { w, wp } = createWindowProperties(t); 103 const elA = appendFormWithName(w, "a"); 104 const el0 = appendIframeWithName(w, 0); 105 106 assert_equals(wp.a, elA, supportedNonIndex); 107 assert_equals(wp[0], el0, supportedIndex); 108 assert_equals(wp[Symbol.toStringTag], "WindowProperties", existingSymbol); 109 110 assert_equals(wp.b, undefined, unsupportedNonIndex); 111 assert_equals(wp[1], undefined, unsupportedIndex); 112 assert_equals(wp[Symbol.iterator], undefined, nonExistingSymbol); 113 }, "[[Get]]"); 114 115 test(t => { 116 const { w, wp } = createWindowProperties(t); 117 118 appendIframeWithName(w, "isPrototypeOf"); 119 appendFormWithName(w, "dispatchEvent"); 120 appendElementWithId(w, "a"); 121 appendElementWithId(w, 0); 122 123 w.EventTarget.prototype.a = 10; 124 w.Object.prototype[0] = 20; 125 126 // These are shadowed by properties higher in [[Prototype]] chain. See https://webidl.spec.whatwg.org/#dfn-named-property-visibility 127 assert_equals(wp.isPrototypeOf, w.Object.prototype.isPrototypeOf, supportedNonIndex); 128 assert_equals(wp.dispatchEvent, w.EventTarget.prototype.dispatchEvent, supportedNonIndex); 129 assert_equals(wp.a, 10, supportedNonIndex); 130 assert_equals(wp[0], 20, supportedIndex); 131 }, "[[Get]] (named property visibility algorithm)"); 132 133 test(t => { 134 const { w, wp } = createWindowProperties(t); 135 const elA = appendIframeWithName(w, "a"); 136 const el0 = appendFormWithName(w, 0); 137 138 assert_set_fails(wp, "a", supportedNonIndex); 139 assert_set_fails(wp, "b", unsupportedNonIndex); 140 assert_set_fails(wp, 0, supportedIndex); 141 assert_set_fails(wp, 1, unsupportedIndex); 142 assert_set_fails(wp, Symbol.toStringTag, existingSymbol); 143 assert_set_fails(wp, Symbol(), nonExistingSymbol); 144 145 assert_equals(wp.a, elA, supportedNonIndex); 146 assert_equals(wp[0], el0, supportedIndex); 147 assert_equals(wp.b, undefined, unsupportedNonIndex); 148 assert_equals(wp[1], undefined, unsupportedIndex); 149 }, "[[Set]] (direct)"); 150 151 test(t => { 152 const { w, wp } = createWindowProperties(t); 153 const receiver = Object.create(wp); 154 155 appendIframeWithName(w, "a"); 156 appendElementWithId(w, 0); 157 158 let setterThisValue; 159 Object.defineProperty(w.Object.prototype, 1, { set() { setterThisValue = this; } }); 160 Object.defineProperty(w.EventTarget.prototype, "b", { writable: false }); 161 162 receiver.a = 10; 163 assert_throws_js(TypeError, () => { receiver.b = {}; }, unsupportedNonIndex); 164 receiver[0] = 20; 165 receiver[1] = {}; 166 167 assert_equals(receiver.a, 10, supportedNonIndex); 168 assert_equals(receiver[0], 20, supportedIndex); 169 assert_false(receiver.hasOwnProperty("b"), unsupportedNonIndex); 170 assert_false(receiver.hasOwnProperty(1), unsupportedIndex); 171 assert_equals(setterThisValue, receiver, "setter |this| value is receiver"); 172 }, "[[Set]] (prototype chain)"); 173 174 test(t => { 175 const { w, wp } = createWindowProperties(t); 176 const receiver = {}; 177 178 appendFormWithName(w, "a"); 179 appendIframeWithName(w, 0); 180 181 let setterThisValue; 182 Object.defineProperty(w.Object.prototype, "b", { set() { setterThisValue = this; } }); 183 Object.defineProperty(w.EventTarget.prototype, 1, { writable: false }); 184 185 assert_true(Reflect.set(wp, "a", 10, receiver), supportedNonIndex); 186 assert_true(Reflect.set(wp, 0, 20, receiver), supportedIndex); 187 assert_true(Reflect.set(wp, "b", {}, receiver), unsupportedNonIndex); 188 assert_false(Reflect.set(wp, 1, {}, receiver), unsupportedIndex); 189 190 assert_equals(receiver.a, 10, supportedNonIndex); 191 assert_equals(receiver[0], 20, supportedIndex); 192 assert_false(receiver.hasOwnProperty("b"), unsupportedNonIndex); 193 assert_equals(setterThisValue, receiver, "setter |this| value is receiver"); 194 assert_false(receiver.hasOwnProperty(1), unsupportedIndex); 195 }, "[[Set]] (Reflect.set)"); 196 197 test(t => { 198 const { w, wp } = createWindowProperties(t); 199 const elA = appendFormWithName(w, "a"); 200 const el0 = appendElementWithId(w, 0); 201 202 assert_delete_fails(wp, "a", supportedNonIndex); 203 assert_delete_fails(wp, 0, supportedIndex); 204 assert_delete_fails(wp, "b", unsupportedNonIndex); 205 assert_delete_fails(wp, 1, unsupportedIndex); 206 assert_delete_fails(wp, Symbol.toStringTag, existingSymbol); 207 assert_delete_fails(wp, Symbol("foo"), nonExistingSymbol); 208 209 assert_equals(wp.a, elA, supportedNonIndex); 210 assert_equals(wp[0], el0, supportedIndex); 211 assert_equals(wp[Symbol.toStringTag], "WindowProperties", existingSymbol); 212 }, "[[Delete]]"); 213 214 test(t => { 215 const { w, wp } = createWindowProperties(t); 216 217 appendIframeWithName(w, "a"); 218 appendElementWithId(w, 0); 219 appendFormWithName(w, "b"); 220 221 const forInKeys = []; 222 for (const key in wp) 223 forInKeys.push(key); 224 225 assert_array_equals(forInKeys, Object.keys(w.EventTarget.prototype)); 226 assert_array_equals(Object.getOwnPropertyNames(wp), []); 227 assert_array_equals(Reflect.ownKeys(wp), [Symbol.toStringTag]); 228 }, "[[OwnPropertyKeys]]"); 229 230 function createWindowProperties(t) { 231 const iframe = document.createElement("iframe"); 232 document.body.append(iframe); 233 t.add_cleanup(() => { iframe.remove(); }); 234 235 const w = iframe.contentWindow; 236 const wp = Object.getPrototypeOf(w.Window.prototype); 237 return { w, wp }; 238 } 239 240 function appendIframeWithName(w, name) { 241 const el = w.document.createElement("iframe"); 242 el.name = name; 243 w.document.body.append(el); 244 return el.contentWindow; 245 } 246 247 function appendFormWithName(w, name) { 248 const el = w.document.createElement("form"); 249 el.name = name; 250 w.document.body.append(el); 251 return el; 252 } 253 254 function appendElementWithId(w, id) { 255 const el = w.document.createElement("div"); 256 el.id = id; 257 w.document.body.append(el); 258 return el; 259 } 260 261 function assert_prop_desc(desc, value, testInfo) { 262 assert_equals(typeof desc, "object", `${testInfo} typeof desc`); 263 assert_equals(desc.value, value, `${testInfo} [[Value]]`); 264 assert_true(desc.writable, `${testInfo} [[Writable]]`); 265 assert_false(desc.enumerable, `${testInfo} [[Enumerable]]`); 266 assert_true(desc.configurable, `${testInfo} [[Configurable]]`); 267 } 268 269 function assert_define_own_property_fails(object, key, desc, testInfo) { 270 assert_throws_js(TypeError, () => { Object.defineProperty(object, key, desc); }, testInfo); 271 assert_false(Reflect.defineProperty(object, key, desc), testInfo); 272 } 273 274 function assert_set_fails(object, key, value, testInfo) { 275 sloppyModeSet(object, key, value); 276 assert_throws_js(TypeError, () => { object[key] = value; }, testInfo); 277 assert_false(Reflect.set(object, key, value), testInfo); 278 } 279 280 function assert_delete_fails(object, key, testInfo) { 281 assert_throws_js(TypeError, () => { delete object[key]; }, testInfo); 282 assert_false(Reflect.deleteProperty(object, key), testInfo); 283 } 284 </script>