happy-dom 10.11.1 → 11.0.0

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 (169) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/cjs/console/VirtualConsole.cjs +351 -0
  3. package/cjs/console/VirtualConsole.cjs.map +1 -0
  4. package/cjs/console/VirtualConsole.d.ts +163 -0
  5. package/cjs/console/VirtualConsole.d.ts.map +1 -0
  6. package/cjs/console/VirtualConsolePrinter.cjs +101 -0
  7. package/cjs/console/VirtualConsolePrinter.cjs.map +1 -0
  8. package/cjs/console/VirtualConsolePrinter.d.ts +55 -0
  9. package/cjs/console/VirtualConsolePrinter.d.ts.map +1 -0
  10. package/cjs/console/enums/VirtualConsoleLogLevelEnum.cjs +16 -0
  11. package/cjs/console/enums/VirtualConsoleLogLevelEnum.cjs.map +1 -0
  12. package/cjs/console/enums/VirtualConsoleLogLevelEnum.d.ts +13 -0
  13. package/cjs/console/enums/VirtualConsoleLogLevelEnum.d.ts.map +1 -0
  14. package/cjs/console/enums/VirtualConsoleLogTypeEnum.cjs +30 -0
  15. package/cjs/console/enums/VirtualConsoleLogTypeEnum.cjs.map +1 -0
  16. package/cjs/console/enums/VirtualConsoleLogTypeEnum.d.ts +23 -0
  17. package/cjs/console/enums/VirtualConsoleLogTypeEnum.d.ts.map +1 -0
  18. package/cjs/console/types/IVirtualConsoleLogEntry.cjs +3 -0
  19. package/cjs/console/types/IVirtualConsoleLogEntry.cjs.map +1 -0
  20. package/cjs/console/types/IVirtualConsoleLogEntry.d.ts +10 -0
  21. package/cjs/console/types/IVirtualConsoleLogEntry.d.ts.map +1 -0
  22. package/cjs/console/types/IVirtualConsoleLogGroup.cjs +3 -0
  23. package/cjs/console/types/IVirtualConsoleLogGroup.cjs.map +1 -0
  24. package/cjs/console/types/IVirtualConsoleLogGroup.d.ts +7 -0
  25. package/cjs/console/types/IVirtualConsoleLogGroup.d.ts.map +1 -0
  26. package/cjs/console/types/IVirtualConsolePrinter.cjs +3 -0
  27. package/cjs/console/types/IVirtualConsolePrinter.cjs.map +1 -0
  28. package/cjs/console/types/IVirtualConsolePrinter.d.ts +52 -0
  29. package/cjs/console/types/IVirtualConsolePrinter.d.ts.map +1 -0
  30. package/cjs/console/utilities/VirtualConsoleLogEntryStringifier.cjs +97 -0
  31. package/cjs/console/utilities/VirtualConsoleLogEntryStringifier.cjs.map +1 -0
  32. package/cjs/console/utilities/VirtualConsoleLogEntryStringifier.d.ts +35 -0
  33. package/cjs/console/utilities/VirtualConsoleLogEntryStringifier.d.ts.map +1 -0
  34. package/cjs/css/CSSParser.cjs +34 -13
  35. package/cjs/css/CSSParser.cjs.map +1 -1
  36. package/cjs/css/CSSParser.d.ts +8 -0
  37. package/cjs/css/CSSParser.d.ts.map +1 -1
  38. package/cjs/event/EventTarget.cjs +23 -5
  39. package/cjs/event/EventTarget.cjs.map +1 -1
  40. package/cjs/event/EventTarget.d.ts.map +1 -1
  41. package/cjs/nodes/element/Element.cjs.map +1 -1
  42. package/cjs/nodes/element/Element.d.ts +1 -1
  43. package/cjs/nodes/element/Element.d.ts.map +1 -1
  44. package/cjs/nodes/html-link-element/HTMLLinkElementUtility.cjs +8 -31
  45. package/cjs/nodes/html-link-element/HTMLLinkElementUtility.cjs.map +1 -1
  46. package/cjs/nodes/html-link-element/HTMLLinkElementUtility.d.ts +0 -7
  47. package/cjs/nodes/html-link-element/HTMLLinkElementUtility.d.ts.map +1 -1
  48. package/cjs/nodes/html-script-element/HTMLScriptElement.cjs +2 -1
  49. package/cjs/nodes/html-script-element/HTMLScriptElement.cjs.map +1 -1
  50. package/cjs/nodes/html-script-element/HTMLScriptElement.d.ts.map +1 -1
  51. package/cjs/nodes/html-script-element/HTMLScriptElementUtility.cjs +10 -55
  52. package/cjs/nodes/html-script-element/HTMLScriptElementUtility.cjs.map +1 -1
  53. package/cjs/nodes/html-script-element/HTMLScriptElementUtility.d.ts +0 -14
  54. package/cjs/nodes/html-script-element/HTMLScriptElementUtility.d.ts.map +1 -1
  55. package/cjs/window/GlobalWindow.cjs +1 -9
  56. package/cjs/window/GlobalWindow.cjs.map +1 -1
  57. package/cjs/window/GlobalWindow.d.ts +1 -7
  58. package/cjs/window/GlobalWindow.d.ts.map +1 -1
  59. package/cjs/window/IHappyDOMOptions.d.ts +2 -0
  60. package/cjs/window/IHappyDOMOptions.d.ts.map +1 -1
  61. package/cjs/window/IWindow.d.ts +2 -7
  62. package/cjs/window/IWindow.d.ts.map +1 -1
  63. package/cjs/window/VMGlobalPropertyScript.cjs +1 -0
  64. package/cjs/window/VMGlobalPropertyScript.cjs.map +1 -1
  65. package/cjs/window/VMGlobalPropertyScript.d.ts +1 -1
  66. package/cjs/window/VMGlobalPropertyScript.d.ts.map +1 -1
  67. package/cjs/window/Window.cjs +51 -64
  68. package/cjs/window/Window.cjs.map +1 -1
  69. package/cjs/window/Window.d.ts +6 -23
  70. package/cjs/window/Window.d.ts.map +1 -1
  71. package/cjs/window/WindowErrorUtility.cjs +84 -0
  72. package/cjs/window/WindowErrorUtility.cjs.map +1 -0
  73. package/cjs/window/WindowErrorUtility.d.ts +38 -0
  74. package/cjs/window/WindowErrorUtility.d.ts.map +1 -0
  75. package/lib/console/VirtualConsole.d.ts +163 -0
  76. package/lib/console/VirtualConsole.d.ts.map +1 -0
  77. package/lib/console/VirtualConsole.js +322 -0
  78. package/lib/console/VirtualConsole.js.map +1 -0
  79. package/lib/console/VirtualConsolePrinter.d.ts +55 -0
  80. package/lib/console/VirtualConsolePrinter.d.ts.map +1 -0
  81. package/lib/console/VirtualConsolePrinter.js +95 -0
  82. package/lib/console/VirtualConsolePrinter.js.map +1 -0
  83. package/lib/console/enums/VirtualConsoleLogLevelEnum.d.ts +13 -0
  84. package/lib/console/enums/VirtualConsoleLogLevelEnum.d.ts.map +1 -0
  85. package/lib/console/enums/VirtualConsoleLogLevelEnum.js +14 -0
  86. package/lib/console/enums/VirtualConsoleLogLevelEnum.js.map +1 -0
  87. package/lib/console/enums/VirtualConsoleLogTypeEnum.d.ts +23 -0
  88. package/lib/console/enums/VirtualConsoleLogTypeEnum.d.ts.map +1 -0
  89. package/lib/console/enums/VirtualConsoleLogTypeEnum.js +28 -0
  90. package/lib/console/enums/VirtualConsoleLogTypeEnum.js.map +1 -0
  91. package/lib/console/types/IVirtualConsoleLogEntry.d.ts +10 -0
  92. package/lib/console/types/IVirtualConsoleLogEntry.d.ts.map +1 -0
  93. package/lib/console/types/IVirtualConsoleLogEntry.js +2 -0
  94. package/lib/console/types/IVirtualConsoleLogEntry.js.map +1 -0
  95. package/lib/console/types/IVirtualConsoleLogGroup.d.ts +7 -0
  96. package/lib/console/types/IVirtualConsoleLogGroup.d.ts.map +1 -0
  97. package/lib/console/types/IVirtualConsoleLogGroup.js +2 -0
  98. package/lib/console/types/IVirtualConsoleLogGroup.js.map +1 -0
  99. package/lib/console/types/IVirtualConsolePrinter.d.ts +52 -0
  100. package/lib/console/types/IVirtualConsolePrinter.d.ts.map +1 -0
  101. package/lib/console/types/IVirtualConsolePrinter.js +2 -0
  102. package/lib/console/types/IVirtualConsolePrinter.js.map +1 -0
  103. package/lib/console/utilities/VirtualConsoleLogEntryStringifier.d.ts +35 -0
  104. package/lib/console/utilities/VirtualConsoleLogEntryStringifier.d.ts.map +1 -0
  105. package/lib/console/utilities/VirtualConsoleLogEntryStringifier.js +91 -0
  106. package/lib/console/utilities/VirtualConsoleLogEntryStringifier.js.map +1 -0
  107. package/lib/css/CSSParser.d.ts +8 -0
  108. package/lib/css/CSSParser.d.ts.map +1 -1
  109. package/lib/css/CSSParser.js +34 -13
  110. package/lib/css/CSSParser.js.map +1 -1
  111. package/lib/event/EventTarget.d.ts.map +1 -1
  112. package/lib/event/EventTarget.js +23 -5
  113. package/lib/event/EventTarget.js.map +1 -1
  114. package/lib/nodes/element/Element.d.ts +1 -1
  115. package/lib/nodes/element/Element.d.ts.map +1 -1
  116. package/lib/nodes/element/Element.js.map +1 -1
  117. package/lib/nodes/html-link-element/HTMLLinkElementUtility.d.ts +0 -7
  118. package/lib/nodes/html-link-element/HTMLLinkElementUtility.d.ts.map +1 -1
  119. package/lib/nodes/html-link-element/HTMLLinkElementUtility.js +8 -31
  120. package/lib/nodes/html-link-element/HTMLLinkElementUtility.js.map +1 -1
  121. package/lib/nodes/html-script-element/HTMLScriptElement.d.ts.map +1 -1
  122. package/lib/nodes/html-script-element/HTMLScriptElement.js +2 -1
  123. package/lib/nodes/html-script-element/HTMLScriptElement.js.map +1 -1
  124. package/lib/nodes/html-script-element/HTMLScriptElementUtility.d.ts +0 -14
  125. package/lib/nodes/html-script-element/HTMLScriptElementUtility.d.ts.map +1 -1
  126. package/lib/nodes/html-script-element/HTMLScriptElementUtility.js +10 -55
  127. package/lib/nodes/html-script-element/HTMLScriptElementUtility.js.map +1 -1
  128. package/lib/window/GlobalWindow.d.ts +1 -7
  129. package/lib/window/GlobalWindow.d.ts.map +1 -1
  130. package/lib/window/GlobalWindow.js +1 -9
  131. package/lib/window/GlobalWindow.js.map +1 -1
  132. package/lib/window/IHappyDOMOptions.d.ts +2 -0
  133. package/lib/window/IHappyDOMOptions.d.ts.map +1 -1
  134. package/lib/window/IWindow.d.ts +2 -7
  135. package/lib/window/IWindow.d.ts.map +1 -1
  136. package/lib/window/VMGlobalPropertyScript.d.ts +1 -1
  137. package/lib/window/VMGlobalPropertyScript.d.ts.map +1 -1
  138. package/lib/window/VMGlobalPropertyScript.js +1 -0
  139. package/lib/window/VMGlobalPropertyScript.js.map +1 -1
  140. package/lib/window/Window.d.ts +6 -23
  141. package/lib/window/Window.d.ts.map +1 -1
  142. package/lib/window/Window.js +51 -64
  143. package/lib/window/Window.js.map +1 -1
  144. package/lib/window/WindowErrorUtility.d.ts +38 -0
  145. package/lib/window/WindowErrorUtility.d.ts.map +1 -0
  146. package/lib/window/WindowErrorUtility.js +78 -0
  147. package/lib/window/WindowErrorUtility.js.map +1 -0
  148. package/package.json +2 -2
  149. package/src/console/VirtualConsole.ts +356 -0
  150. package/src/console/VirtualConsolePrinter.ts +107 -0
  151. package/src/console/enums/VirtualConsoleLogLevelEnum.ts +12 -0
  152. package/src/console/enums/VirtualConsoleLogTypeEnum.ts +29 -0
  153. package/src/console/types/IVirtualConsoleLogEntry.ts +10 -0
  154. package/src/console/types/IVirtualConsoleLogGroup.ts +6 -0
  155. package/src/console/types/IVirtualConsolePrinter.ts +58 -0
  156. package/src/console/utilities/VirtualConsoleLogEntryStringifier.ts +100 -0
  157. package/src/css/CSSParser.ts +35 -14
  158. package/src/event/EventTarget.ts +27 -5
  159. package/src/nodes/element/Element.ts +1 -1
  160. package/src/nodes/html-link-element/HTMLLinkElementUtility.ts +13 -35
  161. package/src/nodes/html-script-element/HTMLScriptElement.ts +4 -1
  162. package/src/nodes/html-script-element/HTMLScriptElementUtility.ts +19 -62
  163. package/src/window/GlobalWindow.ts +1 -10
  164. package/src/window/IHappyDOMOptions.ts +1 -0
  165. package/src/window/IWindow.ts +2 -8
  166. package/src/window/VMGlobalPropertyScript.ts +1 -0
  167. package/src/window/Window.ts +77 -70
  168. package/src/window/WindowErrorUtility.ts +94 -0
  169. package/tsconfig.json +2 -3
@@ -6,6 +6,7 @@ import CSSKeyframesRule from './rules/CSSKeyframesRule.js';
6
6
  import CSSMediaRule from './rules/CSSMediaRule.js';
7
7
  import CSSContainerRule from './rules/CSSContainerRule.js';
8
8
  import CSSSupportsRule from './rules/CSSSupportsRule.js';
9
+ import SelectorParser from '../query-selector/SelectorParser.js';
9
10
 
10
11
  const COMMENT_REGEXP = /\/\*[\s\S]*?\*\//gm;
11
12
 
@@ -96,23 +97,27 @@ export default class CSSParser {
96
97
  parentRule.type === CSSRule.CONTAINER_RULE ||
97
98
  parentRule.type === CSSRule.SUPPORTS_RULE)
98
99
  ) {
99
- const newRule = new CSSStyleRule();
100
- (<string>newRule.selectorText) = selectorText;
101
- newRule.parentStyleSheet = parentStyleSheet;
102
- newRule.parentRule = parentRule;
103
- (<CSSMediaRule>parentRule).cssRules.push(newRule);
104
- parentRule = newRule;
100
+ if (this.validateSelectorText(selectorText)) {
101
+ const newRule = new CSSStyleRule();
102
+ (<string>newRule.selectorText) = selectorText;
103
+ newRule.parentStyleSheet = parentStyleSheet;
104
+ newRule.parentRule = parentRule;
105
+ (<CSSMediaRule>parentRule).cssRules.push(newRule);
106
+ parentRule = newRule;
107
+ }
105
108
  } else {
106
- const newRule = new CSSStyleRule();
107
- (<string>newRule.selectorText) = selectorText;
108
- newRule.parentStyleSheet = parentStyleSheet;
109
- newRule.parentRule = parentRule;
109
+ if (this.validateSelectorText(selectorText)) {
110
+ const newRule = new CSSStyleRule();
111
+ (<string>newRule.selectorText) = selectorText;
112
+ newRule.parentStyleSheet = parentStyleSheet;
113
+ newRule.parentRule = parentRule;
110
114
 
111
- if (!parentRule) {
112
- cssRules.push(newRule);
113
- }
115
+ if (!parentRule) {
116
+ cssRules.push(newRule);
117
+ }
114
118
 
115
- parentRule = newRule;
119
+ parentRule = newRule;
120
+ }
116
121
  }
117
122
 
118
123
  stack.push(parentRule);
@@ -140,4 +145,20 @@ export default class CSSParser {
140
145
 
141
146
  return cssRules;
142
147
  }
148
+
149
+ /**
150
+ * Validates a selector text.
151
+ *
152
+ * @see https://www.w3.org/TR/CSS21/syndata.html#rule-sets
153
+ * @param selectorText Selector text.
154
+ * @returns True if valid, false otherwise.
155
+ */
156
+ private static validateSelectorText(selectorText: string): boolean {
157
+ try {
158
+ SelectorParser.getSelectorGroups(selectorText);
159
+ } catch (e) {
160
+ return false;
161
+ }
162
+ return true;
163
+ }
143
164
  }
@@ -6,6 +6,7 @@ import EventPhaseEnum from './EventPhaseEnum.js';
6
6
  import INode from '../nodes/node/INode.js';
7
7
  import IDocument from '../nodes/document/IDocument.js';
8
8
  import IWindow from '../window/IWindow.js';
9
+ import WindowErrorUtility from '../window/WindowErrorUtility.js';
9
10
 
10
11
  /**
11
12
  * Handles events.
@@ -88,11 +89,12 @@ export default abstract class EventTarget implements IEventTarget {
88
89
  * @returns The return value is false if event is cancelable and at least one of the event handlers which handled this event called Event.preventDefault().
89
90
  */
90
91
  public dispatchEvent(event: Event): boolean {
92
+ const window = this._getWindow();
93
+
91
94
  if (event.eventPhase === EventPhaseEnum.none) {
92
95
  event._target = this;
93
96
 
94
97
  const composedPath = event.composedPath();
95
- const window = this._getWindow();
96
98
 
97
99
  // Capturing phase
98
100
 
@@ -137,7 +139,12 @@ export default abstract class EventTarget implements IEventTarget {
137
139
  const onEventName = 'on' + event.type.toLowerCase();
138
140
 
139
141
  if (typeof this[onEventName] === 'function') {
140
- this[onEventName].call(this, event);
142
+ // We can end up in a never ending loop if the listener for the error event on Window also throws an error.
143
+ if (window && (this !== <IEventTarget>window || event.type !== 'error')) {
144
+ WindowErrorUtility.captureErrorSync(window, this[onEventName].bind(this, event));
145
+ } else {
146
+ this[onEventName].call(this, event);
147
+ }
141
148
  }
142
149
  }
143
150
 
@@ -161,10 +168,25 @@ export default abstract class EventTarget implements IEventTarget {
161
168
  event._isInPassiveEventListener = true;
162
169
  }
163
170
 
164
- if ((<IEventListener>listener).handleEvent) {
165
- (<IEventListener>listener).handleEvent(event);
171
+ // We can end up in a never ending loop if the listener for the error event on Window also throws an error.
172
+ if (window && (this !== <IEventTarget>window || event.type !== 'error')) {
173
+ if ((<IEventListener>listener).handleEvent) {
174
+ WindowErrorUtility.captureErrorSync(
175
+ window,
176
+ (<IEventListener>listener).handleEvent.bind(this, event)
177
+ );
178
+ } else {
179
+ WindowErrorUtility.captureErrorSync(
180
+ window,
181
+ (<(event: Event) => void>listener).bind(this, event)
182
+ );
183
+ }
166
184
  } else {
167
- (<(event: Event) => void>listener).call(this, event);
185
+ if ((<IEventListener>listener).handleEvent) {
186
+ (<IEventListener>listener).handleEvent(event);
187
+ } else {
188
+ (<(event: Event) => void>listener).call(this, event);
189
+ }
168
190
  }
169
191
 
170
192
  event._isInPassiveEventListener = false;
@@ -42,7 +42,7 @@ export default class Element extends Node implements IElement {
42
42
  public static observedAttributes: string[];
43
43
  public tagName: string = null;
44
44
  public nodeType = Node.ELEMENT_NODE;
45
- public shadowRoot: IShadowRoot = null;
45
+ public shadowRoot: IShadowRoot | null = null;
46
46
  public prefix: string = null;
47
47
 
48
48
  public scrollHeight = 0;
@@ -1,11 +1,11 @@
1
1
  import Document from '../document/Document.js';
2
2
  import Event from '../../event/Event.js';
3
- import ErrorEvent from '../../event/events/ErrorEvent.js';
4
3
  import ResourceFetch from '../../fetch/ResourceFetch.js';
5
4
  import HTMLLinkElement from './HTMLLinkElement.js';
6
5
  import CSSStyleSheet from '../../css/CSSStyleSheet.js';
7
6
  import DOMException from '../../exception/DOMException.js';
8
7
  import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
8
+ import WindowErrorUtility from '../../window/WindowErrorUtility.js';
9
9
 
10
10
  /**
11
11
  * Helper class for getting the URL relative to a Location object.
@@ -24,54 +24,32 @@ export default class HTMLLinkElementUtility {
24
24
 
25
25
  if (href !== null && rel && rel.toLowerCase() === 'stylesheet' && element.isConnected) {
26
26
  if (element.ownerDocument.defaultView.happyDOM.settings.disableCSSFileLoading) {
27
- this.onError(
27
+ WindowErrorUtility.dispatchError(
28
28
  element,
29
29
  new DOMException(
30
30
  `Failed to load external stylesheet "${href}". CSS file loading is disabled.`,
31
31
  DOMExceptionNameEnum.notSupportedError
32
32
  )
33
33
  );
34
+
34
35
  return;
35
36
  }
36
37
 
37
38
  (<Document>element.ownerDocument)._readyStateManager.startTask();
38
39
 
39
- let code: string;
40
- try {
41
- code = await ResourceFetch.fetch(element.ownerDocument, href);
42
- } catch (error) {
43
- this.onError(element, error);
44
- return;
40
+ const code: string | null = await WindowErrorUtility.captureErrorAsync<string>(
41
+ element,
42
+ async () => await ResourceFetch.fetch(element.ownerDocument, href)
43
+ );
44
+
45
+ if (code) {
46
+ const styleSheet = new CSSStyleSheet();
47
+ styleSheet.replaceSync(code);
48
+ (<CSSStyleSheet>element.sheet) = styleSheet;
49
+ element.dispatchEvent(new Event('load'));
45
50
  }
46
51
 
47
- const styleSheet = new CSSStyleSheet();
48
- styleSheet.replaceSync(code);
49
- (<CSSStyleSheet>element.sheet) = styleSheet;
50
- element.dispatchEvent(new Event('load'));
51
52
  (<Document>element.ownerDocument)._readyStateManager.endTask();
52
53
  }
53
54
  }
54
-
55
- /**
56
- * Triggered when an error occurs.
57
- *
58
- * @param element Element.
59
- * @param error Error.
60
- */
61
- private static onError(element: HTMLLinkElement, error: Error): void {
62
- element.dispatchEvent(
63
- new ErrorEvent('error', {
64
- message: error.message,
65
- error
66
- })
67
- );
68
- element.ownerDocument.defaultView.dispatchEvent(
69
- new ErrorEvent('error', {
70
- message: error.message,
71
- error
72
- })
73
- );
74
- (<Document>element.ownerDocument)._readyStateManager.endTask();
75
- element.ownerDocument.defaultView.console.error(error);
76
- }
77
55
  }
@@ -6,6 +6,7 @@ import ErrorEvent from '../../event/events/ErrorEvent.js';
6
6
  import INode from '../../nodes/node/INode.js';
7
7
  import INamedNodeMap from '../../named-node-map/INamedNodeMap.js';
8
8
  import HTMLScriptElementNamedNodeMap from './HTMLScriptElementNamedNodeMap.js';
9
+ import WindowErrorUtility from '../../window/WindowErrorUtility.js';
9
10
 
10
11
  /**
11
12
  * HTML Script Element.
@@ -188,7 +189,9 @@ export default class HTMLScriptElement extends HTMLElement implements IHTMLScrip
188
189
  type === 'application/x-javascript' ||
189
190
  type.startsWith('text/javascript'))
190
191
  ) {
191
- HTMLScriptElementUtility.eval(this, textContent);
192
+ WindowErrorUtility.captureErrorSync(this.ownerDocument.defaultView, () =>
193
+ this.ownerDocument.defaultView.eval(textContent)
194
+ );
192
195
  }
193
196
  }
194
197
  }
@@ -1,10 +1,10 @@
1
1
  import Document from '../document/Document.js';
2
2
  import Event from '../../event/Event.js';
3
- import ErrorEvent from '../../event/events/ErrorEvent.js';
4
3
  import DOMException from '../../exception/DOMException.js';
5
4
  import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
6
5
  import ResourceFetch from '../../fetch/ResourceFetch.js';
7
6
  import HTMLScriptElement from './HTMLScriptElement.js';
7
+ import WindowErrorUtility from '../../window/WindowErrorUtility.js';
8
8
 
9
9
  /**
10
10
  * Helper class for getting the URL relative to a Location object.
@@ -25,7 +25,7 @@ export default class HTMLScriptElementUtility {
25
25
  element.ownerDocument.defaultView.happyDOM.settings.disableJavaScriptFileLoading ||
26
26
  element.ownerDocument.defaultView.happyDOM.settings.disableJavaScriptEvaluation
27
27
  ) {
28
- this.onError(
28
+ WindowErrorUtility.dispatchError(
29
29
  element,
30
30
  new DOMException(
31
31
  `Failed to load external script "${src}". JavaScript file loading is disabled.`,
@@ -38,72 +38,29 @@ export default class HTMLScriptElementUtility {
38
38
  if (async) {
39
39
  (<Document>element.ownerDocument)._readyStateManager.startTask();
40
40
 
41
- let code = null;
41
+ const code = await WindowErrorUtility.captureErrorAsync<string>(
42
+ element,
43
+ async () => await ResourceFetch.fetch(element.ownerDocument, src)
44
+ );
42
45
 
43
- try {
44
- code = await ResourceFetch.fetch(element.ownerDocument, src);
45
- } catch (error) {
46
- this.onError(element, error);
47
- return;
46
+ if (code) {
47
+ WindowErrorUtility.captureErrorSync(element.ownerDocument.defaultView, () =>
48
+ element.ownerDocument.defaultView.eval(code)
49
+ );
50
+ element.dispatchEvent(new Event('load'));
48
51
  }
49
-
50
- this.eval(element, code);
51
- element.dispatchEvent(new Event('load'));
52
52
  (<Document>element.ownerDocument)._readyStateManager.endTask();
53
53
  } else {
54
- let code = null;
54
+ const code = WindowErrorUtility.captureErrorSync<string>(element, () =>
55
+ ResourceFetch.fetchSync(element.ownerDocument, src)
56
+ );
55
57
 
56
- try {
57
- code = ResourceFetch.fetchSync(element.ownerDocument, src);
58
- } catch (error) {
59
- this.onError(element, error);
60
- return;
58
+ if (code) {
59
+ WindowErrorUtility.captureErrorSync(element.ownerDocument.defaultView, () =>
60
+ element.ownerDocument.defaultView.eval(code)
61
+ );
62
+ element.dispatchEvent(new Event('load'));
61
63
  }
62
-
63
- this.eval(element, code);
64
- element.dispatchEvent(new Event('load'));
65
64
  }
66
65
  }
67
-
68
- /**
69
- * Evaluates a script code.
70
- *
71
- * @param element Element.
72
- * @param code Code.
73
- */
74
- public static eval(element: HTMLScriptElement, code: string): void {
75
- try {
76
- element.ownerDocument.defaultView.eval(code);
77
- } catch (error) {
78
- element.ownerDocument.defaultView.console.error(error);
79
- element.ownerDocument.defaultView.dispatchEvent(
80
- new ErrorEvent('error', {
81
- message: error.message,
82
- error: error
83
- })
84
- );
85
- }
86
- }
87
-
88
- /**
89
- * Triggered when an error occurs.
90
- *
91
- * @param element Element.
92
- * @param error Error.
93
- */
94
- private static onError(element: HTMLScriptElement, error: Error): void {
95
- element.dispatchEvent(
96
- new ErrorEvent('error', {
97
- message: error.message,
98
- error
99
- })
100
- );
101
- element.ownerDocument.defaultView.dispatchEvent(
102
- new ErrorEvent('error', {
103
- message: error.message,
104
- error
105
- })
106
- );
107
- element.ownerDocument.defaultView.console.error(error);
108
- }
109
66
  }
@@ -52,6 +52,7 @@ export default class GlobalWindow extends Window implements IWindow {
52
52
  public decodeURIComponent: typeof decodeURIComponent = globalThis.decodeURIComponent;
53
53
  public encodeURI: typeof encodeURI = globalThis.encodeURI;
54
54
  public encodeURIComponent: typeof encodeURIComponent = globalThis.encodeURIComponent;
55
+ public eval: typeof eval = globalThis.eval;
55
56
  /**
56
57
  * @deprecated
57
58
  */
@@ -69,16 +70,6 @@ export default class GlobalWindow extends Window implements IWindow {
69
70
  public gc: () => void = globalThis.gc;
70
71
  public v8debug?: unknown = globalThis.v8debug;
71
72
 
72
- /**
73
- * Evaluates code.
74
- *
75
- * @param code Code.
76
- * @returns Result.
77
- */
78
- public override eval(code: string): unknown {
79
- return eval(code);
80
- }
81
-
82
73
  /**
83
74
  * Setup of VM context.
84
75
  */
@@ -5,6 +5,7 @@ export default interface IHappyDOMOptions {
5
5
  width?: number;
6
6
  height?: number;
7
7
  url?: string;
8
+ console?: Console;
8
9
  settings?: {
9
10
  disableJavaScriptEvaluation?: boolean;
10
11
  disableJavaScriptFileLoading?: boolean;
@@ -123,6 +123,7 @@ import IHeadersInit from '../fetch/types/IHeadersInit.js';
123
123
  import RadioNodeList from '../nodes/html-form-element/RadioNodeList.js';
124
124
  import ValidityState from '../validity-state/ValidityState.js';
125
125
  import INodeJSGlobal from './INodeJSGlobal.js';
126
+ import VirtualConsolePrinter from '../console/VirtualConsolePrinter.js';
126
127
 
127
128
  /**
128
129
  * Window without dependencies to server side specific packages.
@@ -135,6 +136,7 @@ export default interface IWindow extends IEventTarget, INodeJSGlobal {
135
136
  asyncTaskManager: AsyncTaskManager;
136
137
  setWindowSize: (options: { width?: number; height?: number }) => void;
137
138
  setURL: (url: string) => void;
139
+ virtualConsolePrinter: VirtualConsolePrinter | null;
138
140
  settings: IHappyDOMSettings;
139
141
 
140
142
  /**
@@ -294,14 +296,6 @@ export default interface IWindow extends IEventTarget, INodeJSGlobal {
294
296
  readonly scrollX: number;
295
297
  readonly scrollY: number;
296
298
 
297
- /**
298
- * Evaluates code.
299
- *
300
- * @param code Code.
301
- * @returns Result.
302
- */
303
- eval(code: string): unknown;
304
-
305
299
  /**
306
300
  * Returns an object containing the values of all CSS properties of an element.
307
301
  *
@@ -39,6 +39,7 @@ this.decodeURI = globalThis.decodeURI;
39
39
  this.decodeURIComponent = globalThis.decodeURIComponent;
40
40
  this.encodeURI = globalThis.encodeURI;
41
41
  this.encodeURIComponent = globalThis.encodeURIComponent;
42
+ this.eval = globalThis.eval;
42
43
  this.escape = globalThis.escape;
43
44
  this.global = globalThis.global;
44
45
  this.isFinite = globalThis.isFinite;
@@ -135,6 +135,10 @@ import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
135
135
  import IHappyDOMOptions from './IHappyDOMOptions.js';
136
136
  import RadioNodeList from '../nodes/html-form-element/RadioNodeList.js';
137
137
  import ValidityState from '../validity-state/ValidityState.js';
138
+ import WindowErrorUtility from './WindowErrorUtility.js';
139
+ import VirtualConsole from '../console/VirtualConsole.js';
140
+ import VirtualConsolePrinter from '../console/VirtualConsolePrinter.js';
141
+ import IHappyDOMSettings from './IHappyDOMSettings.js';
138
142
 
139
143
  const ORIGINAL_SET_TIMEOUT = setTimeout;
140
144
  const ORIGINAL_CLEAR_TIMEOUT = clearTimeout;
@@ -150,7 +154,25 @@ const ORIGINAL_QUEUE_MICROTASK = queueMicrotask;
150
154
  */
151
155
  export default class Window extends EventTarget implements IWindow {
152
156
  // The Happy DOM property
153
- public readonly happyDOM = {
157
+ public readonly happyDOM: {
158
+ whenAsyncComplete: () => Promise<void>;
159
+ cancelAsync: () => void;
160
+ asyncTaskManager: AsyncTaskManager;
161
+ setWindowSize: (options: { width?: number; height?: number }) => void;
162
+ setURL: (url: string) => void;
163
+ virtualConsolePrinter: VirtualConsolePrinter | null;
164
+ settings: IHappyDOMSettings;
165
+
166
+ /**
167
+ * @deprecated
168
+ */
169
+ setInnerWidth: (width: number) => void;
170
+
171
+ /**
172
+ * @deprecated
173
+ */
174
+ setInnerHeight: (height: number) => void;
175
+ } = {
154
176
  whenAsyncComplete: async (): Promise<void> => {
155
177
  return await this.happyDOM.asyncTaskManager.whenComplete();
156
178
  },
@@ -158,9 +180,6 @@ export default class Window extends EventTarget implements IWindow {
158
180
  this.happyDOM.asyncTaskManager.cancelAll();
159
181
  },
160
182
  asyncTaskManager: new AsyncTaskManager(),
161
- setURL: (url: string) => {
162
- this.location.href = url;
163
- },
164
183
  setWindowSize: (options: { width?: number; height?: number }): void => {
165
184
  if (
166
185
  (options.width !== undefined && this.innerWidth !== options.width) ||
@@ -179,6 +198,10 @@ export default class Window extends EventTarget implements IWindow {
179
198
  this.dispatchEvent(new Event('resize'));
180
199
  }
181
200
  },
201
+ virtualConsolePrinter: null,
202
+ setURL: (url: string) => {
203
+ this.location.href = url;
204
+ },
182
205
  settings: {
183
206
  disableJavaScriptEvaluation: false,
184
207
  disableJavaScriptFileLoading: false,
@@ -191,17 +214,7 @@ export default class Window extends EventTarget implements IWindow {
191
214
  mediaType: 'screen'
192
215
  }
193
216
  },
194
-
195
- /**
196
- * @param width The width of the viewport.
197
- * @deprecated
198
- */
199
217
  setInnerWidth: (width: number): void => this.happyDOM.setWindowSize({ width }),
200
-
201
- /**
202
- * @param height The height of the viewport.
203
- * @deprecated
204
- */
205
218
  setInnerHeight: (height: number): void => this.happyDOM.setWindowSize({ height })
206
219
  };
207
220
 
@@ -336,7 +349,7 @@ export default class Window extends EventTarget implements IWindow {
336
349
  public readonly location: Location;
337
350
  public readonly history: History;
338
351
  public readonly navigator: Navigator;
339
- public readonly console = console;
352
+ public readonly console: Console;
340
353
  public readonly self = this;
341
354
  public readonly top = this;
342
355
  public readonly parent = this;
@@ -347,10 +360,10 @@ export default class Window extends EventTarget implements IWindow {
347
360
  public readonly sessionStorage: Storage;
348
361
  public readonly localStorage: Storage;
349
362
  public readonly performance = PerfHooks.performance;
350
- public readonly innerWidth: number;
351
- public readonly innerHeight: number;
352
- public readonly outerWidth: number;
353
- public readonly outerHeight: number;
363
+ public readonly innerWidth: number = 1024;
364
+ public readonly innerHeight: number = 768;
365
+ public readonly outerWidth: number = 1024;
366
+ public readonly outerHeight: number = 768;
354
367
 
355
368
  // Node.js Globals
356
369
  public Array: typeof Array;
@@ -395,6 +408,7 @@ export default class Window extends EventTarget implements IWindow {
395
408
  public decodeURIComponent: typeof decodeURIComponent;
396
409
  public encodeURI: typeof encodeURI;
397
410
  public encodeURIComponent: typeof encodeURIComponent;
411
+ public eval: typeof eval;
398
412
  /**
399
413
  * @deprecated
400
414
  */
@@ -447,41 +461,44 @@ export default class Window extends EventTarget implements IWindow {
447
461
  this.sessionStorage = new Storage();
448
462
  this.localStorage = new Storage();
449
463
 
450
- if (options && options.width !== undefined) {
451
- this.innerWidth = options.width;
452
- this.outerWidth = options.width;
453
- } else if (options && options.innerWidth !== undefined) {
454
- this.innerWidth = options.innerWidth;
455
- this.outerWidth = options.innerWidth;
456
- } else {
457
- this.innerWidth = 1024;
458
- this.outerWidth = 1024;
459
- }
464
+ if (options) {
465
+ if (options.width !== undefined) {
466
+ this.innerWidth = options.width;
467
+ this.outerWidth = options.width;
468
+ } else if (options.innerWidth !== undefined) {
469
+ this.innerWidth = options.innerWidth;
470
+ this.outerWidth = options.innerWidth;
471
+ }
460
472
 
461
- if (options && options.height !== undefined) {
462
- this.innerHeight = options.height;
463
- this.outerHeight = options.height;
464
- } else if (options && options.innerHeight !== undefined) {
465
- this.innerHeight = options.innerHeight;
466
- this.outerHeight = options.innerHeight;
467
- } else {
468
- this.innerHeight = 768;
469
- this.outerHeight = 768;
470
- }
473
+ if (options.height !== undefined) {
474
+ this.innerHeight = options.height;
475
+ this.outerHeight = options.height;
476
+ } else if (options.innerHeight !== undefined) {
477
+ this.innerHeight = options.innerHeight;
478
+ this.outerHeight = options.innerHeight;
479
+ }
471
480
 
472
- if (options && options.url !== undefined) {
473
- this.location.href = options.url;
481
+ if (options.url !== undefined) {
482
+ this.location.href = options.url;
483
+ }
484
+
485
+ if (options.settings) {
486
+ this.happyDOM.settings = {
487
+ ...this.happyDOM.settings,
488
+ ...options.settings,
489
+ device: {
490
+ ...this.happyDOM.settings.device,
491
+ ...options.settings.device
492
+ }
493
+ };
494
+ }
474
495
  }
475
496
 
476
- if (options && options.settings) {
477
- this.happyDOM.settings = {
478
- ...this.happyDOM.settings,
479
- ...options.settings,
480
- device: {
481
- ...this.happyDOM.settings.device,
482
- ...options.settings.device
483
- }
484
- };
497
+ if (options && options.console) {
498
+ this.console = options.console;
499
+ } else {
500
+ this.happyDOM.virtualConsolePrinter = new VirtualConsolePrinter();
501
+ this.console = new VirtualConsole(this.happyDOM.virtualConsolePrinter);
485
502
  }
486
503
 
487
504
  this._setTimeout = ORIGINAL_SET_TIMEOUT;
@@ -622,20 +639,6 @@ export default class Window extends EventTarget implements IWindow {
622
639
  return new CSS();
623
640
  }
624
641
 
625
- /**
626
- * Evaluates code.
627
- *
628
- * @override
629
- * @param code Code.
630
- * @returns Result.
631
- */
632
- public eval(code: string): unknown {
633
- if (VM.isContext(this)) {
634
- return VM.runInContext(code, this);
635
- }
636
- return eval(code);
637
- }
638
-
639
642
  /**
640
643
  * Returns an object containing the values of all CSS properties of an element.
641
644
  *
@@ -721,7 +724,7 @@ export default class Window extends EventTarget implements IWindow {
721
724
  public setTimeout(callback: Function, delay = 0, ...args: unknown[]): NodeJS.Timeout {
722
725
  const id = this._setTimeout(() => {
723
726
  this.happyDOM.asyncTaskManager.endTimer(id);
724
- callback(...args);
727
+ WindowErrorUtility.captureErrorSync(this, () => callback(...args));
725
728
  }, delay);
726
729
  this.happyDOM.asyncTaskManager.startTimer(id);
727
730
  return id;
@@ -746,7 +749,13 @@ export default class Window extends EventTarget implements IWindow {
746
749
  * @returns Interval ID.
747
750
  */
748
751
  public setInterval(callback: Function, delay = 0, ...args: unknown[]): NodeJS.Timeout {
749
- const id = this._setInterval(callback, delay, ...args);
752
+ const id = this._setInterval(() => {
753
+ WindowErrorUtility.captureErrorSync(
754
+ this,
755
+ () => callback(...args),
756
+ () => this.clearInterval(id)
757
+ );
758
+ }, delay);
750
759
  this.happyDOM.asyncTaskManager.startTimer(id);
751
760
  return id;
752
761
  }
@@ -768,9 +777,7 @@ export default class Window extends EventTarget implements IWindow {
768
777
  * @returns Timeout ID.
769
778
  */
770
779
  public requestAnimationFrame(callback: (timestamp: number) => void): NodeJS.Timeout {
771
- return this.setTimeout(() => {
772
- callback(this.performance.now());
773
- });
780
+ return this.setTimeout(() => callback(this.performance.now()));
774
781
  }
775
782
 
776
783
  /**
@@ -792,8 +799,8 @@ export default class Window extends EventTarget implements IWindow {
792
799
  const taskId = this.happyDOM.asyncTaskManager.startTask(() => (isAborted = true));
793
800
  this._queueMicrotask(() => {
794
801
  if (!isAborted) {
802
+ WindowErrorUtility.captureErrorSync(this, <() => unknown>callback);
795
803
  this.happyDOM.asyncTaskManager.endTask(taskId);
796
- callback();
797
804
  }
798
805
  });
799
806
  }