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,
+ });
+ });
+});