Skip to content

Commit 9ed6767

Browse files
authored
Don't serialize all cssRules if multiple text nodes exists (#866)
1 parent 072b81b commit 9ed6767

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

packages/rrweb-snapshot/src/snapshot.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,12 @@ function serializeNode(
632632
if (isStyle && textContent) {
633633
try {
634634
// try to read style sheet
635-
if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) {
635+
if (n.nextSibling || n.previousSibling) {
636+
// This is not the only child of the stylesheet.
637+
// We can't read all of the sheet's .cssRules and expect them
638+
// to _only_ include the current rule(s) added by the text node.
639+
// So we'll be conservative and keep textContent as-is.
640+
} else if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) {
636641
textContent = stringifyStyleSheet(
637642
(n.parentNode as HTMLStyleElement).sheet!,
638643
);

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
14
import { JSDOM } from 'jsdom';
2-
import { absoluteToStylesheet, _isBlockedElement } from '../src/snapshot';
5+
import {
6+
absoluteToStylesheet,
7+
serializeNodeWithId,
8+
_isBlockedElement,
9+
} from '../src/snapshot';
10+
import { serializedNodeWithId } from '../src/types';
311

412
describe('absolute url to stylesheet', () => {
513
const href = 'http://localhost/css/style.css';
@@ -126,3 +134,48 @@ describe('isBlockedElement()', () => {
126134
).toEqual(true);
127135
});
128136
});
137+
138+
describe('style elements', () => {
139+
const serializeNode = (node: Node): serializedNodeWithId | null => {
140+
return serializeNodeWithId(node, {
141+
doc: document,
142+
map: {},
143+
blockClass: 'blockblock',
144+
blockSelector: null,
145+
maskTextClass: 'maskmask',
146+
maskTextSelector: null,
147+
skipChild: false,
148+
inlineStylesheet: true,
149+
maskTextFn: undefined,
150+
maskInputFn: undefined,
151+
slimDOMOptions: {},
152+
});
153+
};
154+
155+
const render = (html: string): HTMLStyleElement => {
156+
document.write(html);
157+
return document.querySelector('style')!;
158+
};
159+
160+
it('should serialize all rules of stylesheet when the sheet has a single child node', () => {
161+
const styleEl = render(`<style>body { color: red; }</style>`);
162+
styleEl.sheet?.insertRule('section { color: blue; }');
163+
expect(serializeNode(styleEl.childNodes[0])).toMatchObject({
164+
isStyle: true,
165+
rootId: undefined,
166+
textContent: 'section {color: blue;}body {color: red;}',
167+
type: 3,
168+
});
169+
});
170+
171+
it('should serialize individual text nodes on stylesheets with multiple child nodes', () => {
172+
const styleEl = render(`<style>body { color: red; }</style>`);
173+
styleEl.append(document.createTextNode('section { color: blue; }'));
174+
expect(serializeNode(styleEl.childNodes[1])).toMatchObject({
175+
isStyle: true,
176+
rootId: undefined,
177+
textContent: 'section { color: blue; }',
178+
type: 3,
179+
});
180+
});
181+
});

0 commit comments

Comments
 (0)