From d9574eda1a53bb91727bd6e3b14fb7c9e07e0bcf Mon Sep 17 00:00:00 2001 From: odan Date: Thu, 4 Apr 2024 22:01:31 +0900 Subject: [PATCH 1/5] fix: [#1363] Fix bug with missing fallback value --- .../CSSStyleDeclarationElementStyle.ts | 16 ++++++++++------ .../happy-dom/test/window/BrowserWindow.test.ts | 7 +++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts index 9bd3080f..87cc9a5e 100644 --- a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts +++ b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts @@ -19,7 +19,6 @@ import CSSMeasurementConverter from '../measurement-converter/CSSMeasurementConv import MediaQueryList from '../../../match-media/MediaQueryList.js'; import WindowBrowserSettingsReader from '../../../window/WindowBrowserSettingsReader.js'; -const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)|var\( *(--[^), ]+), *([^), ]+)\)/; const CSS_MEASUREMENT_REGEXP = /[0-9.]+(px|rem|em|vw|vh|%|vmin|vmax|cm|mm|in|pt|pc|Q)/g; type IStyleAndElement = { @@ -366,16 +365,21 @@ export default class CSSStyleDeclarationElementStyle { * @returns CSS value. */ private parseCSSVariablesInValue(value: string, cssVariables: { [k: string]: string }): string { + const SINGLE_CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)/; + const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+), *([^), ]+)\)/; + let newValue = value; let match; + if (newValue.match(SINGLE_CSS_VARIABLE_REGEXP)) { + // Without fallback value - E.g. var(--my-var) + match = newValue.match(SINGLE_CSS_VARIABLE_REGEXP); + 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; diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index 2fe889f0..0c32392e 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -592,11 +592,15 @@ describe('BrowserWindow', () => { color: var(--my-var, var(--my-background, pink)); --color1: red; + --color2: blue; --result1: var(--color1, var(--unknown, var(--unknown, green))); --result2: var(--unknown, var(--color1, var(--unknown, green))); --result3: var(--unknown, var(--unknown, var(--color1, green))); --result4: var(--unknown, var(--unknown, var(--unknown, var(--unknown, white)))); + + --result5: var(--color1, var(--color2)); + --result6: var(--unknown, blue); } `; @@ -611,6 +615,9 @@ describe('BrowserWindow', () => { expect(computedStyle.getPropertyValue('--result2')).toBe('red'); expect(computedStyle.getPropertyValue('--result4')).toBe('white'); + + expect(computedStyle.getPropertyValue('--result5')).toBe('red'); + expect(computedStyle.getPropertyValue('--result6')).toBe('blue'); }); it('Returns a CSSStyleDeclaration object with computed styles containing "rem" and "em" measurement values converted to pixels.', () => { From 9e0054efbcf172326bc81385bb3b1c30fc8556bc Mon Sep 17 00:00:00 2001 From: odan Date: Thu, 4 Apr 2024 22:03:14 +0900 Subject: [PATCH 2/5] fix: [#1363] Add test case --- packages/happy-dom/test/window/BrowserWindow.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index 0c32392e..a171a317 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -600,7 +600,8 @@ describe('BrowserWindow', () => { --result4: var(--unknown, var(--unknown, var(--unknown, var(--unknown, white)))); --result5: var(--color1, var(--color2)); - --result6: var(--unknown, blue); + --result6: var(--unknown, var(--color2)); + --result7: var(--unknown, blue); } `; @@ -618,6 +619,7 @@ describe('BrowserWindow', () => { expect(computedStyle.getPropertyValue('--result5')).toBe('red'); expect(computedStyle.getPropertyValue('--result6')).toBe('blue'); + expect(computedStyle.getPropertyValue('--result7')).toBe('blue'); }); it('Returns a CSSStyleDeclaration object with computed styles containing "rem" and "em" measurement values converted to pixels.', () => { From 2e4833525d904b03a5bafc5602353c0b7e3eb9de Mon Sep 17 00:00:00 2001 From: odan Date: Sat, 6 Apr 2024 00:13:04 +0900 Subject: [PATCH 3/5] fix: [#1363] Fix a bug in the case of custom property resolution chaining --- .../CSSStyleDeclarationElementStyle.ts | 5 +- .../test/window/BrowserWindow.test.ts | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts index e05956bf..7870c710 100644 --- a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts +++ b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts @@ -389,11 +389,10 @@ export default class CSSStyleDeclarationElementStyle { const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+), *([^), ]+)\)/; let newValue = value; - let match; + let match: RegExpMatchArray | null; - if (newValue.match(SINGLE_CSS_VARIABLE_REGEXP)) { + while ((match = newValue.match(SINGLE_CSS_VARIABLE_REGEXP)) != null) { // Without fallback value - E.g. var(--my-var) - match = newValue.match(SINGLE_CSS_VARIABLE_REGEXP); newValue = newValue.replace(match[0], cssVariables[match[1]] || ''); } diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index cd10e150..f5fb1359 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -622,6 +622,54 @@ describe('BrowserWindow', () => { expect(computedStyle.getPropertyValue('--result7')).toBe('blue'); }); + it('Returns values defined by a CSS variables when multiple chained variables are used.', () => { + const div = document.createElement('div'); + const divStyle = document.createElement('style'); + + divStyle.innerHTML = ` + div { + --my-var1: var(--my-var2); + --my-var2: var(--my-var3); + --my-var3: var(--my-var4); + --my-var4: pink; + + + --color1: red; + --color2: blue; + --case1-result1: var(--case1-result2); + --case1-result2: var(--case1-result3); + --case1-result3: var(--case1-result4); + --case1-result4: var(--color1, var(--color2)); + + --case2-result1: var(--case2-result2); + --case2-result2: var(--case2-result3); + --case2-result3: var(--case2-result4); + --case2-result4: var(--unknown, var(--color2)); + } + `; + + document.body.appendChild(divStyle); + document.body.appendChild(div); + + const computedStyle = window.getComputedStyle(div); + + + expect(computedStyle.getPropertyValue('--my-var1')).toBe('pink'); + expect(computedStyle.getPropertyValue('--my-var2')).toBe('pink'); + expect(computedStyle.getPropertyValue('--my-var3')).toBe('pink'); + expect(computedStyle.getPropertyValue('--my-var4')).toBe('pink'); + + expect(computedStyle.getPropertyValue('--case1-result1')).toBe('red'); + expect(computedStyle.getPropertyValue('--case1-result2')).toBe('red'); + expect(computedStyle.getPropertyValue('--case1-result3')).toBe('red'); + expect(computedStyle.getPropertyValue('--case1-result4')).toBe('red'); + + expect(computedStyle.getPropertyValue('--case2-result1')).toBe('blue'); + expect(computedStyle.getPropertyValue('--case2-result2')).toBe('blue'); + expect(computedStyle.getPropertyValue('--case2-result3')).toBe('blue'); + expect(computedStyle.getPropertyValue('--case2-result4')).toBe('blue'); + }); + it('Returns a CSSStyleDeclaration object with computed styles containing "rem" and "em" measurement values converted to pixels.', () => { const parent = document.createElement('div'); const element = document.createElement('span'); From 012ccc498cac1bedebc2392d52925fc372941540 Mon Sep 17 00:00:00 2001 From: odan Date: Sat, 6 Apr 2024 00:15:19 +0900 Subject: [PATCH 4/5] fix: [#1363] Run code format --- packages/happy-dom/test/window/BrowserWindow.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index f5fb1359..68ea609a 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -653,7 +653,6 @@ describe('BrowserWindow', () => { const computedStyle = window.getComputedStyle(div); - expect(computedStyle.getPropertyValue('--my-var1')).toBe('pink'); expect(computedStyle.getPropertyValue('--my-var2')).toBe('pink'); expect(computedStyle.getPropertyValue('--my-var3')).toBe('pink'); From 1493fa6cd082cb743f98ead8574dd9dbc3eae5b6 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Sat, 6 Apr 2024 00:57:55 +0200 Subject: [PATCH 5/5] chore: [#1363] Simplifies the solution --- .../CSSStyleDeclarationElementStyle.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts index 7870c710..8e981ed8 100644 --- a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts +++ b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts @@ -20,6 +20,7 @@ import MediaQueryList from '../../../match-media/MediaQueryList.js'; import WindowBrowserSettingsReader from '../../../window/WindowBrowserSettingsReader.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\( *(--[^), ]+), *(.+)\)/; type IStyleAndElement = { element: Element | ShadowRoot | Document; @@ -385,20 +386,16 @@ export default class CSSStyleDeclarationElementStyle { * @returns CSS value. */ private parseCSSVariablesInValue(value: string, cssVariables: { [k: string]: string }): string { - const SINGLE_CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)/; - const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+), *([^), ]+)\)/; - 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) - newValue = newValue.replace(match[0], cssVariables[match[1]] || match[2]); + if (match[2] !== undefined) { + newValue = newValue.replace(match[0], cssVariables[match[2]] || match[3]); + } else { + newValue = newValue.replace(match[0], cssVariables[match[1]] || ''); + } } return newValue;