diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index fd4ac71060..4ba50a5407 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -262,9 +262,6 @@ export default class MutationBuffer { ns = ns && ns.nextSibling; nextId = ns && this.mirror.getId((ns as unknown) as INode); } - if (nextId === -1 && isBlocked(n.nextSibling, this.blockClass)) { - nextId = null; - } return nextId; }; const pushAdd = (n: Node) => { @@ -534,11 +531,7 @@ export default class MutationBuffer { const parentId = isShadowRoot(m.target) ? this.mirror.getId((m.target.host as unknown) as INode) : this.mirror.getId(m.target as INode); - if ( - isBlocked(n, this.blockClass) || - isBlocked(m.target, this.blockClass) || - isIgnored(n) - ) { + if (isBlocked(m.target, this.blockClass) || isIgnored(n)) { return; } // removed node has not been serialized yet, just remove it from the Set @@ -582,9 +575,7 @@ export default class MutationBuffer { }; private genAdds = (n: Node | INode, target?: Node | INode) => { - if (isBlocked(n, this.blockClass)) { - return; - } + // parent was blocked, so we can ignore this node if (target && isBlocked(target, this.blockClass)) { return; } @@ -604,7 +595,11 @@ export default class MutationBuffer { this.addedSet.add(n); this.droppedSet.delete(n); } - n.childNodes.forEach((childN) => this.genAdds(childN)); + + // if this node is blocked `serializeNode` will turn it into a placeholder element + // but we have to remove it's children otherwise they will be added as placeholders too + if (!isBlocked(n, this.blockClass)) + n.childNodes.forEach((childN) => this.genAdds(childN)); }; } diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index ca16fefbef..d336859c8b 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -332,6 +332,212 @@ exports[`block 1`] = ` ]" `; +exports[`block 2 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Block record\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"rr-block\\", + \\"rr_width\\": \\"50px\\", + \\"rr_height\\": \\"50px\\" + }, + \\"childNodes\\": [], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 21 + } + ], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 22 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": 18, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": { + \\"class\\": \\"rr-block\\", + \\"rr_width\\": \\"100px\\", + \\"rr_height\\": \\"100px\\" + }, + \\"childNodes\\": [], + \\"id\\": 23 + } + } + ] + } + } +]" +`; + exports[`canvas 1`] = ` "[ { diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 5949b75325..723f890076 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -185,7 +185,9 @@ describe('record integration tests', function (this: ISuite) { // toggle the select box await page.click('.select2-container', { clickCount: 2, delay: 100 }); // test storage of !important style - await page.evaluate('document.getElementById("select2-drop").setAttribute("style", document.getElementById("select2-drop").style.cssText + "color:black !important")'); + await page.evaluate( + 'document.getElementById("select2-drop").setAttribute("style", document.getElementById("select2-drop").style.cssText + "color:black !important")', + ); const snapshots = await page.evaluate('window.snapshots'); assertSnapshot(snapshots, __filename, 'select2'); }); @@ -317,6 +319,26 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots, __filename, 'block'); }); + it('should not record blocked elements dynamically added', async () => { + const page: puppeteer.Page = await this.browser.newPage(); + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, 'block.html')); + + await page.evaluate(() => { + const el = document.createElement('button'); + el.className = 'rr-block'; + el.style.width = '100px'; + el.style.height = '100px'; + el.innerText = 'Should not be recorded'; + + const nextElement = document.querySelector('.rr-block')!; + nextElement.parentNode!.insertBefore(el, nextElement); + }); + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots, __filename, 'block 2'); + }); + it('should record DOM node movement 1', async () => { const page: puppeteer.Page = await this.browser.newPage(); await page.goto('about:blank');