happy-dom 14.11.3 → 14.12.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.
- package/cjs/async-task-manager/AsyncTaskManager.cjs +52 -25
- package/cjs/async-task-manager/AsyncTaskManager.cjs.map +1 -1
- package/cjs/async-task-manager/AsyncTaskManager.d.ts.map +1 -1
- package/cjs/browser/BrowserSettingsFactory.cjs +4 -0
- package/cjs/browser/BrowserSettingsFactory.cjs.map +1 -1
- package/cjs/browser/BrowserSettingsFactory.d.ts.map +1 -1
- package/cjs/browser/DefaultBrowserSettings.cjs +5 -0
- package/cjs/browser/DefaultBrowserSettings.cjs.map +1 -1
- package/cjs/browser/DefaultBrowserSettings.d.ts.map +1 -1
- package/cjs/browser/types/IBrowserSettings.d.ts +6 -0
- package/cjs/browser/types/IBrowserSettings.d.ts.map +1 -1
- package/cjs/browser/types/IOptionalBrowserSettings.d.ts +6 -0
- package/cjs/browser/types/IOptionalBrowserSettings.d.ts.map +1 -1
- package/cjs/browser/utilities/BrowserFrameFactory.cjs +12 -0
- package/cjs/browser/utilities/BrowserFrameFactory.cjs.map +1 -1
- package/cjs/browser/utilities/BrowserFrameFactory.d.ts.map +1 -1
- package/cjs/window/BrowserWindow.cjs +66 -4
- package/cjs/window/BrowserWindow.cjs.map +1 -1
- package/cjs/window/BrowserWindow.d.ts +1 -1
- package/cjs/window/BrowserWindow.d.ts.map +1 -1
- package/lib/async-task-manager/AsyncTaskManager.d.ts.map +1 -1
- package/lib/async-task-manager/AsyncTaskManager.js +52 -25
- package/lib/async-task-manager/AsyncTaskManager.js.map +1 -1
- package/lib/browser/BrowserSettingsFactory.d.ts.map +1 -1
- package/lib/browser/BrowserSettingsFactory.js +4 -0
- package/lib/browser/BrowserSettingsFactory.js.map +1 -1
- package/lib/browser/DefaultBrowserSettings.d.ts.map +1 -1
- package/lib/browser/DefaultBrowserSettings.js +5 -0
- package/lib/browser/DefaultBrowserSettings.js.map +1 -1
- package/lib/browser/types/IBrowserSettings.d.ts +6 -0
- package/lib/browser/types/IBrowserSettings.d.ts.map +1 -1
- package/lib/browser/types/IOptionalBrowserSettings.d.ts +6 -0
- package/lib/browser/types/IOptionalBrowserSettings.d.ts.map +1 -1
- package/lib/browser/utilities/BrowserFrameFactory.d.ts.map +1 -1
- package/lib/browser/utilities/BrowserFrameFactory.js +12 -0
- package/lib/browser/utilities/BrowserFrameFactory.js.map +1 -1
- package/lib/window/BrowserWindow.d.ts +1 -1
- package/lib/window/BrowserWindow.d.ts.map +1 -1
- package/lib/window/BrowserWindow.js +66 -4
- package/lib/window/BrowserWindow.js.map +1 -1
- package/package.json +1 -1
- package/src/async-task-manager/AsyncTaskManager.ts +54 -27
- package/src/browser/BrowserSettingsFactory.ts +4 -0
- package/src/browser/DefaultBrowserSettings.ts +5 -0
- package/src/browser/types/IBrowserSettings.ts +7 -0
- package/src/browser/types/IOptionalBrowserSettings.ts +7 -0
- package/src/browser/utilities/BrowserFrameFactory.ts +12 -0
- package/src/window/BrowserWindow.ts +92 -20
@@ -165,6 +165,19 @@ const TIMER = {
|
|
165
165
|
clearImmediate: globalThis.clearImmediate.bind(globalThis)
|
166
166
|
};
|
167
167
|
const IS_NODE_JS_TIMEOUT_ENVIRONMENT = setTimeout.toString().includes('new Timeout');
|
168
|
+
/**
|
169
|
+
* Zero Timeout.
|
170
|
+
*/
|
171
|
+
class Timeout {
|
172
|
+
public callback: () => void;
|
173
|
+
/**
|
174
|
+
* Constructor.
|
175
|
+
* @param callback Callback.
|
176
|
+
*/
|
177
|
+
constructor(callback: () => void) {
|
178
|
+
this.callback = callback;
|
179
|
+
}
|
180
|
+
}
|
168
181
|
|
169
182
|
/**
|
170
183
|
* Browser window.
|
@@ -494,7 +507,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
494
507
|
// Used for tracking capture event listeners to improve performance when they are not used.
|
495
508
|
// See EventTarget class.
|
496
509
|
public [PropertySymbol.captureEventListenerCount]: { [eventType: string]: number } = {};
|
497
|
-
public
|
510
|
+
public [PropertySymbol.mutationObservers]: MutationObserver[] = [];
|
498
511
|
public readonly [PropertySymbol.readyStateManager] = new DocumentReadyStateManager(this);
|
499
512
|
public [PropertySymbol.asyncTaskManager]: AsyncTaskManager | null = null;
|
500
513
|
public [PropertySymbol.location]: Location;
|
@@ -511,6 +524,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
511
524
|
#outerWidth: number | null = null;
|
512
525
|
#outerHeight: number | null = null;
|
513
526
|
#devicePixelRatio: number | null = null;
|
527
|
+
#zeroTimeouts: Array<Timeout> | null = null;
|
514
528
|
|
515
529
|
/**
|
516
530
|
* Constructor.
|
@@ -1018,19 +1032,55 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
1018
1032
|
* @returns Timeout ID.
|
1019
1033
|
*/
|
1020
1034
|
public setTimeout(callback: Function, delay = 0, ...args: unknown[]): NodeJS.Timeout {
|
1035
|
+
// We can group timeouts with a delay of 0 into one timeout to improve performance.
|
1036
|
+
// Grouping timeouts will also improve the performance of the async task manager.
|
1037
|
+
// It may also make the async task manager to stable as many timeouts may cause waitUntilComplete() to be resolved to early.
|
1038
|
+
if (!delay) {
|
1039
|
+
if (!this.#zeroTimeouts) {
|
1040
|
+
const settings = this.#browserFrame.page?.context?.browser?.settings;
|
1041
|
+
const useTryCatch =
|
1042
|
+
!settings ||
|
1043
|
+
!settings.disableErrorCapturing ||
|
1044
|
+
settings.errorCapture === BrowserErrorCaptureEnum.tryAndCatch;
|
1045
|
+
const id = TIMER.setTimeout(() => {
|
1046
|
+
const zeroTimeouts = this.#zeroTimeouts;
|
1047
|
+
this.#zeroTimeouts = null;
|
1048
|
+
for (const zeroTimeout of zeroTimeouts) {
|
1049
|
+
if (useTryCatch) {
|
1050
|
+
WindowErrorUtility.captureError(this, () => zeroTimeout.callback());
|
1051
|
+
} else {
|
1052
|
+
zeroTimeout.callback();
|
1053
|
+
}
|
1054
|
+
}
|
1055
|
+
this.#browserFrame[PropertySymbol.asyncTaskManager].endTimer(id);
|
1056
|
+
});
|
1057
|
+
this.#zeroTimeouts = [];
|
1058
|
+
this.#browserFrame[PropertySymbol.asyncTaskManager].startTimer(id);
|
1059
|
+
}
|
1060
|
+
const zeroTimeout = new Timeout(() => callback(...args));
|
1061
|
+
this.#zeroTimeouts.push(zeroTimeout);
|
1062
|
+
return <NodeJS.Timeout>(<unknown>zeroTimeout);
|
1063
|
+
}
|
1064
|
+
|
1021
1065
|
const settings = this.#browserFrame.page?.context?.browser?.settings;
|
1022
1066
|
const useTryCatch =
|
1023
1067
|
!settings ||
|
1024
1068
|
!settings.disableErrorCapturing ||
|
1025
1069
|
settings.errorCapture === BrowserErrorCaptureEnum.tryAndCatch;
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1070
|
+
|
1071
|
+
const id = TIMER.setTimeout(
|
1072
|
+
() => {
|
1073
|
+
if (useTryCatch) {
|
1074
|
+
WindowErrorUtility.captureError(this, () => callback(...args));
|
1075
|
+
} else {
|
1076
|
+
callback(...args);
|
1077
|
+
}
|
1078
|
+
this.#browserFrame[PropertySymbol.asyncTaskManager].endTimer(id);
|
1079
|
+
},
|
1080
|
+
settings?.timer.maxTimeout !== -1 && delay && delay > settings?.timer.maxTimeout
|
1081
|
+
? settings?.timer.maxTimeout
|
1082
|
+
: delay
|
1083
|
+
);
|
1034
1084
|
this.#browserFrame[PropertySymbol.asyncTaskManager].startTimer(id);
|
1035
1085
|
return id;
|
1036
1086
|
}
|
@@ -1041,6 +1091,14 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
1041
1091
|
* @param id ID of the timeout.
|
1042
1092
|
*/
|
1043
1093
|
public clearTimeout(id: NodeJS.Timeout): void {
|
1094
|
+
if (id && id instanceof Timeout) {
|
1095
|
+
const zeroTimeouts = this.#zeroTimeouts || [];
|
1096
|
+
const index = zeroTimeouts.indexOf(<Timeout>(<unknown>id));
|
1097
|
+
if (index !== -1) {
|
1098
|
+
zeroTimeouts.splice(index, 1);
|
1099
|
+
}
|
1100
|
+
return;
|
1101
|
+
}
|
1044
1102
|
// We need to make sure that the ID is a Timeout object, otherwise Node.js might throw an error.
|
1045
1103
|
// This is only necessary if we are in a Node.js environment.
|
1046
1104
|
if (IS_NODE_JS_TIMEOUT_ENVIRONMENT && (!id || id.constructor.name !== 'Timeout')) {
|
@@ -1064,17 +1122,29 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
1064
1122
|
!settings ||
|
1065
1123
|
!settings.disableErrorCapturing ||
|
1066
1124
|
settings.errorCapture === BrowserErrorCaptureEnum.tryAndCatch;
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
(
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1125
|
+
let iterations = 0;
|
1126
|
+
const id = TIMER.setInterval(
|
1127
|
+
() => {
|
1128
|
+
if (useTryCatch) {
|
1129
|
+
WindowErrorUtility.captureError(
|
1130
|
+
this,
|
1131
|
+
() => callback(...args),
|
1132
|
+
() => this.clearInterval(id)
|
1133
|
+
);
|
1134
|
+
} else {
|
1135
|
+
callback(...args);
|
1136
|
+
}
|
1137
|
+
if (settings?.timer.maxIntervalIterations !== -1) {
|
1138
|
+
if (iterations >= settings?.timer.maxIntervalIterations) {
|
1139
|
+
this.clearInterval(id);
|
1140
|
+
}
|
1141
|
+
iterations++;
|
1142
|
+
}
|
1143
|
+
},
|
1144
|
+
settings?.timer.maxIntervalTime !== -1 && delay && delay > settings?.timer.maxIntervalTime
|
1145
|
+
? settings?.timer.maxIntervalTime
|
1146
|
+
: delay
|
1147
|
+
);
|
1078
1148
|
this.#browserFrame[PropertySymbol.asyncTaskManager].startTimer(id);
|
1079
1149
|
return id;
|
1080
1150
|
}
|
@@ -1319,6 +1389,8 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
1319
1389
|
mutationObserver.disconnect();
|
1320
1390
|
}
|
1321
1391
|
|
1392
|
+
this[PropertySymbol.mutationObservers] = [];
|
1393
|
+
|
1322
1394
|
// Disconnects nodes from the document, so that they can be garbage collected.
|
1323
1395
|
for (const node of this.document[PropertySymbol.childNodes].slice()) {
|
1324
1396
|
// Makes sure that something won't be triggered by the disconnect.
|