LibWeb: Run the object representation task when the active state changes

Currently, the following JS snippet will hang indefinitely:

    new DOMParser().parseFromString("<object>", "text/html");

Because the document into which the object is inserted is not active. So
the task queued to run the representation steps will never run.

This patch implements the spec steps to rerun the representation steps
when the active state changes, and avoid the hang when the object is
created in an inactive document.
This commit is contained in:
Timothy Flynn 2024-12-11 13:18:21 -05:00 committed by Andrew Kaster
commit 68164aa7ec
Notes: github-actions[bot] 2024-12-12 00:39:30 +00:00
5 changed files with 681 additions and 0 deletions

View file

@ -13,6 +13,7 @@
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/DocumentLoadEventDelayer.h> #include <LibWeb/DOM/DocumentLoadEventDelayer.h>
#include <LibWeb/DOM/DocumentLoading.h> #include <LibWeb/DOM/DocumentLoading.h>
#include <LibWeb/DOM/DocumentObserver.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/Fetch/Fetching/Fetching.h> #include <LibWeb/Fetch/Fetching/Fetching.h>
#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h> #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
@ -54,12 +55,27 @@ void HTMLObjectElement::initialize(JS::Realm& realm)
{ {
Base::initialize(realm); Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLObjectElement); WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLObjectElement);
m_document_observer = realm.create<DOM::DocumentObserver>(realm, document());
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
// Whenever one of the following conditions occur:
// - the element's node document changes whether it is fully active,
// ...the user agent must queue an element task on the DOM manipulation task source given the object element to run
// the following steps to (re)determine what the object element represents.
m_document_observer->set_document_became_active([this]() {
queue_element_task_to_run_object_representation_steps();
});
m_document_observer->set_document_became_inactive([this]() {
queue_element_task_to_run_object_representation_steps();
});
} }
void HTMLObjectElement::visit_edges(Cell::Visitor& visitor) void HTMLObjectElement::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
visitor.visit(m_resource_request); visitor.visit(m_resource_request);
visitor.visit(m_document_observer);
} }
void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const&) void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const&)
@ -186,6 +202,10 @@ bool HTMLObjectElement::has_ancestor_media_element_or_object_element_not_showing
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:queue-an-element-task // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:queue-an-element-task
void HTMLObjectElement::queue_element_task_to_run_object_representation_steps() void HTMLObjectElement::queue_element_task_to_run_object_representation_steps()
{ {
// AD-HOC: If the document isn't fully active, this task will never run, and we will indefinitely delay the load event.
if (!document().is_fully_active())
return;
// This task being queued or actively running must delay the load event of the element's node document. // This task being queued or actively running must delay the load event of the element's node document.
m_document_load_event_delayer_for_object_representation_task.emplace(document()); m_document_load_event_delayer_for_object_representation_task.emplace(document());

View file

@ -90,6 +90,8 @@ private:
GC::Ptr<SharedResourceRequest> m_resource_request; GC::Ptr<SharedResourceRequest> m_resource_request;
GC::Ptr<DOM::DocumentObserver> m_document_observer;
Optional<DOM::DocumentLoadEventDelayer> m_document_load_event_delayer_for_object_representation_task; Optional<DOM::DocumentLoadEventDelayer> m_document_load_event_delayer_for_object_representation_task;
Optional<DOM::DocumentLoadEventDelayer> m_document_load_event_delayer_for_resource_load; Optional<DOM::DocumentLoadEventDelayer> m_document_load_event_delayer_for_resource_load;
}; };

View file

@ -0,0 +1,435 @@
Harness status: OK
Found 429 tests
426 Pass
3 Fail
Pass Interfaces for a: useNS
Pass Interfaces for a: useParser
Pass Interfaces for A: createElement
Pass Interfaces for abbr: useNS
Pass Interfaces for abbr: useParser
Pass Interfaces for ABBR: createElement
Pass Interfaces for acronym: useNS
Pass Interfaces for acronym: useParser
Pass Interfaces for ACRONYM: createElement
Pass Interfaces for address: useNS
Pass Interfaces for address: useParser
Pass Interfaces for ADDRESS: createElement
Pass Interfaces for applet: useNS
Pass Interfaces for applet: useParser
Pass Interfaces for APPLET: createElement
Pass Interfaces for area: useNS
Pass Interfaces for area: useParser
Pass Interfaces for AREA: createElement
Pass Interfaces for article: useNS
Pass Interfaces for article: useParser
Pass Interfaces for ARTICLE: createElement
Pass Interfaces for aside: useNS
Pass Interfaces for aside: useParser
Pass Interfaces for ASIDE: createElement
Pass Interfaces for audio: useNS
Pass Interfaces for audio: useParser
Pass Interfaces for AUDIO: createElement
Pass Interfaces for b: useNS
Pass Interfaces for b: useParser
Pass Interfaces for B: createElement
Pass Interfaces for base: useNS
Pass Interfaces for base: useParser
Pass Interfaces for BASE: createElement
Pass Interfaces for basefont: useNS
Pass Interfaces for basefont: useParser
Pass Interfaces for BASEFONT: createElement
Pass Interfaces for bdi: useNS
Pass Interfaces for bdi: useParser
Pass Interfaces for BDI: createElement
Pass Interfaces for bdo: useNS
Pass Interfaces for bdo: useParser
Pass Interfaces for BDO: createElement
Pass Interfaces for bgsound: useNS
Pass Interfaces for bgsound: useParser
Pass Interfaces for BGSOUND: createElement
Pass Interfaces for big: useNS
Pass Interfaces for big: useParser
Pass Interfaces for BIG: createElement
Pass Interfaces for blink: useNS
Pass Interfaces for blink: useParser
Pass Interfaces for BLINK: createElement
Pass Interfaces for blockquote: useNS
Pass Interfaces for blockquote: useParser
Pass Interfaces for BLOCKQUOTE: createElement
Pass Interfaces for body: useNS
Pass Interfaces for body: useParser
Pass Interfaces for BODY: createElement
Pass Interfaces for br: useNS
Pass Interfaces for br: useParser
Pass Interfaces for BR: createElement
Pass Interfaces for button: useNS
Pass Interfaces for button: useParser
Pass Interfaces for BUTTON: createElement
Pass Interfaces for canvas: useNS
Pass Interfaces for canvas: useParser
Pass Interfaces for CANVAS: createElement
Pass Interfaces for caption: useNS
Pass Interfaces for CAPTION: createElement
Pass Interfaces for center: useNS
Pass Interfaces for center: useParser
Pass Interfaces for CENTER: createElement
Pass Interfaces for cite: useNS
Pass Interfaces for cite: useParser
Pass Interfaces for CITE: createElement
Pass Interfaces for code: useNS
Pass Interfaces for code: useParser
Pass Interfaces for CODE: createElement
Pass Interfaces for col: useNS
Pass Interfaces for COL: createElement
Pass Interfaces for colgroup: useNS
Pass Interfaces for COLGROUP: createElement
Pass Interfaces for command: useNS
Pass Interfaces for command: useParser
Pass Interfaces for COMMAND: createElement
Pass Interfaces for data: useNS
Pass Interfaces for data: useParser
Pass Interfaces for DATA: createElement
Pass Interfaces for datalist: useNS
Pass Interfaces for datalist: useParser
Pass Interfaces for DATALIST: createElement
Pass Interfaces for dd: useNS
Pass Interfaces for dd: useParser
Pass Interfaces for DD: createElement
Pass Interfaces for del: useNS
Pass Interfaces for del: useParser
Pass Interfaces for DEL: createElement
Pass Interfaces for details: useNS
Pass Interfaces for details: useParser
Pass Interfaces for DETAILS: createElement
Pass Interfaces for dfn: useNS
Pass Interfaces for dfn: useParser
Pass Interfaces for DFN: createElement
Pass Interfaces for dialog: useNS
Pass Interfaces for dialog: useParser
Pass Interfaces for DIALOG: createElement
Pass Interfaces for dir: useNS
Pass Interfaces for dir: useParser
Pass Interfaces for DIR: createElement
Pass Interfaces for directory: useNS
Pass Interfaces for directory: useParser
Pass Interfaces for DIRECTORY: createElement
Pass Interfaces for div: useNS
Pass Interfaces for div: useParser
Pass Interfaces for DIV: createElement
Pass Interfaces for dl: useNS
Pass Interfaces for dl: useParser
Pass Interfaces for DL: createElement
Pass Interfaces for dt: useNS
Pass Interfaces for dt: useParser
Pass Interfaces for DT: createElement
Pass Interfaces for em: useNS
Pass Interfaces for em: useParser
Pass Interfaces for EM: createElement
Pass Interfaces for embed: useNS
Pass Interfaces for embed: useParser
Pass Interfaces for EMBED: createElement
Pass Interfaces for fieldset: useNS
Pass Interfaces for fieldset: useParser
Pass Interfaces for FIELDSET: createElement
Pass Interfaces for figcaption: useNS
Pass Interfaces for figcaption: useParser
Pass Interfaces for FIGCAPTION: createElement
Pass Interfaces for figure: useNS
Pass Interfaces for figure: useParser
Pass Interfaces for FIGURE: createElement
Pass Interfaces for font: useNS
Pass Interfaces for font: useParser
Pass Interfaces for FONT: createElement
Pass Interfaces for foo-BAR: useNS
Pass Interfaces for foo-bar: useNS
Pass Interfaces for foo-bar: useParser
Pass Interfaces for FOO-BAR: createElement
Pass Interfaces for foo: useNS
Pass Interfaces for foo: useParser
Pass Interfaces for FOO: createElement
Pass Interfaces for footer: useNS
Pass Interfaces for footer: useParser
Pass Interfaces for FOOTER: createElement
Pass Interfaces for form: useNS
Pass Interfaces for form: useParser
Pass Interfaces for FORM: createElement
Pass Interfaces for frame: useNS
Pass Interfaces for FRAME: createElement
Pass Interfaces for frameset: useNS
Pass Interfaces for FRAMESET: createElement
Pass Interfaces for h1: useNS
Pass Interfaces for h1: useParser
Pass Interfaces for H1: createElement
Pass Interfaces for h2: useNS
Pass Interfaces for h2: useParser
Pass Interfaces for H2: createElement
Pass Interfaces for h3: useNS
Pass Interfaces for h3: useParser
Pass Interfaces for H3: createElement
Pass Interfaces for h4: useNS
Pass Interfaces for h4: useParser
Pass Interfaces for H4: createElement
Pass Interfaces for h5: useNS
Pass Interfaces for h5: useParser
Pass Interfaces for H5: createElement
Pass Interfaces for h6: useNS
Pass Interfaces for h6: useParser
Pass Interfaces for H6: createElement
Pass Interfaces for head: useNS
Pass Interfaces for head: useParser
Pass Interfaces for HEAD: createElement
Pass Interfaces for header: useNS
Pass Interfaces for header: useParser
Pass Interfaces for HEADER: createElement
Pass Interfaces for hgroup: useNS
Pass Interfaces for hgroup: useParser
Pass Interfaces for HGROUP: createElement
Pass Interfaces for hr: useNS
Pass Interfaces for hr: useParser
Pass Interfaces for HR: createElement
Pass Interfaces for html: useNS
Pass Interfaces for html: useParser
Pass Interfaces for HTML: createElement
Pass Interfaces for i: useNS
Pass Interfaces for i: useParser
Pass Interfaces for I: createElement
Pass Interfaces for iframe: useNS
Pass Interfaces for iframe: useParser
Pass Interfaces for IFRAME: createElement
Pass Interfaces for image: useNS
Pass Interfaces for IMAGE: createElement
Pass Interfaces for img: useNS
Pass Interfaces for img: useParser
Pass Interfaces for IMG: createElement
Pass Interfaces for input: useNS
Pass Interfaces for input: useParser
Pass Interfaces for INPUT: createElement
Pass Interfaces for ins: useNS
Pass Interfaces for ins: useParser
Pass Interfaces for INS: createElement
Pass Interfaces for isindex: useNS
Pass Interfaces for isindex: useParser
Pass Interfaces for ISINDEX: createElement
Pass Interfaces for kbd: useNS
Pass Interfaces for kbd: useParser
Pass Interfaces for KBD: createElement
Pass Interfaces for keygen: useNS
Pass Interfaces for keygen: useParser
Pass Interfaces for KEYGEN: createElement
Pass Interfaces for label: useNS
Pass Interfaces for label: useParser
Pass Interfaces for LABEL: createElement
Pass Interfaces for legend: useNS
Pass Interfaces for legend: useParser
Pass Interfaces for LEGEND: createElement
Pass Interfaces for li: useNS
Pass Interfaces for li: useParser
Pass Interfaces for LI: createElement
Pass Interfaces for link: useNS
Pass Interfaces for link: useParser
Pass Interfaces for LINK: createElement
Pass Interfaces for listing: useNS
Pass Interfaces for listing: useParser
Pass Interfaces for LISTING: createElement
Pass Interfaces for main: useNS
Pass Interfaces for main: useParser
Pass Interfaces for MAIN: createElement
Pass Interfaces for map: useNS
Pass Interfaces for map: useParser
Pass Interfaces for MAP: createElement
Pass Interfaces for mark: useNS
Pass Interfaces for mark: useParser
Pass Interfaces for MARK: createElement
Pass Interfaces for marquee: useNS
Pass Interfaces for marquee: useParser
Pass Interfaces for MARQUEE: createElement
Pass Interfaces for menu: useNS
Pass Interfaces for menu: useParser
Pass Interfaces for MENU: createElement
Pass Interfaces for meta: useNS
Pass Interfaces for meta: useParser
Pass Interfaces for META: createElement
Pass Interfaces for meter: useNS
Pass Interfaces for meter: useParser
Pass Interfaces for METER: createElement
Pass Interfaces for mod: useNS
Pass Interfaces for mod: useParser
Pass Interfaces for MOD: createElement
Pass Interfaces for multicol: useNS
Pass Interfaces for multicol: useParser
Pass Interfaces for MULTICOL: createElement
Pass Interfaces for nav: useNS
Pass Interfaces for nav: useParser
Pass Interfaces for NAV: createElement
Pass Interfaces for nextid: useNS
Pass Interfaces for nextid: useParser
Pass Interfaces for NEXTID: createElement
Pass Interfaces for nobr: useNS
Pass Interfaces for nobr: useParser
Pass Interfaces for NOBR: createElement
Pass Interfaces for noembed: useNS
Pass Interfaces for noembed: useParser
Pass Interfaces for NOEMBED: createElement
Pass Interfaces for noframes: useNS
Pass Interfaces for noframes: useParser
Pass Interfaces for NOFRAMES: createElement
Pass Interfaces for noscript: useNS
Pass Interfaces for noscript: useParser
Pass Interfaces for NOSCRIPT: createElement
Pass Interfaces for object: useNS
Pass Interfaces for object: useParser
Pass Interfaces for OBJECT: createElement
Pass Interfaces for ol: useNS
Pass Interfaces for ol: useParser
Pass Interfaces for OL: createElement
Pass Interfaces for optgroup: useNS
Pass Interfaces for optgroup: useParser
Pass Interfaces for OPTGROUP: createElement
Pass Interfaces for option: useNS
Pass Interfaces for option: useParser
Pass Interfaces for OPTION: createElement
Pass Interfaces for output: useNS
Pass Interfaces for output: useParser
Pass Interfaces for OUTPUT: createElement
Pass Interfaces for p: useNS
Pass Interfaces for p: useParser
Pass Interfaces for P: createElement
Pass Interfaces for param: useNS
Pass Interfaces for param: useParser
Pass Interfaces for PARAM: createElement
Fail Interfaces for permission: useNS
Fail Interfaces for permission: useParser
Fail Interfaces for PERMISSION: createElement
Pass Interfaces for picture: useNS
Pass Interfaces for picture: useParser
Pass Interfaces for PICTURE: createElement
Pass Interfaces for plaintext: useNS
Pass Interfaces for plaintext: useParser
Pass Interfaces for PLAINTEXT: createElement
Pass Interfaces for pre: useNS
Pass Interfaces for pre: useParser
Pass Interfaces for PRE: createElement
Pass Interfaces for progress: useNS
Pass Interfaces for progress: useParser
Pass Interfaces for PROGRESS: createElement
Pass Interfaces for q: useNS
Pass Interfaces for q: useParser
Pass Interfaces for Q: createElement
Pass Interfaces for quasit: useNS
Pass Interfaces for quasit: useParser
Pass Interfaces for QUASIT: createElement
Pass Interfaces for rb: useNS
Pass Interfaces for rb: useParser
Pass Interfaces for RB: createElement
Pass Interfaces for rp: useNS
Pass Interfaces for rp: useParser
Pass Interfaces for RP: createElement
Pass Interfaces for rt: useNS
Pass Interfaces for rt: useParser
Pass Interfaces for RT: createElement
Pass Interfaces for rtc: useNS
Pass Interfaces for rtc: useParser
Pass Interfaces for RTC: createElement
Pass Interfaces for ruby: useNS
Pass Interfaces for ruby: useParser
Pass Interfaces for RUBY: createElement
Pass Interfaces for s: useNS
Pass Interfaces for s: useParser
Pass Interfaces for S: createElement
Pass Interfaces for samp: useNS
Pass Interfaces for samp: useParser
Pass Interfaces for SAMP: createElement
Pass Interfaces for script: useNS
Pass Interfaces for script: useParser
Pass Interfaces for SCRIPT: createElement
Pass Interfaces for section: useNS
Pass Interfaces for section: useParser
Pass Interfaces for SECTION: createElement
Pass Interfaces for select: useNS
Pass Interfaces for select: useParser
Pass Interfaces for SELECT: createElement
Pass Interfaces for slot: useNS
Pass Interfaces for slot: useParser
Pass Interfaces for SLOT: createElement
Pass Interfaces for small: useNS
Pass Interfaces for small: useParser
Pass Interfaces for SMALL: createElement
Pass Interfaces for source: useNS
Pass Interfaces for source: useParser
Pass Interfaces for SOURCE: createElement
Pass Interfaces for spacer: useNS
Pass Interfaces for spacer: useParser
Pass Interfaces for SPACER: createElement
Pass Interfaces for span: useNS
Pass Interfaces for span: useParser
Pass Interfaces for SPAN: createElement
Pass Interfaces for strike: useNS
Pass Interfaces for strike: useParser
Pass Interfaces for STRIKE: createElement
Pass Interfaces for strong: useNS
Pass Interfaces for strong: useParser
Pass Interfaces for STRONG: createElement
Pass Interfaces for style: useNS
Pass Interfaces for style: useParser
Pass Interfaces for STYLE: createElement
Pass Interfaces for sub: useNS
Pass Interfaces for sub: useParser
Pass Interfaces for SUB: createElement
Pass Interfaces for summary: useNS
Pass Interfaces for summary: useParser
Pass Interfaces for SUMMARY: createElement
Pass Interfaces for sup: useNS
Pass Interfaces for sup: useParser
Pass Interfaces for SUP: createElement
Pass Interfaces for table: useNS
Pass Interfaces for table: useParser
Pass Interfaces for TABLE: createElement
Pass Interfaces for tbody: useNS
Pass Interfaces for TBODY: createElement
Pass Interfaces for td: useNS
Pass Interfaces for TD: createElement
Pass Interfaces for textarea: useNS
Pass Interfaces for textarea: useParser
Pass Interfaces for TEXTAREA: createElement
Pass Interfaces for tfoot: useNS
Pass Interfaces for TFOOT: createElement
Pass Interfaces for th: useNS
Pass Interfaces for TH: createElement
Pass Interfaces for thead: useNS
Pass Interfaces for THEAD: createElement
Pass Interfaces for time: useNS
Pass Interfaces for time: useParser
Pass Interfaces for TIME: createElement
Pass Interfaces for title: useNS
Pass Interfaces for title: useParser
Pass Interfaces for TITLE: createElement
Pass Interfaces for tr: useNS
Pass Interfaces for TR: createElement
Pass Interfaces for track: useNS
Pass Interfaces for track: useParser
Pass Interfaces for TRACK: createElement
Pass Interfaces for tt: useNS
Pass Interfaces for tt: useParser
Pass Interfaces for TT: createElement
Pass Interfaces for u: useNS
Pass Interfaces for u: useParser
Pass Interfaces for U: createElement
Pass Interfaces for ul: useNS
Pass Interfaces for ul: useParser
Pass Interfaces for UL: createElement
Pass Interfaces for var: useNS
Pass Interfaces for var: useParser
Pass Interfaces for VAR: createElement
Pass Interfaces for video: useNS
Pass Interfaces for video: useParser
Pass Interfaces for VIDEO: createElement
Pass Interfaces for wbr: useNS
Pass Interfaces for wbr: useParser
Pass Interfaces for WBR: createElement
Pass Interfaces for xmp: useNS
Pass Interfaces for xmp: useParser
Pass Interfaces for XMP: createElement
Pass Interfaces for å-bar: useNS
Pass Interfaces for Å-BAR: createElement

View file

@ -0,0 +1,74 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test of interfaces</title>
<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
<link rel="help" href="https://html.spec.whatwg.org/multipage/">
<link rel="help" href="https://webidl.spec.whatwg.org/#host-objects">
<link rel="help" href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf#page=96">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src=interfaces.js></script>
<div id="log"></div>
<script>
function do_test(local_name, iface, variant) {
test(function() {
var e;
var i = "HTML" + iface + "Element";
if (variant === "useNS") {
// Use createElementNS here to preserve the case of local_name.
e = document.createElementNS("http://www.w3.org/1999/xhtml", local_name);
} else if (variant === "useParser") {
e = new DOMParser().parseFromString("<" + local_name + ">", "text/html").querySelector(local_name);
} else {
e = document.createElement(local_name);
}
assert_class_string(e, i,
"Element " + local_name + " should have " + i +
" as its primary interface.");
assert_true(e instanceof window[i],
"Element " + local_name + " should implement " + i + ".");
assert_true(e instanceof HTMLElement,
"Element " + local_name + " should implement HTMLElement.");
assert_true(e instanceof Element,
"Element " + local_name + " should implement Element.");
assert_true(e instanceof Node,
"Element " + local_name + " should implement Node.");
}, "Interfaces for " + local_name + ": " + variant);
}
// Some elements have weird parser behavior / insertion modes and would be
// skipped by the parser, so skip those.
function should_do_parser_test(local_name) {
return ![
"foo-BAR",
"tbody",
"td",
"tfoot",
"th",
"thead",
"tr",
"å-bar",
"caption",
"col",
"colgroup",
"frame",
"image",
"frameset",
].includes(local_name)
}
elements.forEach(function(a) {
do_test(a[0], a[1], "useNS");
if (should_do_parser_test(a[0])) {
do_test(a[0], a[1], "useParser");
}
// Only run the createElement variant if the input is all-lowercase, because createElement
// case-folds to lowercase. Custom elements are required to use all-lowercase to implement
// HTMLElement, otherwise they use HTMLUnknownElement per spec. Example: "foo-BAR".
if (a[0] === a[0].toLowerCase()) {
do_test(a[0].toUpperCase(), a[1], "createElement");
}
})
</script>

View file

@ -0,0 +1,150 @@
var elements = [
["a", "Anchor"],
["abbr", ""],
["acronym", ""],
["address", ""],
["applet", "Unknown"],
["area", "Area"],
["article", ""],
["aside", ""],
["audio", "Audio"],
["b", ""],
["base", "Base"],
["basefont", ""],
["bdi", ""],
["bdo", ""],
["bgsound", "Unknown"],
["big", ""],
["blink", "Unknown"],
["blockquote", "Quote"],
["body", "Body"],
["br", "BR"],
["button", "Button"],
["canvas", "Canvas"],
["caption", "TableCaption"],
["center", ""],
["cite", ""],
["code", ""],
["col", "TableCol"],
["colgroup", "TableCol"],
["command", "Unknown"],
["data", "Data"],
["datalist", "DataList"],
["dd", ""],
["del", "Mod"],
["details", "Details"],
["dfn", ""],
["dialog", "Dialog"],
["dir", "Directory"],
["directory", "Unknown"],
["div", "Div"],
["dl", "DList"],
["dt", ""],
["em", ""],
["embed", "Embed"],
["fieldset", "FieldSet"],
["figcaption", ""],
["figure", ""],
["font", "Font"],
["foo-BAR", "Unknown"], // not a valid custom element name
["foo-bar", ""], // valid custom element name
["foo", "Unknown"],
["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", "Unknown"],
["img", "Image"],
["input", "Input"],
["ins", "Mod"],
["isindex", "Unknown"],
["kbd", ""],
["keygen", "Unknown"],
["label", "Label"],
["legend", "Legend"],
["li", "LI"],
["link", "Link"],
["listing", "Pre"],
["main", ""],
["map", "Map"],
["mark", ""],
["marquee", "Marquee"],
["menu", "Menu"],
["meta", "Meta"],
["meter", "Meter"],
["mod", "Unknown"],
["multicol", "Unknown"],
["nav", ""],
["nextid", "Unknown"],
["nobr", ""],
["noembed", ""],
["noframes", ""],
["noscript", ""],
["object", "Object"],
["ol", "OList"],
["optgroup", "OptGroup"],
["option", "Option"],
["output", "Output"],
["p", "Paragraph"],
["param", "Param"],
["permission", "Permission"],
["picture", "Picture"],
["plaintext", ""],
["pre", "Pre"],
["progress", "Progress"],
["q", "Quote"],
["quasit", "Unknown"],
["rb", ""],
["rp", ""],
["rt", ""],
["rtc", ""],
["ruby", ""],
["s", ""],
["samp", ""],
["script", "Script"],
["section", ""],
["select", "Select"],
["slot", "Slot"],
["small", ""],
["source", "Source"],
["spacer", "Unknown"],
["span", "Span"],
["strike", ""],
["strong", ""],
["style", "Style"],
["sub", ""],
["summary", ""],
["sup", ""],
["table", "Table"],
["tbody", "TableSection"],
["td", "TableCell"],
["textarea", "TextArea"],
["tfoot", "TableSection"],
["th", "TableCell"],
["thead", "TableSection"],
["time", "Time"],
["title", "Title"],
["tr", "TableRow"],
["track", "Track"],
["tt", ""],
["u", ""],
["ul", "UList"],
["var", ""],
["video", "Video"],
["wbr", ""],
["xmp", "Pre"],
["\u00E5-bar", "Unknown"], // not a valid custom element name
];