newtarget.html (4612B)
1 <!DOCTYPE html> 2 <title>Custom Elements: [HTMLConstructor] derives prototype from NewTarget</title> 3 <meta name="author" title="Domenic Denicola" href="mailto:d@domenic.me"> 4 <meta name="help" content="https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="../resources/custom-elements-helpers.js"></script> 8 <body> 9 <script> 10 "use strict"; 11 12 test_with_window(w => { 13 let beforeDefinition = true; 14 const proto1 = { "proto": "number one" }; 15 const proto2 = { "proto": "number two" }; 16 17 function TestElement() { 18 const o = Reflect.construct(w.HTMLElement, [], new.target); 19 assert_equals(Object.getPrototypeOf(o), proto2, 20 "Must use the value returned from new.target.prototype"); 21 assert_not_equals(Object.getPrototypeOf(o), proto1, 22 "Must not use the prototype stored at definition time"); 23 } 24 25 const ElementWithDynamicPrototype = new Proxy(TestElement, { 26 get: function (target, name) { 27 if (name == "prototype") 28 return beforeDefinition ? proto1 : proto2; 29 return target[name]; 30 } 31 }); 32 33 w.customElements.define("test-element", ElementWithDynamicPrototype); 34 35 beforeDefinition = false; 36 new ElementWithDynamicPrototype(); 37 }, "Use NewTarget's prototype, not the one stored at definition time"); 38 39 test_with_window(w => { 40 // We have to not throw during define(), but throw during super() 41 let throws = false; 42 43 let err = { name: "prototype throws" }; 44 function TestElement() { 45 throws = true; 46 assert_throws_exactly(err, () => { 47 Reflect.construct(w.HTMLElement, [], new.target); 48 }); 49 } 50 51 const ElementWithDynamicPrototype = new Proxy(TestElement, { 52 get: function (target, name) { 53 if (throws && name == "prototype") 54 throw err; 55 return target[name]; 56 } 57 }); 58 59 w.customElements.define("test-element", ElementWithDynamicPrototype); 60 61 new ElementWithDynamicPrototype(); 62 63 }, "Rethrow any exceptions thrown while getting the prototype"); 64 65 [null, undefined, 5, "string"].forEach(function (notAnObject) { 66 test_with_window(w => { 67 // We have to return an object during define(), but not during super() 68 let returnNotAnObject = false; 69 70 function TestElement() { 71 const o = Reflect.construct(w.HTMLElement, [], new.target); 72 73 assert_equals(Object.getPrototypeOf(new.target), window.Function.prototype); 74 assert_equals(Object.getPrototypeOf(o), window.HTMLElement.prototype, 75 "Must use the HTMLElement from the realm of NewTarget"); 76 assert_not_equals(Object.getPrototypeOf(o), w.HTMLElement.prototype, 77 "Must not use the HTMLElement from the realm of the active function object (w.HTMLElement)"); 78 79 return o; 80 } 81 82 const ElementWithDynamicPrototype = new Proxy(TestElement, { 83 get: function (target, name) { 84 if (name == "prototype") 85 return returnNotAnObject ? notAnObject : {}; 86 return target[name]; 87 } 88 }); 89 90 w.customElements.define("test-element", ElementWithDynamicPrototype); 91 92 returnNotAnObject = true; 93 new ElementWithDynamicPrototype(); 94 }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (autonomous custom elements)"); 95 96 test_with_window(w => { 97 // We have to return an object during define(), but not during super() 98 let returnNotAnObject = false; 99 100 function TestElement() { 101 const o = Reflect.construct(w.HTMLElement, [], new.target); 102 103 assert_equals(Object.getPrototypeOf(new.target), window.Function.prototype); 104 assert_equals(Object.getPrototypeOf(o), window.HTMLElement.prototype, 105 "Must use the HTMLElement from the realm of NewTarget"); 106 assert_not_equals(Object.getPrototypeOf(o), w.HTMLElement.prototype, 107 "Must not use the HTMLElement from the realm of the active function object (w.HTMLElement)"); 108 109 return o; 110 } 111 112 // Create the proxy in the subframe, which should not affect what our 113 // prototype ends up as. 114 const ElementWithDynamicPrototype = new w.Proxy(TestElement, { 115 get: function (target, name) { 116 if (name == "prototype") 117 return returnNotAnObject ? notAnObject : {}; 118 return target[name]; 119 } 120 }); 121 122 w.customElements.define("test-element", ElementWithDynamicPrototype); 123 124 returnNotAnObject = true; 125 new ElementWithDynamicPrototype(); 126 }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's GetFunctionRealm (autonomous custom elements)"); 127 }); 128 129 </script> 130 </body>