From 9ed6767adac4e0afab66fc3fb9716ab87bfc81f8 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 31 Mar 2022 15:51:25 +0200 Subject: [PATCH] Don't serialize all cssRules if multiple text nodes exists (#866) --- packages/rrweb-snapshot/src/snapshot.ts | 7 ++- packages/rrweb-snapshot/test/snapshot.test.ts | 55 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index ae7f05f01c..6c283b796a 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -632,7 +632,12 @@ function serializeNode( if (isStyle && textContent) { try { // try to read style sheet - if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) { + if (n.nextSibling || n.previousSibling) { + // This is not the only child of the stylesheet. + // We can't read all of the sheet's .cssRules and expect them + // to _only_ include the current rule(s) added by the text node. + // So we'll be conservative and keep textContent as-is. + } else if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) { textContent = stringifyStyleSheet( (n.parentNode as HTMLStyleElement).sheet!, ); diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index 5b9810c57c..d1e94f9a82 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -1,5 +1,13 @@ +/** + * @jest-environment jsdom + */ import { JSDOM } from 'jsdom'; -import { absoluteToStylesheet, _isBlockedElement } from '../src/snapshot'; +import { + absoluteToStylesheet, + serializeNodeWithId, + _isBlockedElement, +} from '../src/snapshot'; +import { serializedNodeWithId } from '../src/types'; describe('absolute url to stylesheet', () => { const href = 'http://localhost/css/style.css'; @@ -126,3 +134,48 @@ describe('isBlockedElement()', () => { ).toEqual(true); }); }); + +describe('style elements', () => { + const serializeNode = (node: Node): serializedNodeWithId | null => { + return serializeNodeWithId(node, { + doc: document, + map: {}, + blockClass: 'blockblock', + blockSelector: null, + maskTextClass: 'maskmask', + maskTextSelector: null, + skipChild: false, + inlineStylesheet: true, + maskTextFn: undefined, + maskInputFn: undefined, + slimDOMOptions: {}, + }); + }; + + const render = (html: string): HTMLStyleElement => { + document.write(html); + return document.querySelector('style')!; + }; + + it('should serialize all rules of stylesheet when the sheet has a single child node', () => { + const styleEl = render(``); + styleEl.sheet?.insertRule('section { color: blue; }'); + expect(serializeNode(styleEl.childNodes[0])).toMatchObject({ + isStyle: true, + rootId: undefined, + textContent: 'section {color: blue;}body {color: red;}', + type: 3, + }); + }); + + it('should serialize individual text nodes on stylesheets with multiple child nodes', () => { + const styleEl = render(``); + styleEl.append(document.createTextNode('section { color: blue; }')); + expect(serializeNode(styleEl.childNodes[1])).toMatchObject({ + isStyle: true, + rootId: undefined, + textContent: 'section { color: blue; }', + type: 3, + }); + }); +});