diff --git a/.changeset/inlineImage-maybeNot-crossOrigin.md b/.changeset/inlineImage-maybeNot-crossOrigin.md new file mode 100644 index 0000000000..89eb7bb340 --- /dev/null +++ b/.changeset/inlineImage-maybeNot-crossOrigin.md @@ -0,0 +1,6 @@ +--- +"rrweb": patch +"rrweb-snapshot": patch +--- + +inlineImages: during snapshot avoid adding an event listener for inlining of same-origin images (async listener mutates the snapshot which can be problematic) diff --git a/.changeset/kind-kids-design.md b/.changeset/kind-kids-design.md new file mode 100644 index 0000000000..764d3fde1c --- /dev/null +++ b/.changeset/kind-kids-design.md @@ -0,0 +1,5 @@ +--- +"rrweb": patch +--- + +Optimize performance of isParentRemoved by converting it to an iterative procedure diff --git a/.changeset/no-neg-lookbehind.md b/.changeset/no-neg-lookbehind.md new file mode 100644 index 0000000000..f152328ed3 --- /dev/null +++ b/.changeset/no-neg-lookbehind.md @@ -0,0 +1,6 @@ +--- +"rrweb-snapshot": patch +"rrweb": patch +--- + +Replay: Replace negative lookbehind in regexes from css parser as it causes issues with Safari 16 diff --git a/.changeset/pre.json b/.changeset/pre.json index f752b6ec00..f9b75dbbdf 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -27,16 +27,22 @@ "cool-grapes-hug", "cuddly-readers-warn", "curvy-apples-lay", + "curvy-balloons-brake", "date-now-guard", "dirty-rules-dress", "eight-terms-hunt", + "eleven-bobcats-peel", "empty-bikes-cheer", + "event-single-wrap", "fair-dragons-greet", + "fair-ducks-clean", "fast-chefs-smell", + "fast-pets-exist", "few-rockets-travel", "few-turkeys-reflect", "five-peas-lay", "fluffy-planes-retire", + "format-head-prettier", "forty-elephants-attack", "fresh-cars-impress", "fresh-spoons-drive", @@ -45,6 +51,7 @@ "gold-terms-look", "grumpy-ways-own", "hip-worms-relax", + "hungry-dodos-taste", "itchy-dryers-double", "khaki-dots-bathe", "large-ants-prove", @@ -61,8 +68,10 @@ "mighty-ads-worry", "mighty-bulldogs-begin", "mighty-frogs-sparkle", + "modern-doors-watch", "moody-dots-refuse", "nervous-buses-pump", + "nervous-kiwis-nail", "nervous-mirrors-perform", "nervous-poets-grin", "nervous-tables-travel", diff --git a/.changeset/proud-clocks-hope.md b/.changeset/proud-clocks-hope.md new file mode 100644 index 0000000000..692b2081d0 --- /dev/null +++ b/.changeset/proud-clocks-hope.md @@ -0,0 +1,5 @@ +--- +"rrweb-snapshot": patch +--- + +(when `recordCanvas: true`): ensure we use doc.createElement instead of document.createElement to allow use in non-browser e.g. jsdom environments diff --git a/.changeset/shadow-dom-unbusify.md b/.changeset/shadow-dom-unbusify.md new file mode 100644 index 0000000000..ca84f9e55d --- /dev/null +++ b/.changeset/shadow-dom-unbusify.md @@ -0,0 +1,5 @@ +--- +"rrweb": patch +--- + +Refactor to preclude the need for a continuous raf loop running in the background which is related to shadowDom diff --git a/.changeset/silent-plants-perform.md b/.changeset/silent-plants-perform.md new file mode 100644 index 0000000000..1b234ec2a0 --- /dev/null +++ b/.changeset/silent-plants-perform.md @@ -0,0 +1,5 @@ +--- +"rrweb": patch +--- + +Return early for child same origin frames diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md index 3e550009cb..fd817ebee4 100644 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -1,5 +1,13 @@ # rrdom-nodejs +## 2.0.0-alpha.14 + +### Patch Changes + +- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`5e7943d`](https://github.com/rrweb-io/rrweb/commit/5e7943dbae6e2cde76c484bdd26bc0b96f1b6dce), [`c0f83af`](https://github.com/rrweb-io/rrweb/commit/c0f83afab8f1565633de0e986b7e96fa56f2d25c), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: + - rrweb-snapshot@2.0.0-alpha.14 + - rrdom@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Patch Changes diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md index aa73fc9c4a..f9b806983d 100644 --- a/packages/rrdom/CHANGELOG.md +++ b/packages/rrdom/CHANGELOG.md @@ -1,5 +1,12 @@ # rrdom +## 2.0.0-alpha.14 + +### Patch Changes + +- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`5e7943d`](https://github.com/rrweb-io/rrweb/commit/5e7943dbae6e2cde76c484bdd26bc0b96f1b6dce), [`c0f83af`](https://github.com/rrweb-io/rrweb/commit/c0f83afab8f1565633de0e986b7e96fa56f2d25c), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: + - rrweb-snapshot@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Patch Changes diff --git a/packages/rrvideo/CHANGELOG.md b/packages/rrvideo/CHANGELOG.md index 46339afe44..0322eca274 100644 --- a/packages/rrvideo/CHANGELOG.md +++ b/packages/rrvideo/CHANGELOG.md @@ -1,5 +1,12 @@ # rrvideo +## 2.0.0-alpha.14 + +### Patch Changes + +- Updated dependencies []: + - rrweb-player@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Patch Changes diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md index 31485fea43..ceed98cf3f 100644 --- a/packages/rrweb-player/CHANGELOG.md +++ b/packages/rrweb-player/CHANGELOG.md @@ -1,5 +1,12 @@ # rrweb-player +## 2.0.0-alpha.14 + +### Patch Changes + +- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`ae6908d`](https://github.com/rrweb-io/rrweb/commit/ae6908dcdcd7c732c1ce79eea19de5240bec1151), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: + - rrweb@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Patch Changes diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md index a04e46b87f..5c3eff7dee 100644 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -1,5 +1,26 @@ # rrweb-snapshot +## 2.0.0-alpha.14 + +### Patch Changes + +- [#1464](https://github.com/rrweb-io/rrweb/pull/1464) [`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91) Thanks [@colingm](https://github.com/colingm)! - better support for coexistence with older libraries (e.g. MooTools & Prototype.js) which modify the in-built `Array.from` function + +- [#1481](https://github.com/rrweb-io/rrweb/pull/1481) [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Fix and test for bug #1457 which was affecting replay of complex tailwind css + +- [#1476](https://github.com/rrweb-io/rrweb/pull/1476) [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Fixup for multiple background-clip replacement + +- [#1387](https://github.com/rrweb-io/rrweb/pull/1387) [`5e7943d`](https://github.com/rrweb-io/rrweb/commit/5e7943dbae6e2cde76c484bdd26bc0b96f1b6dce) Thanks [@H4ad](https://github.com/H4ad)! - Avoid recreating the same element every time, instead, we cache and we just update the element. + + Before: 779k ops/s + After: 860k ops/s + + Benchmark: https://jsbench.me/ktlqztuf95/1 + +- [#1440](https://github.com/rrweb-io/rrweb/pull/1440) [`c0f83af`](https://github.com/rrweb-io/rrweb/commit/c0f83afab8f1565633de0e986b7e96fa56f2d25c) Thanks [@daibhin](https://github.com/daibhin)! - better nested css selector splitting when commas or brackets happen to be in quoted text + +- [#1467](https://github.com/rrweb-io/rrweb/pull/1467) [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Bugfix after #1434 perf improvements: fix that blob urls persist on the shared anchor element and can't be later modified + ## 2.0.0-alpha.13 ### Minor Changes diff --git a/packages/rrweb-snapshot/src/css.ts b/packages/rrweb-snapshot/src/css.ts index 09d56a7001..aaf7e5b69d 100644 --- a/packages/rrweb-snapshot/src/css.ts +++ b/packages/rrweb-snapshot/src/css.ts @@ -898,8 +898,8 @@ export function parse(css: string, options: ParserOptions = {}) { name + '\\s*((?:' + [ - '(? { const ctx = (node as HTMLCanvasElement).getContext('2d'); if (ctx) { diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 8d7f891470..f3d900364a 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -956,7 +956,7 @@ function serializeElementNode( ); // create blank canvas of same dimensions - const blankCanvas = document.createElement('canvas'); + const blankCanvas = doc.createElement('canvas'); blankCanvas.width = (n as HTMLCanvasElement).width; blankCanvas.height = (n as HTMLCanvasElement).height; const blankCanvasDataURL = blankCanvas.toDataURL( @@ -977,8 +977,9 @@ function serializeElementNode( canvasCtx = canvasService.getContext('2d'); } const image = n as HTMLImageElement; - const oldValue = image.crossOrigin; - image.crossOrigin = 'anonymous'; + const imageSrc: string = + image.currentSrc || image.getAttribute('src') || ''; + const priorCrossOrigin = image.crossOrigin; const recordInlineImage = () => { image.removeEventListener('load', recordInlineImage); try { @@ -990,13 +991,23 @@ function serializeElementNode( dataURLOptions.quality, ); } catch (err) { - console.warn( - `Cannot inline img src=${image.currentSrc}! Error: ${err as string}`, - ); + if (image.crossOrigin !== 'anonymous') { + image.crossOrigin = 'anonymous'; + if (image.complete && image.naturalWidth !== 0) + recordInlineImage(); // too early due to image reload + else image.addEventListener('load', recordInlineImage); + return; + } else { + console.warn( + `Cannot inline img src=${imageSrc}! Error: ${err as string}`, + ); + } + } + if (image.crossOrigin === 'anonymous') { + priorCrossOrigin + ? (attributes.crossOrigin = priorCrossOrigin) + : image.removeAttribute('crossorigin'); } - oldValue - ? (attributes.crossOrigin = oldValue) - : image.removeAttribute('crossorigin'); }; // The image content may not have finished loading yet. if (image.complete && image.naturalWidth !== 0) recordInlineImage(); diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index 5412826dac..db8b3040c7 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -413,6 +413,7 @@ exports[`integration tests [html file]: mask-text.html 1`] = ` exports[`integration tests [html file]: picture.html 1`] = ` " + diff --git a/packages/rrweb-snapshot/test/html/picture.html b/packages/rrweb-snapshot/test/html/picture.html index e005310b77..2401ca0c61 100644 --- a/packages/rrweb-snapshot/test/html/picture.html +++ b/packages/rrweb-snapshot/test/html/picture.html @@ -1,6 +1,7 @@ + diff --git a/packages/rrweb-snapshot/test/images/rrweb-favicon-20x20.png b/packages/rrweb-snapshot/test/images/rrweb-favicon-20x20.png new file mode 100644 index 0000000000..561f9060d7 Binary files /dev/null and b/packages/rrweb-snapshot/test/images/rrweb-favicon-20x20.png differ diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index dcc6a3ec0b..f212b0fd4c 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -6,7 +6,7 @@ import * as puppeteer from 'puppeteer'; import * as rollup from 'rollup'; import * as typescript from 'rollup-plugin-typescript2'; import * as assert from 'assert'; -import { waitForRAF } from './utils'; +import { waitForRAF, getServerURL } from './utils'; const _typescript = typescript as unknown as () => rollup.Plugin; @@ -209,12 +209,63 @@ iframe.contentDocument.querySelector('center').clientHeight inlineImages: true, inlineStylesheet: false })`); - await waitForRAF(page); - const snapshot = (await page.evaluate( - 'JSON.stringify(snapshot, null, 2);', - )) as string; - assert(snapshot.includes('"rr_dataURL"')); - assert(snapshot.includes('data:image/webp;base64,')); + // don't wait, as we want to ensure that the same-origin image can be inlined immediately + const bodyChildren = (await page.evaluate(` + snapshot.childNodes[0].childNodes[1].childNodes.filter((cn) => cn.type === 2); +`)) as any[]; + expect(bodyChildren[1]).toEqual( + expect.objectContaining({ + tagName: 'img', + attributes: { + src: expect.stringMatching(/images\/robot.png$/), + alt: 'This is a robot', + rr_dataURL: expect.stringMatching(/^data:image\/webp;base64,/), + }, + }), + ); + }); + + it('correctly saves cross-origin images offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('about:blank', { + waitUntil: 'load', + }); + await page.setContent( + ` + + + CORS restricted but has access-control-allow-origin: * + + +`, + { + waitUntil: 'load', + }, + ); + + await page.waitForSelector('img', { timeout: 1000 }); + await page.evaluate(`${code}var snapshot = rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false + })`); + await waitForRAF(page); // need a small wait, as after the crossOrigin="anonymous" change, the snapshot triggers a reload of the image (after which, the snapshot is mutated) + const bodyChildren = (await page.evaluate(` + snapshot.childNodes[0].childNodes[1].childNodes.filter((cn) => cn.type === 2); +`)) as any[]; + expect(bodyChildren[0]).toEqual( + expect.objectContaining({ + tagName: 'img', + attributes: { + src: getServerURL(server) + '/images/rrweb-favicon-20x20.png', + alt: 'CORS restricted but has access-control-allow-origin: *', + rr_dataURL: expect.stringMatching(/^data:image\/webp;base64,/), + }, + }), + ); }); it('correctly saves blob:images offline', async () => { diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index e3190ece3f..d8984029e4 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -9,6 +9,7 @@ import { _isBlockedElement, needMaskingText, } from '../src/snapshot'; +import snapshot from '../src/snapshot'; import { serializedNodeWithId } from '../src/types'; import { Mirror } from '../src/utils'; @@ -578,3 +579,27 @@ describe('needMaskingText', () => { }); }); }); + +describe('jsdom snapshot', () => { + const render = (html: string): Document => { + document.write(html); + return document; + }; + + it("doesn't rely on global browser objects", () => { + // this test is incomplete in terms of coverage, + // but the idea being that we are checking that all features use the + // passed-in `doc` object rather than the global `document` + // (which is only present in browsers) + // in any case, supporting jsdom is not a primary goal + + const doc = render(`

Hello world

`); + const sn = snapshot(doc, { + // JSDOM Error: Not implemented: HTMLCanvasElement.prototype.toDataURL (without installing the canvas npm package) + //recordCanvas: true, + }); + expect(sn).toMatchObject({ + type: 0, + }); + }); +}); diff --git a/packages/rrweb-snapshot/test/utils.ts b/packages/rrweb-snapshot/test/utils.ts index 43d4484bb4..631f8640a6 100644 --- a/packages/rrweb-snapshot/test/utils.ts +++ b/packages/rrweb-snapshot/test/utils.ts @@ -1,4 +1,5 @@ import * as puppeteer from 'puppeteer'; +import * as http from 'http'; export async function waitForRAF(page: puppeteer.Page) { return await page.evaluate(() => { @@ -9,3 +10,12 @@ export async function waitForRAF(page: puppeteer.Page) { }); }); } + +export function getServerURL(server: http.Server): string { + const address = server.address(); + if (address && typeof address !== 'string') { + return `http://localhost:${address.port}`; + } else { + return `${address}`; + } +} diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index f3cabd4890..72e0e417b7 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,24 @@ # rrweb +## 2.0.0-alpha.14 + +### Patch Changes + +- [#1464](https://github.com/rrweb-io/rrweb/pull/1464) [`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91) Thanks [@colingm](https://github.com/colingm)! - better support for coexistence with older libraries (e.g. MooTools & Prototype.js) which modify the in-built `Array.from` function + +- [#1441](https://github.com/rrweb-io/rrweb/pull/1441) [`ae6908d`](https://github.com/rrweb-io/rrweb/commit/ae6908dcdcd7c732c1ce79eea19de5240bec1151) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - perf: Avoid an extra function call and object clone during event emission + +- [#1481](https://github.com/rrweb-io/rrweb/pull/1481) [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Fix and test for bug #1457 which was affecting replay of complex tailwind css + +- [#1476](https://github.com/rrweb-io/rrweb/pull/1476) [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Fixup for multiple background-clip replacement + +- [#1467](https://github.com/rrweb-io/rrweb/pull/1467) [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Bugfix after #1434 perf improvements: fix that blob urls persist on the shared anchor element and can't be later modified + +- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`5e7943d`](https://github.com/rrweb-io/rrweb/commit/5e7943dbae6e2cde76c484bdd26bc0b96f1b6dce), [`c0f83af`](https://github.com/rrweb-io/rrweb/commit/c0f83afab8f1565633de0e986b7e96fa56f2d25c), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: + - rrweb-snapshot@2.0.0-alpha.14 + - rrdom@2.0.0-alpha.14 + - @rrweb/types@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Minor Changes diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index c9e26e714b..b4c348e327 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -151,6 +151,11 @@ function record( if (inEmittingFrame && !emit) { throw new Error('emit function is required'); } + if (!inEmittingFrame && !passEmitsToParent) { + return () => { + /* no-op since in this case we don't need to record anything from this frame in particular */ + }; + } // move departed options to new options if (mousemoveWait !== undefined && sampling.mousemove === undefined) { sampling.mousemove = mousemoveWait; diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 58cb7e4137..2b7b0bcdb0 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -861,15 +861,15 @@ function _isParentRemoved( n: Node, mirror: Mirror, ): boolean { - const { parentNode } = n; - if (!parentNode) { - return false; - } - const parentId = mirror.getId(parentNode); - if (removes.some((r) => r.id === parentId)) { - return true; + let node: ParentNode | null = n.parentNode; + while (node) { + const parentId = mirror.getId(node); + if (removes.some((r) => r.id === parentId)) { + return true; + } + node = node.parentNode; } - return _isParentRemoved(removes, parentNode, mirror); + return false; } function isAncestorInSet(set: Set, n: Node): boolean { diff --git a/packages/rrweb/src/record/processed-node-manager.ts b/packages/rrweb/src/record/processed-node-manager.ts index 89e63ea881..ad4ec205ba 100644 --- a/packages/rrweb/src/record/processed-node-manager.ts +++ b/packages/rrweb/src/record/processed-node-manager.ts @@ -6,19 +6,8 @@ import type MutationBuffer from './mutation'; */ export default class ProcessedNodeManager { private nodeMap: WeakMap> = new WeakMap(); - // Whether to continue RAF loop. - private loop = true; - constructor() { - this.periodicallyClear(); - } - - private periodicallyClear() { - onRequestAnimationFrame(() => { - this.clear(); - if (this.loop) this.periodicallyClear(); - }); - } + private active = false; public inOtherBuffer(node: Node, thisBuffer: MutationBuffer) { const buffers = this.nodeMap.get(node); @@ -28,15 +17,17 @@ export default class ProcessedNodeManager { } public add(node: Node, buffer: MutationBuffer) { + if (!this.active) { + this.active = true; + onRequestAnimationFrame(() => { + this.nodeMap = new WeakMap(); + this.active = false; + }); + } this.nodeMap.set(node, (this.nodeMap.get(node) || new Set()).add(buffer)); } - private clear() { - this.nodeMap = new WeakMap(); - } - public destroy() { - // Stop the RAF loop. - this.loop = false; + // cleanup no longer needed } } diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index bd6c008c44..11e83090a0 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -18037,40 +18037,6 @@ exports[`record integration tests should record images inside iframe with blob u } ] } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 41, - \\"attributes\\": { - \\"crossorigin\\": \\"anonymous\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 41, - \\"attributes\\": { - \\"crossorigin\\": null - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } } ]" `; @@ -18505,40 +18471,6 @@ exports[`record integration tests should record images inside iframe with blob u } ] } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 47, - \\"attributes\\": { - \\"crossorigin\\": \\"anonymous\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 47, - \\"attributes\\": { - \\"crossorigin\\": null - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } } ]" `; @@ -18746,40 +18678,6 @@ exports[`record integration tests should record images with blob url 1`] = ` } ] } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 24, - \\"attributes\\": { - \\"crossorigin\\": \\"anonymous\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 24, - \\"attributes\\": { - \\"crossorigin\\": null - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } } ]" `; diff --git a/packages/rrweb/test/benchmark/dom-mutation.test.ts b/packages/rrweb/test/benchmark/dom-mutation.test.ts index 94019c6d4c..909b09ae3a 100644 --- a/packages/rrweb/test/benchmark/dom-mutation.test.ts +++ b/packages/rrweb/test/benchmark/dom-mutation.test.ts @@ -22,6 +22,12 @@ const suites: Array< // eval: 'document.querySelector("button").click()', // times: 10, // }, + { + title: 'create 1000x 1 DOM nodes with deeply nested children', + html: 'benchmark-dom-mutation-deep-nested.html', + eval: 'window.workload()', + times: 10, + }, { title: 'create 1000x10 DOM nodes', html: 'benchmark-dom-mutation.html', diff --git a/packages/rrweb/test/html/benchmark-dom-mutation-deep-nested.html b/packages/rrweb/test/html/benchmark-dom-mutation-deep-nested.html new file mode 100644 index 0000000000..fd0a4258b2 --- /dev/null +++ b/packages/rrweb/test/html/benchmark-dom-mutation-deep-nested.html @@ -0,0 +1,31 @@ + + + + diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index e0966d11bc..5f81e6862d 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,12 @@ # @rrweb/types +## 2.0.0-alpha.14 + +### Patch Changes + +- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`5e7943d`](https://github.com/rrweb-io/rrweb/commit/5e7943dbae6e2cde76c484bdd26bc0b96f1b6dce), [`c0f83af`](https://github.com/rrweb-io/rrweb/commit/c0f83afab8f1565633de0e986b7e96fa56f2d25c), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: + - rrweb-snapshot@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Patch Changes diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index 0e09b1fa4b..7c1c0f9b5e 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,13 @@ # @rrweb/web-extension +## 2.0.0-alpha.14 + +### Patch Changes + +- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`ae6908d`](https://github.com/rrweb-io/rrweb/commit/ae6908dcdcd7c732c1ce79eea19de5240bec1151), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: + - rrweb@2.0.0-alpha.14 + - rrweb-player@2.0.0-alpha.14 + ## 2.0.0-alpha.13 ### Patch Changes