Repository: capricorn86/happy-dom (TypeScript)

Issue:
`getComputedStyle(element)` fails to correctly parse `var()` inside other css functions
**Describe the bug**
The current implementation of regexing for variables inside of [`CSSStyleDeclarationComputedStyle`](https://github.com/capricorn86/happy-dom/blob/fe66e764f4ceccbed314ddac786cea25a555652f/packages/happy-dom/src/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.ts#L23) does not correctly work when a variable definition is inside a non var() call (eg `background-color: rgb(0 128 0/var(--bg-color-alpha, 1));` computes down to `rgb(231 253 251/1` and leaves out the closing parenthesis)

This is in part because the last part of this regex: `/var\( *(--[^), ]+)\)|var\( *(--[^), ]+), *(.+)\)/` is too greedy using a `.+` selector.

The resulting behavior is that the css property definition is invalid and is thrown out. 

**To Reproduce**
Steps to reproduce the behavior:
#1838 reproduces the issue in tests

**Expected behavior**
The correct behavior was happening prior to [1493fa6cd082cb743f98ead8574dd9dbc3eae5b6](https://github.com/capricorn86/happy-dom/commit/1493fa6cd082cb743f98ead8574dd9dbc3eae5b6).



**Screenshots**
If applicable, add screenshots to help explain your problem.

**Device:**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Additional context**

(I have been working on updating some dependencies, and noticed this bug, so I've spent this morning hunting down the issue and was able to pinpoint it to a specific version where it started breaking)

I don't know if reverting to the previous behavior is a good idea, or if it would be better to try and make a more complicated regex.



Discussion:

Patch:
diff --git a/packages/happy-dom/src/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.ts b/packages/happy-dom/src/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.ts
index 946583fbf..ddb2e7d6c 100644
--- a/packages/happy-dom/src/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.ts
+++ b/packages/happy-dom/src/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.ts
@@ -23,8 +23,9 @@ import CSSScopeRule from '../../rules/CSSScopeRule.js';
 import CSSStyleSheet from '../../CSSStyleSheet.js';
 
 const CSS_MEASUREMENT_REGEXP = /[0-9.]+(px|rem|em|vw|vh|%|vmin|vmax|cm|mm|in|pt|pc|Q)/g;
-const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)|var\( *(--[^), ]+), *(.+)\)/;
 const HOST_REGEXP = /:host\s*\(([^)]+)\)|:host-context\s*\(([^)]+)\)/;
+const SINGLE_CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)/;
+const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+), *([^), ]+)\)/;
 
 type IStyleAndElement = {
 	element: Element | ShadowRoot | Document | null;
@@ -460,13 +461,14 @@ export default class CSSStyleDeclarationComputedStyle {
 		let newValue = value;
 		let match: RegExpMatchArray | null;
 
+		while ((match = newValue.match(SINGLE_CSS_VARIABLE_REGEXP)) != null) {
+			// Without fallback value - E.g. var(--my-var)
+			newValue = newValue.replace(match[0], cssVariables[match[1]] || '');
+		}
+
 		while ((match = newValue.match(CSS_VARIABLE_REGEXP)) !== null) {
 			// Fallback value - E.g. var(--my-var, #FFFFFF)
-			if (match[2] !== undefined) {
-				newValue = newValue.replace(match[0], cssVariables[match[2]] || match[3]);
-			} else {
-				newValue = newValue.replace(match[0], cssVariables[match[1]] || '');
-			}
+			newValue = newValue.replace(match[0], cssVariables[match[1]] || match[2]);
 		}
 
 		return newValue;


Test patch:
diff --git a/packages/happy-dom/test/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.test.ts b/packages/happy-dom/test/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.test.ts
index 168cb8507..3c7f019c7 100644
--- a/packages/happy-dom/test/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.test.ts
+++ b/packages/happy-dom/test/css/declaration/computed-style/CSSStyleDeclarationComputedStyle.test.ts
@@ -28,5 +28,27 @@ describe('CSSStyleDeclarationElementStyle', () => {
 
 			expect(computedElementStyleDeclaration.getComputedStyle()).not.toBe(computedElementStyle);
 		});
+		it('parses variables correctly.', () => {
+			document.body.appendChild(element);
+			element.setAttribute(
+				'style',
+				`--bg-color: rgb(0 128 0 / 1); background-color: var(--bg-color);`
+			);
+
+			const computedElementStyleDeclaration = new CSSStyleDeclarationElementStyle(element);
+			const computedElementStyle = computedElementStyleDeclaration.getComputedStyle();
+			expect(computedElementStyle.get('background-color').value).toBe('rgb(0 128 0 / 1)');
+		});
+		it('parses nested variables correctly.', () => {
+			document.body.appendChild(element);
+			element.setAttribute(
+				'style',
+				`--bg-color-alpha: 1; background-color: rgb(0 128 0 / var(--bg-color-alpha, 1));`
+			);
+
+			const computedElementStyleDeclaration = new CSSStyleDeclarationElementStyle(element);
+			const computedElementStyle = computedElementStyleDeclaration.getComputedStyle();
+			expect(computedElementStyle.get('background-color').value).toBe('rgb(0 128 0 / 1)');
+		});
 	});
 });