happy-dom 19.0.1 → 20.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.
- package/lib/PropertySymbol.d.ts +1 -0
- package/lib/PropertySymbol.d.ts.map +1 -1
- package/lib/PropertySymbol.js +1 -0
- package/lib/PropertySymbol.js.map +1 -1
- package/lib/browser/DefaultBrowserSettings.d.ts.map +1 -1
- package/lib/browser/DefaultBrowserSettings.js +2 -0
- package/lib/browser/DefaultBrowserSettings.js.map +1 -1
- package/lib/browser/types/IBrowserSettings.d.ts +16 -1
- package/lib/browser/types/IBrowserSettings.d.ts.map +1 -1
- package/lib/browser/types/IOptionalBrowserSettings.d.ts +16 -1
- package/lib/browser/types/IOptionalBrowserSettings.d.ts.map +1 -1
- package/lib/browser/utilities/BrowserFrameNavigator.js +3 -3
- package/lib/browser/utilities/BrowserFrameNavigator.js.map +1 -1
- package/lib/html-parser/HTMLParser.d.ts.map +1 -1
- package/lib/html-parser/HTMLParser.js +3 -2
- package/lib/html-parser/HTMLParser.js.map +1 -1
- package/lib/module/ECMAScriptModuleCompiler.d.ts.map +1 -1
- package/lib/module/ECMAScriptModuleCompiler.js +6 -2
- package/lib/module/ECMAScriptModuleCompiler.js.map +1 -1
- package/lib/nodes/document/Document.d.ts +2 -2
- package/lib/nodes/document/Document.js +1 -1
- package/lib/nodes/element/ElementEventAttributeUtility.d.ts.map +1 -1
- package/lib/nodes/element/ElementEventAttributeUtility.js +5 -4
- package/lib/nodes/element/ElementEventAttributeUtility.js.map +1 -1
- package/lib/nodes/html-link-element/HTMLLinkElement.d.ts +1 -1
- package/lib/nodes/html-link-element/HTMLLinkElement.d.ts.map +1 -1
- package/lib/nodes/html-link-element/HTMLLinkElement.js +4 -4
- package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
- package/lib/nodes/html-media-element/TextTrackList.d.ts +1 -0
- package/lib/nodes/html-media-element/TextTrackList.d.ts.map +1 -1
- package/lib/nodes/html-media-element/TextTrackList.js.map +1 -1
- package/lib/nodes/html-script-element/HTMLScriptElement.d.ts +1 -1
- package/lib/nodes/html-script-element/HTMLScriptElement.d.ts.map +1 -1
- package/lib/nodes/html-script-element/HTMLScriptElement.js +47 -37
- package/lib/nodes/html-script-element/HTMLScriptElement.js.map +1 -1
- package/lib/nodes/html-select-element/HTMLSelectElement.d.ts +1 -0
- package/lib/nodes/html-select-element/HTMLSelectElement.d.ts.map +1 -1
- package/lib/nodes/html-select-element/HTMLSelectElement.js.map +1 -1
- package/lib/query-selector/QuerySelector.d.ts.map +1 -1
- package/lib/query-selector/QuerySelector.js +13 -3
- package/lib/query-selector/QuerySelector.js.map +1 -1
- package/lib/query-selector/SelectorItem.d.ts +3 -3
- package/lib/query-selector/SelectorItem.d.ts.map +1 -1
- package/lib/query-selector/SelectorItem.js +4 -2
- package/lib/query-selector/SelectorItem.js.map +1 -1
- package/lib/query-selector/SelectorParser.d.ts +6 -7
- package/lib/query-selector/SelectorParser.d.ts.map +1 -1
- package/lib/query-selector/SelectorParser.js +4 -4
- package/lib/query-selector/SelectorParser.js.map +1 -1
- package/lib/version.js +1 -1
- package/lib/window/BrowserWindow.d.ts +11 -0
- package/lib/window/BrowserWindow.d.ts.map +1 -1
- package/lib/window/BrowserWindow.js +31 -0
- package/lib/window/BrowserWindow.js.map +1 -1
- package/lib/window/GlobalWindow.d.ts +7 -1
- package/lib/window/GlobalWindow.d.ts.map +1 -1
- package/lib/window/GlobalWindow.js +10 -1
- package/lib/window/GlobalWindow.js.map +1 -1
- package/lib/xml-parser/XMLParser.d.ts +0 -3
- package/lib/xml-parser/XMLParser.d.ts.map +1 -1
- package/lib/xml-parser/XMLParser.js +0 -3
- package/lib/xml-parser/XMLParser.js.map +1 -1
- package/package.json +1 -1
- package/src/PropertySymbol.ts +1 -0
- package/src/browser/DefaultBrowserSettings.ts +2 -0
- package/src/browser/types/IBrowserSettings.ts +18 -1
- package/src/browser/types/IOptionalBrowserSettings.ts +18 -1
- package/src/browser/utilities/BrowserFrameNavigator.ts +3 -3
- package/src/html-parser/HTMLParser.ts +3 -2
- package/src/module/ECMAScriptModuleCompiler.ts +6 -2
- package/src/nodes/document/Document.ts +3 -3
- package/src/nodes/element/ElementEventAttributeUtility.ts +5 -4
- package/src/nodes/html-link-element/HTMLLinkElement.ts +4 -4
- package/src/nodes/html-media-element/TextTrackList.ts +3 -0
- package/src/nodes/html-script-element/HTMLScriptElement.ts +51 -42
- package/src/nodes/html-select-element/HTMLSelectElement.ts +3 -0
- package/src/query-selector/QuerySelector.ts +16 -3
- package/src/query-selector/SelectorItem.ts +6 -5
- package/src/query-selector/SelectorParser.ts +16 -8
- package/src/window/BrowserWindow.ts +38 -0
- package/src/window/GlobalWindow.ts +14 -1
- package/src/xml-parser/XMLParser.ts +0 -3
|
@@ -5,6 +5,7 @@ import IBrowserSettings from './types/IBrowserSettings.js';
|
|
|
5
5
|
|
|
6
6
|
export default <IBrowserSettings>{
|
|
7
7
|
disableJavaScriptEvaluation: false,
|
|
8
|
+
enableJavaScriptEvaluation: false,
|
|
8
9
|
disableJavaScriptFileLoading: false,
|
|
9
10
|
disableCSSFileLoading: false,
|
|
10
11
|
disableIframePageLoading: false,
|
|
@@ -13,6 +14,7 @@ export default <IBrowserSettings>{
|
|
|
13
14
|
disableErrorCapturing: false,
|
|
14
15
|
errorCapture: BrowserErrorCaptureEnum.tryAndCatch,
|
|
15
16
|
enableFileSystemHttpRequests: false,
|
|
17
|
+
suppressCodeGenerationFromStringsWarning: false,
|
|
16
18
|
timer: {
|
|
17
19
|
maxTimeout: -1,
|
|
18
20
|
maxIntervalTime: -1,
|
|
@@ -11,9 +11,23 @@ import BrowserWindow from '../../window/BrowserWindow.js';
|
|
|
11
11
|
* Browser settings.
|
|
12
12
|
*/
|
|
13
13
|
export default interface IBrowserSettings {
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Disables JavaScript evaluation.
|
|
16
|
+
*
|
|
17
|
+
* @deprecated Javascript evaluation is now disabled by default. Use "enableJavaScriptEvaluation" if you want to enable it.
|
|
18
|
+
*/
|
|
15
19
|
disableJavaScriptEvaluation: boolean;
|
|
16
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Enables JavaScript evaluation.
|
|
23
|
+
*
|
|
24
|
+
* A VM Context is not an isolated environment, and if you run untrusted code you are at risk of RCE (Remote Code Execution) attacks.
|
|
25
|
+
* It is recommended to disable code generation at process level by running node with the "--disallow-code-generation-from-strings" flag enabled to protect against these types of attacks.
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.com/capricorn86/happy-dom/wiki/Code-Generation-From-Strings-Warning
|
|
28
|
+
*/
|
|
29
|
+
enableJavaScriptEvaluation: boolean;
|
|
30
|
+
|
|
17
31
|
/** Disables JavaScript file loading. */
|
|
18
32
|
disableJavaScriptFileLoading: boolean;
|
|
19
33
|
|
|
@@ -26,6 +40,9 @@ export default interface IBrowserSettings {
|
|
|
26
40
|
/** Handle disabled resource loading as success */
|
|
27
41
|
handleDisabledFileLoadingAsSuccess: boolean;
|
|
28
42
|
|
|
43
|
+
/** Suppresses the warning that is printed when code generation from strings is enabled at process level. */
|
|
44
|
+
suppressCodeGenerationFromStringsWarning: boolean;
|
|
45
|
+
|
|
29
46
|
/**
|
|
30
47
|
* Settings for timers
|
|
31
48
|
*/
|
|
@@ -8,9 +8,23 @@ import IOptionalTimerLoopsLimit from '../../window/IOptionalTimerLoopsLimit.js';
|
|
|
8
8
|
import BrowserWindow from '../../window/BrowserWindow.js';
|
|
9
9
|
|
|
10
10
|
export default interface IOptionalBrowserSettings {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Disables JavaScript evaluation.
|
|
13
|
+
*
|
|
14
|
+
* @deprecated Javascript evaluation is now disabled by default. Use "enableJavaScriptEvaluation" if you want to enable it.
|
|
15
|
+
*/
|
|
12
16
|
disableJavaScriptEvaluation?: boolean;
|
|
13
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Enables JavaScript evaluation.
|
|
20
|
+
*
|
|
21
|
+
* A VM Context is not an isolated environment, and if you run untrusted code you are at risk of RCE (Remote Code Execution) attacks.
|
|
22
|
+
* It is recommended to disable code generation at process level by running node with the "--disallow-code-generation-from-strings" flag enabled to protect against these types of attacks.
|
|
23
|
+
*
|
|
24
|
+
* @see https://github.com/capricorn86/happy-dom/wiki/Code-Generation-From-Strings-Warning
|
|
25
|
+
*/
|
|
26
|
+
enableJavaScriptEvaluation?: boolean;
|
|
27
|
+
|
|
14
28
|
/** Disables JavaScript file loading. */
|
|
15
29
|
disableJavaScriptFileLoading?: boolean;
|
|
16
30
|
|
|
@@ -23,6 +37,9 @@ export default interface IOptionalBrowserSettings {
|
|
|
23
37
|
/** Handle disabled file loading as success */
|
|
24
38
|
handleDisabledFileLoadingAsSuccess?: boolean;
|
|
25
39
|
|
|
40
|
+
/** Suppresses the warning that is printed when code generation from strings is enabled at process level. */
|
|
41
|
+
suppressCodeGenerationFromStringsWarning?: boolean;
|
|
42
|
+
|
|
26
43
|
/** Settings for timers */
|
|
27
44
|
timer?: {
|
|
28
45
|
maxTimeout?: number;
|
|
@@ -89,12 +89,12 @@ export default class BrowserFrameNavigator {
|
|
|
89
89
|
|
|
90
90
|
// Javascript protocol
|
|
91
91
|
if (targetURL.protocol === 'javascript:') {
|
|
92
|
-
if (frame &&
|
|
92
|
+
if (frame && frame.page.context.browser.settings.enableJavaScriptEvaluation) {
|
|
93
93
|
const readyStateManager = frame.window[PropertySymbol.readyStateManager];
|
|
94
94
|
const asyncTaskManager = frame[PropertySymbol.asyncTaskManager];
|
|
95
95
|
|
|
96
96
|
const taskID = readyStateManager.startTask();
|
|
97
|
-
const code =
|
|
97
|
+
const code = targetURL.href.replace('javascript:', '');
|
|
98
98
|
|
|
99
99
|
// The browser will wait for the next tick before executing the script.
|
|
100
100
|
// Fixes issue where evaluating the response can throw an error.
|
|
@@ -110,7 +110,7 @@ export default class BrowserFrameNavigator {
|
|
|
110
110
|
clearImmediate(immediate);
|
|
111
111
|
resolve(null);
|
|
112
112
|
});
|
|
113
|
-
frame.window.
|
|
113
|
+
frame.window[PropertySymbol.evaluateScript](code, { filename: frame.url });
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
116
|
|
|
@@ -776,10 +776,11 @@ export default class HTMLParser {
|
|
|
776
776
|
// However, they are allowed to be executed when document.write() is used.
|
|
777
777
|
// See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement
|
|
778
778
|
if (upperTagName === 'SCRIPT') {
|
|
779
|
-
(<HTMLScriptElement>this.currentNode)[PropertySymbol.
|
|
779
|
+
(<HTMLScriptElement>this.currentNode)[PropertySymbol.disableEvaluation] =
|
|
780
|
+
!this.evaluateScripts;
|
|
780
781
|
} else if (upperTagName === 'LINK') {
|
|
781
782
|
// An assumption that the same rule should be applied for the HTMLLinkElement is made here.
|
|
782
|
-
(<HTMLLinkElement>this.currentNode)[PropertySymbol.
|
|
783
|
+
(<HTMLLinkElement>this.currentNode)[PropertySymbol.disableEvaluation] = !this.evaluateScripts;
|
|
783
784
|
}
|
|
784
785
|
|
|
785
786
|
// Plain text elements such as <script> and <style> should only contain text.
|
|
@@ -401,10 +401,14 @@ export default class ECMAScriptModuleCompiler {
|
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
newCode += '})';
|
|
404
|
-
newCode += `\n//# sourceURL=${sourceURL || moduleURL}`;
|
|
405
404
|
|
|
406
405
|
try {
|
|
407
|
-
return {
|
|
406
|
+
return {
|
|
407
|
+
imports,
|
|
408
|
+
execute: this.window[PropertySymbol.evaluateScript](newCode, {
|
|
409
|
+
filename: sourceURL || moduleURL
|
|
410
|
+
})
|
|
411
|
+
};
|
|
408
412
|
} catch (e) {
|
|
409
413
|
const errorMessage = this.getError(moduleURL, code, sourceURL) || (<Error>e).message;
|
|
410
414
|
const error = new this.window.SyntaxError(
|
|
@@ -1472,7 +1472,7 @@ export default class Document extends Node {
|
|
|
1472
1472
|
): NodeList<IHTMLElementTagNameMap[K]>;
|
|
1473
1473
|
|
|
1474
1474
|
/**
|
|
1475
|
-
* Query CSS selector to find matching
|
|
1475
|
+
* Query CSS selector to find matching elements.
|
|
1476
1476
|
*
|
|
1477
1477
|
* @param selector CSS selector.
|
|
1478
1478
|
* @returns Matching elements.
|
|
@@ -1482,7 +1482,7 @@ export default class Document extends Node {
|
|
|
1482
1482
|
): NodeList<ISVGElementTagNameMap[K]>;
|
|
1483
1483
|
|
|
1484
1484
|
/**
|
|
1485
|
-
* Query CSS selector to find matching
|
|
1485
|
+
* Query CSS selector to find matching elements.
|
|
1486
1486
|
*
|
|
1487
1487
|
* @param selector CSS selector.
|
|
1488
1488
|
* @returns Matching elements.
|
|
@@ -1490,7 +1490,7 @@ export default class Document extends Node {
|
|
|
1490
1490
|
public querySelectorAll(selector: string): NodeList<Element>;
|
|
1491
1491
|
|
|
1492
1492
|
/**
|
|
1493
|
-
* Query CSS selector to find matching
|
|
1493
|
+
* Query CSS selector to find matching elements.
|
|
1494
1494
|
*
|
|
1495
1495
|
* @param selector CSS selector.
|
|
1496
1496
|
* @returns Matching elements.
|
|
@@ -32,7 +32,7 @@ export default class ElementEventAttributeUtility {
|
|
|
32
32
|
|
|
33
33
|
const browserSettings = new WindowBrowserContext(window).getSettings();
|
|
34
34
|
|
|
35
|
-
if (!browserSettings) {
|
|
35
|
+
if (!browserSettings || !browserSettings.enableJavaScriptEvaluation) {
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -63,13 +63,14 @@ export default class ElementEventAttributeUtility {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
newCode += '})';
|
|
66
|
-
newCode += `\n//# sourceURL=${window.location.href}`;
|
|
67
66
|
|
|
68
67
|
let listener: ((event: Event) => void) | null = null;
|
|
69
68
|
|
|
70
69
|
try {
|
|
71
|
-
listener = window.
|
|
72
|
-
|
|
70
|
+
listener = window[PropertySymbol.evaluateScript](newCode, {
|
|
71
|
+
filename: window.location.href
|
|
72
|
+
}).bind(element, {
|
|
73
|
+
dispatchError: window[PropertySymbol.dispatchError].bind(window)
|
|
73
74
|
});
|
|
74
75
|
} catch (e) {
|
|
75
76
|
const error = new window.SyntaxError(
|
|
@@ -24,7 +24,7 @@ import IResourceFetchResponse from '../../fetch/types/IResourceFetchResponse.js'
|
|
|
24
24
|
export default class HTMLLinkElement extends HTMLElement {
|
|
25
25
|
// Internal properties
|
|
26
26
|
public [PropertySymbol.sheet]: CSSStyleSheet | null = null;
|
|
27
|
-
public [PropertySymbol.
|
|
27
|
+
public [PropertySymbol.disableEvaluation] = false;
|
|
28
28
|
public [PropertySymbol.relList]: DOMTokenList | null = null;
|
|
29
29
|
#loadedStyleSheetURL: string | null = null;
|
|
30
30
|
|
|
@@ -305,7 +305,7 @@ export default class HTMLLinkElement extends HTMLElement {
|
|
|
305
305
|
!browserSettings ||
|
|
306
306
|
!this[PropertySymbol.isConnected] ||
|
|
307
307
|
browserSettings.disableJavaScriptFileLoading ||
|
|
308
|
-
browserSettings.
|
|
308
|
+
!browserSettings.enableJavaScriptEvaluation
|
|
309
309
|
) {
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
@@ -351,7 +351,7 @@ export default class HTMLLinkElement extends HTMLElement {
|
|
|
351
351
|
|
|
352
352
|
if (
|
|
353
353
|
as === 'script' &&
|
|
354
|
-
(browserSettings.disableJavaScriptFileLoading || browserSettings.
|
|
354
|
+
(browserSettings.disableJavaScriptFileLoading || !browserSettings.enableJavaScriptEvaluation)
|
|
355
355
|
) {
|
|
356
356
|
return;
|
|
357
357
|
}
|
|
@@ -422,7 +422,7 @@ export default class HTMLLinkElement extends HTMLElement {
|
|
|
422
422
|
|
|
423
423
|
const browserSettings = browserFrame.page.context.browser.settings;
|
|
424
424
|
|
|
425
|
-
if (
|
|
425
|
+
if (this[PropertySymbol.disableEvaluation] || !this[PropertySymbol.isConnected]) {
|
|
426
426
|
return;
|
|
427
427
|
}
|
|
428
428
|
|
|
@@ -9,6 +9,9 @@ import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';
|
|
|
9
9
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrackList
|
|
10
10
|
*/
|
|
11
11
|
export default class TextTrackList extends EventTarget {
|
|
12
|
+
// Index signature
|
|
13
|
+
[index: number]: TextTrack | undefined;
|
|
14
|
+
|
|
12
15
|
// Internal properties
|
|
13
16
|
public [PropertySymbol.items]: TextTrack[] = [];
|
|
14
17
|
|
|
@@ -25,7 +25,7 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
25
25
|
public declare cloneNode: (deep?: boolean) => HTMLScriptElement;
|
|
26
26
|
|
|
27
27
|
// Internal properties
|
|
28
|
-
public [PropertySymbol.
|
|
28
|
+
public [PropertySymbol.disableEvaluation] = false;
|
|
29
29
|
public [PropertySymbol.blocking]: DOMTokenList | null = null;
|
|
30
30
|
|
|
31
31
|
// Private properties
|
|
@@ -349,32 +349,34 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
349
349
|
|
|
350
350
|
super[PropertySymbol.connectedToDocument]();
|
|
351
351
|
|
|
352
|
-
if (this[PropertySymbol.
|
|
353
|
-
|
|
352
|
+
if (this[PropertySymbol.disableEvaluation]) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
354
355
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
356
|
+
const src = this.getAttribute('src');
|
|
357
|
+
|
|
358
|
+
if (src !== null) {
|
|
359
|
+
if (this.getAttribute('type') === 'module') {
|
|
360
|
+
this.#loadModule(src);
|
|
361
|
+
} else {
|
|
362
|
+
this.#loadScript(src);
|
|
363
|
+
}
|
|
364
|
+
} else if (browserSettings && browserSettings.enableJavaScriptEvaluation) {
|
|
365
|
+
const source = this.textContent;
|
|
366
|
+
const type = this.getAttribute('type');
|
|
367
|
+
|
|
368
|
+
if (source) {
|
|
369
|
+
if (type === 'module') {
|
|
370
|
+
this.#evaluateModule(source);
|
|
371
|
+
} else if (type === 'importmap') {
|
|
372
|
+
this.#evaluateImportMap(source);
|
|
373
|
+
} else if (
|
|
374
|
+
type === null ||
|
|
375
|
+
type === 'application/x-ecmascript' ||
|
|
376
|
+
type === 'application/x-javascript' ||
|
|
377
|
+
type.startsWith('text/javascript')
|
|
378
|
+
) {
|
|
379
|
+
this.#evaluateScript(source);
|
|
378
380
|
}
|
|
379
381
|
}
|
|
380
382
|
}
|
|
@@ -413,7 +415,7 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
413
415
|
const browserSettings = new WindowBrowserContext(window).getSettings();
|
|
414
416
|
const browserFrame = new WindowBrowserContext(window).getBrowserFrame();
|
|
415
417
|
|
|
416
|
-
if (!browserFrame || !browserSettings) {
|
|
418
|
+
if (!browserFrame || !browserSettings || !browserSettings.enableJavaScriptEvaluation) {
|
|
417
419
|
return;
|
|
418
420
|
}
|
|
419
421
|
|
|
@@ -446,7 +448,12 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
446
448
|
const browserSettings = new WindowBrowserContext(window).getSettings();
|
|
447
449
|
const browserFrame = new WindowBrowserContext(window).getBrowserFrame();
|
|
448
450
|
|
|
449
|
-
if (
|
|
451
|
+
if (
|
|
452
|
+
!browserFrame ||
|
|
453
|
+
!browserSettings ||
|
|
454
|
+
window[PropertySymbol.moduleImportMap] ||
|
|
455
|
+
!browserSettings.enableJavaScriptEvaluation
|
|
456
|
+
) {
|
|
450
457
|
return;
|
|
451
458
|
}
|
|
452
459
|
|
|
@@ -510,9 +517,9 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
510
517
|
/**
|
|
511
518
|
* Evaluates a script.
|
|
512
519
|
*
|
|
513
|
-
* @param
|
|
520
|
+
* @param code Code.
|
|
514
521
|
*/
|
|
515
|
-
#evaluateScript(
|
|
522
|
+
#evaluateScript(code: string): void {
|
|
516
523
|
const window = this[PropertySymbol.window];
|
|
517
524
|
const browserSettings = new WindowBrowserContext(window).getSettings();
|
|
518
525
|
|
|
@@ -522,16 +529,18 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
522
529
|
|
|
523
530
|
this[PropertySymbol.ownerDocument][PropertySymbol.currentScript] = this;
|
|
524
531
|
|
|
525
|
-
const code = `${source}\n//# sourceURL=${this[PropertySymbol.ownerDocument].location.href}`;
|
|
526
|
-
|
|
527
532
|
if (
|
|
528
533
|
browserSettings.disableErrorCapturing ||
|
|
529
534
|
browserSettings.errorCapture !== BrowserErrorCaptureEnum.tryAndCatch
|
|
530
535
|
) {
|
|
531
|
-
window.
|
|
536
|
+
window[PropertySymbol.evaluateScript](code, {
|
|
537
|
+
filename: this[PropertySymbol.ownerDocument].location.href
|
|
538
|
+
});
|
|
532
539
|
} else {
|
|
533
540
|
try {
|
|
534
|
-
window.
|
|
541
|
+
window[PropertySymbol.evaluateScript](code, {
|
|
542
|
+
filename: this[PropertySymbol.ownerDocument].location.href
|
|
543
|
+
});
|
|
535
544
|
} catch (error) {
|
|
536
545
|
window[PropertySymbol.dispatchError](<Error>error);
|
|
537
546
|
}
|
|
@@ -560,7 +569,7 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
560
569
|
|
|
561
570
|
if (
|
|
562
571
|
browserSettings &&
|
|
563
|
-
(browserSettings.disableJavaScriptFileLoading || browserSettings.
|
|
572
|
+
(browserSettings.disableJavaScriptFileLoading || !browserSettings.enableJavaScriptEvaluation)
|
|
564
573
|
) {
|
|
565
574
|
if (browserSettings.handleDisabledFileLoadingAsSuccess) {
|
|
566
575
|
this.dispatchEvent(new Event('load'));
|
|
@@ -639,7 +648,7 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
639
648
|
|
|
640
649
|
if (
|
|
641
650
|
browserSettings &&
|
|
642
|
-
(browserSettings.disableJavaScriptFileLoading || browserSettings.
|
|
651
|
+
(browserSettings.disableJavaScriptFileLoading || !browserSettings.enableJavaScriptEvaluation)
|
|
643
652
|
) {
|
|
644
653
|
if (browserSettings.handleDisabledFileLoadingAsSuccess) {
|
|
645
654
|
this.dispatchEvent(new Event('load'));
|
|
@@ -693,18 +702,18 @@ export default class HTMLScriptElement extends HTMLElement {
|
|
|
693
702
|
|
|
694
703
|
this[PropertySymbol.ownerDocument][PropertySymbol.currentScript] = this;
|
|
695
704
|
|
|
696
|
-
const code = `${response.content}\n//# sourceURL=${
|
|
697
|
-
response.virtualServerFile || absoluteURLString
|
|
698
|
-
}`;
|
|
699
|
-
|
|
700
705
|
if (
|
|
701
706
|
browserSettings.disableErrorCapturing ||
|
|
702
707
|
browserSettings.errorCapture !== BrowserErrorCaptureEnum.tryAndCatch
|
|
703
708
|
) {
|
|
704
|
-
this[PropertySymbol.window].
|
|
709
|
+
this[PropertySymbol.window][PropertySymbol.evaluateScript](response.content, {
|
|
710
|
+
filename: response.virtualServerFile || absoluteURLString
|
|
711
|
+
});
|
|
705
712
|
} else {
|
|
706
713
|
try {
|
|
707
|
-
this[PropertySymbol.window].
|
|
714
|
+
this[PropertySymbol.window][PropertySymbol.evaluateScript](response.content, {
|
|
715
|
+
filename: response.virtualServerFile || absoluteURLString
|
|
716
|
+
});
|
|
708
717
|
} catch (error) {
|
|
709
718
|
this[PropertySymbol.ownerDocument][PropertySymbol.currentScript] = null;
|
|
710
719
|
window[PropertySymbol.dispatchError](<Error>error);
|
|
@@ -25,6 +25,9 @@ import BrowserWindow from '../../window/BrowserWindow.js';
|
|
|
25
25
|
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement.
|
|
26
26
|
*/
|
|
27
27
|
export default class HTMLSelectElement extends HTMLElement {
|
|
28
|
+
// Index signature
|
|
29
|
+
[index: number]: HTMLOptionElement | undefined;
|
|
30
|
+
|
|
28
31
|
// Injected by WindowContextClassExtender
|
|
29
32
|
protected declare [PropertySymbol.window]: BrowserWindow;
|
|
30
33
|
|
|
@@ -115,7 +115,11 @@ export default class QuerySelector {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const
|
|
118
|
+
const scope =
|
|
119
|
+
node[PropertySymbol.nodeType] === NodeTypeEnum.documentNode
|
|
120
|
+
? (<Document>node).documentElement
|
|
121
|
+
: node;
|
|
122
|
+
const groups = SelectorParser.getSelectorGroups(selector, { scope });
|
|
119
123
|
const items: Element[] = [];
|
|
120
124
|
const nodeList = new NodeList<Element>(PropertySymbol.illegalConstructor, items);
|
|
121
125
|
const matchesMap: Map<string, Element> = new Map();
|
|
@@ -251,7 +255,11 @@ export default class QuerySelector {
|
|
|
251
255
|
|
|
252
256
|
const matchesMap: Map<string, Element> = new Map();
|
|
253
257
|
const matchedPositions: string[] = [];
|
|
254
|
-
|
|
258
|
+
const scope =
|
|
259
|
+
node[PropertySymbol.nodeType] === NodeTypeEnum.documentNode
|
|
260
|
+
? (<Document>node).documentElement
|
|
261
|
+
: node;
|
|
262
|
+
for (const items of SelectorParser.getSelectorGroups(selector, { scope })) {
|
|
255
263
|
const match =
|
|
256
264
|
node[PropertySymbol.nodeType] === NodeTypeEnum.elementNode
|
|
257
265
|
? this.findFirst(<Element>node, [<Element>node], items, cachedItem)
|
|
@@ -350,9 +358,14 @@ export default class QuerySelector {
|
|
|
350
358
|
);
|
|
351
359
|
}
|
|
352
360
|
|
|
361
|
+
const scopeOrElement = options?.scope || element;
|
|
362
|
+
const scope =
|
|
363
|
+
scopeOrElement[PropertySymbol.nodeType] === NodeTypeEnum.documentNode
|
|
364
|
+
? (<Document>scopeOrElement).documentElement
|
|
365
|
+
: scopeOrElement;
|
|
353
366
|
for (const items of SelectorParser.getSelectorGroups(selector, {
|
|
354
367
|
...options,
|
|
355
|
-
scope
|
|
368
|
+
scope
|
|
356
369
|
})) {
|
|
357
370
|
const result = this.matchSelector(element, items.reverse(), cachedItem);
|
|
358
371
|
|
|
@@ -5,7 +5,6 @@ import SelectorCombinatorEnum from './SelectorCombinatorEnum.js';
|
|
|
5
5
|
import ISelectorAttribute from './ISelectorAttribute.js';
|
|
6
6
|
import ISelectorMatch from './ISelectorMatch.js';
|
|
7
7
|
import ISelectorPseudo from './ISelectorPseudo.js';
|
|
8
|
-
import Document from '../nodes/document/Document.js';
|
|
9
8
|
import DocumentFragment from '../nodes/document-fragment/DocumentFragment.js';
|
|
10
9
|
|
|
11
10
|
const SPACE_REGEXP = /\s+/;
|
|
@@ -14,7 +13,8 @@ const SPACE_REGEXP = /\s+/;
|
|
|
14
13
|
* Selector item.
|
|
15
14
|
*/
|
|
16
15
|
export default class SelectorItem {
|
|
17
|
-
public
|
|
16
|
+
public root: Element | DocumentFragment | null;
|
|
17
|
+
public scope: Element | DocumentFragment | null;
|
|
18
18
|
public tagName: string | null;
|
|
19
19
|
public id: string | null;
|
|
20
20
|
public classNames: string[] | null;
|
|
@@ -39,7 +39,7 @@ export default class SelectorItem {
|
|
|
39
39
|
* @param [options.ignoreErrors] Ignore errors.
|
|
40
40
|
*/
|
|
41
41
|
constructor(options?: {
|
|
42
|
-
scope?: Element |
|
|
42
|
+
scope?: Element | DocumentFragment;
|
|
43
43
|
tagName?: string;
|
|
44
44
|
id?: string;
|
|
45
45
|
classNames?: string[];
|
|
@@ -49,6 +49,7 @@ export default class SelectorItem {
|
|
|
49
49
|
combinator?: SelectorCombinatorEnum;
|
|
50
50
|
ignoreErrors?: boolean;
|
|
51
51
|
}) {
|
|
52
|
+
this.root = options?.scope ? options.scope[PropertySymbol.ownerDocument].documentElement : null;
|
|
52
53
|
this.scope = options?.scope || null;
|
|
53
54
|
this.tagName = options?.tagName || null;
|
|
54
55
|
this.id = options?.id || null;
|
|
@@ -251,7 +252,7 @@ export default class SelectorItem {
|
|
|
251
252
|
? { priorityWeight: 10 }
|
|
252
253
|
: null;
|
|
253
254
|
case 'root':
|
|
254
|
-
return
|
|
255
|
+
return this.root && element === this.root ? { priorityWeight: 10 } : null;
|
|
255
256
|
case 'not':
|
|
256
257
|
for (const selectorItem of pseudo.selectorItems!) {
|
|
257
258
|
if (selectorItem.match(element)) {
|
|
@@ -385,7 +386,7 @@ export default class SelectorItem {
|
|
|
385
386
|
? { priorityWeight: 10 }
|
|
386
387
|
: null;
|
|
387
388
|
case 'scope':
|
|
388
|
-
return this.scope === element ? { priorityWeight: 10 } : null;
|
|
389
|
+
return this.scope && this.scope === element ? { priorityWeight: 10 } : null;
|
|
389
390
|
default:
|
|
390
391
|
return null;
|
|
391
392
|
}
|
|
@@ -3,7 +3,6 @@ import SelectorCombinatorEnum from './SelectorCombinatorEnum.js';
|
|
|
3
3
|
import DOMException from '../exception/DOMException.js';
|
|
4
4
|
import ISelectorPseudo from './ISelectorPseudo.js';
|
|
5
5
|
import Element from '../nodes/element/Element.js';
|
|
6
|
-
import Document from '../nodes/document/Document.js';
|
|
7
6
|
import DocumentFragment from '../nodes/document-fragment/DocumentFragment.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -72,13 +71,16 @@ export default class SelectorParser {
|
|
|
72
71
|
*
|
|
73
72
|
* @param selector Selector.
|
|
74
73
|
* @param options Options.
|
|
75
|
-
* @param options.scope Scope.
|
|
74
|
+
* @param [options.scope] Scope.
|
|
76
75
|
* @param [options.ignoreErrors] Ignores errors.
|
|
77
76
|
* @returns Selector item.
|
|
78
77
|
*/
|
|
79
78
|
public static getSelectorItem(
|
|
80
79
|
selector: string,
|
|
81
|
-
options?: {
|
|
80
|
+
options?: {
|
|
81
|
+
scope?: Element | DocumentFragment;
|
|
82
|
+
ignoreErrors?: boolean;
|
|
83
|
+
}
|
|
82
84
|
): SelectorItem {
|
|
83
85
|
return this.getSelectorGroups(selector, options)[0][0];
|
|
84
86
|
}
|
|
@@ -88,13 +90,16 @@ export default class SelectorParser {
|
|
|
88
90
|
*
|
|
89
91
|
* @param selector Selector.
|
|
90
92
|
* @param options Options.
|
|
91
|
-
* @param options.scope Scope.
|
|
93
|
+
* @param [options.scope] Scope.
|
|
92
94
|
* @param [options.ignoreErrors] Ignores errors.
|
|
93
95
|
* @returns Selector groups.
|
|
94
96
|
*/
|
|
95
97
|
public static getSelectorGroups(
|
|
96
98
|
selector: string,
|
|
97
|
-
options?: {
|
|
99
|
+
options?: {
|
|
100
|
+
scope?: Element | DocumentFragment;
|
|
101
|
+
ignoreErrors?: boolean;
|
|
102
|
+
}
|
|
98
103
|
): Array<Array<SelectorItem>> {
|
|
99
104
|
selector = selector.trim();
|
|
100
105
|
const ignoreErrors = options?.ignoreErrors;
|
|
@@ -296,15 +301,18 @@ export default class SelectorParser {
|
|
|
296
301
|
*
|
|
297
302
|
* @param name Pseudo name.
|
|
298
303
|
* @param args Pseudo arguments.
|
|
299
|
-
* @param options Options.
|
|
300
|
-
* @param options.scope Scope.
|
|
304
|
+
* @param [options] Options.
|
|
305
|
+
* @param [options.scope] Scope.
|
|
301
306
|
* @param [options.ignoreErrors] Ignores errors.
|
|
302
307
|
* @returns Pseudo.
|
|
303
308
|
*/
|
|
304
309
|
private static getPseudo(
|
|
305
310
|
name: string,
|
|
306
311
|
args: string | null | undefined,
|
|
307
|
-
options?: {
|
|
312
|
+
options?: {
|
|
313
|
+
scope?: Element | DocumentFragment;
|
|
314
|
+
ignoreErrors?: boolean;
|
|
315
|
+
}
|
|
308
316
|
): ISelectorPseudo {
|
|
309
317
|
const lowerName = name.toLowerCase();
|
|
310
318
|
|
|
@@ -331,6 +331,16 @@ const TIMER = {
|
|
|
331
331
|
|
|
332
332
|
const IS_NODE_JS_TIMEOUT_ENVIRONMENT = setTimeout.toString().includes('new Timeout');
|
|
333
333
|
|
|
334
|
+
const IS_PROCESS_LEVEL_CODE_GENERATION_FROM_STRINGS_ALLOWED = (() => {
|
|
335
|
+
try {
|
|
336
|
+
// eslint-disable-next-line no-new-func
|
|
337
|
+
new Function('return true;')();
|
|
338
|
+
return true;
|
|
339
|
+
} catch {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
})();
|
|
343
|
+
|
|
334
344
|
/**
|
|
335
345
|
* Class for PerformanceObserverEntryList as it is only available as an interface from Node.js.
|
|
336
346
|
*/
|
|
@@ -779,6 +789,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
|
779
789
|
public declare encodeURI: typeof encodeURI;
|
|
780
790
|
public declare encodeURIComponent: typeof encodeURIComponent;
|
|
781
791
|
public declare eval: typeof eval;
|
|
792
|
+
|
|
782
793
|
/**
|
|
783
794
|
* @deprecated
|
|
784
795
|
*/
|
|
@@ -847,6 +858,21 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
|
847
858
|
constructor(browserFrame: IBrowserFrame, options?: { url?: string }) {
|
|
848
859
|
super();
|
|
849
860
|
|
|
861
|
+
if (
|
|
862
|
+
IS_PROCESS_LEVEL_CODE_GENERATION_FROM_STRINGS_ALLOWED &&
|
|
863
|
+
browserFrame.page.context.browser.settings.enableJavaScriptEvaluation &&
|
|
864
|
+
!browserFrame.page.context.browser.settings.suppressCodeGenerationFromStringsWarning
|
|
865
|
+
) {
|
|
866
|
+
// eslint-disable-next-line no-console
|
|
867
|
+
console.warn(
|
|
868
|
+
'\nWarning! Happy DOM has JavaScript evaluation enabled and is running in an environment with code generation from strings (eval) enabled at process level.' +
|
|
869
|
+
'\n\nA VM Context is not an isolated environment, and if you run untrusted code you are at risk of RCE (Remote Code Execution) attacks. The attacker can use code generation to escape the VM and run code at process level.' +
|
|
870
|
+
'\n\nIt is recommended to disable code generation at process level by running node with the "--disallow-code-generation-from-strings" flag enabled when Javascript evaluation is enabled in Happy DOM.' +
|
|
871
|
+
' You can suppress this warning by setting "suppressCodeGenerationFromStringsWarning" to "true" at your own risk.' +
|
|
872
|
+
'\n\nFor more information, see https://github.com/capricorn86/happy-dom/wiki/Code-Generation-From-Strings-Warning\n\n'
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
|
|
850
876
|
this.#browserFrame = browserFrame;
|
|
851
877
|
|
|
852
878
|
this.console = browserFrame.page.console;
|
|
@@ -1806,6 +1832,18 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
|
|
|
1806
1832
|
this.dispatchEvent(new ErrorEvent('error', { message: error.message, error }));
|
|
1807
1833
|
}
|
|
1808
1834
|
|
|
1835
|
+
/**
|
|
1836
|
+
* Evaluates code in a VM context.
|
|
1837
|
+
*
|
|
1838
|
+
* @param code Code.
|
|
1839
|
+
* @param [options] Options.
|
|
1840
|
+
* @param [options.filename] Filename.
|
|
1841
|
+
* @returns any.
|
|
1842
|
+
*/
|
|
1843
|
+
public [PropertySymbol.evaluateScript](code: string, options?: { filename?: string }): any {
|
|
1844
|
+
return new VM.Script(code, options).runInContext(this);
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1809
1847
|
/**
|
|
1810
1848
|
* Setup of VM context.
|
|
1811
1849
|
*/
|