Skip to content

Commit

Permalink
Merge pull request #26 from pendo-io/ajax-fix-chrome-grid-template
Browse files Browse the repository at this point in the history
Fix chrome grid template
  • Loading branch information
guntherjh authored Oct 24, 2024
2 parents ec7d7e6 + 2f4c2e7 commit 7233382
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-plants-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"rrweb-snapshot": patch
---

Fix issue with chrome improperly parsing grid-template-areas to grid-template shorthand.
46 changes: 42 additions & 4 deletions packages/rrweb-snapshot/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,14 @@ export function stringifyRule(rule: CSSRule, sheetHref: string | null): string {
return importStringified;
} else {
let ruleStringified = rule.cssText;
if (isCSSStyleRule(rule) && rule.selectorText.includes(':')) {
// Safari does not escape selectors with : properly
// see https://bugs.webkit.org/show_bug.cgi?id=184604
ruleStringified = fixSafariColons(ruleStringified);
if (isCSSStyleRule(rule)) {
ruleStringified = replaceChromeGridTemplateAreas(rule);

if (rule.selectorText.includes(':')) {
// Safari does not escape selectors with : properly
// see https://bugs.webkit.org/show_bug.cgi?id=184604
ruleStringified = fixSafariColons(ruleStringified);
}
}
if (sheetHref) {
return absolutifyURLs(ruleStringified, sheetHref);
Expand All @@ -189,6 +193,40 @@ export function stringifyRule(rule: CSSRule, sheetHref: string | null): string {
}
}

export function replaceChromeGridTemplateAreas(rule: CSSStyleRule): string {
// chrome does not correctly provide the grid-template-areas in the rule.cssText
// when it parses them to grid-template short-hand syntax
// e.g. https://bugs.chromium.org/p/chromium/issues/detail?id=1303968
// so, we manually rebuild the cssText using rule.style when
// we find the cssText contains grid-template:, rule.style contains grid-template-areas, but
// cssText does not include grid-template-areas
const hasGridTemplateInCSSText = rule.cssText.includes('grid-template:');
const hasGridTemplateAreaInStyleRules =
rule.style.getPropertyValue('grid-template-areas') !== '';
const hasGridTemplateAreaInCSSText = rule.cssText.includes(
'grid-template-areas:',
);

if (
hasGridTemplateInCSSText &&
hasGridTemplateAreaInStyleRules &&
!hasGridTemplateAreaInCSSText
) {
const styleDeclarations = [];

for (let i = 0; i < rule.style.length; i++) {
const styleName = rule.style[i];
const styleValue = rule.style.getPropertyValue(styleName);

styleDeclarations.push(`${styleName}: ${styleValue}`);
}

return `${rule.selectorText} { ${styleDeclarations.join('; ')}; }`;
}

return rule.cssText;
}

export function fixSafariColons(cssStringified: string): string {
// Replace e.g. [aa:bb] with [aa\\:bb]
const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
Expand Down
94 changes: 94 additions & 0 deletions packages/rrweb-snapshot/test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { NodeType, serializedNode } from '../src/types';
import {
escapeImportStatement,
extractFileExtension,
replaceChromeGridTemplateAreas,
fixSafariColons,
isNodeMetaEqual,
} from '../src/utils';
Expand Down Expand Up @@ -268,6 +269,99 @@ describe('utils', () => {
expect(out5).toEqual(`@import url("/foo.css;900;800\\"") layer;`);
});
});

describe('replaceChromeGridTemplateAreas', () => {
it('does not alter corectly parsed grid template rules', () => {
const cssText =
'#wrapper { display: grid; width: 100%; height: 100%; grid-template: minmax(2, 1fr); margin: 0px auto; }';
const mockCssRule = {
cssText,
selectorText: '#wrapper',
style: {
getPropertyValue(prop) {
return {
'grid-template-areas': '',
}[prop];
},
},
} as Partial<CSSStyleRule> as CSSStyleRule;

expect(replaceChromeGridTemplateAreas(mockCssRule)).toEqual(cssText);
});

it('fixes incorrectly parsed grid template rules', () => {
const cssText1 =
'#wrapper { grid-template-areas: "header header" "main main" "footer footer"; grid-template-rows: minmax(2, 1fr); grid-template-columns: minmax(2, 1fr); display: grid; margin: 0px auto; }';
const cssText2 =
'.some-class { color: purple; grid-template: "TopNav TopNav" 65px "SideNav Content" 52px "SideNav Content" / 255px auto; column-gap: 32px; }';

const mockCssRule1 = {
cssText: cssText1,
selectorText: '#wrapper',
style: {
length: 5,
0: 'grid-template-areas',
1: 'grid-template-rows',
2: 'grid-template-columns',
3: 'display',
4: 'margin',
getPropertyValue: (key: string): string => {
switch (key) {
case 'grid-template-areas':
return '"header header" "main main" "footer footer"';
case 'grid-template-rows':
return 'minmax(2, 1fr)';
case 'grid-template-columns':
return 'minmax(2, 1fr)';
case 'display':
return 'grid';
case 'margin':
return '0px auto';
default:
return '';
}
},
} as Record<string | number, any>,
} as Partial<CSSStyleRule> as CSSStyleRule;

const mockCssRule2 = {
cssText: cssText2,
selectorText: '.some-class',
style: {
length: 5,
0: 'color',
1: 'grid-template-areas',
2: 'grid-template-rows',
3: 'grid-template-columns',
4: 'column-gap',
getPropertyValue: (key: string): string => {
switch (key) {
case 'color':
return 'purple';
case 'grid-template-areas':
return '"TopNav TopNav" "SideNav Content" "SideNav Content"';
case 'grid-template-rows':
return '65px 52px auto';
case 'grid-template-columns':
return '255px auto';
case 'column-gap':
return '32px';
default:
return '';
}
},
} as Record<string | number, any>,
} as Partial<CSSStyleRule> as CSSStyleRule;

expect(replaceChromeGridTemplateAreas(mockCssRule1)).toEqual(
'#wrapper { grid-template-areas: "header header" "main main" "footer footer"; grid-template-rows: minmax(2, 1fr); grid-template-columns: minmax(2, 1fr); display: grid; margin: 0px auto; }',
);
expect(replaceChromeGridTemplateAreas(mockCssRule2)).toEqual(
'.some-class { color: purple; grid-template-areas: "TopNav TopNav" "SideNav Content" "SideNav Content"; grid-template-rows: 65px 52px auto; grid-template-columns: 255px auto; column-gap: 32px; }',
);
});
});

describe('fixSafariColons', () => {
it('parses : in attribute selectors correctly', () => {
const out1 = fixSafariColons('[data-foo] { color: red; }');
Expand Down

0 comments on commit 7233382

Please sign in to comment.