diff --git a/static/app/utils/replays/replayReader.spec.tsx b/static/app/utils/replays/replayReader.spec.tsx index 67197be35dc7be..afdafb26a31e83 100644 --- a/static/app/utils/replays/replayReader.spec.tsx +++ b/static/app/utils/replays/replayReader.spec.tsx @@ -494,4 +494,115 @@ describe('ReplayReader', () => { ]); }); }); + + describe('getRRWebFramesWithoutStyles', () => { + it('should remove style nodes and their content', () => { + const reader = ReplayReader.factory({ + attachments: [ + { + type: EventType.FullSnapshot, + data: { + node: { + type: 1, + tagName: 'html', + childNodes: [], + }, + }, + timestamp: 0, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + adds: [ + { + parentId: 4, + nextId: 21, + node: { + type: 2, + tagName: 'style', + attributes: { + 'data-emotion': 'css', + 'data-s': '', + _cssText: '.css {...} ', + }, + childNodes: [], + id: 47, + }, + }, + { + parentId: 414, + nextId: null, + node: { + type: 3, + textContent: '.css {...}', + isStyle: true, + id: 427, + }, + }, + { + parentId: 5, + nextId: 22, + node: { + type: 1, + tagName: 'div', + attributes: { + class: 'test', + }, + childNodes: [], + id: 48, + }, + }, + ], + }, + timestamp: 0, + }, + ], + errors: [], + fetching: false, + replayRecord: ReplayRecordFixture(), + }); + + if (!reader) { + throw new Error('Failed to create ReplayReader instance'); + } + + const result = reader.getRRWebFramesWithoutStyles(); + + expect(result).toEqual([ + {data: {node: {childNodes: [], tagName: 'html', type: 1}}, timestamp: 0, type: 2}, + { + data: { + adds: [ + { + nextId: 21, + node: {attributes: {}, childNodes: [], id: 47, tagName: 'style', type: 2}, + parentId: 4, + }, + { + nextId: null, + node: {id: 427, isStyle: true, textContent: '', type: 3}, + parentId: 414, + }, + { + nextId: 22, + node: { + attributes: {class: 'test'}, + childNodes: [], + id: 48, + tagName: 'div', + type: 1, + }, + parentId: 5, + }, + ], + source: 0, + }, + timestamp: 0, + type: 3, + }, + {data: {payload: {}, tag: 'replay.end'}, timestamp: expect.any(Number), type: 5}, + ]); + }); + }); }); diff --git a/static/app/utils/replays/replayReader.tsx b/static/app/utils/replays/replayReader.tsx index acf08de43058d1..0e0f469c767eb1 100644 --- a/static/app/utils/replays/replayReader.tsx +++ b/static/app/utils/replays/replayReader.tsx @@ -571,8 +571,8 @@ export default class ReplayReader { }); /** - * Filter out style mutations as they can cause perf problems especially when - * used in replayStepper + * Do not include style mutation content as they can cause perf problems when + * used in replayStepper. However, we need to keep the nodes itself as to not affect the tree structure. */ getRRWebFramesWithoutStyles = memoize(() => { return this.getRRWebFrames().map(e => { @@ -581,17 +581,62 @@ export default class ReplayReader { 'source' in e.data && e.data.source === IncrementalSource.Mutation ) { + // Example `data` object: + // [{ + // "parentId": 4, + // "nextId": 21, + // "node": { + // "type": 2, + // "tagName": "style", + // "attributes": { + // "data-emotion": "css", + // "data-s": "", + // "_cssText": ".css {...} " + // }, + // "childNodes": [], + // "id": 47 + // } + // }, + // { + // "parentId": 414, + // "nextId": null, + // "node": { + // "type": 3, + // "textContent": ".css {...}", + // "isStyle": true, + // "id": 427 + // } + // } + // ] + return { ...e, data: { ...e.data, - adds: e.data.adds.filter( - add => - !( - (add.node.type === 3 && add.node.isStyle) || - (add.node.type === 2 && add.node.tagName === 'style') - ) - ), + adds: e.data.adds.map(add => { + if (add.node.type === 3 && add.node.isStyle) { + return { + ...add, + node: { + ...add.node, + textContent: '', + }, + }; + } + + if (add.node.type === 2 && add.node.tagName === 'style') { + return { + ...add, + node: { + ...add.node, + attributes: {}, + childNodes: [], + }, + }; + } + + return add; + }), }, }; }