happy-dom 7.7.1 → 7.8.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 (109) hide show
  1. package/README.md +62 -27
  2. package/lib/async-task-manager/AsyncTaskManager.d.ts +3 -8
  3. package/lib/async-task-manager/AsyncTaskManager.js +21 -24
  4. package/lib/async-task-manager/AsyncTaskManager.js.map +1 -1
  5. package/lib/cookie/Cookie.d.ts +53 -0
  6. package/lib/cookie/Cookie.js +146 -0
  7. package/lib/cookie/Cookie.js.map +1 -0
  8. package/lib/cookie/CookieJar.d.ts +30 -0
  9. package/lib/cookie/CookieJar.js +84 -0
  10. package/lib/cookie/CookieJar.js.map +1 -0
  11. package/lib/event/IEventListener.d.ts +1 -1
  12. package/lib/exception/DOMExceptionNameEnum.d.ts +4 -1
  13. package/lib/exception/DOMExceptionNameEnum.js +3 -0
  14. package/lib/exception/DOMExceptionNameEnum.js.map +1 -1
  15. package/lib/fetch/FetchHandler.d.ts +3 -2
  16. package/lib/fetch/FetchHandler.js +31 -3
  17. package/lib/fetch/FetchHandler.js.map +1 -1
  18. package/lib/fetch/RequestInfo.d.ts +5 -0
  19. package/lib/fetch/RequestInfo.js +3 -0
  20. package/lib/fetch/RequestInfo.js.map +1 -0
  21. package/lib/fetch/ResourceFetchHandler.d.ts +2 -2
  22. package/lib/fetch/ResourceFetchHandler.js +9 -8
  23. package/lib/fetch/ResourceFetchHandler.js.map +1 -1
  24. package/lib/index.d.ts +1 -2
  25. package/lib/index.js +3 -4
  26. package/lib/index.js.map +1 -1
  27. package/lib/location/Location.d.ts +2 -1
  28. package/lib/location/Location.js +4 -7
  29. package/lib/location/Location.js.map +1 -1
  30. package/lib/location/RelativeURL.d.ts +3 -1
  31. package/lib/location/RelativeURL.js +2 -11
  32. package/lib/location/RelativeURL.js.map +1 -1
  33. package/lib/nodes/document/Document.d.ts +14 -3
  34. package/lib/nodes/document/Document.js +21 -6
  35. package/lib/nodes/document/Document.js.map +1 -1
  36. package/lib/nodes/document/IDocument.d.ts +2 -0
  37. package/lib/nodes/html-link-element/HTMLLinkElement.js +5 -2
  38. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  39. package/lib/nodes/html-script-element/HTMLScriptElement.js +1 -1
  40. package/lib/nodes/html-script-element/HTMLScriptElement.js.map +1 -1
  41. package/lib/nodes/html-script-element/ScriptUtility.js +4 -0
  42. package/lib/nodes/html-script-element/ScriptUtility.js.map +1 -1
  43. package/lib/window/IHappyDOMSettings.d.ts +9 -0
  44. package/lib/window/IHappyDOMSettings.js +3 -0
  45. package/lib/window/IHappyDOMSettings.js.map +1 -0
  46. package/lib/window/IWindow.d.ts +15 -6
  47. package/lib/window/Window.d.ts +18 -3
  48. package/lib/window/Window.js +26 -4
  49. package/lib/window/Window.js.map +1 -1
  50. package/lib/xml-http-request/XMLHttpRequest.d.ts +196 -0
  51. package/lib/xml-http-request/XMLHttpRequest.js +777 -0
  52. package/lib/xml-http-request/XMLHttpRequest.js.map +1 -0
  53. package/lib/xml-http-request/XMLHttpRequestCertificate.d.ts +5 -0
  54. package/lib/xml-http-request/XMLHttpRequestCertificate.js +55 -0
  55. package/lib/xml-http-request/XMLHttpRequestCertificate.js.map +1 -0
  56. package/lib/xml-http-request/XMLHttpRequestEventTarget.d.ts +15 -0
  57. package/lib/xml-http-request/XMLHttpRequestEventTarget.js +23 -0
  58. package/lib/xml-http-request/XMLHttpRequestEventTarget.js.map +1 -0
  59. package/lib/xml-http-request/XMLHttpRequestReadyStateEnum.d.ts +8 -0
  60. package/lib/xml-http-request/XMLHttpRequestReadyStateEnum.js +12 -0
  61. package/lib/xml-http-request/XMLHttpRequestReadyStateEnum.js.map +1 -0
  62. package/lib/xml-http-request/XMLHttpRequestUpload.d.ts +6 -0
  63. package/lib/xml-http-request/XMLHttpRequestUpload.js +13 -0
  64. package/lib/xml-http-request/XMLHttpRequestUpload.js.map +1 -0
  65. package/lib/xml-http-request/XMLHttpResponseTypeEnum.d.ts +8 -0
  66. package/lib/xml-http-request/XMLHttpResponseTypeEnum.js +12 -0
  67. package/lib/xml-http-request/XMLHttpResponseTypeEnum.js.map +1 -0
  68. package/lib/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.d.ts +15 -0
  69. package/lib/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.js +55 -0
  70. package/lib/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.js.map +1 -0
  71. package/lib/xml-http-request/utilities/XMLHttpRequestURLUtility.d.ts +35 -0
  72. package/lib/xml-http-request/utilities/XMLHttpRequestURLUtility.js +62 -0
  73. package/lib/xml-http-request/utilities/XMLHttpRequestURLUtility.js.map +1 -0
  74. package/package.json +2 -2
  75. package/src/async-task-manager/AsyncTaskManager.ts +24 -26
  76. package/src/cookie/Cookie.ts +158 -0
  77. package/src/cookie/CookieJar.ts +82 -0
  78. package/src/event/IEventListener.ts +1 -1
  79. package/src/exception/DOMExceptionNameEnum.ts +4 -1
  80. package/src/fetch/FetchHandler.ts +40 -4
  81. package/src/fetch/RequestInfo.ts +6 -0
  82. package/src/fetch/ResourceFetchHandler.ts +10 -8
  83. package/src/index.ts +1 -2
  84. package/src/location/Location.ts +3 -3
  85. package/src/location/RelativeURL.ts +3 -14
  86. package/src/nodes/document/Document.ts +24 -6
  87. package/src/nodes/document/IDocument.ts +2 -0
  88. package/src/nodes/html-link-element/HTMLLinkElement.ts +7 -2
  89. package/src/nodes/html-script-element/HTMLScriptElement.ts +1 -1
  90. package/src/nodes/html-script-element/ScriptUtility.ts +8 -0
  91. package/src/window/IHappyDOMSettings.ts +9 -0
  92. package/src/window/IWindow.ts +15 -6
  93. package/src/window/Window.ts +36 -5
  94. package/src/xml-http-request/XMLHttpRequest.ts +998 -0
  95. package/src/xml-http-request/XMLHttpRequestCertificate.ts +52 -0
  96. package/src/xml-http-request/XMLHttpRequestEventTarget.ts +17 -0
  97. package/src/xml-http-request/XMLHttpRequestReadyStateEnum.ts +9 -0
  98. package/src/xml-http-request/XMLHttpRequestUpload.ts +6 -0
  99. package/src/xml-http-request/XMLHttpResponseTypeEnum.ts +9 -0
  100. package/src/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.ts +53 -0
  101. package/src/xml-http-request/utilities/XMLHttpRequestURLUtility.ts +64 -0
  102. package/lib/cookie/CookieUtility.d.ts +0 -15
  103. package/lib/cookie/CookieUtility.js +0 -83
  104. package/lib/cookie/CookieUtility.js.map +0 -1
  105. package/lib/location/URL.d.ts +0 -53
  106. package/lib/location/URL.js +0 -96
  107. package/lib/location/URL.js.map +0 -1
  108. package/src/cookie/CookieUtility.ts +0 -87
  109. package/src/location/URL.ts +0 -102
@@ -0,0 +1,158 @@
1
+ const CookiePairRegex = /([^=]+)(?:=([\s\S]*))?/;
2
+
3
+ /**
4
+ * Cookie.
5
+ */
6
+ export default class Cookie {
7
+ private pairs: { [key: string]: string } = {};
8
+ //
9
+ public key = '';
10
+ public value = '';
11
+ public size = 0;
12
+ // Optional
13
+ public domain = '';
14
+ public path = '';
15
+ public expriesOrMaxAge: Date = null;
16
+ public httpOnly = false;
17
+ public secure = false;
18
+ public sameSite = '';
19
+
20
+ /**
21
+ * Constructor.
22
+ *
23
+ * @param cookie Cookie.
24
+ */
25
+ constructor(cookie: string) {
26
+ let match: RegExpExecArray | null;
27
+
28
+ const parts = cookie.split(';').filter(Boolean);
29
+
30
+ // Part[0] is the key-value pair.
31
+ match = new RegExp(CookiePairRegex).exec(parts[0]);
32
+ if (!match) {
33
+ throw new Error(`Invalid cookie: ${cookie}`);
34
+ }
35
+ this.key = match[1].trim();
36
+ this.value = match[2];
37
+ // Set key is empty if match[2] is undefined.
38
+ if (!match[2]) {
39
+ this.value = this.key;
40
+ this.key = '';
41
+ }
42
+ this.pairs[this.key] = this.value;
43
+ this.size = this.key.length + this.value.length;
44
+ // Attribute.
45
+ for (const part of parts.slice(1)) {
46
+ match = new RegExp(CookiePairRegex).exec(part);
47
+ if (!match) {
48
+ throw new Error(`Invalid cookie: ${part}`);
49
+ }
50
+ const key = match[1].trim();
51
+ const value = match[2];
52
+
53
+ switch (key.toLowerCase()) {
54
+ case 'expires':
55
+ this.expriesOrMaxAge = new Date(value);
56
+ break;
57
+ case 'max-age':
58
+ this.expriesOrMaxAge = new Date(parseInt(value, 10) * 1000 + Date.now());
59
+ break;
60
+ case 'domain':
61
+ this.domain = value;
62
+ break;
63
+ case 'path':
64
+ this.path = value.startsWith('/') ? value : `/${value}`;
65
+ break;
66
+ case 'httponly':
67
+ this.httpOnly = true;
68
+ break;
69
+ case 'secure':
70
+ this.secure = true;
71
+ break;
72
+ case 'samesite':
73
+ this.sameSite = value;
74
+ break;
75
+ default:
76
+ continue; // Skip.
77
+ }
78
+ // Skip unknown key-value pair.
79
+ if (
80
+ ['expires', 'max-age', 'domain', 'path', 'httponly', 'secure', 'samesite'].indexOf(
81
+ key.toLowerCase()
82
+ ) === -1
83
+ ) {
84
+ continue;
85
+ }
86
+ this.pairs[key] = value;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Returns a raw string of the cookie.
92
+ */
93
+ public rawString(): string {
94
+ return Object.keys(this.pairs)
95
+ .map((key) => {
96
+ if (key) {
97
+ return `${key}=${this.pairs[key]}`;
98
+ }
99
+ return this.pairs[key];
100
+ })
101
+ .join('; ');
102
+ }
103
+
104
+ /**
105
+ *
106
+ */
107
+ public cookieString(): string {
108
+ if (this.key) {
109
+ return `${this.key}=${this.value}`;
110
+ }
111
+ return this.value;
112
+ }
113
+
114
+ /**
115
+ *
116
+ */
117
+ public isExpired(): boolean {
118
+ // If the expries/maxage is set, then determine whether it is expired.
119
+ if (this.expriesOrMaxAge && this.expriesOrMaxAge.getTime() < Date.now()) {
120
+ return true;
121
+ }
122
+ // If the expries/maxage is not set, it's a session-level cookie that will expire when the browser is closed.
123
+ // (it's never expired in happy-dom)
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ *
129
+ */
130
+ public isHttpOnly(): boolean {
131
+ return this.httpOnly;
132
+ }
133
+
134
+ /**
135
+ *
136
+ */
137
+ public isSecure(): boolean {
138
+ return this.secure;
139
+ }
140
+
141
+ /**
142
+ * Parse a cookie string.
143
+ *
144
+ * @param cookieString
145
+ */
146
+ public static parse(cookieString: string): Cookie {
147
+ return new Cookie(cookieString);
148
+ }
149
+
150
+ /**
151
+ * Stringify a Cookie object.
152
+ *
153
+ * @param cookie
154
+ */
155
+ public static stringify(cookie: Cookie): string {
156
+ return cookie.toString();
157
+ }
158
+ }
@@ -0,0 +1,82 @@
1
+ import Location from 'src/location/Location';
2
+ import Cookie from './Cookie';
3
+
4
+ /**
5
+ * CookieJar.
6
+ *
7
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie.
8
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.
9
+ */
10
+ export default class CookieJar {
11
+ private cookies: Cookie[] = [];
12
+
13
+ /**
14
+ * Validate cookie.
15
+ *
16
+ * @param cookie
17
+ */
18
+ private validateCookie(cookie: Cookie): boolean {
19
+ if (cookie.key.toLowerCase().startsWith('__secure-') && !cookie.isSecure()) {
20
+ return false;
21
+ }
22
+ if (
23
+ cookie.key.toLowerCase().startsWith('__host-') &&
24
+ (!cookie.isSecure() || cookie.path !== '/' || cookie.domain)
25
+ ) {
26
+ return false;
27
+ }
28
+ return true;
29
+ }
30
+
31
+ /**
32
+ * Set cookie.
33
+ *
34
+ * @param cookieString
35
+ */
36
+ public setCookiesString(cookieString: string): void {
37
+ if (!cookieString) {
38
+ return;
39
+ }
40
+ const newCookie = new Cookie(cookieString);
41
+ if (!this.validateCookie(newCookie)) {
42
+ return;
43
+ }
44
+ this.cookies
45
+ .filter((cookie) => cookie.key === newCookie.key)
46
+ .forEach((cookie) => {
47
+ this.cookies.splice(this.cookies.indexOf(cookie), 1);
48
+ });
49
+ this.cookies.push(newCookie);
50
+ }
51
+
52
+ /**
53
+ * Get cookie.
54
+ *
55
+ * @param location Location.
56
+ * @param fromDocument If true, the caller is a document.
57
+ * @returns Cookie string.
58
+ */
59
+ public getCookiesString(location: Location, fromDocument: boolean): string {
60
+ const cookies = this.cookies.filter((cookie) => {
61
+ // Skip when use document.cookie and the cookie is httponly.
62
+ if (fromDocument && cookie.isHttpOnly()) {
63
+ return false;
64
+ }
65
+ if (cookie.isExpired()) {
66
+ return false;
67
+ }
68
+ if (cookie.isSecure() && location.protocol !== 'https:') {
69
+ return false;
70
+ }
71
+ if (cookie.domain && !location.hostname.endsWith(cookie.domain)) {
72
+ return false;
73
+ }
74
+ if (cookie.path && !location.pathname.startsWith(cookie.path)) {
75
+ return false;
76
+ }
77
+ // TODO: check SameSite.
78
+ return true;
79
+ });
80
+ return cookies.map((cookie) => cookie.cookieString()).join('; ');
81
+ }
82
+ }
@@ -7,7 +7,7 @@ export default interface IEventListener {
7
7
  /**
8
8
  * Handles event.
9
9
  *
10
- * @param type Event type.
10
+ * @param event Event.
11
11
  */
12
12
  handleEvent(event: Event): void;
13
13
  }
@@ -8,6 +8,9 @@ enum DOMExceptionNameEnum {
8
8
  invalidNodeTypeError = 'InvalidNodeTypeError',
9
9
  invalidCharacterError = 'InvalidCharacterError',
10
10
  notFoundError = 'NotFoundError',
11
- domException = 'DOMException'
11
+ securityError = 'SecurityError',
12
+ networkError = 'NetworkError',
13
+ domException = 'DOMException',
14
+ invalidAccessError = 'InvalidAccessError'
12
15
  }
13
16
  export default DOMExceptionNameEnum;
@@ -4,27 +4,63 @@ import IDocument from '../nodes/document/IDocument';
4
4
  import IResponse from './IResponse';
5
5
  import Response from './Response';
6
6
  import NodeFetch from 'node-fetch';
7
+ import Request from './Request';
8
+ import RequestInfo from './RequestInfo';
9
+ import { URL } from 'url';
7
10
 
8
11
  /**
9
12
  * Helper class for performing fetch.
10
13
  */
11
14
  export default class FetchHandler {
12
15
  /**
13
- * Returns resource data asynchonously.
16
+ * Returns resource data asynchronously.
14
17
  *
15
18
  * @param document Document.
16
19
  * @param url URL to resource.
17
20
  * @param [init] Init.
18
21
  * @returns Response.
19
22
  */
20
- public static fetch(document: IDocument, url: string, init?: IRequestInit): Promise<IResponse> {
21
- // We want to only load NodeFetch when it is needed to improve performance and not have direct dependencies to server side packages.
23
+ public static fetch(
24
+ document: IDocument,
25
+ url: RequestInfo,
26
+ init?: IRequestInit
27
+ ): Promise<IResponse> {
22
28
  const taskManager = document.defaultView.happyDOM.asyncTaskManager;
29
+ const requestInit = { ...init, headers: { ...init?.headers } };
30
+ const cookie = document.defaultView.document.cookie;
31
+ const referer = document.defaultView.location.origin;
32
+
33
+ requestInit.headers['user-agent'] = document.defaultView.navigator.userAgent;
34
+
35
+ // We need set referer to solve anti-hotlinking.
36
+ // And the browser will set the referer to the origin of the page.
37
+ // Referer is "null" when the URL is set to "about:blank".
38
+ // This is also how the browser behaves.
39
+ if (referer !== 'null') {
40
+ requestInit.headers['referer'] = referer;
41
+ }
42
+
43
+ if (cookie) {
44
+ requestInit.headers['set-cookie'] = cookie;
45
+ }
46
+
47
+ let request;
48
+
49
+ if (typeof url === 'string') {
50
+ request = new Request(RelativeURL.getAbsoluteURL(document.defaultView.location, url));
51
+ } else if (url instanceof URL) {
52
+ // URLs are always absolute, no need for getAbsoluteURL.
53
+ request = new Request(url);
54
+ } else {
55
+ request = new Request(RelativeURL.getAbsoluteURL(document.defaultView.location, url.url), {
56
+ ...url
57
+ });
58
+ }
23
59
 
24
60
  return new Promise((resolve, reject) => {
25
61
  const taskID = taskManager.startTask();
26
62
 
27
- NodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url), init)
63
+ NodeFetch(request, requestInit)
28
64
  .then((response) => {
29
65
  if (taskManager.getTaskCount() === 0) {
30
66
  reject(new Error('Failed to complete fetch request. Task was canceled.'));
@@ -0,0 +1,6 @@
1
+ import { URL } from 'url';
2
+ import IRequest from './IRequest';
3
+
4
+ type RequestInfo = IRequest | string | URL;
5
+
6
+ export default RequestInfo;
@@ -7,7 +7,7 @@ import IDocument from '../nodes/document/IDocument';
7
7
  */
8
8
  export default class ResourceFetchHandler {
9
9
  /**
10
- * Returns resource data asynchonously.
10
+ * Returns resource data asynchronously.
11
11
  *
12
12
  * @param document Document.
13
13
  * @param url URL.
@@ -24,7 +24,7 @@ export default class ResourceFetchHandler {
24
24
  }
25
25
 
26
26
  /**
27
- * Returns resource data synchonously.
27
+ * Returns resource data synchronously.
28
28
  *
29
29
  * @param document Document.
30
30
  * @param url URL.
@@ -32,16 +32,18 @@ export default class ResourceFetchHandler {
32
32
  */
33
33
  public static fetchSync(document: IDocument, url: string): string {
34
34
  // We want to only load SyncRequest when it is needed to improve performance and not have direct dependencies to server side packages.
35
- const absoluteURL = RelativeURL.getAbsoluteURL(document.defaultView.location, url);
36
- const syncRequest = require('sync-request');
37
- const response = syncRequest('GET', absoluteURL);
35
+ const absoluteURL = RelativeURL.getAbsoluteURL(document.defaultView.location, url).href;
38
36
 
39
- if (response.isError()) {
37
+ const xhr = new document.defaultView.XMLHttpRequest();
38
+ xhr.open('GET', absoluteURL, false);
39
+ xhr.send();
40
+
41
+ if (xhr.status !== 200) {
40
42
  throw new DOMException(
41
- `Failed to perform request to "${absoluteURL}". Status code: ${response.statusCode}`
43
+ `Failed to perform request to "${absoluteURL}". Status code: ${xhr.status}`
42
44
  );
43
45
  }
44
46
 
45
- return response.getBody().toString();
47
+ return xhr.responseText;
46
48
  }
47
49
  }
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@ import Window from './window/Window';
4
4
  import DataTransfer from './event/DataTransfer';
5
5
  import DataTransferItem from './event/DataTransferItem';
6
6
  import DataTransferItemList from './event/DataTransferItemList';
7
- import URL from './location/URL';
7
+ import { URL, URLSearchParams } from 'url';
8
8
  import Location from './location/Location';
9
9
  import MutationObserver from './mutation-observer/MutationObserver';
10
10
  import ResizeObserver from './resize-observer/ResizeObserver';
@@ -115,7 +115,6 @@ import CSSMediaRule from './css/rules/CSSMediaRule';
115
115
  import CSSStyleRule from './css/rules/CSSStyleRule';
116
116
  import Storage from './storage/Storage';
117
117
  import DOMRect from './nodes/element/DOMRect';
118
- import { URLSearchParams } from 'url';
119
118
  import Selection from './selection/Selection';
120
119
  import Range from './range/Range';
121
120
  import HTMLDialogElement from './nodes/html-dialog-element/HTMLDialogElement';
@@ -1,4 +1,4 @@
1
- import URL from './URL';
1
+ import { URL } from 'url';
2
2
 
3
3
  /**
4
4
  *
@@ -8,7 +8,7 @@ export default class Location extends URL {
8
8
  * Constructor.
9
9
  */
10
10
  constructor() {
11
- super('');
11
+ super('about:blank');
12
12
  }
13
13
 
14
14
  /**
@@ -17,7 +17,7 @@ export default class Location extends URL {
17
17
  * @param url URL.
18
18
  */
19
19
  public replace(url: string): void {
20
- this.parse(url);
20
+ this.href = url;
21
21
  }
22
22
 
23
23
  /**
@@ -1,4 +1,5 @@
1
1
  import Location from './Location';
2
+ import { URL } from 'url';
2
3
 
3
4
  /**
4
5
  * Helper class for getting the URL relative to a Location object.
@@ -10,19 +11,7 @@ export default class RelativeURL {
10
11
  * @param location Location.
11
12
  * @param url URL.
12
13
  */
13
- public static getAbsoluteURL(location: Location, url: string): string {
14
- if (url.startsWith('/')) {
15
- return location.origin + url;
16
- }
17
-
18
- if (!url.startsWith('https://') && !url.startsWith('http://')) {
19
- let pathname = location.pathname;
20
- if (pathname.endsWith('/')) {
21
- pathname = pathname.slice(0, -1);
22
- }
23
- return location.origin + pathname + '/' + url;
24
- }
25
-
26
- return url;
14
+ public static getAbsoluteURL(location: Location, url: string): URL {
15
+ return new URL(url, location.href);
27
16
  }
28
17
  }
@@ -19,7 +19,7 @@ import QuerySelector from '../../query-selector/QuerySelector';
19
19
  import IDocument from './IDocument';
20
20
  import CSSStyleSheet from '../../css/CSSStyleSheet';
21
21
  import DOMException from '../../exception/DOMException';
22
- import CookieUtility from '../../cookie/CookieUtility';
22
+ import CookieJar from '../../cookie/CookieJar';
23
23
  import IElement from '../element/IElement';
24
24
  import IHTMLScriptElement from '../html-script-element/IHTMLScriptElement';
25
25
  import IHTMLElement from '../html-element/IHTMLElement';
@@ -63,10 +63,12 @@ export default class Document extends Node implements IDocument {
63
63
 
64
64
  // Used as an unique identifier which is updated whenever the DOM gets modified.
65
65
  public _cacheID = 0;
66
+ // Public in order to be accessible by the fetch and xhr.
67
+ public _cookie = new CookieJar();
66
68
 
67
69
  protected _isFirstWrite = true;
68
70
  protected _isFirstWriteAfterOpen = false;
69
- private _cookie = '';
71
+
70
72
  private _selection: Selection = null;
71
73
 
72
74
  // Events
@@ -183,7 +185,6 @@ export default class Document extends Node implements IDocument {
183
185
  /**
184
186
  * Creates an instance of Document.
185
187
  *
186
- * @param defaultView Default view.
187
188
  */
188
189
  constructor() {
189
190
  super();
@@ -258,7 +259,7 @@ export default class Document extends Node implements IDocument {
258
259
  * @returns Cookie.
259
260
  */
260
261
  public get cookie(): string {
261
- return this._cookie;
262
+ return this._cookie.getCookiesString(this.defaultView.location, true);
262
263
  }
263
264
 
264
265
  /**
@@ -267,7 +268,7 @@ export default class Document extends Node implements IDocument {
267
268
  * @param cookie Cookie string.
268
269
  */
269
270
  public set cookie(cookie: string) {
270
- this._cookie = CookieUtility.getCookieString(this.defaultView.location, this._cookie, cookie);
271
+ this._cookie.setCookiesString(cookie);
271
272
  }
272
273
 
273
274
  /**
@@ -404,6 +405,24 @@ export default class Document extends Node implements IDocument {
404
405
  return this.defaultView.location.href;
405
406
  }
406
407
 
408
+ /**
409
+ * Returns URL.
410
+ *
411
+ * @returns the URL of the current document.
412
+ * */
413
+ public get URL(): string {
414
+ return this.defaultView.location.href;
415
+ }
416
+
417
+ /**
418
+ * Returns document URI.
419
+ *
420
+ * @returns the URL of the current document.
421
+ * */
422
+ public get documentURI(): string {
423
+ return this.URL;
424
+ }
425
+
407
426
  /**
408
427
  * Inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
409
428
  *
@@ -866,7 +885,6 @@ export default class Document extends Node implements IDocument {
866
885
  *
867
886
  * @param node Node to import.
868
887
  * @param [deep=false] Set to "true" if the clone should be deep.
869
- * @param Imported Node.
870
888
  */
871
889
  public importNode(node: INode, deep = false): INode {
872
890
  if (!(node instanceof Node)) {
@@ -39,6 +39,8 @@ export default interface IDocument extends IParentNode {
39
39
  readonly readyState: DocumentReadyStateEnum;
40
40
  readonly charset: string;
41
41
  readonly characterSet: string;
42
+ readonly URL: string;
43
+ readonly documentURI: string;
42
44
  cookie: string;
43
45
 
44
46
  // Events
@@ -196,7 +196,8 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
196
196
  href !== null &&
197
197
  rel &&
198
198
  rel.toLowerCase() === 'stylesheet' &&
199
- this.isConnected
199
+ this.isConnected &&
200
+ !this.ownerDocument.defaultView.happyDOM.settings.disableCSSFileLoading
200
201
  ) {
201
202
  (<Document>this.ownerDocument)._readyStateManager.startTask();
202
203
  ResourceFetchHandler.fetch(this.ownerDocument, href)
@@ -255,7 +256,11 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
255
256
 
256
257
  super._connectToNode(parentNode);
257
258
 
258
- if (isConnected !== isParentConnected && this._evaluateCSS) {
259
+ if (
260
+ isConnected !== isParentConnected &&
261
+ this._evaluateCSS &&
262
+ !this.ownerDocument.defaultView.happyDOM.settings.disableCSSFileLoading
263
+ ) {
259
264
  const href = this.getAttributeNS(null, 'href');
260
265
  const rel = this.getAttributeNS(null, 'rel');
261
266
 
@@ -193,7 +193,7 @@ export default class HTMLScriptElement extends HTMLElement implements IHTMLScrip
193
193
 
194
194
  if (src !== null) {
195
195
  ScriptUtility.loadExternalScript(this);
196
- } else {
196
+ } else if (!this.ownerDocument.defaultView.happyDOM.settings.disableJavaScriptEvaluation) {
197
197
  const textContent = this.textContent;
198
198
  const type = this.getAttributeNS(null, 'type');
199
199
  if (
@@ -18,6 +18,14 @@ export default class ScriptUtility {
18
18
  public static async loadExternalScript(element: HTMLScriptElement): Promise<void> {
19
19
  const src = element.getAttributeNS(null, 'src');
20
20
  const async = element.getAttributeNS(null, 'async') !== null;
21
+
22
+ if (
23
+ element.ownerDocument.defaultView.happyDOM.settings.disableJavaScriptFileLoading ||
24
+ element.ownerDocument.defaultView.happyDOM.settings.disableJavaScriptEvaluation
25
+ ) {
26
+ return;
27
+ }
28
+
21
29
  if (async) {
22
30
  let code = null;
23
31
  (<Document>element.ownerDocument)._readyStateManager.startTask();
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Happy DOM settings.
3
+ */
4
+ export default interface IHappyDOMSettings {
5
+ disableJavaScriptEvaluation: boolean;
6
+ disableJavaScriptFileLoading: boolean;
7
+ disableCSSFileLoading: boolean;
8
+ enableFileSystemHttpRequests: boolean;
9
+ }