tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLElement-constructor.html (8465B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <title>Custom Elements: HTMLElement must allow subclassing</title>
      5 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
      6 <meta name="assert" content="HTMLElement must allow subclassing">
      7 <link rel="help" href="https://html.spec.whatwg.org/#html-element-constructors">
      8 <script src="/resources/testharness.js"></script>
      9 <script src="/resources/testharnessreport.js"></script>
     10 </head>
     11 <body>
     12 <div id="log"></div>
     13 <script>
     14 
     15 test(function () {
     16    customElements.define('html-custom-element', HTMLElement);
     17    assert_throws_js(TypeError, function () { new HTMLElement(); });
     18 }, 'HTMLElement constructor must throw a TypeError when NewTarget is equal to itself');
     19 
     20 test(function () {
     21    customElements.define('html-proxy-custom-element', new Proxy(HTMLElement, {}));
     22    assert_throws_js(TypeError, function () { new HTMLElement(); });
     23 }, 'HTMLElement constructor must throw a TypeError when NewTarget is equal to itself via a Proxy object');
     24 
     25 test(function () {
     26    class SomeCustomElement extends HTMLElement {};
     27    assert_throws_js(TypeError, function () { new SomeCustomElement; });
     28 }, 'HTMLElement constructor must throw TypeError when it has not been defined by customElements.define');
     29 
     30 test(function () {
     31    class SomeCustomElement extends HTMLParagraphElement {};
     32    customElements.define('some-custom-element', SomeCustomElement);
     33    assert_throws_js(TypeError, function () { new SomeCustomElement(); });
     34 }, 'Custom element constructor must throw TypeError when it does not extend HTMLElement');
     35 
     36 test(function () {
     37    class SomeCustomButtonElement extends HTMLButtonElement {};
     38    customElements.define('some-custom-button-element', SomeCustomButtonElement, { extends: "p" });
     39    assert_throws_js(TypeError, function () { new SomeCustomButtonElement(); });
     40 }, 'Custom element constructor must throw TypeError when it does not extend the proper element interface');
     41 
     42 test(function () {
     43    class CustomElementWithInferredTagName extends HTMLElement {};
     44    customElements.define('inferred-name', CustomElementWithInferredTagName);
     45 
     46    var instance = new CustomElementWithInferredTagName;
     47    assert_true(instance instanceof Element, 'A custom element must inherit from Element');
     48    assert_true(instance instanceof Node, 'A custom element must inherit from Node');
     49    assert_equals(instance.localName, 'inferred-name');
     50    assert_equals(instance.nodeName, 'INFERRED-NAME');
     51    assert_equals(instance.namespaceURI, 'http://www.w3.org/1999/xhtml', 'A custom HTML element must use HTML namespace');
     52 
     53    document.body.appendChild(instance);
     54    assert_equals(document.body.lastChild, instance,
     55        'document.body.appendChild must be able to insert a custom element');
     56    assert_equals(document.querySelector('inferred-name'), instance,
     57        'document.querySelector must be able to find a custom element by its tag name');
     58 
     59 }, 'HTMLElement constructor must infer the tag name from the element interface');
     60 
     61 test(function () {
     62    class ConcreteCustomElement extends HTMLElement { };
     63    class SubCustomElement extends ConcreteCustomElement { };
     64    customElements.define('concrete-custom-element', ConcreteCustomElement);
     65    customElements.define('sub-custom-element', SubCustomElement);
     66 
     67    var instance = new ConcreteCustomElement();
     68    assert_true(instance instanceof ConcreteCustomElement);
     69    assert_false(instance instanceof SubCustomElement);
     70    assert_equals(instance.localName, 'concrete-custom-element');
     71    assert_equals(instance.nodeName, 'CONCRETE-CUSTOM-ELEMENT');
     72 
     73    var instance = new SubCustomElement();
     74    assert_true(instance instanceof ConcreteCustomElement);
     75    assert_true(instance instanceof SubCustomElement);
     76    assert_equals(instance.localName, 'sub-custom-element');
     77    assert_equals(instance.nodeName, 'SUB-CUSTOM-ELEMENT');
     78 
     79 }, 'HTMLElement constructor must allow subclassing a custom element');
     80 
     81 test(function () {
     82    class AbstractCustomElement extends HTMLElement { };
     83    class ConcreteSubCustomElement extends AbstractCustomElement { };
     84    customElements.define('concrete-sub-custom-element', ConcreteSubCustomElement);
     85 
     86    var instance = new ConcreteSubCustomElement();
     87    assert_true(instance instanceof AbstractCustomElement);
     88    assert_true(instance instanceof ConcreteSubCustomElement);
     89    assert_equals(instance.localName, 'concrete-sub-custom-element');
     90    assert_equals(instance.nodeName, 'CONCRETE-SUB-CUSTOM-ELEMENT');
     91 
     92 }, 'HTMLElement constructor must allow subclassing an user-defined subclass of HTMLElement');
     93 
     94 test(function() {
     95    class SomeCustomElement extends HTMLElement {};
     96    var getCount = 0;
     97    var countingProxy = new Proxy(SomeCustomElement, {
     98        get: function(target, prop, receiver) {
     99            if (prop == "prototype") {
    100                ++getCount;
    101            }
    102            return Reflect.get(target, prop, receiver);
    103        }
    104    });
    105    customElements.define("success-counting-element-1", countingProxy);
    106    // define() gets the prototype of the constructor it's passed, so
    107    // reset the counter.
    108    getCount = 0;
    109    var instance = new countingProxy();
    110    assert_equals(getCount, 1, "Should have gotten .prototype once");
    111    assert_true(instance instanceof countingProxy);
    112    assert_true(instance instanceof HTMLElement);
    113    assert_true(instance instanceof SomeCustomElement);
    114    assert_equals(instance.localName, "success-counting-element-1");
    115    assert_equals(instance.nodeName, "SUCCESS-COUNTING-ELEMENT-1");
    116 }, 'HTMLElement constructor must only get .prototype once, calling proxy constructor directly');
    117 
    118 test(function() {
    119    class SomeCustomElement extends HTMLElement {};
    120    var getCount = 0;
    121    var countingProxy = new Proxy(SomeCustomElement, {
    122        get: function(target, prop, receiver) {
    123            if (prop == "prototype") {
    124                ++getCount;
    125            }
    126            return Reflect.get(target, prop, receiver);
    127        }
    128    });
    129    customElements.define("success-counting-element-2", countingProxy);
    130    // define() gets the prototype of the constructor it's passed, so
    131    // reset the counter.
    132    getCount = 0;
    133    var instance = Reflect.construct(HTMLElement, [], countingProxy);
    134    assert_equals(getCount, 1, "Should have gotten .prototype once");
    135    assert_true(instance instanceof countingProxy);
    136    assert_true(instance instanceof HTMLElement);
    137    assert_true(instance instanceof SomeCustomElement);
    138    assert_equals(instance.localName, "success-counting-element-2");
    139    assert_equals(instance.nodeName, "SUCCESS-COUNTING-ELEMENT-2");
    140 }, 'HTMLElement constructor must only get .prototype once, calling proxy constructor via Reflect');
    141 
    142 test(function() {
    143    class SomeCustomElement {};
    144    var getCount = 0;
    145    var countingProxy = new Proxy(SomeCustomElement, {
    146        get: function(target, prop, receiver) {
    147            if (prop == "prototype") {
    148                ++getCount;
    149            }
    150            return Reflect.get(target, prop, receiver);
    151        }
    152    });
    153    customElements.define("success-counting-element-3", countingProxy);
    154    // define() gets the prototype of the constructor it's passed, so
    155    // reset the counter.
    156    getCount = 0;
    157    var instance = Reflect.construct(HTMLElement, [], countingProxy);
    158    assert_equals(getCount, 1, "Should have gotten .prototype once");
    159    assert_true(instance instanceof countingProxy);
    160    assert_true(instance instanceof SomeCustomElement);
    161    assert_equals(instance.localName, undefined);
    162    assert_equals(instance.nodeName, undefined);
    163 }, 'HTMLElement constructor must only get .prototype once, calling proxy constructor via Reflect with no inheritance');
    164 
    165 test(function() {
    166    class SomeCustomElement extends HTMLElement {};
    167    var getCount = 0;
    168    var countingProxy = new Proxy(SomeCustomElement, {
    169        get: function(target, prop, receiver) {
    170            if (prop == "prototype") {
    171                ++getCount;
    172            }
    173            return Reflect.get(target, prop, receiver);
    174        }
    175    });
    176 
    177    // Purposefully don't register it.
    178    assert_throws_js(TypeError,
    179                     function () { Reflect.construct(HTMLElement, [], countingProxy) },
    180                     "Should not be able to construct an HTMLElement named 'button'");
    181    assert_equals(getCount, 0, "Should never have gotten .prototype");
    182 }, 'HTMLElement constructor must not get .prototype until it finishes its registration  sanity checks, calling via Reflect');
    183 </script>
    184 </body>
    185 </html>