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 committed Jan 9, 2024
1 parent b2460a4 commit a3126e0
Show file tree
Hide file tree
Showing 2 changed files with 102 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,
} from './types';

export function isElement(n: Node): n is Element {
Expand Down Expand Up @@ -106,8 +106,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 @@ -125,7 +165,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
52 changes: 51 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 @@ -119,6 +123,52 @@ describe('css parser', () => {
expect(out3).toEqual('[data-aa\\:other] { color: red; }');
});

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', () => {
const out1 = escapeImportStatement({
cssText: `@import url("/foo.css;900;800"");`,
Expand Down

0 comments on commit a3126e0

Please sign in to comment.