Skip to content

Commit 4012c77

Browse files
author
Gusted
authored
Handle calc in gradients parsing (darkreader#9372)
- Ensure that `calc(...)` isn't parsed with an legitimate part (changes to the regex). - Ensure that there are no double commas in the result output (the other changes :D). - Resolves darkreader#9340
1 parent 3336fc8 commit 4012c77

3 files changed

Lines changed: 29 additions & 3 deletions

File tree

src/inject/dynamic-theme/modify-css.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ export function getBgImageModifier(
320320
const getGradientModifier = (gradient: parsedGradient) => {
321321
const {typeGradient, match, hasComma} = gradient;
322322

323-
const partsRegex = /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?[^\(\),]*),?/g;
323+
const partsRegex = /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?([^\(\), ]|( (?!calc)))*),?/g;
324324
const colorStopRegex = /^(from|color-stop|to)\(([^\(\)]*?,\s*)?(.*?)\)$/;
325325

326326
const parts = getMatches(partsRegex, match, 1).map((part) => {
@@ -436,14 +436,28 @@ export function getBgImageModifier(
436436
const modifiers: CSSValueModifier[] = [];
437437

438438
let matchIndex = 0;
439+
let prevHasComma = false;
439440
matches.forEach(({type, match, index, typeGradient, hasComma, offset}, i) => {
440441
const matchStart = index;
441442
const prefixStart = matchIndex;
442443
const matchEnd = matchStart + match.length + offset;
443444
matchIndex = matchEnd;
444445

445446
// Make sure we still push all the unrelated content between gradients and URLs.
446-
prefixStart !== matchStart && modifiers.push(() => value.substring(prefixStart, matchStart));
447+
if (prefixStart !== matchStart) {
448+
if (prevHasComma) {
449+
modifiers.push(() => {
450+
let betweenValue = value.substring(prefixStart, matchStart);
451+
if (betweenValue[0] === ',') {
452+
betweenValue = betweenValue.substring(1);
453+
}
454+
return betweenValue;
455+
});
456+
} else {
457+
modifiers.push(() => value.substring(prefixStart, matchStart));
458+
}
459+
}
460+
prevHasComma = hasComma || false;
447461

448462
if (type === 'url') {
449463
modifiers.push(getURLModifier(match));

tests/inject/dynamic/color.tests.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,15 @@ describe('COLOR PARSING', () => {
111111
createOrUpdateDynamicTheme(theme, null, false);
112112
expect(getComputedStyle(container.querySelector('h1')).backgroundColor).toBe('rgb(28, 31, 32)');
113113
});
114+
115+
it('should handle gradients with calc(...) cases', () => {
116+
container.innerHTML = multiline(
117+
'<style>',
118+
' h1 { background-image: linear-gradient(rgb(249, 249, 251) calc(100% - 3px), transparent), linear-gradient(-90deg, rgb(255, 145, 0), rgb(241, 3, 102) 50%, rgb(97, 115, 255)) }',
119+
'</style>',
120+
'<h1>Weird color <strong>Power</strong>!</h1>',
121+
);
122+
createOrUpdateDynamicTheme(theme, null, false);
123+
expect(getComputedStyle(container.querySelector('h1')).backgroundImage).toBe('linear-gradient(rgb(27, 29, 30), calc(100% - 3px), rgba(0, 0, 0, 0)), linear-gradient(-90deg, rgb(204, 116, 0), rgb(193, 2, 82) 50%, rgb(0, 17, 146))');
124+
});
114125
});

tests/inject/dynamic/image-analysis.tests.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import '../support/polyfills';
22
import {DEFAULT_THEME} from '../../../src/defaults';
33
import {createOrUpdateDynamicTheme, removeDynamicTheme} from '../../../src/inject/dynamic-theme';
44
import {getImageDetails} from '../../../src/inject/dynamic-theme/image';
5-
import {multiline, waitForEvent} from '../support/test-utils';
5+
import {multiline, timeout, waitForEvent} from '../support/test-utils';
66
import type {DynamicThemeFix} from '../../../src/definitions';
77
import {isFirefox} from '../../../src/utils/platform';
88

@@ -261,6 +261,7 @@ describe('IMAGE ANALYSIS', () => {
261261
);
262262
createOrUpdateDynamicTheme(theme, null, false);
263263
await waitForEvent('__darkreader__test__asyncQueueComplete');
264+
await timeout(500);
264265
expect(getComputedStyle(container.querySelector('h1')).backgroundImage).toBe('linear-gradient(rgb(204, 0, 0), rgb(0, 0, 0)), url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iOCIgaGVpZ2h0PSI4Ij48ZGVmcz48ZmlsdGVyIGlkPSJkYXJrcmVhZGVyLWltYWdlLWZpbHRlciI+PGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAuMzMzIC0wLjY2NyAtMC42NjcgMC4wMDAgMS4wMDAgLTAuNjY3IDAuMzMzIC0wLjY2NyAwLjAwMCAxLjAwMCAtMC42NjcgLTAuNjY3IDAuMzMzIDAuMDAwIDEuMDAwIDAuMDAwIDAuMDAwIDAuMDAwIDEuMDAwIDAuMDAwIiAvPjwvZmlsdGVyPjwvZGVmcz48aW1hZ2Ugd2lkdGg9IjgiIGhlaWdodD0iOCIgZmlsdGVyPSJ1cmwoI2RhcmtyZWFkZXItaW1hZ2UtZmlsdGVyKSIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhacFpYZENiM2c5SWpBZ01DQTRJRGdpSUhkcFpIUm9QU0k0SWlCb1pXbG5hSFE5SWpnaVBnb2dJQ0FnUEhKbFkzUWdabWxzYkQwaWQyaHBkR1VpSUhkcFpIUm9QU0l4TURBbElpQm9aV2xuYUhROUlqRXdNQ1VpSUM4K0Nqd3ZjM1puUGc9PSIgLz48L3N2Zz4=")');
265266
});
266267

0 commit comments

Comments
 (0)