[
  // [TagName, InterfaceName]
  ["a", "Anchor"],
  ["abbr", ""],
  ["acronym", ""],
  ["address", ""],
  ["area", "Area"],
  ["article", ""],
  ["aside", ""],
  ["audio", "Audio"],
  ["b", ""],
  ["base", "Base"],
  ["basefont", ""],
  ["bdo", ""],
  ["big", ""],
  ["blockquote", "Quote"],
  ["body", "Body"],
  ["br", "BR"],
  ["button", "Button"],
  ["canvas", "Canvas"],
  ["caption", "TableCaption"],
  ["center", ""],
  ["cite", ""],
  ["code", ""],
  ["col", "TableCol"],
  ["colgroup", "TableCol"],
  ["data", "Data"],
  ["datalist", "DataList"],
  ["dd", ""],
  ["del", "Mod"],
  ["details", "Details"],
  ["dfn", ""],
  ["dir", "Directory"],
  ["div", "Div"],
  ["dl", "DList"],
  ["dt", ""],
  ["em", ""],
  ["embed", "Embed"],
  ["fieldset", "FieldSet"],
  ["figcaption", ""],
  ["figure", ""],
  ["font", "Font"],
  ["footer", ""],
  ["form", "Form"],
  ["frame", "Frame"],
  ["frameset", "FrameSet"],
  ["h1", "Heading"],
  ["h2", "Heading"],
  ["h3", "Heading"],
  ["h4", "Heading"],
  ["h5", "Heading"],
  ["h6", "Heading"],
  ["head", "Head"],
  ["header", ""],
  ["hgroup", ""],
  ["hr", "HR"],
  ["html", "Html"],
  ["i", ""],
  ["iframe", "IFrame"],
  ["image", ""],
  ["img", "Image"],
  ["input", "Input"],
  ["ins", "Mod"],
  ["kbd", ""],
  ["label", "Label"],
  ["legend", "Legend"],
  ["li", "LI"],
  ["link", "Link"],
  ["listing", "Pre"],
  ["main", ""],
  ["map", "Map"],
  ["mark", ""],
  ["marquee", "Marquee"],
  ["menu", "Menu"],
  ["meta", "Meta"],
  ["meter", "Meter"],
  ["nav", ""],
  ["nobr", ""],
  ["noembed", ""],
  ["noframes", ""],
  ["noscript", ""],
  ["object", "Object"],
  ["ol", "OList"],
  ["optgroup", "OptGroup"],
  ["option", "Option"],
  ["output", "Output"],
  ["p", "Paragraph"],
  ["param", "Param"],
  ["picture", "Picture"],
  ["plaintext", ""],
  ["pre", "Pre"],
  ["progress", "Progress"],
  ["q", "Quote"],
  ["rb", ""],
  ["rp", ""],
  ["rt", ""],
  ["rtc", ""],
  ["ruby", ""],
  ["s", ""],
  ["samp", ""],
  ["script", "Script"],
  ["section", ""],
  ["select", "Select"],
  ["small", ""],
  ["source", "Source"],
  ["span", "Span"],
  ["strike", ""],
  ["strong", ""],
  ["style", "Style"],
  ["sub", ""],
  ["summary", ""],
  ["sup", ""],
  ["table", "Table"],
  ["tbody", "TableSection"],
  ["td", "TableCell"],
  ["textarea", "TextArea"],
  ["tfoot", "TableSection"],
  ["th", "TableCell"],
  ["thead", "TableSection"],
  ["template", "Template"],
  ["time", "Time"],
  ["title", "Title"],
  ["tr", "TableRow"],
  ["track", "Track"],
  ["tt", ""],
  ["u", ""],
  ["ul", "UList"],
  ["var", ""],
  ["video", "Video"],
  ["wbr", ""],
  ["xmp", "Pre"],
].forEach(e => {
  let tagName = e[0];
  let interfaceName = "HTML" + e[1] + "Element";
  promises.push(
    test_with_new_window(testWindow => {
      // Use window from iframe to isolate the test.
      // Test calling the HTML*Element constructor.
      (() => {
        SimpleTest.doesThrow(() => {
          testWindow[interfaceName]();
        }, "calling the " + interfaceName + " constructor should throw a TypeError");
      })();

      // Test constructing a HTML*ELement.
      (() => {
        SimpleTest.doesThrow(() => {
          new testWindow[interfaceName]();
        }, "constructing a " + interfaceName + " should throw a TypeError");
      })();

      // Test constructing a custom element with defining HTML*Element as entry.
      (() => {
        testWindow.customElements.define(
          "x-defining-" + tagName,
          testWindow[interfaceName]
        );
        SimpleTest.doesThrow(() => {
          new testWindow[interfaceName]();
        }, "constructing a custom element with defining " + interfaceName + " as registry entry should throw a TypeError");
      })();

      // Since HTMLElement can be registered without specifying "extends", skip
      // testing HTMLElement tags.
      if (interfaceName !== "HTMLElement") {
        // Test constructing a customized HTML*Element with defining a registry entry
        // without specifying "extends".
        (() => {
          class X extends testWindow[interfaceName] {}
          testWindow.customElements.define("x-defining-invalid-" + tagName, X);
          SimpleTest.doesThrow(() => {
            new X();
          }, "constructing a customized " + interfaceName + " with defining a " + 'registry entry without specifying "extends" should throw a TypeError');
        })();
      }

      // Test constructing a built-in custom element with defining a registry entry
      // with incorrect "extends" information.
      (() => {
        class X extends testWindow[interfaceName] {}
        testWindow.customElements.define("x-defining-incorrect-" + tagName, X, {
          extends: tagName === "img" ? "p" : "img",
        });
        SimpleTest.doesThrow(() => {
          new X();
        }, "constructing a customized " + interfaceName + " with defining a " + 'registry entry with incorrect "extends" should throw a TypeError');
      })();

      // Test calling a custom element constructor and constructing a built-in
      // custom element.
      (() => {
        let num_constructor_invocations = 0;
        class X extends testWindow[interfaceName] {
          constructor() {
            super();
            num_constructor_invocations++;
          }
        }
        testWindow.customElements.define("x-" + tagName, X, {
          extends: tagName,
        });
        SimpleTest.doesThrow(() => {
          X();
        }, "calling a customized " + interfaceName + " constructor should throw a TypeError");

        let element = new X();

        SimpleTest.is(
          Object.getPrototypeOf(Cu.waiveXrays(element)),
          X.prototype,
          "constructing a customized " +
            interfaceName +
            "; the element should be a registered constructor"
        );
        SimpleTest.is(
          element.localName,
          tagName,
          "constructing a customized " +
            interfaceName +
            '; the element tag name should be "' +
            tagName +
            '"'
        );
        SimpleTest.is(
          element.namespaceURI,
          "http://www.w3.org/1999/xhtml",
          "constructing a customized " +
            interfaceName +
            "; the element should be in the HTML namespace"
        );
        SimpleTest.is(
          element.prefix,
          null,
          "constructing a customized " +
            interfaceName +
            "; the element name should not have a prefix"
        );
        SimpleTest.is(
          element.ownerDocument,
          testWindow.document,
          "constructing a customized " +
            interfaceName +
            "; the element should be owned by the registry's associated " +
            "document"
        );
        SimpleTest.is(
          num_constructor_invocations,
          1,
          "constructing a customized " +
            interfaceName +
            "; the constructor should have been invoked once"
        );
      })();

      // Test if prototype is no an object.
      (() => {
        function ElementWithNonObjectPrototype() {
          let o = Reflect.construct(testWindow[interfaceName], [], new.target);
          SimpleTest.is(
            Object.getPrototypeOf(Cu.waiveXrays(o)),
            window[interfaceName].prototype,
            "constructing a customized " +
              interfaceName +
              "; if prototype is not object, fallback from NewTarget's realm"
          );
        }

        // Prototype have to be an object during define(), otherwise define will
        // throw an TypeError exception.
        ElementWithNonObjectPrototype.prototype = {};
        testWindow.customElements.define(
          "x-non-object-prototype-" + tagName,
          ElementWithNonObjectPrototype,
          { extends: tagName }
        );

        ElementWithNonObjectPrototype.prototype = "string";
        new ElementWithNonObjectPrototype();
      })();
    })
  );
});
