| <!DOCTYPE html> | |
| <title>Custom Elements: [HTMLConstructor] derives prototype from NewTarget</title> | |
| <meta name="author" title="Domenic Denicola" href="mailto:d@domenic.me"> | |
| <meta name="help" content="https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor"> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="../resources/custom-elements-helpers.js"></script> | |
| <body> | |
| <script> | |
| "use strict"; | |
| test_with_window(w => { | |
| let beforeDefinition = true; | |
| const proto1 = { "proto": "number one" }; | |
| const proto2 = { "proto": "number two" }; | |
| function TestElement() { | |
| const o = Reflect.construct(w.HTMLElement, [], new.target); | |
| assert_equals(Object.getPrototypeOf(o), proto2, | |
| "Must use the value returned from new.target.prototype"); | |
| assert_not_equals(Object.getPrototypeOf(o), proto1, | |
| "Must not use the prototype stored at definition time"); | |
| } | |
| const ElementWithDynamicPrototype = new Proxy(TestElement, { | |
| get: function (target, name) { | |
| if (name == "prototype") | |
| return beforeDefinition ? proto1 : proto2; | |
| return target[name]; | |
| } | |
| }); | |
| w.customElements.define("test-element", ElementWithDynamicPrototype); | |
| beforeDefinition = false; | |
| new ElementWithDynamicPrototype(); | |
| }, "Use NewTarget's prototype, not the one stored at definition time"); | |
| test_with_window(w => { | |
| // We have to not throw during define(), but throw during super() | |
| let throws = false; | |
| function TestElement() { | |
| throws = true; | |
| assert_throws({ name: "prototype throws" }, () => { | |
| Reflect.construct(w.HTMLElement, [], new.target); | |
| }); | |
| } | |
| const ElementWithDynamicPrototype = new Proxy(TestElement, { | |
| get: function (target, name) { | |
| if (throws && name == "prototype") | |
| throw { name: "prototype throws" }; | |
| return target[name]; | |
| } | |
| }); | |
| w.customElements.define("test-element", ElementWithDynamicPrototype); | |
| new ElementWithDynamicPrototype(); | |
| }, "Rethrow any exceptions thrown while getting the prototype"); | |
| [null, undefined, 5, "string"].forEach(function (notAnObject) { | |
| test_with_window(w => { | |
| // We have to return an object during define(), but not during super() | |
| let returnNotAnObject = false; | |
| function TestElement() { | |
| const o = Reflect.construct(w.HTMLElement, [], new.target); | |
| assert_equals(Object.getPrototypeOf(new.target), window.Function.prototype); | |
| assert_equals(Object.getPrototypeOf(o), window.HTMLElement.prototype, | |
| "Must use the HTMLElement from the realm of NewTarget"); | |
| assert_not_equals(Object.getPrototypeOf(o), w.HTMLElement.prototype, | |
| "Must not use the HTMLElement from the realm of the active function object (w.HTMLElement)"); | |
| return o; | |
| } | |
| const ElementWithDynamicPrototype = new Proxy(TestElement, { | |
| get: function (target, name) { | |
| if (name == "prototype") | |
| return returnNotAnObject ? notAnObject : {}; | |
| return target[name]; | |
| } | |
| }); | |
| w.customElements.define("test-element", ElementWithDynamicPrototype); | |
| returnNotAnObject = true; | |
| new ElementWithDynamicPrototype(); | |
| }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (autonomous custom elements)"); | |
| }); | |
| [null, undefined, 5, "string"].forEach(function (notAnObject) { | |
| test_with_window(w => { | |
| // We have to return an object during define(), but not during super() | |
| let returnNotAnObject = false; | |
| function TestElement() { | |
| const o = Reflect.construct(w.HTMLParagraphElement, [], new.target); | |
| assert_equals(Object.getPrototypeOf(o), window.HTMLParagraphElement.prototype, | |
| "Must use the HTMLParagraphElement from the realm of NewTarget"); | |
| assert_not_equals(Object.getPrototypeOf(o), w.HTMLParagraphElement.prototype, | |
| "Must not use the HTMLParagraphElement from the realm of the active function object (w.HTMLParagraphElement)"); | |
| return o; | |
| } | |
| const ElementWithDynamicPrototype = new Proxy(TestElement, { | |
| get: function (target, name) { | |
| if (name == "prototype") | |
| return returnNotAnObject ? notAnObject : {}; | |
| return target[name]; | |
| } | |
| }); | |
| w.customElements.define("test-element", ElementWithDynamicPrototype, { extends: "p" }); | |
| returnNotAnObject = true; | |
| new ElementWithDynamicPrototype(); | |
| }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (customized built-in elements)"); | |
| }); | |
| </script> | |
| </body> |