happy-dom 14.3.8 → 14.3.10

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 (49) hide show
  1. package/cjs/css/declaration/css-parser/CSSStyleDeclarationCSSParser.cjs +20 -9
  2. package/cjs/css/declaration/css-parser/CSSStyleDeclarationCSSParser.cjs.map +1 -1
  3. package/cjs/css/declaration/css-parser/CSSStyleDeclarationCSSParser.d.ts +10 -1
  4. package/cjs/css/declaration/css-parser/CSSStyleDeclarationCSSParser.d.ts.map +1 -1
  5. package/cjs/css/declaration/element-style/CSSStyleDeclarationElementStyle.cjs +30 -17
  6. package/cjs/css/declaration/element-style/CSSStyleDeclarationElementStyle.cjs.map +1 -1
  7. package/cjs/css/declaration/element-style/CSSStyleDeclarationElementStyle.d.ts +1 -0
  8. package/cjs/css/declaration/element-style/CSSStyleDeclarationElementStyle.d.ts.map +1 -1
  9. package/cjs/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.cjs +5 -4
  10. package/cjs/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.cjs.map +1 -1
  11. package/cjs/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.d.ts.map +1 -1
  12. package/cjs/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.cjs +4 -3
  13. package/cjs/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.cjs.map +1 -1
  14. package/cjs/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.d.ts.map +1 -1
  15. package/cjs/nodes/html-style-element/HTMLStyleElement.cjs +44 -9
  16. package/cjs/nodes/html-style-element/HTMLStyleElement.cjs.map +1 -1
  17. package/cjs/nodes/html-style-element/HTMLStyleElement.d.ts +17 -0
  18. package/cjs/nodes/html-style-element/HTMLStyleElement.d.ts.map +1 -1
  19. package/cjs/storage/StorageFactory.cjs +19 -1
  20. package/cjs/storage/StorageFactory.cjs.map +1 -1
  21. package/cjs/storage/StorageFactory.d.ts.map +1 -1
  22. package/lib/css/declaration/css-parser/CSSStyleDeclarationCSSParser.d.ts +10 -1
  23. package/lib/css/declaration/css-parser/CSSStyleDeclarationCSSParser.d.ts.map +1 -1
  24. package/lib/css/declaration/css-parser/CSSStyleDeclarationCSSParser.js +20 -9
  25. package/lib/css/declaration/css-parser/CSSStyleDeclarationCSSParser.js.map +1 -1
  26. package/lib/css/declaration/element-style/CSSStyleDeclarationElementStyle.d.ts +1 -0
  27. package/lib/css/declaration/element-style/CSSStyleDeclarationElementStyle.d.ts.map +1 -1
  28. package/lib/css/declaration/element-style/CSSStyleDeclarationElementStyle.js +30 -17
  29. package/lib/css/declaration/element-style/CSSStyleDeclarationElementStyle.js.map +1 -1
  30. package/lib/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.d.ts.map +1 -1
  31. package/lib/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.js +5 -4
  32. package/lib/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.js.map +1 -1
  33. package/lib/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.d.ts.map +1 -1
  34. package/lib/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.js +4 -3
  35. package/lib/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.js.map +1 -1
  36. package/lib/nodes/html-style-element/HTMLStyleElement.d.ts +17 -0
  37. package/lib/nodes/html-style-element/HTMLStyleElement.d.ts.map +1 -1
  38. package/lib/nodes/html-style-element/HTMLStyleElement.js +45 -11
  39. package/lib/nodes/html-style-element/HTMLStyleElement.js.map +1 -1
  40. package/lib/storage/StorageFactory.d.ts.map +1 -1
  41. package/lib/storage/StorageFactory.js +19 -1
  42. package/lib/storage/StorageFactory.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/css/declaration/css-parser/CSSStyleDeclarationCSSParser.ts +27 -12
  45. package/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts +36 -16
  46. package/src/css/declaration/property-manager/CSSStyleDeclarationPropertyManager.ts +5 -4
  47. package/src/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.ts +4 -6
  48. package/src/nodes/html-style-element/HTMLStyleElement.ts +49 -8
  49. package/src/storage/StorageFactory.ts +19 -1
@@ -1,7 +1,8 @@
1
- // PropName => \s*([^:;]+?)\s*:
2
- // PropValue => \s*((?:[^(;]*?(?:\([^)]*\))?)*?) <- will match any non ';' char except inside (), nested parentheses are not supported
3
- // !important => \s*(!important)?
4
- // EndOfRule => \s*(?:$|;)
1
+ // Groups:
2
+ // Property name => \s*([^:;]+?)\s*:
3
+ // Property value => \s*((?:[^(;]*?(?:\([^)]*\))?)*?) <- will match any non ';' char except inside (), nested parentheses are not supported
4
+ // Important ("!important") => \s*(!important)?
5
+ // End of rule => \s*(?:$|;)
5
6
  const SPLIT_RULES_REGEXP =
6
7
  /\s*([^:;]+?)\s*:\s*((?:[^(;]*?(?:\([^)]*\))?)*?)\s*(!important)?\s*(?:$|;)/g;
7
8
 
@@ -15,15 +16,29 @@ export default class CSSStyleDeclarationCSSParser {
15
16
  * @param cssText CSS string.
16
17
  * @param callback Callback.
17
18
  */
18
- public static parse(
19
- cssText: string,
20
- callback: (name: string, value: string, important: boolean) => void
21
- ): void {
22
- const rules = Array.from(cssText.matchAll(SPLIT_RULES_REGEXP));
23
- for (const [, key, value, important] of rules) {
24
- if (key && value) {
25
- callback(key.trim(), value.trim(), !!important);
19
+ public static parse(cssText: string): {
20
+ rules: Array<{ name: string; value: string; important: boolean }>;
21
+ properties: { [name: string]: string };
22
+ } {
23
+ const properties: { [name: string]: string } = {};
24
+ const rules: Array<{ name: string; value: string; important: boolean }> = [];
25
+ const regexp = new RegExp(SPLIT_RULES_REGEXP);
26
+ let match;
27
+
28
+ while ((match = regexp.exec(cssText))) {
29
+ const name = (match[1] ?? '').trim();
30
+ const value = (match[2] ?? '').trim();
31
+ const important = match[3] ? true : false;
32
+
33
+ if (name && value) {
34
+ if (name.startsWith('--')) {
35
+ properties[name] = value;
36
+ }
37
+
38
+ rules.push({ name, value, important });
26
39
  }
27
40
  }
41
+
42
+ return { rules, properties };
28
43
  }
29
44
  }
@@ -132,15 +132,23 @@ export default class CSSStyleDeclarationElementStyle {
132
132
  if (sheet) {
133
133
  this.parseCSSRules({
134
134
  elements: documentElements,
135
+ rootElement:
136
+ documentElements[0].element[PropertySymbol.tagName] === 'HTML'
137
+ ? documentElements[0]
138
+ : null,
135
139
  cssRules: sheet.cssRules
136
140
  });
137
141
  }
138
142
  }
139
143
 
140
- for (const styleSheet of this.element[PropertySymbol.ownerDocument].adoptedStyleSheets) {
144
+ for (const sheet of this.element[PropertySymbol.ownerDocument].adoptedStyleSheets) {
141
145
  this.parseCSSRules({
142
146
  elements: documentElements,
143
- cssRules: styleSheet.cssRules
147
+ rootElement:
148
+ documentElements[0].element[PropertySymbol.tagName] === 'HTML'
149
+ ? documentElements[0]
150
+ : null,
151
+ cssRules: sheet.cssRules
144
152
  });
145
153
  }
146
154
 
@@ -170,10 +178,10 @@ export default class CSSStyleDeclarationElementStyle {
170
178
  }
171
179
  }
172
180
 
173
- for (const styleSheet of shadowRoot.adoptedStyleSheets) {
181
+ for (const sheet of shadowRoot.adoptedStyleSheets) {
174
182
  this.parseCSSRules({
175
183
  elements: shadowRootElements,
176
- cssRules: styleSheet.cssRules,
184
+ cssRules: sheet.cssRules,
177
185
  hostElement: styleAndElement
178
186
  });
179
187
  }
@@ -190,7 +198,7 @@ export default class CSSStyleDeclarationElementStyle {
190
198
  // Concatenates all parent element CSS to one string.
191
199
  const targetElement = parentElements[parentElements.length - 1];
192
200
  const propertyManager = new CSSStyleDeclarationPropertyManager();
193
- const cssVariables: { [k: string]: string } = {};
201
+ const cssProperties: { [k: string]: string } = {};
194
202
  let rootFontSize: string | number = 16;
195
203
  let parentFontSize: string | number = 16;
196
204
 
@@ -239,24 +247,27 @@ export default class CSSStyleDeclarationElementStyle {
239
247
  const elementStyleAttribute = (<Element>parentElement.element)[PropertySymbol.attributes][
240
248
  'style'
241
249
  ];
250
+
242
251
  if (elementStyleAttribute) {
243
252
  elementCSSText += elementStyleAttribute[PropertySymbol.value];
244
253
  }
245
254
 
246
- CSSStyleDeclarationCSSParser.parse(elementCSSText, (name, value, important) => {
247
- const isCSSVariable = name.startsWith('--');
255
+ const rulesAndProperties = CSSStyleDeclarationCSSParser.parse(elementCSSText);
256
+ const rules = rulesAndProperties.rules;
257
+
258
+ Object.assign(cssProperties, rulesAndProperties.properties);
259
+
260
+ for (const { name, value, important } of rules) {
248
261
  if (
249
- isCSSVariable ||
250
262
  CSSStyleDeclarationElementInheritedProperties[name] ||
251
263
  parentElement === targetElement
252
264
  ) {
253
- const cssValue = this.parseCSSVariablesInValue(value, cssVariables);
254
- if (cssValue && (!propertyManager.get(name)?.important || important)) {
255
- propertyManager.set(name, cssValue, important);
265
+ const parsedValue = this.parseCSSVariablesInValue(value.trim(), cssProperties);
256
266
 
257
- if (isCSSVariable) {
258
- cssVariables[name] = cssValue;
259
- } else if (name === 'font' || name === 'font-size') {
267
+ if (parsedValue && (!propertyManager.get(name)?.important || important)) {
268
+ propertyManager.set(name, parsedValue, important);
269
+
270
+ if (name === 'font' || name === 'font-size') {
260
271
  const fontSize = propertyManager.properties['font-size'];
261
272
  if (fontSize !== null) {
262
273
  const parsedValue = this.parseMeasurementsInValue({
@@ -274,7 +285,7 @@ export default class CSSStyleDeclarationElementStyle {
274
285
  }
275
286
  }
276
287
  }
277
- });
288
+ }
278
289
  }
279
290
 
280
291
  for (const name of CSSStyleDeclarationElementMeasurementProperties) {
@@ -302,11 +313,13 @@ export default class CSSStyleDeclarationElementStyle {
302
313
  * @param options Options.
303
314
  * @param options.elements Elements.
304
315
  * @param options.cssRules CSS rules.
316
+ * @param options.rootElement Root element.
305
317
  * @param [options.hostElement] Host element.
306
318
  */
307
319
  private parseCSSRules(options: {
308
320
  cssRules: CSSRule[];
309
- elements: Array<IStyleAndElement>;
321
+ elements: IStyleAndElement[];
322
+ rootElement?: IStyleAndElement;
310
323
  hostElement?: IStyleAndElement;
311
324
  }): void {
312
325
  if (!options.elements.length) {
@@ -326,6 +339,13 @@ export default class CSSStyleDeclarationElementStyle {
326
339
  priorityWeight: 0
327
340
  });
328
341
  }
342
+ } else if (selectorText.startsWith(':root')) {
343
+ if (options.rootElement) {
344
+ options.rootElement.cssTexts.push({
345
+ cssText: (<CSSStyleRule>rule)[PropertySymbol.cssText],
346
+ priorityWeight: 0
347
+ });
348
+ }
329
349
  } else {
330
350
  for (const element of options.elements) {
331
351
  const match = QuerySelector.matches(<Element>element.element, selectorText, {
@@ -30,11 +30,12 @@ export default class CSSStyleDeclarationPropertyManager {
30
30
  */
31
31
  constructor(options?: { cssText?: string }) {
32
32
  if (options?.cssText) {
33
- CSSStyleDeclarationCSSParser.parse(options.cssText, (name, value, important) => {
34
- if (important || !this.get(name)?.important) {
35
- this.set(name, value, important);
33
+ const { rules } = CSSStyleDeclarationCSSParser.parse(options.cssText);
34
+ for (const rule of rules) {
35
+ if (rule.important || !this.get(rule.name)?.important) {
36
+ this.set(rule.name, rule.value, rule.important);
36
37
  }
37
- });
38
+ }
38
39
  }
39
40
  }
40
41
 
@@ -38,6 +38,7 @@ export default class HTMLLinkElementStyleSheetLoader {
38
38
  public async loadStyleSheet(url: string | null, rel: string | null): Promise<void> {
39
39
  const element = this.#element;
40
40
  const browserSettings = this.#browserFrame.page.context.browser.settings;
41
+ const window = element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow];
41
42
 
42
43
  if (
43
44
  !url ||
@@ -50,10 +51,7 @@ export default class HTMLLinkElementStyleSheetLoader {
50
51
 
51
52
  let absoluteURL: string;
52
53
  try {
53
- absoluteURL = new URL(
54
- url,
55
- element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].location.href
56
- ).href;
54
+ absoluteURL = new URL(url, window.location.href).href;
57
55
  } catch (error) {
58
56
  return;
59
57
  }
@@ -79,10 +77,10 @@ export default class HTMLLinkElementStyleSheetLoader {
79
77
 
80
78
  const resourceFetch = new ResourceFetch({
81
79
  browserFrame: this.#browserFrame,
82
- window: element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow]
80
+ window: window
83
81
  });
84
82
  const readyStateManager = (<{ [PropertySymbol.readyStateManager]: DocumentReadyStateManager }>(
85
- (<unknown>element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow])
83
+ (<unknown>window)
86
84
  ))[PropertySymbol.readyStateManager];
87
85
 
88
86
  this.#loadedStyleSheetURL = absoluteURL;
@@ -1,6 +1,7 @@
1
1
  import CSSStyleSheet from '../../css/CSSStyleSheet.js';
2
2
  import * as PropertySymbol from '../../PropertySymbol.js';
3
3
  import HTMLElement from '../html-element/HTMLElement.js';
4
+ import Node from '../node/Node.js';
4
5
 
5
6
  /**
6
7
  * HTML Style Element.
@@ -17,14 +18,7 @@ export default class HTMLStyleElement extends HTMLElement {
17
18
  * @returns CSS style sheet.
18
19
  */
19
20
  public get sheet(): CSSStyleSheet {
20
- if (!this[PropertySymbol.isConnected]) {
21
- return null;
22
- }
23
- if (!this[PropertySymbol.sheet]) {
24
- this[PropertySymbol.sheet] = new CSSStyleSheet();
25
- }
26
- this[PropertySymbol.sheet].replaceSync(this.textContent);
27
- return this[PropertySymbol.sheet];
21
+ return this[PropertySymbol.sheet] ? this[PropertySymbol.sheet] : null;
28
22
  }
29
23
 
30
24
  /**
@@ -84,4 +78,51 @@ export default class HTMLStyleElement extends HTMLElement {
84
78
  this.setAttribute('disabled', '');
85
79
  }
86
80
  }
81
+
82
+ /**
83
+ * @override
84
+ */
85
+ public override appendChild(node: Node): Node {
86
+ const returnValue = super.appendChild(node);
87
+ if (this[PropertySymbol.sheet]) {
88
+ this[PropertySymbol.sheet].replaceSync(this.textContent);
89
+ }
90
+ return returnValue;
91
+ }
92
+
93
+ /**
94
+ * @override
95
+ */
96
+ public override removeChild(node: Node): Node {
97
+ const returnValue = super.removeChild(node);
98
+ if (this[PropertySymbol.sheet]) {
99
+ this[PropertySymbol.sheet].replaceSync(this.textContent);
100
+ }
101
+ return returnValue;
102
+ }
103
+
104
+ /**
105
+ * @override
106
+ */
107
+ public override insertBefore(newNode: Node, referenceNode: Node | null): Node {
108
+ const returnValue = super.insertBefore(newNode, referenceNode);
109
+ if (this[PropertySymbol.sheet]) {
110
+ this[PropertySymbol.sheet].replaceSync(this.textContent);
111
+ }
112
+ return returnValue;
113
+ }
114
+
115
+ /**
116
+ * @override
117
+ */
118
+ public override [PropertySymbol.connectToNode](parentNode: Node = null): void {
119
+ super[PropertySymbol.connectToNode](parentNode);
120
+
121
+ if (parentNode) {
122
+ this[PropertySymbol.sheet] = new CSSStyleSheet();
123
+ this[PropertySymbol.sheet].replaceSync(this.textContent);
124
+ } else {
125
+ this[PropertySymbol.sheet] = null;
126
+ }
127
+ }
87
128
  }
@@ -41,7 +41,25 @@ export default class StorageFactory {
41
41
  return storage[PropertySymbol.data][key] !== undefined;
42
42
  },
43
43
  defineProperty(storage: Storage, key: string, descriptor: PropertyDescriptor): boolean {
44
- if (Storage.prototype.hasOwnProperty(key) || descriptor.value === undefined) {
44
+ if (Storage.prototype.hasOwnProperty(key)) {
45
+ if (descriptor.get || descriptor.set) {
46
+ Object.defineProperty(storage, key, {
47
+ ...descriptor,
48
+ get: descriptor.get ? descriptor.get.bind(storage) : undefined,
49
+ set: descriptor.set ? descriptor.set.bind(storage) : undefined
50
+ });
51
+ } else {
52
+ Object.defineProperty(storage, key, {
53
+ ...descriptor,
54
+ value:
55
+ typeof descriptor.value === 'function'
56
+ ? descriptor.value.bind(storage)
57
+ : descriptor.value
58
+ });
59
+ }
60
+ return true;
61
+ }
62
+ if (descriptor.value === undefined) {
45
63
  return false;
46
64
  }
47
65
  storage[PropertySymbol.data][key] = String(descriptor.value);