happy-dom 13.3.8 → 13.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of happy-dom might be problematic. Click here for more details.

Files changed (187) hide show
  1. package/README.md +3 -0
  2. package/cjs/PropertySymbol.cjs +3 -1
  3. package/cjs/PropertySymbol.cjs.map +1 -1
  4. package/cjs/PropertySymbol.d.ts +2 -0
  5. package/cjs/PropertySymbol.d.ts.map +1 -1
  6. package/cjs/browser/detached-browser/DetachedBrowserPage.cjs +5 -3
  7. package/cjs/browser/detached-browser/DetachedBrowserPage.cjs.map +1 -1
  8. package/cjs/browser/detached-browser/DetachedBrowserPage.d.ts.map +1 -1
  9. package/cjs/browser/utilities/BrowserFrameExceptionObserver.cjs +6 -0
  10. package/cjs/browser/utilities/BrowserFrameExceptionObserver.cjs.map +1 -1
  11. package/cjs/browser/utilities/BrowserFrameExceptionObserver.d.ts.map +1 -1
  12. package/cjs/browser/utilities/BrowserFrameFactory.cjs +14 -6
  13. package/cjs/browser/utilities/BrowserFrameFactory.cjs.map +1 -1
  14. package/cjs/browser/utilities/BrowserFrameFactory.d.ts.map +1 -1
  15. package/cjs/config/HTMLElementLocalNameToClass.cjs +122 -0
  16. package/{lib/config/ElementTag.js.map → cjs/config/HTMLElementLocalNameToClass.cjs.map} +1 -1
  17. package/cjs/config/{ElementTag.d.ts → HTMLElementLocalNameToClass.d.ts} +1 -1
  18. package/cjs/config/HTMLElementLocalNameToClass.d.ts.map +1 -0
  19. package/cjs/config/{PlainTextElements.cjs → HTMLElementPlainText.cjs} +1 -1
  20. package/cjs/config/HTMLElementPlainText.cjs.map +1 -0
  21. package/{lib/config/PlainTextElements.d.ts → cjs/config/HTMLElementPlainText.d.ts} +1 -1
  22. package/cjs/config/HTMLElementPlainText.d.ts.map +1 -0
  23. package/cjs/config/{UnnestableElements.cjs → HTMLElementUnnestable.cjs} +1 -1
  24. package/cjs/config/HTMLElementUnnestable.cjs.map +1 -0
  25. package/{lib/config/UnnestableElements.d.ts → cjs/config/HTMLElementUnnestable.d.ts} +1 -1
  26. package/cjs/config/HTMLElementUnnestable.d.ts.map +1 -0
  27. package/cjs/config/{VoidElements.cjs → HTMLElementVoid.cjs} +1 -1
  28. package/cjs/config/HTMLElementVoid.cjs.map +1 -0
  29. package/{lib/config/VoidElements.d.ts → cjs/config/HTMLElementVoid.d.ts} +1 -1
  30. package/cjs/config/HTMLElementVoid.d.ts.map +1 -0
  31. package/cjs/config/NamespaceURI.cjs +2 -1
  32. package/cjs/config/NamespaceURI.cjs.map +1 -1
  33. package/cjs/config/NamespaceURI.d.ts +1 -0
  34. package/cjs/config/NamespaceURI.d.ts.map +1 -1
  35. package/cjs/custom-element/CustomElementRegistry.cjs +67 -30
  36. package/cjs/custom-element/CustomElementRegistry.cjs.map +1 -1
  37. package/cjs/custom-element/CustomElementRegistry.d.ts +18 -10
  38. package/cjs/custom-element/CustomElementRegistry.d.ts.map +1 -1
  39. package/cjs/navigator/Navigator.cjs +2 -2
  40. package/cjs/navigator/Navigator.cjs.map +1 -1
  41. package/cjs/navigator/Navigator.d.ts.map +1 -1
  42. package/cjs/nodes/document/Document.cjs +44 -16
  43. package/cjs/nodes/document/Document.cjs.map +1 -1
  44. package/cjs/nodes/document/Document.d.ts.map +1 -1
  45. package/cjs/nodes/element/Element.cjs +8 -6
  46. package/cjs/nodes/element/Element.cjs.map +1 -1
  47. package/cjs/nodes/element/Element.d.ts +1 -0
  48. package/cjs/nodes/element/Element.d.ts.map +1 -1
  49. package/cjs/nodes/html-element/HTMLElement.cjs +89 -2
  50. package/cjs/nodes/html-element/HTMLElement.cjs.map +1 -1
  51. package/cjs/nodes/html-element/HTMLElement.d.ts +8 -0
  52. package/cjs/nodes/html-element/HTMLElement.d.ts.map +1 -1
  53. package/cjs/nodes/html-unknown-element/HTMLUnknownElement.cjs +0 -126
  54. package/cjs/nodes/html-unknown-element/HTMLUnknownElement.cjs.map +1 -1
  55. package/cjs/nodes/html-unknown-element/HTMLUnknownElement.d.ts +0 -9
  56. package/cjs/nodes/html-unknown-element/HTMLUnknownElement.d.ts.map +1 -1
  57. package/cjs/nodes/parent-node/ParentNodeUtility.cjs +5 -3
  58. package/cjs/nodes/parent-node/ParentNodeUtility.cjs.map +1 -1
  59. package/cjs/nodes/parent-node/ParentNodeUtility.d.ts.map +1 -1
  60. package/cjs/nodes/svg-element/SVGElement.cjs +1 -1
  61. package/cjs/nodes/svg-element/SVGElement.cjs.map +1 -1
  62. package/cjs/version.cjs +1 -1
  63. package/cjs/window/BrowserWindow.cjs +10 -1
  64. package/cjs/window/BrowserWindow.cjs.map +1 -1
  65. package/cjs/window/BrowserWindow.d.ts.map +1 -1
  66. package/cjs/window/DetachedWindowAPI.cjs +6 -0
  67. package/cjs/window/DetachedWindowAPI.cjs.map +1 -1
  68. package/cjs/window/DetachedWindowAPI.d.ts +4 -0
  69. package/cjs/window/DetachedWindowAPI.d.ts.map +1 -1
  70. package/cjs/xml-parser/XMLParser.cjs +17 -13
  71. package/cjs/xml-parser/XMLParser.cjs.map +1 -1
  72. package/cjs/xml-parser/XMLParser.d.ts.map +1 -1
  73. package/cjs/xml-serializer/XMLSerializer.cjs +6 -6
  74. package/cjs/xml-serializer/XMLSerializer.cjs.map +1 -1
  75. package/lib/PropertySymbol.d.ts +2 -0
  76. package/lib/PropertySymbol.d.ts.map +1 -1
  77. package/lib/PropertySymbol.js +2 -0
  78. package/lib/PropertySymbol.js.map +1 -1
  79. package/lib/browser/detached-browser/DetachedBrowserPage.d.ts.map +1 -1
  80. package/lib/browser/detached-browser/DetachedBrowserPage.js +5 -3
  81. package/lib/browser/detached-browser/DetachedBrowserPage.js.map +1 -1
  82. package/lib/browser/utilities/BrowserFrameExceptionObserver.d.ts.map +1 -1
  83. package/lib/browser/utilities/BrowserFrameExceptionObserver.js +6 -0
  84. package/lib/browser/utilities/BrowserFrameExceptionObserver.js.map +1 -1
  85. package/lib/browser/utilities/BrowserFrameFactory.d.ts.map +1 -1
  86. package/lib/browser/utilities/BrowserFrameFactory.js +14 -6
  87. package/lib/browser/utilities/BrowserFrameFactory.js.map +1 -1
  88. package/lib/config/{ElementTag.d.ts → HTMLElementLocalNameToClass.d.ts} +1 -1
  89. package/lib/config/HTMLElementLocalNameToClass.d.ts.map +1 -0
  90. package/lib/config/HTMLElementLocalNameToClass.js +120 -0
  91. package/lib/config/HTMLElementLocalNameToClass.js.map +1 -0
  92. package/{cjs/config/PlainTextElements.d.ts → lib/config/HTMLElementPlainText.d.ts} +1 -1
  93. package/lib/config/HTMLElementPlainText.d.ts.map +1 -0
  94. package/lib/config/{PlainTextElements.js → HTMLElementPlainText.js} +1 -1
  95. package/lib/config/HTMLElementPlainText.js.map +1 -0
  96. package/{cjs/config/UnnestableElements.d.ts → lib/config/HTMLElementUnnestable.d.ts} +1 -1
  97. package/lib/config/HTMLElementUnnestable.d.ts.map +1 -0
  98. package/lib/config/{UnnestableElements.js → HTMLElementUnnestable.js} +1 -1
  99. package/lib/config/HTMLElementUnnestable.js.map +1 -0
  100. package/{cjs/config/VoidElements.d.ts → lib/config/HTMLElementVoid.d.ts} +1 -1
  101. package/lib/config/HTMLElementVoid.d.ts.map +1 -0
  102. package/lib/config/{VoidElements.js → HTMLElementVoid.js} +1 -1
  103. package/lib/config/HTMLElementVoid.js.map +1 -0
  104. package/lib/config/NamespaceURI.d.ts +1 -0
  105. package/lib/config/NamespaceURI.d.ts.map +1 -1
  106. package/lib/config/NamespaceURI.js +2 -1
  107. package/lib/config/NamespaceURI.js.map +1 -1
  108. package/lib/custom-element/CustomElementRegistry.d.ts +18 -10
  109. package/lib/custom-element/CustomElementRegistry.d.ts.map +1 -1
  110. package/lib/custom-element/CustomElementRegistry.js +67 -30
  111. package/lib/custom-element/CustomElementRegistry.js.map +1 -1
  112. package/lib/navigator/Navigator.d.ts.map +1 -1
  113. package/lib/navigator/Navigator.js +2 -2
  114. package/lib/navigator/Navigator.js.map +1 -1
  115. package/lib/nodes/document/Document.d.ts.map +1 -1
  116. package/lib/nodes/document/Document.js +44 -16
  117. package/lib/nodes/document/Document.js.map +1 -1
  118. package/lib/nodes/element/Element.d.ts +1 -0
  119. package/lib/nodes/element/Element.d.ts.map +1 -1
  120. package/lib/nodes/element/Element.js +8 -6
  121. package/lib/nodes/element/Element.js.map +1 -1
  122. package/lib/nodes/html-element/HTMLElement.d.ts +8 -0
  123. package/lib/nodes/html-element/HTMLElement.d.ts.map +1 -1
  124. package/lib/nodes/html-element/HTMLElement.js +90 -4
  125. package/lib/nodes/html-element/HTMLElement.js.map +1 -1
  126. package/lib/nodes/html-unknown-element/HTMLUnknownElement.d.ts +0 -9
  127. package/lib/nodes/html-unknown-element/HTMLUnknownElement.d.ts.map +1 -1
  128. package/lib/nodes/html-unknown-element/HTMLUnknownElement.js +0 -103
  129. package/lib/nodes/html-unknown-element/HTMLUnknownElement.js.map +1 -1
  130. package/lib/nodes/parent-node/ParentNodeUtility.d.ts.map +1 -1
  131. package/lib/nodes/parent-node/ParentNodeUtility.js +5 -3
  132. package/lib/nodes/parent-node/ParentNodeUtility.js.map +1 -1
  133. package/lib/nodes/svg-element/SVGElement.js +1 -1
  134. package/lib/nodes/svg-element/SVGElement.js.map +1 -1
  135. package/lib/version.js +1 -1
  136. package/lib/window/BrowserWindow.d.ts.map +1 -1
  137. package/lib/window/BrowserWindow.js +10 -1
  138. package/lib/window/BrowserWindow.js.map +1 -1
  139. package/lib/window/DetachedWindowAPI.d.ts +4 -0
  140. package/lib/window/DetachedWindowAPI.d.ts.map +1 -1
  141. package/lib/window/DetachedWindowAPI.js +6 -0
  142. package/lib/window/DetachedWindowAPI.js.map +1 -1
  143. package/lib/xml-parser/XMLParser.d.ts.map +1 -1
  144. package/lib/xml-parser/XMLParser.js +17 -13
  145. package/lib/xml-parser/XMLParser.js.map +1 -1
  146. package/lib/xml-serializer/XMLSerializer.js +6 -6
  147. package/lib/xml-serializer/XMLSerializer.js.map +1 -1
  148. package/package.json +1 -1
  149. package/src/PropertySymbol.ts +2 -0
  150. package/src/browser/detached-browser/DetachedBrowserPage.ts +4 -3
  151. package/src/browser/utilities/BrowserFrameExceptionObserver.ts +12 -0
  152. package/src/browser/utilities/BrowserFrameFactory.ts +14 -7
  153. package/src/config/HTMLElementLocalNameToClass.ts +119 -0
  154. package/src/config/NamespaceURI.ts +2 -1
  155. package/src/custom-element/CustomElementRegistry.ts +76 -28
  156. package/src/navigator/Navigator.ts +4 -2
  157. package/src/nodes/document/Document.ts +61 -17
  158. package/src/nodes/element/Element.ts +3 -1
  159. package/src/nodes/html-element/HTMLElement.ts +127 -0
  160. package/src/nodes/html-unknown-element/HTMLUnknownElement.ts +1 -129
  161. package/src/nodes/parent-node/ParentNodeUtility.ts +5 -3
  162. package/src/nodes/svg-element/SVGElement.ts +1 -1
  163. package/src/window/BrowserWindow.ts +13 -1
  164. package/src/window/DetachedWindowAPI.ts +7 -0
  165. package/src/xml-parser/XMLParser.ts +20 -14
  166. package/src/xml-serializer/XMLSerializer.ts +6 -6
  167. package/cjs/config/ElementTag.cjs +0 -133
  168. package/cjs/config/ElementTag.cjs.map +0 -1
  169. package/cjs/config/ElementTag.d.ts.map +0 -1
  170. package/cjs/config/PlainTextElements.cjs.map +0 -1
  171. package/cjs/config/PlainTextElements.d.ts.map +0 -1
  172. package/cjs/config/UnnestableElements.cjs.map +0 -1
  173. package/cjs/config/UnnestableElements.d.ts.map +0 -1
  174. package/cjs/config/VoidElements.cjs.map +0 -1
  175. package/cjs/config/VoidElements.d.ts.map +0 -1
  176. package/lib/config/ElementTag.d.ts.map +0 -1
  177. package/lib/config/ElementTag.js +0 -131
  178. package/lib/config/PlainTextElements.d.ts.map +0 -1
  179. package/lib/config/PlainTextElements.js.map +0 -1
  180. package/lib/config/UnnestableElements.d.ts.map +0 -1
  181. package/lib/config/UnnestableElements.js.map +0 -1
  182. package/lib/config/VoidElements.d.ts.map +0 -1
  183. package/lib/config/VoidElements.js.map +0 -1
  184. package/src/config/ElementTag.ts +0 -130
  185. /package/src/config/{PlainTextElements.ts → HTMLElementPlainText.ts} +0 -0
  186. /package/src/config/{UnnestableElements.ts → HTMLElementUnnestable.ts} +0 -0
  187. /package/src/config/{VoidElements.ts → HTMLElementVoid.ts} +0 -0
@@ -10,44 +10,54 @@ export default class CustomElementRegistry {
10
10
  public [PropertySymbol.registry]: {
11
11
  [k: string]: { elementClass: typeof HTMLElement; extends: string };
12
12
  } = {};
13
+ public [PropertySymbol.registedClass]: Map<typeof HTMLElement, string> = new Map();
13
14
  public [PropertySymbol.callbacks]: { [k: string]: (() => void)[] } = {};
14
15
 
15
16
  /**
16
17
  * Defines a custom element class.
17
18
  *
18
- * @param tagName Tag name of element.
19
+ * @param name Tag name of element.
19
20
  * @param elementClass Element class.
20
21
  * @param [options] Options.
21
- * @param options.extends
22
+ * @param [options.extends] Extends tag name.
22
23
  */
23
24
  public define(
24
- tagName: string,
25
+ name: string,
25
26
  elementClass: typeof HTMLElement,
26
- options?: { extends: string }
27
+ options?: { extends?: string }
27
28
  ): void {
28
- const upperTagName = tagName.toUpperCase();
29
+ if (!this.#isValidCustomElementName(name)) {
30
+ throw new DOMException(
31
+ `Failed to execute 'define' on 'CustomElementRegistry': "${name}" is not a valid custom element name`
32
+ );
33
+ }
34
+
35
+ if (this[PropertySymbol.registry][name]) {
36
+ throw new DOMException(
37
+ `Failed to execute 'define' on 'CustomElementRegistry': the name "${name}" has already been used with this registry`
38
+ );
39
+ }
29
40
 
30
- if (!upperTagName.includes('-')) {
41
+ if (this[PropertySymbol.registedClass].has(elementClass)) {
31
42
  throw new DOMException(
32
- "Failed to execute 'define' on 'CustomElementRegistry': \"" +
33
- tagName +
34
- '" is not a valid custom element name.'
43
+ "Failed to execute 'define' on 'CustomElementRegistry': this constructor has already been used with this registry"
35
44
  );
36
45
  }
37
46
 
38
- this[PropertySymbol.registry][upperTagName] = {
47
+ this[PropertySymbol.registry][name] = {
39
48
  elementClass,
40
49
  extends: options && options.extends ? options.extends.toLowerCase() : null
41
50
  };
51
+ this[PropertySymbol.registedClass].set(elementClass, name);
42
52
 
43
53
  // ObservedAttributes should only be called once by CustomElementRegistry (see #117)
44
54
  if (elementClass.prototype.attributeChangedCallback) {
45
55
  elementClass[PropertySymbol.observedAttributes] = elementClass.observedAttributes;
46
56
  }
47
57
 
48
- if (this[PropertySymbol.callbacks][upperTagName]) {
49
- const callbacks = this[PropertySymbol.callbacks][upperTagName];
50
- delete this[PropertySymbol.callbacks][upperTagName];
58
+ if (this[PropertySymbol.callbacks][name]) {
59
+ const callbacks = this[PropertySymbol.callbacks][name];
60
+ delete this[PropertySymbol.callbacks][name];
51
61
  for (const callback of callbacks) {
52
62
  callback();
53
63
  }
@@ -57,14 +67,11 @@ export default class CustomElementRegistry {
57
67
  /**
58
68
  * Returns a defined element class.
59
69
  *
60
- * @param tagName Tag name of element.
61
- * @param HTMLElement Class defined.
70
+ * @param name Tag name of element.
71
+ * @returns HTMLElement Class defined or undefined.
62
72
  */
63
- public get(tagName: string): typeof HTMLElement {
64
- const upperTagName = tagName.toUpperCase();
65
- return this[PropertySymbol.registry][upperTagName]
66
- ? this[PropertySymbol.registry][upperTagName].elementClass
67
- : undefined;
73
+ public get(name: string): typeof HTMLElement | undefined {
74
+ return this[PropertySymbol.registry][name]?.elementClass;
68
75
  }
69
76
 
70
77
  /**
@@ -81,18 +88,59 @@ export default class CustomElementRegistry {
81
88
  /**
82
89
  * When defined.
83
90
  *
84
- * @param tagName Tag name of element.
85
- * @returns Promise.
91
+ * @param name Tag name of element.
86
92
  */
87
- public whenDefined(tagName: string): Promise<void> {
88
- const upperTagName = tagName.toUpperCase();
89
- if (this.get(upperTagName)) {
93
+ public whenDefined(name: string): Promise<void> {
94
+ if (!this.#isValidCustomElementName(name)) {
95
+ return Promise.reject(new DOMException(`Invalid custom element name: "${name}"`));
96
+ }
97
+ if (this.get(name)) {
90
98
  return Promise.resolve();
91
99
  }
92
100
  return new Promise((resolve) => {
93
- this[PropertySymbol.callbacks][upperTagName] =
94
- this[PropertySymbol.callbacks][upperTagName] || [];
95
- this[PropertySymbol.callbacks][upperTagName].push(resolve);
101
+ this[PropertySymbol.callbacks][name] = this[PropertySymbol.callbacks][name] || [];
102
+ this[PropertySymbol.callbacks][name].push(resolve);
96
103
  });
97
104
  }
105
+
106
+ /**
107
+ * Reverse lookup searching for name by given element class.
108
+ *
109
+ * @param elementClass Class constructor.
110
+ * @returns Found tag name or `null`.
111
+ */
112
+ public getName(elementClass: typeof HTMLElement): string | null {
113
+ return this[PropertySymbol.registedClass].get(elementClass) || null;
114
+ }
115
+
116
+ /**
117
+ * Validates the correctness of custom element tag names.
118
+ *
119
+ * @param name Custom element tag name.
120
+ * @returns True, if tag name is standard compliant.
121
+ */
122
+ #isValidCustomElementName(name: string): boolean {
123
+ // Validation criteria based on:
124
+ // https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
125
+ const PCENChar =
126
+ '[-_.]|[0-9]|[a-z]|\u{B7}|[\u{C0}-\u{D6}]|[\u{D8}-\u{F6}]' +
127
+ '|[\u{F8}-\u{37D}]|[\u{37F}-\u{1FFF}]' +
128
+ '|[\u{200C}-\u{200D}]|[\u{203F}-\u{2040}]|[\u{2070}-\u{218F}]' +
129
+ '|[\u{2C00}-\u{2FEF}]|[\u{3001}-\u{D7FF}]' +
130
+ '|[\u{F900}-\u{FDCF}]|[\u{FDF0}-\u{FFFD}]|[\u{10000}-\u{EFFFF}]';
131
+
132
+ const PCEN = new RegExp(`^[a-z](${PCENChar})*-(${PCENChar})*$`, 'u');
133
+
134
+ const reservedNames = [
135
+ 'annotation-xml',
136
+ 'color-profile',
137
+ 'font-face',
138
+ 'font-face-src',
139
+ 'font-face-uri',
140
+ 'font-face-format',
141
+ 'font-face-name',
142
+ 'missing-glyph'
143
+ ];
144
+ return PCEN.test(name) && !reservedNames.includes(name);
145
+ }
98
146
  }
@@ -77,7 +77,9 @@ export default class Navigator {
77
77
  * Maximum number of simultaneous touch contact points are supported by the current device.
78
78
  */
79
79
  public get maxTouchPoints(): number {
80
- return WindowBrowserSettingsReader.getSettings(this.#ownerWindow).navigator.maxTouchPoints;
80
+ return (
81
+ WindowBrowserSettingsReader.getSettings(this.#ownerWindow)?.navigator.maxTouchPoints || 0
82
+ );
81
83
  }
82
84
 
83
85
  /**
@@ -154,7 +156,7 @@ export default class Navigator {
154
156
  * "appCodeName/appVersion number (Platform; Security; OS-or-CPU; Localization; rv: revision-version-number) product/productSub Application-Name Application-Name-version".
155
157
  */
156
158
  public get userAgent(): string {
157
- return WindowBrowserSettingsReader.getSettings(this.#ownerWindow).navigator.userAgent;
159
+ return WindowBrowserSettingsReader.getSettings(this.#ownerWindow)?.navigator.userAgent || '';
158
160
  }
159
161
 
160
162
  /**
@@ -1,6 +1,5 @@
1
1
  import Element from '../element/Element.js';
2
2
  import * as PropertySymbol from '../../PropertySymbol.js';
3
- import HTMLUnknownElement from '../html-unknown-element/HTMLUnknownElement.js';
4
3
  import IBrowserWindow from '../../window/IBrowserWindow.js';
5
4
  import Node from '../node/Node.js';
6
5
  import NodeIterator from '../../tree-walker/NodeIterator.js';
@@ -9,7 +8,7 @@ import DocumentFragment from '../document-fragment/DocumentFragment.js';
9
8
  import XMLParser from '../../xml-parser/XMLParser.js';
10
9
  import Event from '../../event/Event.js';
11
10
  import DOMImplementation from '../../dom-implementation/DOMImplementation.js';
12
- import ElementTag from '../../config/ElementTag.js';
11
+ import HTMLElementLocalNameToClass from '../../config/HTMLElementLocalNameToClass.js';
13
12
  import INodeFilter from '../../tree-walker/INodeFilter.js';
14
13
  import NamespaceURI from '../../config/NamespaceURI.js';
15
14
  import DocumentType from '../document-type/DocumentType.js';
@@ -879,28 +878,73 @@ export default class Document extends Node implements IDocument {
879
878
  qualifiedName: string,
880
879
  options?: { is?: string }
881
880
  ): IElement {
882
- const tagName = String(qualifiedName).toUpperCase();
881
+ qualifiedName = String(qualifiedName);
883
882
 
884
- let customElementClass;
885
- if (options && options.is) {
886
- customElementClass = this[PropertySymbol.ownerWindow].customElements.get(String(options.is));
887
- } else {
888
- customElementClass = this[PropertySymbol.ownerWindow].customElements.get(tagName);
883
+ if (!qualifiedName) {
884
+ throw new DOMException(
885
+ "Failed to execute 'createElementNS' on 'Document': The qualified name provided is empty."
886
+ );
887
+ }
888
+
889
+ // SVG element
890
+ if (namespaceURI === NamespaceURI.svg) {
891
+ const element = NodeFactory.createNode<IElement>(
892
+ this,
893
+ qualifiedName === 'svg'
894
+ ? this[PropertySymbol.ownerWindow].SVGSVGElement
895
+ : this[PropertySymbol.ownerWindow].SVGElement
896
+ );
897
+ element[PropertySymbol.tagName] = qualifiedName;
898
+ element[PropertySymbol.localName] = qualifiedName;
899
+ element[PropertySymbol.namespaceURI] = namespaceURI;
900
+ element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
901
+ return element;
889
902
  }
890
903
 
891
- const elementClass: typeof Element =
892
- customElementClass ||
893
- this[PropertySymbol.ownerWindow][ElementTag[tagName]] ||
894
- HTMLUnknownElement;
904
+ // Custom HTML element
905
+ const customElement =
906
+ this[PropertySymbol.ownerWindow].customElements[PropertySymbol.registry]?.[
907
+ options && options.is ? String(options.is) : qualifiedName
908
+ ];
909
+
910
+ if (customElement) {
911
+ const element = NodeFactory.createNode<IElement>(this, customElement.elementClass);
912
+ element[PropertySymbol.tagName] = qualifiedName.toUpperCase();
913
+ element[PropertySymbol.localName] = qualifiedName;
914
+ element[PropertySymbol.namespaceURI] = namespaceURI;
915
+ element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
916
+ return element;
917
+ }
895
918
 
896
- const element = NodeFactory.createNode<IElement>(this, elementClass);
897
- element[PropertySymbol.tagName] = tagName;
919
+ const localName = qualifiedName.toLowerCase();
920
+ const elementClass = this[PropertySymbol.ownerWindow][HTMLElementLocalNameToClass[localName]];
898
921
 
899
- element[PropertySymbol.namespaceURI] = namespaceURI;
900
- if (element instanceof Element && options && options.is) {
901
- element[PropertySymbol.isValue] = String(options.is);
922
+ // Known HTML element
923
+ if (elementClass) {
924
+ const element = NodeFactory.createNode<IElement>(this, elementClass);
925
+
926
+ element[PropertySymbol.tagName] = qualifiedName.toUpperCase();
927
+ element[PropertySymbol.localName] = localName;
928
+ element[PropertySymbol.namespaceURI] = namespaceURI;
929
+ element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
930
+
931
+ return element;
902
932
  }
903
933
 
934
+ // Unknown HTML element
935
+ const element = NodeFactory.createNode<IElement>(
936
+ this,
937
+ // If the tag name contains a hyphen, it is an unknown custom element and we should use HTMLElement.
938
+ localName.includes('-')
939
+ ? this[PropertySymbol.ownerWindow].HTMLElement
940
+ : this[PropertySymbol.ownerWindow].HTMLUnknownElement
941
+ );
942
+
943
+ element[PropertySymbol.tagName] = qualifiedName.toUpperCase();
944
+ element[PropertySymbol.localName] = localName;
945
+ element[PropertySymbol.namespaceURI] = namespaceURI;
946
+ element[PropertySymbol.isValue] = options && options.is ? String(options.is) : null;
947
+
904
948
  return element;
905
949
  }
906
950
 
@@ -87,6 +87,7 @@ export default class Element extends Node implements IElement {
87
87
  public [PropertySymbol.computedStyle]: CSSStyleDeclaration | null = null;
88
88
  public [PropertySymbol.nodeType] = NodeTypeEnum.elementNode;
89
89
  public [PropertySymbol.tagName]: string | null = null;
90
+ public [PropertySymbol.localName]: string | null = null;
90
91
  public [PropertySymbol.prefix]: string | null = null;
91
92
  public [PropertySymbol.shadowRoot]: IShadowRoot | null = null;
92
93
  public [PropertySymbol.scrollHeight] = 0;
@@ -266,7 +267,7 @@ export default class Element extends Node implements IElement {
266
267
  * @returns Local name.
267
268
  */
268
269
  public get localName(): string {
269
- return this[PropertySymbol.tagName] ? this[PropertySymbol.tagName].toLowerCase() : 'unknown';
270
+ return this[PropertySymbol.localName];
270
271
  }
271
272
 
272
273
  /**
@@ -486,6 +487,7 @@ export default class Element extends Node implements IElement {
486
487
  }
487
488
 
488
489
  clone[PropertySymbol.tagName] = this[PropertySymbol.tagName];
490
+ clone[PropertySymbol.localName] = this[PropertySymbol.localName];
489
491
  clone[PropertySymbol.namespaceURI] = this[PropertySymbol.namespaceURI];
490
492
 
491
493
  return <IElement>clone;
@@ -10,6 +10,12 @@ import Event from '../../event/Event.js';
10
10
  import HTMLElementUtility from './HTMLElementUtility.js';
11
11
  import INamedNodeMap from '../../named-node-map/INamedNodeMap.js';
12
12
  import HTMLElementNamedNodeMap from './HTMLElementNamedNodeMap.js';
13
+ import INodeList from '../node/INodeList.js';
14
+ import INode from '../node/INode.js';
15
+ import IHTMLCollection from '../element/IHTMLCollection.js';
16
+ import IElement from '../element/IElement.js';
17
+ import NodeList from '../node/NodeList.js';
18
+ import HTMLCollection from '../element/HTMLCollection.js';
13
19
 
14
20
  /**
15
21
  * HTML Element.
@@ -62,6 +68,7 @@ export default class HTMLElement extends Element implements IHTMLElement {
62
68
 
63
69
  // Private properties
64
70
  #dataset: Dataset = null;
71
+ #customElementDefineCallback: () => void = null;
65
72
 
66
73
  /**
67
74
  * Returns access key.
@@ -473,4 +480,124 @@ export default class HTMLElement extends Element implements IHTMLElement {
473
480
 
474
481
  return clone;
475
482
  }
483
+
484
+ /**
485
+ * Connects this element to another element.
486
+ *
487
+ * @see https://html.spec.whatwg.org/multipage/dom.html#htmlelement
488
+ * @param parentNode Parent node.
489
+ */
490
+ public [PropertySymbol.connectToNode](parentNode: INode = null): void {
491
+ const localName = this[PropertySymbol.localName];
492
+
493
+ // This element can potentially be a custom element that has not been defined yet
494
+ // Therefore we need to register a callback for when it is defined in CustomElementRegistry and replace it with the registered element (see #404)
495
+ if (
496
+ this.constructor === HTMLElement &&
497
+ localName.includes('-') &&
498
+ this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].customElements[
499
+ PropertySymbol.callbacks
500
+ ]
501
+ ) {
502
+ const callbacks =
503
+ this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].customElements[
504
+ PropertySymbol.callbacks
505
+ ];
506
+
507
+ if (parentNode && !this.#customElementDefineCallback) {
508
+ const callback = (): void => {
509
+ if (this[PropertySymbol.parentNode]) {
510
+ const newElement = <HTMLElement>(
511
+ this[PropertySymbol.ownerDocument].createElement(localName)
512
+ );
513
+ (<INodeList<INode>>newElement[PropertySymbol.childNodes]) =
514
+ this[PropertySymbol.childNodes];
515
+ (<IHTMLCollection<IElement>>newElement[PropertySymbol.children]) =
516
+ this[PropertySymbol.children];
517
+ (<boolean>newElement[PropertySymbol.isConnected]) = this[PropertySymbol.isConnected];
518
+
519
+ newElement[PropertySymbol.rootNode] = this[PropertySymbol.rootNode];
520
+ newElement[PropertySymbol.formNode] = this[PropertySymbol.formNode];
521
+ newElement[PropertySymbol.selectNode] = this[PropertySymbol.selectNode];
522
+ newElement[PropertySymbol.textAreaNode] = this[PropertySymbol.textAreaNode];
523
+ newElement[PropertySymbol.observers] = this[PropertySymbol.observers];
524
+ newElement[PropertySymbol.isValue] = this[PropertySymbol.isValue];
525
+
526
+ for (let i = 0, max = this[PropertySymbol.attributes].length; i < max; i++) {
527
+ newElement[PropertySymbol.attributes].setNamedItem(
528
+ this[PropertySymbol.attributes][i]
529
+ );
530
+ }
531
+
532
+ (<INodeList<INode>>this[PropertySymbol.childNodes]) = new NodeList();
533
+ (<IHTMLCollection<IElement>>this[PropertySymbol.children]) = new HTMLCollection();
534
+ this[PropertySymbol.rootNode] = null;
535
+ this[PropertySymbol.formNode] = null;
536
+ this[PropertySymbol.selectNode] = null;
537
+ this[PropertySymbol.textAreaNode] = null;
538
+ this[PropertySymbol.observers] = [];
539
+ this[PropertySymbol.isValue] = null;
540
+ (<HTMLElementNamedNodeMap>this[PropertySymbol.attributes]) =
541
+ new HTMLElementNamedNodeMap(this);
542
+
543
+ for (
544
+ let i = 0,
545
+ max = (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.childNodes]
546
+ .length;
547
+ i < max;
548
+ i++
549
+ ) {
550
+ if (
551
+ (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.childNodes][i] ===
552
+ this
553
+ ) {
554
+ (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.childNodes][i] =
555
+ newElement;
556
+ break;
557
+ }
558
+ }
559
+
560
+ if ((<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children]) {
561
+ for (
562
+ let i = 0,
563
+ max = (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children]
564
+ .length;
565
+ i < max;
566
+ i++
567
+ ) {
568
+ if (
569
+ (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children][i] ===
570
+ this
571
+ ) {
572
+ (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children][i] =
573
+ newElement;
574
+ break;
575
+ }
576
+ }
577
+ }
578
+
579
+ if (newElement[PropertySymbol.isConnected] && newElement.connectedCallback) {
580
+ newElement.connectedCallback();
581
+ }
582
+
583
+ this[PropertySymbol.connectToNode](null);
584
+ }
585
+ };
586
+ callbacks[localName] = callbacks[localName] || [];
587
+ callbacks[localName].push(callback);
588
+ this.#customElementDefineCallback = callback;
589
+ } else if (!parentNode && callbacks[localName] && this.#customElementDefineCallback) {
590
+ const index = callbacks[localName].indexOf(this.#customElementDefineCallback);
591
+ if (index !== -1) {
592
+ callbacks[localName].splice(index, 1);
593
+ }
594
+ if (!callbacks[localName].length) {
595
+ delete callbacks[localName];
596
+ }
597
+ this.#customElementDefineCallback = null;
598
+ }
599
+ }
600
+
601
+ super[PropertySymbol.connectToNode](parentNode);
602
+ }
476
603
  }
@@ -1,13 +1,5 @@
1
1
  import HTMLElement from '../html-element/HTMLElement.js';
2
- import * as PropertySymbol from '../../PropertySymbol.js';
3
- import INode from '../node/INode.js';
4
2
  import IHTMLElement from '../html-element/IHTMLElement.js';
5
- import INodeList from '../node/INodeList.js';
6
- import IHTMLCollection from '../element/IHTMLCollection.js';
7
- import IElement from '../element/IElement.js';
8
- import NodeList from '../node/NodeList.js';
9
- import HTMLCollection from '../element/HTMLCollection.js';
10
- import HTMLElementNamedNodeMap from '../html-element/HTMLElementNamedNodeMap.js';
11
3
 
12
4
  /**
13
5
  * HTML Unknown Element.
@@ -15,124 +7,4 @@ import HTMLElementNamedNodeMap from '../html-element/HTMLElementNamedNodeMap.js'
15
7
  * Reference:
16
8
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLUnknownElement.
17
9
  */
18
- export default class HTMLUnknownElement extends HTMLElement implements IHTMLElement {
19
- #customElementDefineCallback: () => void = null;
20
-
21
- /**
22
- * Connects this element to another element.
23
- *
24
- * @param parentNode Parent node.
25
- */
26
- public [PropertySymbol.connectToNode](parentNode: INode = null): void {
27
- const tagName = this[PropertySymbol.tagName];
28
-
29
- // This element can potentially be a custom element that has not been defined yet
30
- // Therefore we need to register a callback for when it is defined in CustomElementRegistry and replace it with the registered element (see #404)
31
- if (
32
- tagName.includes('-') &&
33
- this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].customElements[
34
- PropertySymbol.callbacks
35
- ]
36
- ) {
37
- const callbacks =
38
- this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].customElements[
39
- PropertySymbol.callbacks
40
- ];
41
-
42
- if (parentNode && !this.#customElementDefineCallback) {
43
- const callback = (): void => {
44
- if (this[PropertySymbol.parentNode]) {
45
- const newElement = <HTMLElement>(
46
- this[PropertySymbol.ownerDocument].createElement(tagName)
47
- );
48
- (<INodeList<INode>>newElement[PropertySymbol.childNodes]) =
49
- this[PropertySymbol.childNodes];
50
- (<IHTMLCollection<IElement>>newElement[PropertySymbol.children]) =
51
- this[PropertySymbol.children];
52
- (<boolean>newElement[PropertySymbol.isConnected]) = this[PropertySymbol.isConnected];
53
-
54
- newElement[PropertySymbol.rootNode] = this[PropertySymbol.rootNode];
55
- newElement[PropertySymbol.formNode] = this[PropertySymbol.formNode];
56
- newElement[PropertySymbol.selectNode] = this[PropertySymbol.selectNode];
57
- newElement[PropertySymbol.textAreaNode] = this[PropertySymbol.textAreaNode];
58
- newElement[PropertySymbol.observers] = this[PropertySymbol.observers];
59
- newElement[PropertySymbol.isValue] = this[PropertySymbol.isValue];
60
-
61
- for (let i = 0, max = this[PropertySymbol.attributes].length; i < max; i++) {
62
- newElement[PropertySymbol.attributes].setNamedItem(
63
- this[PropertySymbol.attributes][i]
64
- );
65
- }
66
-
67
- (<INodeList<INode>>this[PropertySymbol.childNodes]) = new NodeList();
68
- (<IHTMLCollection<IElement>>this[PropertySymbol.children]) = new HTMLCollection();
69
- this[PropertySymbol.rootNode] = null;
70
- this[PropertySymbol.formNode] = null;
71
- this[PropertySymbol.selectNode] = null;
72
- this[PropertySymbol.textAreaNode] = null;
73
- this[PropertySymbol.observers] = [];
74
- this[PropertySymbol.isValue] = null;
75
- (<HTMLElementNamedNodeMap>this[PropertySymbol.attributes]) =
76
- new HTMLElementNamedNodeMap(this);
77
-
78
- for (
79
- let i = 0,
80
- max = (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.childNodes]
81
- .length;
82
- i < max;
83
- i++
84
- ) {
85
- if (
86
- (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.childNodes][i] ===
87
- this
88
- ) {
89
- (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.childNodes][i] =
90
- newElement;
91
- break;
92
- }
93
- }
94
-
95
- if ((<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children]) {
96
- for (
97
- let i = 0,
98
- max = (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children]
99
- .length;
100
- i < max;
101
- i++
102
- ) {
103
- if (
104
- (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children][i] ===
105
- this
106
- ) {
107
- (<HTMLElement>this[PropertySymbol.parentNode])[PropertySymbol.children][i] =
108
- newElement;
109
- break;
110
- }
111
- }
112
- }
113
-
114
- if (newElement[PropertySymbol.isConnected] && newElement.connectedCallback) {
115
- newElement.connectedCallback();
116
- }
117
-
118
- this[PropertySymbol.connectToNode](null);
119
- }
120
- };
121
- callbacks[tagName] = callbacks[tagName] || [];
122
- callbacks[tagName].push(callback);
123
- this.#customElementDefineCallback = callback;
124
- } else if (!parentNode && callbacks[tagName] && this.#customElementDefineCallback) {
125
- const index = callbacks[tagName].indexOf(this.#customElementDefineCallback);
126
- if (index !== -1) {
127
- callbacks[tagName].splice(index, 1);
128
- }
129
- if (!callbacks[tagName].length) {
130
- delete callbacks[tagName];
131
- }
132
- this.#customElementDefineCallback = null;
133
- }
134
- }
135
-
136
- super[PropertySymbol.connectToNode](parentNode);
137
- }
138
- }
10
+ export default class HTMLUnknownElement extends HTMLElement implements IHTMLElement {}
@@ -7,6 +7,7 @@ import IHTMLCollection from '../element/IHTMLCollection.js';
7
7
  import INode from '../node/INode.js';
8
8
  import HTMLCollection from '../element/HTMLCollection.js';
9
9
  import DocumentFragment from '../document-fragment/DocumentFragment.js';
10
+ import NamespaceURI from '../../config/NamespaceURI.js';
10
11
 
11
12
  /**
12
13
  * Parent node utility.
@@ -115,7 +116,7 @@ export default class ParentNodeUtility {
115
116
  let matches = new HTMLCollection<IElement>();
116
117
 
117
118
  for (const child of (<DocumentFragment>parentNode)[PropertySymbol.children]) {
118
- if (includeAll || child[PropertySymbol.tagName] === upperTagName) {
119
+ if (includeAll || child[PropertySymbol.tagName].toUpperCase() === upperTagName) {
119
120
  matches.push(child);
120
121
  }
121
122
  matches = <HTMLCollection<IElement>>(
@@ -139,13 +140,14 @@ export default class ParentNodeUtility {
139
140
  namespaceURI: string,
140
141
  tagName: string
141
142
  ): IHTMLCollection<IElement> {
142
- const upperTagName = tagName.toUpperCase();
143
+ // When the namespace is HTML, the tag name is case-insensitive.
144
+ const formattedTagName = namespaceURI === NamespaceURI.html ? tagName.toUpperCase() : tagName;
143
145
  const includeAll = tagName === '*';
144
146
  let matches = new HTMLCollection<IElement>();
145
147
 
146
148
  for (const child of (<DocumentFragment>parentNode)[PropertySymbol.children]) {
147
149
  if (
148
- (includeAll || child[PropertySymbol.tagName] === upperTagName) &&
150
+ (includeAll || child[PropertySymbol.tagName] === formattedTagName) &&
149
151
  child[PropertySymbol.namespaceURI] === namespaceURI
150
152
  ) {
151
153
  matches.push(child);
@@ -48,7 +48,7 @@ export default class SVGElement extends Element implements ISVGElement {
48
48
  public get ownerSVGElement(): ISVGSVGElement {
49
49
  let parent = this[PropertySymbol.parentNode];
50
50
  while (parent) {
51
- if (parent['tagName'] === 'SVG') {
51
+ if (parent[PropertySymbol.localName] === 'svg') {
52
52
  return <ISVGSVGElement>parent;
53
53
  }
54
54
 
@@ -935,6 +935,7 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow
935
935
  // When using a Window instance directly, the Window instance is the main frame and we will close the page and destroy the browser.
936
936
  // When using the Browser API we should only close the page when the Window instance is connected to the main frame (we should not close child frames such as iframes).
937
937
  if (this.#browserFrame.page?.mainFrame === this.#browserFrame) {
938
+ this[PropertySymbol.destroy]();
938
939
  this.#browserFrame.page.close();
939
940
  }
940
941
  }
@@ -1241,16 +1242,27 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow
1241
1242
  * Destroys the window.
1242
1243
  */
1243
1244
  public [PropertySymbol.destroy](): void {
1245
+ if (!this.Audio[PropertySymbol.ownerDocument]) {
1246
+ return;
1247
+ }
1248
+
1244
1249
  (<boolean>this.closed) = true;
1245
1250
  this.Audio[PropertySymbol.ownerDocument] = null;
1246
1251
  this.Image[PropertySymbol.ownerDocument] = null;
1247
1252
  this.DocumentFragment[PropertySymbol.ownerDocument] = null;
1248
- for (const mutationObserver of this[PropertySymbol.mutationObservers]) {
1253
+
1254
+ const mutationObservers = this[PropertySymbol.mutationObservers];
1255
+
1256
+ for (const mutationObserver of mutationObservers) {
1249
1257
  mutationObserver.disconnect();
1250
1258
  }
1251
1259
 
1252
1260
  // Disconnects nodes from the document, so that they can be garbage collected.
1253
1261
  for (const node of this.document[PropertySymbol.childNodes].slice()) {
1262
+ // Makes sure that something won't be triggered by the disconnect.
1263
+ if (node.disconnectedCallback) {
1264
+ delete node.disconnectedCallback;
1265
+ }
1254
1266
  this.document.removeChild(node);
1255
1267
  }
1256
1268