newtarget-customized-builtins.html (4919B)
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 [null, undefined, 5, "string"].forEach(function (notAnObject) { 13 test_with_window(w => { 14 // We have to return an object during define(), but not during super() 15 let returnNotAnObject = false; 16 17 function TestElement() { 18 const o = Reflect.construct(w.HTMLParagraphElement, [], new.target); 19 20 assert_equals(Object.getPrototypeOf(o), window.HTMLParagraphElement.prototype, 21 "Must use the HTMLParagraphElement from the realm of NewTarget"); 22 assert_not_equals(Object.getPrototypeOf(o), w.HTMLParagraphElement.prototype, 23 "Must not use the HTMLParagraphElement from the realm of the active function object (w.HTMLParagraphElement)"); 24 25 return o; 26 } 27 28 const ElementWithDynamicPrototype = new Proxy(TestElement, { 29 get: function (target, name) { 30 if (name == "prototype") 31 return returnNotAnObject ? notAnObject : {}; 32 return target[name]; 33 } 34 }); 35 36 w.customElements.define("test-element", ElementWithDynamicPrototype, { extends: "p" }); 37 38 returnNotAnObject = true; 39 new ElementWithDynamicPrototype(); 40 }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (customized built-in elements)"); 41 42 test_with_window(w => { 43 // We have to return an object during define(), but not during super() 44 let returnNotAnObject = false; 45 46 function TestElement() { 47 const o = Reflect.construct(w.HTMLParagraphElement, [], new.target); 48 49 assert_equals(Object.getPrototypeOf(o), window.HTMLParagraphElement.prototype, 50 "Must use the HTMLParagraphElement from the realm of NewTarget"); 51 assert_not_equals(Object.getPrototypeOf(o), w.HTMLParagraphElement.prototype, 52 "Must not use the HTMLParagraphElement from the realm of the active function object (w.HTMLParagraphElement)"); 53 54 return o; 55 } 56 57 // Create the proxy in the subframe, which should not affect what our 58 // prototype ends up as. 59 const ElementWithDynamicPrototype = new w.Proxy(TestElement, { 60 get: function (target, name) { 61 if (name == "prototype") 62 return returnNotAnObject ? notAnObject : {}; 63 return target[name]; 64 } 65 }); 66 67 w.customElements.define("test-element", ElementWithDynamicPrototype, { extends: "p" }); 68 69 returnNotAnObject = true; 70 new ElementWithDynamicPrototype(); 71 }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's GetFunctionRealm (customized built-in elements)"); 72 }); 73 74 test_with_window(w => { 75 class SomeCustomElement extends HTMLParagraphElement {}; 76 var getCount = 0; 77 var countingProxy = new Proxy(SomeCustomElement, { 78 get: function(target, prop, receiver) { 79 if (prop == "prototype") { 80 ++getCount; 81 } 82 return Reflect.get(target, prop, receiver); 83 } 84 }); 85 w.customElements.define("failure-counting-element", countingProxy); 86 // define() gets the prototype of the constructor it's passed, so 87 // reset the counter. 88 getCount = 0; 89 assert_throws_js(TypeError, 90 function () { new countingProxy() }, 91 "Should not be able to construct an HTMLParagraphElement not named 'p'"); 92 assert_equals(getCount, 0, "Should never have gotten .prototype"); 93 }, 'HTMLParagraphElement constructor must not get .prototype until it finishes its extends sanity checks, calling proxy constructor directly'); 94 95 test_with_window(w => { 96 class SomeCustomElement extends HTMLParagraphElement {}; 97 var getCount = 0; 98 var countingProxy = new Proxy(SomeCustomElement, { 99 get: function(target, prop, receiver) { 100 if (prop == "prototype") { 101 ++getCount; 102 } 103 return Reflect.get(target, prop, receiver); 104 } 105 }); 106 w.customElements.define("failure-counting-element", countingProxy); 107 // define() gets the prototype of the constructor it's passed, so 108 // reset the counter. 109 getCount = 0; 110 assert_throws_js(TypeError, 111 function () { Reflect.construct(HTMLParagraphElement, [], countingProxy) }, 112 "Should not be able to construct an HTMLParagraphElement not named 'p'"); 113 assert_equals(getCount, 0, "Should never have gotten .prototype"); 114 }, 'HTMLParagraphElement constructor must not get .prototype until it finishes its extends sanity checks, calling via Reflect'); 115 </script> 116 </body>