diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index a369b17345..93e0faede2 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -34,6 +34,7 @@ import { inDom, getShadowHost, closestElementOfNode, + splitStyleAttributes, } from '../utils'; type DoubleLinkedListNode = { @@ -659,7 +660,15 @@ export default class MutationBuffer { } const old = this.unattachedDoc.createElement('span'); if (m.oldValue) { - old.setAttribute('style', m.oldValue); + // Split the style string into individual style rules + const styleAttributes = splitStyleAttributes(m.oldValue); + + // set each style property individually to avoid csp error + styleAttributes.forEach(({ property, value }) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + old.style[property] = value; + }); } for (const pname of Array.from(target.style)) { const newValue = target.style.getPropertyValue(pname); diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 046ca50e17..d98039d9eb 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -670,3 +670,28 @@ export function clearTimeout( ): ReturnType { return getImplementation('clearTimeout')(...rest); } + +/** + * Takes a styles attribute string and converts it to key value pairs + * @param styles - The full styles attributes string + * + */ +export function splitStyleAttributes(styles: string) { + const splitStyles = styles.split(';'); + return splitStyles + .filter((value) => value.split(':').length == 2) + .map((style) => { + let [property, value] = style.trim().split(':'); + + property = property.trim(); + value = value.trim(); + + // Convert kebab-case to camelCase + const camelCasedProperty = property.replace( + /-([a-z])/g, + (match, letter: string) => letter.toUpperCase(), + ); + + return { property: camelCasedProperty, value }; + }); +} diff --git a/packages/rrweb/test/util.test.ts b/packages/rrweb/test/util.test.ts index 46ae81db2a..18a546cc01 100644 --- a/packages/rrweb/test/util.test.ts +++ b/packages/rrweb/test/util.test.ts @@ -7,6 +7,7 @@ import { inDom, shadowHostInDom, getShadowHost, + splitStyleAttributes, } from '../src/utils'; describe('Utilities for other modules', () => { @@ -142,5 +143,18 @@ describe('Utilities for other modules', () => { expect(shadowHostInDom(a.childNodes[0])).toBeTruthy(); expect(inDom(a.childNodes[0])).toBeTruthy(); }); + + it('should split styles attributes', () => { + const styleAttribute = + 'background-color: peachpuff; padding: 20px; margin-top: 0;'; + + const splitStyles = splitStyleAttributes(styleAttribute); + + expect(splitStyles).toEqual([ + { property: 'backgroundColor', value: 'peachpuff' }, + { property: 'padding', value: '20px' }, + { property: 'marginTop', value: '0' }, + ]); + }); }); });