Skip to content

Commit

Permalink
fix: grid template is parsed incorrectly by chrome
Browse files Browse the repository at this point in the history
  • Loading branch information
pauldambra authored and dkindel committed Apr 30, 2024
1 parent 6ac06c1 commit dd97de7
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 8 deletions.
58 changes: 51 additions & 7 deletions packages/rrweb-snapshot/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import {
documentNode,
documentTypeNode,
elementNode,
idNodeMap,
IMirror,
MaskInputFn,
MaskInputOptions,
nodeMetaMap,
IMirror,
serializedNodeWithId,
serializedNode,
NodeType,
documentNode,
documentTypeNode,
serializedNode,
serializedNodeWithId,
textNode,
elementNode,
IWindow,
} from './types';

Expand Down Expand Up @@ -137,8 +137,48 @@ export function stringifyStylesheet(s: CSSStyleSheet): string | null {
}
}

function replaceChromeGridTemplateAreas(rule: CSSStyleRule): string {
const hasGridTemplateInCSSText = rule.cssText.includes('grid-template:');
const hasGridTemplateAreaInStyleRules =
rule.style.getPropertyValue('grid-template-areas') !== '';
const hasGridTemplateAreaInCSSText = rule.cssText.includes(
'grid-template-areas:',
);
if (
isCSSStyleRule(rule) &&
hasGridTemplateInCSSText &&
hasGridTemplateAreaInStyleRules &&
!hasGridTemplateAreaInCSSText
) {
// chrome does not correctly provide the grid template areas in the rules cssText
// e.g. https://bugs.chromium.org/p/chromium/issues/detail?id=1303968
// we remove the grid-template rule from the text... so everything from grid-template: to the next semicolon
// and then add each grid-template-x rule into the css text because Chrome isn't doing this correctly
const parts = rule.cssText
.split(';')
.filter((s) => !s.includes('grid-template:'))
.map((s) => s.trim());

const gridStyles: string[] = [];

for (let i = 0; i < rule.style.length; i++) {
const styleName = rule.style[i];
if (styleName.startsWith('grid-template')) {
gridStyles.push(
`${styleName}: ${rule.style.getPropertyValue(styleName)}`,
);
}
}
parts.splice(parts.length - 1, 0, gridStyles.join('; '));
return parts.join('; ');
}
return rule.cssText;
}

export function stringifyRule(rule: CSSRule): string {
let importStringified;
let gridTemplateFixed;

if (isCSSImportRule(rule)) {
try {
importStringified =
Expand All @@ -156,7 +196,11 @@ export function stringifyRule(rule: CSSRule): string {
return fixSafariColons(rule.cssText);
}

return importStringified || rule.cssText;
if (isCSSStyleRule(rule)) {
gridTemplateFixed = replaceChromeGridTemplateAreas(rule);
}

return importStringified || gridTemplateFixed || rule.cssText;
}

export function fixSafariColons(cssStringified: string): string {
Expand Down
53 changes: 52 additions & 1 deletion packages/rrweb-snapshot/test/css.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { parse, Rule, Media } from '../src/css';
import { fixSafariColons, escapeImportStatement } from './../src/utils';
import {
fixSafariColons,
escapeImportStatement,
stringifyRule,
} from './../src/utils';

describe('css parser', () => {
it('should save the filename and source', () => {
Expand Down Expand Up @@ -190,6 +194,53 @@ li[attr="has,comma"] a:hover {
expect(
(commainstrresult.stylesheet!.rules[0] as Rule)!.selectors!.length,
).toEqual(1);

});

it('does not alter correctly parsed grid template rules', () => {
const cssText =
'#wrapper { display: grid; width: 100%; height: 100%; grid-template: repeat(2, 1fr); margin: 0px auto; }';
const stringified = stringifyRule({
cssText: cssText,
} as Partial<CSSStyleRule> as CSSStyleRule);
expect(stringified).toEqual(cssText);
});

it('fixes incorrectly parsed grid template rules', () => {
const cssText =
'#wrapper { display: grid; grid-template: "header header" max-content / repeat(2, 1fr); margin: 0px auto; }';
// to avoid using JSDom we can fake as much of the CSSStyleDeclaration as we ned
const cssStyleDeclaration: Record<string | number, any> = {
length: 3,
0: 'grid-template-areas',
1: 'grid-template-rows',
2: 'grid-template-columns',
items: (i: number): string => {
return [cssStyleDeclaration[i]].toString();
},
getPropertyValue: (key: string): string => {
if (key === 'grid-template-areas') {
return '"header header" "main main" "footer footer"';
}
if (key === 'grid-template-rows') {
return 'repeat(2, 1fr)';
}
if (key === 'grid-template-columns') {
return 'repeat(2, 1fr)';
}
return '';
},
};

const stringified = stringifyRule({
cssText: cssText,
selectorText: '#wrapper',
style: cssStyleDeclaration as unknown as CSSStyleDeclaration,
} as Partial<CSSStyleRule> as CSSStyleRule);

expect(stringified).toEqual(
'#wrapper { display: grid; margin: 0px auto; grid-template-areas: "header header" "main main" "footer footer"; grid-template-rows: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr); }',
);
});

it('parses imports with quotes correctly', () => {
Expand Down

0 comments on commit dd97de7

Please sign in to comment.