Skip to content

Commit 7551bb1

Browse files
mydeabillyvg
andauthored
fix: Fix CSS rules captured in Safari (#86)
Safari does not escape `:` in attribute selectors correctly, so we need to do that instead. See rrweb-io#1208 ref getsentry/sentry-javascript#7703 --------- Co-authored-by: Billy Vong <[email protected]>
1 parent b4261e7 commit 7551bb1

File tree

3 files changed

+26
-1
lines changed

3 files changed

+26
-1
lines changed

packages/rrweb-snapshot/src/snapshot.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ function getCssRuleString(rule: CSSRule): string {
7070
// ignore
7171
}
7272
}
73+
74+
return validateStringifiedCssRule(cssStringified);
75+
}
76+
77+
export function validateStringifiedCssRule(cssStringified: string): string {
78+
// Safari does not escape selectors with : properly
79+
if (cssStringified.indexOf(':') > -1) {
80+
// Replace e.g. [aa:bb] with [aa\\:bb]
81+
const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;
82+
return cssStringified.replace(regex, '$1\\$2');
83+
}
84+
7385
return cssStringified;
7486
}
7587

@@ -80,7 +92,7 @@ function isCSSImportRule(rule: CSSRule): rule is CSSImportRule {
8092
function stringifyStyleSheet(sheet: CSSStyleSheet): string {
8193
return sheet.cssRules
8294
? Array.from(sheet.cssRules)
83-
.map((rule) => rule.cssText || '')
95+
.map((rule) => rule.cssText ? validateStringifiedCssRule(rule.cssText) : '')
8496
.join('')
8597
: '';
8698
}

packages/rrweb-snapshot/test/css.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { parse, Rule, Media } from '../src/css';
2+
import {validateStringifiedCssRule} from './../src/snapshot';
23

34
describe('css parser', () => {
45
it('should save the filename and source', () => {
@@ -106,4 +107,15 @@ describe('css parser', () => {
106107
decl = rule.declarations![0];
107108
expect(decl.parent).toEqual(rule);
108109
});
110+
111+
it('parses : in attribute selectors correctly', () => {
112+
const out1 = validateStringifiedCssRule('[data-foo] { color: red; }');
113+
expect(out1).toEqual('[data-foo] { color: red; }');
114+
115+
const out2 = validateStringifiedCssRule('[data-foo:other] { color: red; }');
116+
expect(out2).toEqual('[data-foo\\:other] { color: red; }');
117+
118+
const out3 = validateStringifiedCssRule('[data-aa\\:other] { color: red; }');
119+
expect(out3).toEqual('[data-aa\\:other] { color: red; }');
120+
})
109121
});

packages/rrweb-snapshot/typings/snapshot.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types';
22
export declare const IGNORED_NODE = -2;
3+
export declare function validateStringifiedCssRule(cssStringified: string): string;
34
export declare function absoluteToStylesheet(cssText: string | null, href: string): string;
45
export declare function absoluteToDoc(doc: Document, attributeValue: string): string;
56
export declare function transformAttribute(doc: Document, element: HTMLElement, _tagName: string, _name: string, value: string | null, maskAllText: boolean, unmaskTextSelector: string | undefined | null, maskTextFn: MaskTextFn | undefined): string | null;

0 commit comments

Comments
 (0)