From 99c508607ded92e518662a191f3a285c76ae0dfa Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 31 May 2022 18:09:37 +0200 Subject: [PATCH 1/2] inline stylesheets when loaded set empty link elements to loaded by default Clean up stylesheet manager Remove attribute mutation code Update packages/rrweb/test/record.test.ts Update packages/rrweb/test/record.test.ts Update packages/rrweb/test/record.test.ts Bump minimist from 1.2.5 to 1.2.6 (#902) Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Speed up snapshotting of many new dom nodes (#903) * Speed up snapshotting of many new dom nodes By avoiding reflow we shave about 15-25% off our snapshotting time * Improve newlyAddedElement docs * Optimize needMaskingText by using el.closest and less recursion * Serve all rrweb dist files * Split serializeNode into smaller functions Makes it easier to profile * Slow down cpu enhance tracing on fast machines * Increase timeout * Perf: only loop through ancestors when they have something to compare to * Perf: `hasNode` is cheaper than `getMeta` * Perf: If parents where already checked, no need to do it again * Perf: reverse for loops are faster Because they only do the .lenght check once. In this case I don't think we'll see much performance gains if any * Clean up code * Perf: check ancestors once with isBlocked * guessing this might fixes canvas test * Update packages/rrweb/src/record/observers/canvas/webgl.ts Co-authored-by: yz-yu * Fix #904 (#906) Properly remove crossorigin attribute * Bump minimist from 1.2.5 to 1.2.6 (#902) Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: yz-yu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Move require out of time sensitive assert Update packages/rrweb/scripts/repl.js Update packages/rrweb/test/record.test.ts Update packages/rrweb/src/record/index.ts Add todo Add waitForRAF, its more reliable than waitForTimeout Remove flaky tests Add recording stylesheets in iframes Remove variability from flaky test Make test more robust Fix naming Update dive-into-event.md (#914) move browser-only rrdom features to the new rrdom package (#913) integrate turborepo in monorepo (#918) * integrate turborepo in monorepo * integrate turborepo in monorepo unify typescript version and rollup plugins (#921) Add test cases for inlineImages Add test cases for inlineImages Record iframe mutations cross page Test: should record images inside iframe with blob url after iframe was reloaded --- .gitignore | 2 + .vscode/rrweb-monorepo.code-workspace | 4 + docs/recipes/dive-into-event.md | 2 +- package.json | 16 +- packages/rrdom-nodejs/.gitignore | 4 + packages/rrdom-nodejs/.vscode/extensions.json | 3 + packages/rrdom-nodejs/.vscode/launch.json | 15 + packages/rrdom-nodejs/.vscode/settings.json | 3 + packages/rrdom-nodejs/jest.config.js | 5 + packages/rrdom-nodejs/package.json | 55 + packages/rrdom-nodejs/rollup.config.js | 84 + .../src/document-nodejs.ts | 8 +- packages/rrdom-nodejs/src/index.ts | 13 + .../{rrdom => rrdom-nodejs}/src/polyfill.ts | 0 .../test/document-nodejs.test.ts | 4 +- .../test/polyfill.test.ts | 0 packages/rrdom-nodejs/tsconfig.json | 20 + packages/rrdom/package.json | 43 +- packages/rrdom/rollup.config.js | 10 - packages/rrdom/src/diff.ts | 2 +- packages/rrdom/src/index.ts | 461 +++- packages/rrdom/src/virtual-dom.ts | 450 --- packages/rrdom/test/diff.test.ts | 2 +- packages/rrdom/test/virtual-dom.test.ts | 8 +- packages/rrdom/tsconfig.json | 2 +- packages/rrweb-player/package.json | 4 +- packages/rrweb-player/rollup.config.js | 3 +- packages/rrweb-snapshot/package.json | 4 +- packages/rrweb-snapshot/rollup.config.js | 2 +- packages/rrweb-snapshot/src/snapshot.ts | 7 +- .../test/html/picture-blob-in-frame.html | 5 + .../test/html/picture-blob.html | 16 + .../test/html/picture-in-frame.html | 5 + .../rrweb-snapshot/test/integration.test.ts | 73 +- packages/rrweb-snapshot/test/utils.ts | 11 + packages/rrweb/jest.config.js | 1 - packages/rrweb/package.json | 2 +- packages/rrweb/src/record/index.ts | 6 + packages/rrweb/src/record/mutation.ts | 17 +- .../rrweb/src/record/stylesheet-manager.ts | 5 +- packages/rrweb/src/replay/index.ts | 19 +- packages/rrweb/src/types.ts | 4 +- packages/rrweb/src/utils.ts | 2 +- .../__snapshots__/integration.test.ts.snap | 2423 +++++++++++++---- packages/rrweb/test/html/assets/robot.png | Bin 0 -> 11004 bytes .../rrweb/test/html/frame-image-blob-url.html | 11 + packages/rrweb/test/html/image-blob-url.html | 21 + packages/rrweb/test/integration.test.ts | 82 +- packages/rrweb/test/utils.ts | 57 +- packages/rrweb/typings/record/mutation.d.ts | 1 + packages/rrweb/typings/replay/index.d.ts | 2 +- packages/rrweb/typings/types.d.ts | 5 +- packages/rrweb/typings/utils.d.ts | 2 +- turbo.json | 19 + yarn.lock | 151 +- 55 files changed, 3053 insertions(+), 1123 deletions(-) create mode 100644 packages/rrdom-nodejs/.gitignore create mode 100644 packages/rrdom-nodejs/.vscode/extensions.json create mode 100644 packages/rrdom-nodejs/.vscode/launch.json create mode 100644 packages/rrdom-nodejs/.vscode/settings.json create mode 100644 packages/rrdom-nodejs/jest.config.js create mode 100644 packages/rrdom-nodejs/package.json create mode 100644 packages/rrdom-nodejs/rollup.config.js rename packages/{rrdom => rrdom-nodejs}/src/document-nodejs.ts (98%) create mode 100644 packages/rrdom-nodejs/src/index.ts rename packages/{rrdom => rrdom-nodejs}/src/polyfill.ts (100%) rename packages/{rrdom => rrdom-nodejs}/test/document-nodejs.test.ts (99%) rename packages/{rrdom => rrdom-nodejs}/test/polyfill.test.ts (100%) create mode 100644 packages/rrdom-nodejs/tsconfig.json delete mode 100644 packages/rrdom/src/virtual-dom.ts create mode 100644 packages/rrweb-snapshot/test/html/picture-blob-in-frame.html create mode 100644 packages/rrweb-snapshot/test/html/picture-blob.html create mode 100644 packages/rrweb-snapshot/test/html/picture-in-frame.html create mode 100644 packages/rrweb-snapshot/test/utils.ts create mode 100644 packages/rrweb/test/html/assets/robot.png create mode 100644 packages/rrweb/test/html/frame-image-blob-url.html create mode 100644 packages/rrweb/test/html/image-blob-url.html create mode 100644 turbo.json diff --git a/.gitignore b/.gitignore index 895e1115..80867609 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ temp build dist + +.turbo diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index 1d100ed3..896a62e5 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -8,6 +8,10 @@ "name": "rrdom (package)", "path": "../packages/rrdom" }, + { + "name": "rrdom-nodejs (package)", + "path": "../packages/rrdom-nodejs" + }, { "name": "rrweb (package)", "path": "../packages/rrweb" diff --git a/docs/recipes/dive-into-event.md b/docs/recipes/dive-into-event.md index b1044261..4df9152c 100644 --- a/docs/recipes/dive-into-event.md +++ b/docs/recipes/dive-into-event.md @@ -67,4 +67,4 @@ source -> IncrementalSource.Font data -> fontData ``` -enum IncrementalSource's definition can be found in this [list](https://github.com/rrweb-io/rrweb/blob/master/src/types.ts#L64). +enum IncrementalSource's definition can be found in this [list](https://github.com/rrweb-io/rrweb/blob/master/packages/rrweb/typings/types.d.ts#L62). diff --git a/package.json b/package.json index b22370ae..8574443a 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,7 @@ "private": true, "homepage": "https://github.com/rrweb-io/rrweb#readme", "workspaces": [ - "packages/rrweb", - "packages/rrweb-snapshot", - "packages/rrweb-player", - "packages/rrdom" + "packages/*" ], "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.25.0", @@ -29,14 +26,15 @@ "lerna": "^4.0.0", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", - "typescript": "^4.6.4" + "turbo": "^1.2.4", + "typescript": "^4.7.3" }, "scripts": { "lerna": "lerna", - "build:all": "yarn lerna run prepublish", - "test": "yarn lerna run test", - "test:watch": "yarn lerna run test:watch --parallel", - "dev": "yarn lerna run dev --parallel", + "build:all": "yarn turbo run prepublish", + "test": "yarn turbo run test", + "test:watch": "yarn turbo run test:watch", + "dev": "yarn turbo run dev", "repl": "cd packages/rrweb && npm run repl", "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'", "clean": "rm -rf dist node_modules packages/**/build packages/**/es packages/**/dist packages/**/lib packages/**/node_modules" diff --git a/packages/rrdom-nodejs/.gitignore b/packages/rrdom-nodejs/.gitignore new file mode 100644 index 00000000..fb77b328 --- /dev/null +++ b/packages/rrdom-nodejs/.gitignore @@ -0,0 +1,4 @@ +dist +es +lib +typings diff --git a/packages/rrdom-nodejs/.vscode/extensions.json b/packages/rrdom-nodejs/.vscode/extensions.json new file mode 100644 index 00000000..bd47279f --- /dev/null +++ b/packages/rrdom-nodejs/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["orta.vscode-jest"] +} diff --git a/packages/rrdom-nodejs/.vscode/launch.json b/packages/rrdom-nodejs/.vscode/launch.json new file mode 100644 index 00000000..25d2357c --- /dev/null +++ b/packages/rrdom-nodejs/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "type": "node", + "name": "vscode-jest-tests", + "request": "launch", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "cwd": "${workspaceFolder}", + "runtimeExecutable": "yarn", + "args": ["test", "--runInBand", "--watchAll=false"] + } + ] +} diff --git a/packages/rrdom-nodejs/.vscode/settings.json b/packages/rrdom-nodejs/.vscode/settings.json new file mode 100644 index 00000000..634876ef --- /dev/null +++ b/packages/rrdom-nodejs/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "jest.jestCommandLine": "yarn test" +} diff --git a/packages/rrdom-nodejs/jest.config.js b/packages/rrdom-nodejs/jest.config.js new file mode 100644 index 00000000..e86e13ba --- /dev/null +++ b/packages/rrdom-nodejs/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json new file mode 100644 index 00000000..9c9c8903 --- /dev/null +++ b/packages/rrdom-nodejs/package.json @@ -0,0 +1,55 @@ +{ + "name": "rrdom-nodejs", + "version": "0.1.2", + "scripts": { + "dev": "rollup -c -w", + "bundle": "rollup --config", + "bundle:es-only": "cross-env ES_ONLY=true rollup --config", + "check-types": "tsc -noEmit", + "test": "jest", + "prepublish": "npm run bundle", + "lint": "yarn eslint src/**/*.ts" + }, + "keywords": [ + "rrweb", + "rrdom-nodejs" + ], + "license": "MIT", + "main": "lib/rrdom-nodejs.js", + "module": "es/rrdom-nodejs.js", + "typings": "es", + "files": [ + "dist", + "lib", + "es", + "typings" + ], + "devDependencies": { + "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-node-resolve": "^13.0.4", + "@types/cssom": "^0.4.1", + "@types/cssstyle": "^2.2.1", + "@types/jest": "^27.4.1", + "@types/nwsapi": "^2.2.2", + "@types/puppeteer": "^5.4.4", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", + "compare-versions": "^4.1.3", + "eslint": "^8.15.0", + "jest": "^27.5.1", + "puppeteer": "^9.1.1", + "rollup": "^2.56.3", + "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-typescript2": "^0.31.2", + "rollup-plugin-web-worker-loader": "^1.6.1", + "ts-jest": "^27.1.3", + "typescript": "^4.7.3" + }, + "dependencies": { + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "nwsapi": "^2.2.0", + "rrweb-snapshot": "^1.1.14", + "rrdom": "^0.1.2" + } +} diff --git a/packages/rrdom-nodejs/rollup.config.js b/packages/rrdom-nodejs/rollup.config.js new file mode 100644 index 00000000..233b0f1f --- /dev/null +++ b/packages/rrdom-nodejs/rollup.config.js @@ -0,0 +1,84 @@ +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import { terser } from 'rollup-plugin-terser'; +import typescript from 'rollup-plugin-typescript2'; +import webWorkerLoader from 'rollup-plugin-web-worker-loader'; +import pkg from './package.json'; + +function toMinPath(path) { + return path.replace(/\.js$/, '.min.js'); +} + +const basePlugins = [ + resolve({ browser: true }), + commonjs(), + + // supports bundling `web-worker:..filename` from rrweb + webWorkerLoader(), + + typescript({ + tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, + }), +]; + +const baseConfigs = [ + { + input: './src/index.ts', + name: pkg.name, + path: pkg.name, + }, + { + input: './src/document-nodejs.ts', + name: 'RRDocument', + path: 'document-nodejs', + }, +]; + +let configs = []; +let extraConfigs = []; +for (let config of baseConfigs) { + configs.push( + // ES module + { + input: config.input, + plugins: basePlugins, + output: [ + { + format: 'esm', + file: pkg.module.replace(pkg.name, config.path), + }, + ], + }, + ); + extraConfigs.push( + // CommonJS + { + input: config.input, + plugins: basePlugins, + output: [ + { + format: 'cjs', + file: pkg.main.replace(pkg.name, config.path), + }, + ], + }, + // ES module (packed) + { + input: config.input, + plugins: basePlugins.concat(terser()), + output: [ + { + format: 'esm', + file: toMinPath(pkg.module).replace(pkg.name, config.path), + sourcemap: true, + }, + ], + }, + ); +} + +if (!process.env.ES_ONLY) { + configs.push(...extraConfigs); +} + +export default configs; diff --git a/packages/rrdom/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts similarity index 98% rename from packages/rrdom/src/document-nodejs.ts rename to packages/rrdom-nodejs/src/document-nodejs.ts index 6bc34cdc..277810ae 100644 --- a/packages/rrdom/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -13,7 +13,7 @@ import { ClassList, IRRDocument, CSSStyleDeclaration, -} from './document'; +} from 'rrdom'; const nwsapi = require('nwsapi'); const cssom = require('cssom'); const cssstyle = require('cssstyle'); @@ -53,22 +53,27 @@ export class RRDocument return this._nwsapi; } + // @ts-ignore get documentElement(): RRElement | null { return super.documentElement as RRElement | null; } + // @ts-ignore get body(): RRElement | null { return super.body as RRElement | null; } + // @ts-ignore get head() { return super.head as RRElement | null; } + // @ts-ignore get implementation(): RRDocument { return this; } + // @ts-ignore get firstElementChild(): RRElement | null { return this.documentElement; } @@ -198,6 +203,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) { }); } + // @ts-ignore get style() { return (this._style as unknown) as CSSStyleDeclaration; } diff --git a/packages/rrdom-nodejs/src/index.ts b/packages/rrdom-nodejs/src/index.ts new file mode 100644 index 00000000..ac14bf73 --- /dev/null +++ b/packages/rrdom-nodejs/src/index.ts @@ -0,0 +1,13 @@ +import { + polyfillPerformance, + polyfillRAF, + polyfillEvent, + polyfillNode, + polyfillDocument, +} from './polyfill'; +polyfillPerformance(); +polyfillRAF(); +polyfillEvent(); +polyfillNode(); +polyfillDocument(); +export * from './document-nodejs'; diff --git a/packages/rrdom/src/polyfill.ts b/packages/rrdom-nodejs/src/polyfill.ts similarity index 100% rename from packages/rrdom/src/polyfill.ts rename to packages/rrdom-nodejs/src/polyfill.ts diff --git a/packages/rrdom/test/document-nodejs.test.ts b/packages/rrdom-nodejs/test/document-nodejs.test.ts similarity index 99% rename from packages/rrdom/test/document-nodejs.test.ts rename to packages/rrdom-nodejs/test/document-nodejs.test.ts index 36605446..ea064c10 100644 --- a/packages/rrdom/test/document-nodejs.test.ts +++ b/packages/rrdom-nodejs/test/document-nodejs.test.ts @@ -16,7 +16,7 @@ import { RRStyleElement, RRText, } from '../src/document-nodejs'; -import { buildFromDom } from '../src/virtual-dom'; +import { buildFromDom } from 'rrdom'; describe('RRDocument for nodejs environment', () => { describe('RRDocument API', () => { @@ -542,6 +542,6 @@ describe('RRDocument for nodejs environment', () => { }); function getHtml(fileName: string) { - const filePath = path.resolve(__dirname, `./html/${fileName}`); + const filePath = path.resolve(__dirname, `../../rrdom/test/html/${fileName}`); return fs.readFileSync(filePath, 'utf8'); } diff --git a/packages/rrdom/test/polyfill.test.ts b/packages/rrdom-nodejs/test/polyfill.test.ts similarity index 100% rename from packages/rrdom/test/polyfill.test.ts rename to packages/rrdom-nodejs/test/polyfill.test.ts diff --git a/packages/rrdom-nodejs/tsconfig.json b/packages/rrdom-nodejs/tsconfig.json new file mode 100644 index 00000000..4a4f18a0 --- /dev/null +++ b/packages/rrdom-nodejs/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs", + "noImplicitAny": true, + "strictNullChecks": true, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": true, + "rootDir": "src", + "outDir": "build", + "lib": ["es6", "dom"], + "skipLibCheck": true, + "declaration": true, + "importsNotUsedAsValues": "error" + }, + "compileOnSave": true, + "exclude": ["test"], + "include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"] +} diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index cffffc36..5075a649 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,19 +1,7 @@ { "name": "@highlight-run/rrdom", "version": "0.1.11", - "scripts": { - "dev": "rollup -c -w", - "bundle": "rollup --config", - "bundle:es-only": "cross-env ES_ONLY=true rollup --config", - "check-types": "tsc -noEmit", - "test": "jest", - "prepublish": "npm run bundle", - "lint": "yarn eslint src/**/*.ts" - }, - "keywords": [ - "rrweb", - "rrdom" - ], + "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.js", "module": "es/rrdom.js", @@ -25,18 +13,29 @@ "es", "typings" ], + "repository": { + "type": "git", + "url": "git+https://github.com/rrweb-io/rrweb.git" + }, + "scripts": { + "dev": "rollup -c -w", + "bundle": "rollup --config", + "bundle:es-only": "cross-env ES_ONLY=true rollup --config", + "check-types": "tsc -noEmit", + "test": "jest", + "prepublish": "npm run bundle", + "lint": "yarn eslint src/**/*.ts" + }, + "bugs": { + "url": "https://github.com/rrweb-io/rrweb/issues" + }, "devDependencies": { "@highlight-run/rrweb-snapshot": ">1.1.15", "@rollup/plugin-commonjs": "^20.0.0", - "@rollup/plugin-node-resolve": "^13.0.4", - "@types/cssom": "^0.4.1", - "@types/cssstyle": "^2.2.1", "@types/jest": "^27.4.1", - "@types/nwsapi": "^2.2.2", - "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", - "compare-versions": "^4.1.3", + "@types/puppeteer": "^5.4.4", "eslint": "^8.15.0", "jest": "^27.5.1", "puppeteer": "^9.1.1", @@ -45,12 +44,10 @@ "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", "ts-jest": "^27.1.3", - "typescript": "^4.6.2" + "typescript": "^4.7.3" }, "dependencies": { - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "nwsapi": "^2.2.0" + "rrweb-snapshot": "^1.1.14" }, "gitHead": "c43f25b8babc23a90f15f8224248c2fe9c757537" } diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index 465231e0..a69dc3ad 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -27,16 +27,6 @@ const baseConfigs = [ name: 'rrdom', path: 'rrdom', }, - { - input: './src/document-nodejs.ts', - name: 'RRDocument', - path: 'document-nodejs', - }, - { - input: './src/virtual-dom.ts', - name: 'RRDocument', - path: 'virtual-dom', - }, ]; let configs = []; diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 6e0744ae..3de6fe0f 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -25,7 +25,7 @@ import type { RRStyleElement, RRDocument, Mirror, -} from './virtual-dom'; +} from '.'; const NAMESPACES: Record = { svg: 'http://www.w3.org/2000/svg', diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index ac14bf73..c81f6af6 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -1,13 +1,450 @@ import { - polyfillPerformance, - polyfillRAF, - polyfillEvent, - polyfillNode, - polyfillDocument, -} from './polyfill'; -polyfillPerformance(); -polyfillRAF(); -polyfillEvent(); -polyfillNode(); -polyfillDocument(); -export * from './document-nodejs'; + NodeType as RRNodeType, + createMirror as createNodeMirror, +} from 'rrweb-snapshot'; +import type { + Mirror as NodeMirror, + IMirror, + serializedNodeWithId, +} from 'rrweb-snapshot'; +import type { + canvasMutationData, + canvasEventWithTime, + inputData, + scrollData, +} from 'rrweb/src/types'; +import type { VirtualStyleRules } from './diff'; +import { + BaseRRNode as RRNode, + BaseRRCDATASectionImpl, + BaseRRCommentImpl, + BaseRRDocumentImpl, + BaseRRDocumentTypeImpl, + BaseRRElementImpl, + BaseRRMediaElementImpl, + BaseRRTextImpl, + IRRDocument, + IRRElement, + IRRNode, + NodeType, + IRRDocumentType, + IRRText, + IRRComment, +} from './document'; + +export class RRDocument extends BaseRRDocumentImpl(RRNode) { + // In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules. + // These unserialized nodes may interfere the execution of the diff algorithm. + // The id of serialized node is larger than 0. So this value ​​less than 0 is used as id for these unserialized nodes. + private _unserializedId = -1; + + /** + * Every time the id is used, it will minus 1 automatically to avoid collisions. + */ + public get unserializedId(): number { + return this._unserializedId--; + } + + public mirror: Mirror = createMirror(); + + public scrollData: scrollData | null = null; + + constructor(mirror?: Mirror) { + super(); + if (mirror) { + this.mirror = mirror; + } + } + + createDocument( + _namespace: string | null, + _qualifiedName: string | null, + _doctype?: DocumentType | null, + ) { + return new RRDocument(); + } + + createDocumentType( + qualifiedName: string, + publicId: string, + systemId: string, + ) { + const documentTypeNode = new RRDocumentType( + qualifiedName, + publicId, + systemId, + ); + documentTypeNode.ownerDocument = this; + return documentTypeNode; + } + + createElement( + tagName: K, + ): RRElementType; + createElement(tagName: string): RRElement; + createElement(tagName: string) { + const upperTagName = tagName.toUpperCase(); + let element; + switch (upperTagName) { + case 'AUDIO': + case 'VIDEO': + element = new RRMediaElement(upperTagName); + break; + case 'IFRAME': + element = new RRIFrameElement(upperTagName, this.mirror); + break; + case 'CANVAS': + element = new RRCanvasElement(upperTagName); + break; + case 'STYLE': + element = new RRStyleElement(upperTagName); + break; + default: + element = new RRElement(upperTagName); + break; + } + element.ownerDocument = this; + return element; + } + + createComment(data: string) { + const commentNode = new RRComment(data); + commentNode.ownerDocument = this; + return commentNode; + } + + createCDATASection(data: string) { + const sectionNode = new RRCDATASection(data); + sectionNode.ownerDocument = this; + return sectionNode; + } + + createTextNode(data: string) { + const textNode = new RRText(data); + textNode.ownerDocument = this; + return textNode; + } + + destroyTree() { + this.childNodes = []; + this.mirror.reset(); + } + + open() { + super.open(); + this._unserializedId = -1; + } +} + +export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); + +export class RRElement extends BaseRRElementImpl(RRNode) { + inputData: inputData | null = null; + scrollData: scrollData | null = null; +} + +export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} + +export class RRCanvasElement extends RRElement implements IRRElement { + public canvasMutations: { + event: canvasEventWithTime; + mutation: canvasMutationData; + }[] = []; + /** + * This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement. + */ + getContext(): RenderingContext | null { + return null; + } +} + +export class RRStyleElement extends RRElement { + public rules: VirtualStyleRules = []; +} + +export class RRIFrameElement extends RRElement { + contentDocument: RRDocument = new RRDocument(); + constructor(upperTagName: string, mirror: Mirror) { + super(upperTagName); + this.contentDocument.mirror = mirror; + } +} + +export const RRText = BaseRRTextImpl(RRNode); +export type RRText = typeof RRText; + +export const RRComment = BaseRRCommentImpl(RRNode); +export type RRComment = typeof RRComment; + +export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); +export type RRCDATASection = typeof RRCDATASection; + +interface RRElementTagNameMap { + audio: RRMediaElement; + canvas: RRCanvasElement; + iframe: RRIFrameElement; + style: RRStyleElement; + video: RRMediaElement; +} + +type RRElementType< + K extends keyof HTMLElementTagNameMap +> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; + +function getValidTagName(element: HTMLElement): string { + // https://github.com/rrweb-io/rrweb-snapshot/issues/56 + if (element instanceof HTMLFormElement) { + return 'FORM'; + } + return element.tagName.toUpperCase(); +} + +/** + * Build a RRNode from a real Node. + * @param node the real Node + * @param rrdom the RRDocument + * @param domMirror the NodeMirror that records the real document tree + * @returns the built RRNode + */ +export function buildFromNode( + node: Node, + rrdom: IRRDocument, + domMirror: NodeMirror, + parentRRNode?: IRRNode | null, +): IRRNode | null { + let rrNode: IRRNode; + + switch (node.nodeType) { + case NodeType.DOCUMENT_NODE: + if (parentRRNode && parentRRNode.nodeName === 'IFRAME') + rrNode = (parentRRNode as RRIFrameElement).contentDocument; + else { + rrNode = rrdom; + (rrNode as IRRDocument).compatMode = (node as Document).compatMode as + | 'BackCompat' + | 'CSS1Compat'; + } + break; + case NodeType.DOCUMENT_TYPE_NODE: + const documentType = node as DocumentType; + rrNode = rrdom.createDocumentType( + documentType.name, + documentType.publicId, + documentType.systemId, + ); + break; + case NodeType.ELEMENT_NODE: + const elementNode = node as HTMLElement; + const tagName = getValidTagName(elementNode); + rrNode = rrdom.createElement(tagName); + const rrElement = rrNode as IRRElement; + for (const { name, value } of Array.from(elementNode.attributes)) { + rrElement.attributes[name] = value; + } + elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft); + elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop); + /** + * We don't have to record special values of input elements at the beginning. + * Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed. + */ + break; + case NodeType.TEXT_NODE: + rrNode = rrdom.createTextNode((node as Text).textContent || ''); + break; + case NodeType.CDATA_SECTION_NODE: + rrNode = rrdom.createCDATASection((node as CDATASection).data); + break; + case NodeType.COMMENT_NODE: + rrNode = rrdom.createComment((node as Comment).textContent || ''); + break; + // if node is a shadow root + case NodeType.DOCUMENT_FRAGMENT_NODE: + rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' }); + break; + default: + return null; + } + + let sn: serializedNodeWithId | null = domMirror.getMeta(node); + + if (rrdom instanceof RRDocument) { + if (!sn) { + sn = getDefaultSN(rrNode, rrdom.unserializedId); + domMirror.add(node, sn); + } + rrdom.mirror.add(rrNode, { ...sn }); + } + + return rrNode; +} + +/** + * Build a RRDocument from a real document tree. + * @param dom the real document tree + * @param domMirror the NodeMirror that records the real document tree + * @param rrdom the rrdom object to be constructed + * @returns the build rrdom + */ +export function buildFromDom( + dom: Document, + domMirror: NodeMirror = createNodeMirror(), + rrdom: IRRDocument = new RRDocument(), +) { + function walk(node: Node, parentRRNode: IRRNode | null) { + const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode); + if (rrNode === null) return; + if ( + // if the parentRRNode isn't a RRIFrameElement + parentRRNode?.nodeName !== 'IFRAME' && + // if node isn't a shadow root + node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE + ) { + parentRRNode?.appendChild(rrNode); + rrNode.parentNode = parentRRNode; + rrNode.parentElement = parentRRNode as RRElement; + } + + if (node.nodeName === 'IFRAME') { + walk((node as HTMLIFrameElement).contentDocument!, rrNode); + } else if ( + node.nodeType === NodeType.DOCUMENT_NODE || + node.nodeType === NodeType.ELEMENT_NODE || + node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE + ) { + // if the node is a shadow dom + if ( + node.nodeType === NodeType.ELEMENT_NODE && + (node as HTMLElement).shadowRoot + ) + walk((node as HTMLElement).shadowRoot!, rrNode); + node.childNodes.forEach((childNode) => walk(childNode, rrNode)); + } + } + walk(dom, null); + return rrdom; +} + +export function createMirror(): Mirror { + return new Mirror(); +} + +// based on Mirror from rrweb-snapshots +export class Mirror implements IMirror { + private idNodeMap: Map = new Map(); + private nodeMetaMap: WeakMap = new WeakMap(); + + getId(n: RRNode | undefined | null): number { + if (!n) return -1; + + const id = this.getMeta(n)?.id; + + // if n is not a serialized Node, use -1 as its id. + return id ?? -1; + } + + getNode(id: number): RRNode | null { + return this.idNodeMap.get(id) || null; + } + + getIds(): number[] { + return Array.from(this.idNodeMap.keys()); + } + + getMeta(n: RRNode): serializedNodeWithId | null { + return this.nodeMetaMap.get(n) || null; + } + + // removes the node from idNodeMap + // doesn't remove the node from nodeMetaMap + removeNodeFromMap(n: RRNode) { + const id = this.getId(n); + this.idNodeMap.delete(id); + + if (n.childNodes) { + n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); + } + } + has(id: number): boolean { + return this.idNodeMap.has(id); + } + + hasNode(node: RRNode): boolean { + return this.nodeMetaMap.has(node); + } + + add(n: RRNode, meta: serializedNodeWithId) { + const id = meta.id; + this.idNodeMap.set(id, n); + this.nodeMetaMap.set(n, meta); + } + + replace(id: number, n: RRNode) { + this.idNodeMap.set(id, n); + } + + reset() { + this.idNodeMap = new Map(); + this.nodeMetaMap = new WeakMap(); + } +} + +/** + * Get a default serializedNodeWithId value for a RRNode. + * @param id the serialized id to assign + */ +export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId { + switch (node.RRNodeType) { + case RRNodeType.Document: + return { + id, + type: node.RRNodeType, + childNodes: [], + }; + case RRNodeType.DocumentType: + const doctype = node as IRRDocumentType; + return { + id, + type: node.RRNodeType, + name: doctype.name, + publicId: doctype.publicId, + systemId: doctype.systemId, + }; + case RRNodeType.Element: + return { + id, + type: node.RRNodeType, + tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase. + attributes: {}, + childNodes: [], + }; + case RRNodeType.Text: + return { + id, + type: node.RRNodeType, + textContent: (node as IRRText).textContent || '', + }; + case RRNodeType.Comment: + return { + id, + type: node.RRNodeType, + textContent: (node as IRRComment).textContent || '', + }; + case RRNodeType.CDATA: + return { + id, + type: node.RRNodeType, + textContent: '', + }; + } +} + +export { RRNode }; + +export { + diff, + createOrGetNode, + StyleRuleType, + ReplayerHandler, + VirtualStyleRules, +} from './diff'; +export * from './document'; diff --git a/packages/rrdom/src/virtual-dom.ts b/packages/rrdom/src/virtual-dom.ts deleted file mode 100644 index 807704ba..00000000 --- a/packages/rrdom/src/virtual-dom.ts +++ /dev/null @@ -1,450 +0,0 @@ -import { - NodeType as RRNodeType, - createMirror as createNodeMirror, -} from '@highlight-run/rrweb-snapshot'; -import type { - Mirror as NodeMirror, - IMirror, - serializedNodeWithId, -} from '@highlight-run/rrweb-snapshot'; -import type { - canvasMutationData, - canvasEventWithTime, - inputData, - scrollData, -} from '@highlight-run/rrweb/src/types'; -import { - BaseRRNode as RRNode, - BaseRRCDATASectionImpl, - BaseRRCommentImpl, - BaseRRDocumentImpl, - BaseRRDocumentTypeImpl, - BaseRRElementImpl, - BaseRRMediaElementImpl, - BaseRRTextImpl, - IRRDocument, - IRRElement, - IRRNode, - NodeType, - IRRDocumentType, - IRRText, - IRRComment, -} from './document'; -import type { VirtualStyleRules } from './diff'; - -export class RRDocument extends BaseRRDocumentImpl(RRNode) { - // In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules. - // These unserialized nodes may interfere the execution of the diff algorithm. - // The id of serialized node is larger than 0. So this value ​​less than 0 is used as id for these unserialized nodes. - private _unserializedId = -1; - - /** - * Every time the id is used, it will minus 1 automatically to avoid collisions. - */ - public get unserializedId(): number { - return this._unserializedId--; - } - - public mirror: Mirror = createMirror(); - - public scrollData: scrollData | null = null; - - constructor(mirror?: Mirror) { - super(); - if (mirror) { - this.mirror = mirror; - } - } - - createDocument( - _namespace: string | null, - _qualifiedName: string | null, - _doctype?: DocumentType | null, - ) { - return new RRDocument(); - } - - createDocumentType( - qualifiedName: string, - publicId: string, - systemId: string, - ) { - const documentTypeNode = new RRDocumentType( - qualifiedName, - publicId, - systemId, - ); - documentTypeNode.ownerDocument = this; - return documentTypeNode; - } - - createElement( - tagName: K, - ): RRElementType; - createElement(tagName: string): RRElement; - createElement(tagName: string) { - const upperTagName = tagName.toUpperCase(); - let element; - switch (upperTagName) { - case 'AUDIO': - case 'VIDEO': - element = new RRMediaElement(upperTagName); - break; - case 'IFRAME': - element = new RRIFrameElement(upperTagName, this.mirror); - break; - case 'CANVAS': - element = new RRCanvasElement(upperTagName); - break; - case 'STYLE': - element = new RRStyleElement(upperTagName); - break; - default: - element = new RRElement(upperTagName); - break; - } - element.ownerDocument = this; - return element; - } - - createComment(data: string) { - const commentNode = new RRComment(data); - commentNode.ownerDocument = this; - return commentNode; - } - - createCDATASection(data: string) { - const sectionNode = new RRCDATASection(data); - sectionNode.ownerDocument = this; - return sectionNode; - } - - createTextNode(data: string) { - const textNode = new RRText(data); - textNode.ownerDocument = this; - return textNode; - } - - destroyTree() { - this.childNodes = []; - this.mirror.reset(); - } - - open() { - super.open(); - this._unserializedId = -1; - } -} - -export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); - -export class RRElement extends BaseRRElementImpl(RRNode) { - inputData: inputData | null = null; - scrollData: scrollData | null = null; -} - -export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} - -export class RRCanvasElement extends RRElement implements IRRElement { - public canvasMutations: { - event: canvasEventWithTime; - mutation: canvasMutationData; - }[] = []; - /** - * This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement. - */ - getContext(): RenderingContext | null { - return null; - } -} - -export class RRStyleElement extends RRElement { - public rules: VirtualStyleRules = []; -} - -export class RRIFrameElement extends RRElement { - contentDocument: RRDocument = new RRDocument(); - constructor(upperTagName: string, mirror: Mirror) { - super(upperTagName); - this.contentDocument.mirror = mirror; - } -} - -export const RRText = BaseRRTextImpl(RRNode); -export type RRText = typeof RRText; - -export const RRComment = BaseRRCommentImpl(RRNode); -export type RRComment = typeof RRComment; - -export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); -export type RRCDATASection = typeof RRCDATASection; - -interface RRElementTagNameMap { - audio: RRMediaElement; - canvas: RRCanvasElement; - iframe: RRIFrameElement; - style: RRStyleElement; - video: RRMediaElement; -} - -type RRElementType< - K extends keyof HTMLElementTagNameMap -> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; - -function getValidTagName(element: HTMLElement): string { - // https://github.com/rrweb-io/rrweb-snapshot/issues/56 - if (element instanceof HTMLFormElement) { - return 'FORM'; - } - return element.tagName.toUpperCase(); -} - -/** - * Build a RRNode from a real Node. - * @param node the real Node - * @param rrdom the RRDocument - * @param domMirror the NodeMirror that records the real document tree - * @returns the built RRNode - */ -export function buildFromNode( - node: Node, - rrdom: IRRDocument, - domMirror: NodeMirror, - parentRRNode?: IRRNode | null, -): IRRNode | null { - let rrNode: IRRNode; - - switch (node.nodeType) { - case NodeType.DOCUMENT_NODE: - if (parentRRNode && parentRRNode.nodeName === 'IFRAME') - rrNode = (parentRRNode as RRIFrameElement).contentDocument; - else { - rrNode = rrdom; - (rrNode as IRRDocument).compatMode = (node as Document).compatMode as - | 'BackCompat' - | 'CSS1Compat'; - } - break; - case NodeType.DOCUMENT_TYPE_NODE: - const documentType = (node ) as DocumentType; - rrNode = rrdom.createDocumentType( - documentType.name, - documentType.publicId, - documentType.systemId, - ); - break; - case NodeType.ELEMENT_NODE: - const elementNode = (node ) as HTMLElement; - const tagName = getValidTagName(elementNode); - rrNode = rrdom.createElement(tagName); - const rrElement = rrNode as IRRElement; - for (const { name, value } of Array.from(elementNode.attributes)) { - rrElement.attributes[name] = value; - } - elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft); - elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop); - /** - * We don't have to record special values of input elements at the beginning. - * Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed. - */ - break; - case NodeType.TEXT_NODE: - rrNode = rrdom.createTextNode(((node ) as Text).textContent || ''); - break; - case NodeType.CDATA_SECTION_NODE: - rrNode = rrdom.createCDATASection(((node ) as CDATASection).data); - break; - case NodeType.COMMENT_NODE: - rrNode = rrdom.createComment( - ((node ) as Comment).textContent || '', - ); - break; - // if node is a shadow root - case NodeType.DOCUMENT_FRAGMENT_NODE: - rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' }); - break; - default: - return null; - } - - let sn: serializedNodeWithId | null = domMirror.getMeta(node); - - if (rrdom instanceof RRDocument) { - if (!sn) { - sn = getDefaultSN(rrNode, rrdom.unserializedId); - domMirror.add(node, sn); - } - rrdom.mirror.add(rrNode, { ...sn }); - } - - return rrNode; -} - -/** - * Build a RRDocument from a real document tree. - * @param dom the real document tree - * @param domMirror the NodeMirror that records the real document tree - * @param rrdom the rrdom object to be constructed - * @returns the build rrdom - */ -export function buildFromDom( - dom: Document, - domMirror: NodeMirror = createNodeMirror(), - rrdom: IRRDocument = new RRDocument(), -) { - function walk(node: Node, parentRRNode: IRRNode | null) { - const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode); - if (rrNode === null) return; - if ( - // if the parentRRNode isn't a RRIFrameElement - parentRRNode?.nodeName !== 'IFRAME' && - // if node isn't a shadow root - node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE - ) { - parentRRNode?.appendChild(rrNode); - rrNode.parentNode = parentRRNode; - rrNode.parentElement = parentRRNode as RRElement; - } - - if (node.nodeName === 'IFRAME') { - walk((node as HTMLIFrameElement).contentDocument!, rrNode); - } else if ( - node.nodeType === NodeType.DOCUMENT_NODE || - node.nodeType === NodeType.ELEMENT_NODE || - node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE - ) { - // if the node is a shadow dom - if ( - node.nodeType === NodeType.ELEMENT_NODE && - ((node ) as HTMLElement).shadowRoot - ) - walk(((node ) as HTMLElement).shadowRoot!, rrNode); - node.childNodes.forEach((childNode) => walk(childNode, rrNode)); - } - } - walk(dom, null); - return rrdom; -} - -export function createMirror(): Mirror { - return new Mirror(); -} - -// based on Mirror from rrweb-snapshots -export class Mirror implements IMirror { - private idNodeMap: Map = new Map(); - private nodeMetaMap: WeakMap = new WeakMap(); - - getId(n: RRNode | undefined | null): number { - if (!n) return -1; - - const id = this.getMeta(n)?.id; - - // if n is not a serialized Node, use -1 as its id. - return id ?? -1; - } - - getNode(id: number): RRNode | null { - return this.idNodeMap.get(id) || null; - } - - getIds(): number[] { - return Array.from(this.idNodeMap.keys()); - } - - getMeta(n: RRNode): serializedNodeWithId | null { - return this.nodeMetaMap.get(n) || null; - } - - // removes the node from idNodeMap - // doesn't remove the node from nodeMetaMap - removeNodeFromMap(n: RRNode) { - const id = this.getId(n); - this.idNodeMap.delete(id); - - if (n.childNodes) { - n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); - } - } - has(id: number): boolean { - return this.idNodeMap.has(id); - } - - hasNode(node: RRNode): boolean { - return this.nodeMetaMap.has(node); - } - - add(n: RRNode, meta: serializedNodeWithId) { - const id = meta.id; - this.idNodeMap.set(id, n); - this.nodeMetaMap.set(n, meta); - } - - replace(id: number, n: RRNode) { - this.idNodeMap.set(id, n); - } - - reset() { - this.idNodeMap = new Map(); - this.nodeMetaMap = new WeakMap(); - } -} - -/** - * Get a default serializedNodeWithId value for a RRNode. - * @param id the serialized id to assign - */ -export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId { - switch (node.RRNodeType) { - case RRNodeType.Document: - return { - id, - type: node.RRNodeType, - childNodes: [], - }; - case RRNodeType.DocumentType: - const doctype = node as IRRDocumentType; - return { - id, - type: node.RRNodeType, - name: doctype.name, - publicId: doctype.publicId, - systemId: doctype.systemId, - }; - case RRNodeType.Element: - return { - id, - type: node.RRNodeType, - tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase. - attributes: {}, - childNodes: [], - }; - case RRNodeType.Text: - return { - id, - type: node.RRNodeType, - textContent: (node as IRRText).textContent || '', - }; - case RRNodeType.Comment: - return { - id, - type: node.RRNodeType, - textContent: (node as IRRComment).textContent || '', - }; - case RRNodeType.CDATA: - return { - id, - type: node.RRNodeType, - textContent: '', - }; - } -} - -export { RRNode }; -export { - diff, - createOrGetNode, - StyleRuleType, - VirtualStyleRules, - ReplayerHandler, -} from './diff'; diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index 656d10fb..4c0a83ea 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -1,7 +1,7 @@ /** * @jest-environment jsdom */ -import { getDefaultSN, RRDocument, RRMediaElement } from '../src/virtual-dom'; +import { getDefaultSN, RRDocument, RRMediaElement } from '../src'; import { applyVirtualStyleRulesToNode, createOrGetNode, diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index 6d5acf84..182fbcdf 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -27,8 +27,8 @@ import { RRCanvasElement, RRDocument, RRElement, - RRNode, -} from '../src/virtual-dom'; + BaseRRNode as RRNode, +} from '../src'; const _typescript = (typescript as unknown) as typeof typescript.default; const printRRDomCode = ` @@ -219,9 +219,9 @@ describe('RRDocument for browser environment', () => { beforeAll(async () => { browser = await puppeteer.launch(); const bundle = await rollup.rollup({ - input: path.resolve(__dirname, '../src/virtual-dom.ts'), + input: path.resolve(__dirname, '../src/index.ts'), plugins: [ - resolve(), + (resolve() as unknown) as rollup.Plugin, (_typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, }) as unknown) as rollup.Plugin, diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 4a4f18a0..c5d366ad 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -16,5 +16,5 @@ }, "compileOnSave": true, "exclude": ["test"], - "include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"] + "include": ["src", "../rrweb/src/record/workers/workers.d.ts"] } diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 598a4eb9..92a10fe9 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -4,7 +4,7 @@ "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rollup/plugin-typescript": "^8.3.2", + "rollup-plugin-typescript2": "^0.31.2", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -20,7 +20,7 @@ "svelte-check": "^1.4.0", "svelte-preprocess": "^4.0.0", "tslib": "^2.0.0", - "typescript": "^4.6.4" + "typescript": "^4.7.3" }, "dependencies": { "@highlight-run/rrweb": ">2.0.3", diff --git a/packages/rrweb-player/rollup.config.js b/packages/rrweb-player/rollup.config.js index 403bf883..67aebc80 100644 --- a/packages/rrweb-player/rollup.config.js +++ b/packages/rrweb-player/rollup.config.js @@ -5,7 +5,7 @@ import livereload from 'rollup-plugin-livereload'; import { terser } from 'rollup-plugin-terser'; import sveltePreprocess from 'svelte-preprocess'; import webWorkerLoader from 'rollup-plugin-web-worker-loader'; -import typescript from '@rollup/plugin-typescript'; +import typescript from 'rollup-plugin-typescript2'; import pkg from './package.json'; import css from 'rollup-plugin-css-only'; @@ -64,6 +64,7 @@ export default entries.map((output) => ({ resolve({ browser: true, dedupe: ['svelte'], + extensions: ['.js', '.ts', '.svelte'], }), commonjs(), diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 4905f97d..6ed8bda4 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -40,7 +40,7 @@ }, "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/rrweb-snapshot#readme", "devDependencies": { - "@rollup/plugin-typescript": "^8.2.5", + "rollup-plugin-typescript2": "^0.31.2", "@types/chai": "^4.1.4", "@types/jest": "^27.0.2", "@types/jsdom": "^16.2.4", @@ -56,7 +56,7 @@ "ts-jest": "^27.0.5", "ts-node": "^7.0.1", "tslib": "^1.9.3", - "typescript": "^3.9.7" + "typescript": "^4.7.3" }, "gitHead": "5db953fd4478f89aabf8af047f8b03046a05e6e9" } diff --git a/packages/rrweb-snapshot/rollup.config.js b/packages/rrweb-snapshot/rollup.config.js index 6a8b0a09..b13f4478 100644 --- a/packages/rrweb-snapshot/rollup.config.js +++ b/packages/rrweb-snapshot/rollup.config.js @@ -1,4 +1,4 @@ -import typescript from '@rollup/plugin-typescript'; +import typescript from 'rollup-plugin-typescript2'; import { terser } from 'rollup-plugin-terser'; import pkg from './package.json'; diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index d07d8340..c257e89c 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -373,7 +373,8 @@ function onceIframeLoaded( // iframe was already loaded, make sure we wait to trigger the listener // till _after_ the mutation that found this iframe has had time to process setTimeout(listener, 0); - return; + + return iframeEl.addEventListener('load', listener); // keep listing for future loads } // use default listener iframeEl.addEventListener('load', listener); @@ -387,7 +388,7 @@ function isStylesheetLoaded(link: HTMLLinkElement) { function onceStylesheetLoaded( link: HTMLLinkElement, listener: () => unknown, - iframeLoadTimeout: number, + styleSheetLoadTimeout: number, ) { let fired = false; let styleSheetLoaded: StyleSheet | null; @@ -404,7 +405,7 @@ function onceStylesheetLoaded( listener(); fired = true; } - }, iframeLoadTimeout); + }, styleSheetLoadTimeout); link.addEventListener('load', () => { clearTimeout(timer); diff --git a/packages/rrweb-snapshot/test/html/picture-blob-in-frame.html b/packages/rrweb-snapshot/test/html/picture-blob-in-frame.html new file mode 100644 index 00000000..f6237f22 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/picture-blob-in-frame.html @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/rrweb-snapshot/test/html/picture-blob.html b/packages/rrweb-snapshot/test/html/picture-blob.html new file mode 100644 index 00000000..d2a32658 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/picture-blob.html @@ -0,0 +1,16 @@ + + + This is a robot + + + diff --git a/packages/rrweb-snapshot/test/html/picture-in-frame.html b/packages/rrweb-snapshot/test/html/picture-in-frame.html new file mode 100644 index 00000000..31684d2c --- /dev/null +++ b/packages/rrweb-snapshot/test/html/picture-in-frame.html @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index c34bfefc..b6a0c851 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -4,10 +4,11 @@ import * as http from 'http'; import * as url from 'url'; import * as puppeteer from 'puppeteer'; import * as rollup from 'rollup'; -import * as typescript from '@rollup/plugin-typescript'; +import * as typescript from 'rollup-plugin-typescript2'; import * as assert from 'assert'; +import { waitForRAF } from './utils'; -const _typescript = (typescript as unknown) as typeof typescript.default; +const _typescript = (typescript as unknown) as () => rollup.Plugin; const htmlFolder = path.join(__dirname, 'html'); const htmls = fs.readdirSync(htmlFolder).map((filePath) => { @@ -209,6 +210,74 @@ iframe.contentDocument.querySelector('center').clientHeight assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); + + it('correctly saves blob:images offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('http://localhost:3030/html/picture-blob.html', { + 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 page.waitFor(100); + const snapshot = await page.evaluate('JSON.stringify(snapshot, null, 2);'); + assert(snapshot.includes('"rr_dataURL"')); + assert(snapshot.includes('data:image/webp;base64,')); + }); + + it('correctly saves images in iframes offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('http://localhost:3030/html/picture-in-frame.html', { + waitUntil: 'load', + }); + await page.waitForSelector('iframe', { timeout: 1000 }); + await waitForRAF(page); // wait for page to render + await page.evaluate(`${code} + rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false, + onIframeLoad: function(iframe, sn) { + window.snapshot = sn; + } + })`); + await page.waitFor(100); + const snapshot = await page.evaluate( + 'JSON.stringify(window.snapshot, null, 2);', + ); + assert(snapshot.includes('"rr_dataURL"')); + assert(snapshot.includes('data:image/webp;base64,')); + }); + + it('correctly saves blob:images in iframes offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('http://localhost:3030/html/picture-blob-in-frame.html', { + waitUntil: 'load', + }); + await page.waitForSelector('iframe', { timeout: 1000 }); + await waitForRAF(page); // wait for page to render + await page.evaluate(`${code} + rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false, + onIframeLoad: function(iframe, sn) { + window.snapshot = sn; + } + })`); + await page.waitFor(100); + const snapshot = await page.evaluate( + 'JSON.stringify(window.snapshot, null, 2);', + ); + assert(snapshot.includes('"rr_dataURL"')); + assert(snapshot.includes('data:image/webp;base64,')); + }); }); describe('iframe integration tests', function (this: ISuite) { diff --git a/packages/rrweb-snapshot/test/utils.ts b/packages/rrweb-snapshot/test/utils.ts new file mode 100644 index 00000000..43d4484b --- /dev/null +++ b/packages/rrweb-snapshot/test/utils.ts @@ -0,0 +1,11 @@ +import * as puppeteer from 'puppeteer'; + +export async function waitForRAF(page: puppeteer.Page) { + return await page.evaluate(() => { + return new Promise((resolve) => { + requestAnimationFrame(() => { + requestAnimationFrame(resolve); + }); + }); + }); +} diff --git a/packages/rrweb/jest.config.js b/packages/rrweb/jest.config.js index c1f271c9..29db4e7f 100644 --- a/packages/rrweb/jest.config.js +++ b/packages/rrweb/jest.config.js @@ -5,6 +5,5 @@ module.exports = { testMatch: ['**/**.test.ts'], moduleNameMapper: { '\\.css$': 'identity-obj-proxy', - 'rrdom/es/(.*)': 'rrdom/lib/$1', }, }; diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index c7eaac5a..5abd9bf4 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -71,7 +71,7 @@ "ts-jest": "^27.1.3", "ts-node": "^10.7.0", "tslib": "^2.3.1", - "typescript": "^4.6.2" + "typescript": "^4.7.3" }, "dependencies": { "@highlight-run/rrdom": ">=0.1.10", diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index a0bea392..fe314016 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -222,6 +222,10 @@ function record( mutationCb: wrappedMutationEmit, }); + const stylesheetManager = new StylesheetManager({ + mutationCb: wrappedMutationEmit, + }); + const canvasManager = new CanvasManager({ recordCanvas, mutationCb: wrappedCanvasMutationEmit, @@ -250,6 +254,7 @@ function record( iframeManager, stylesheetManager, canvasManager, + keepIframeSrcFn, enableStrictPrivacy }, mirror, @@ -447,6 +452,7 @@ function record( doc, maskInputFn, maskTextFn, + keepIframeSrcFn, blockSelector, slimDOMOptions, mirror, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index c4779fb7..bdabd297 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -164,6 +164,7 @@ export default class MutationBuffer { private maskInputOptions: observerParam['maskInputOptions']; private maskTextFn: observerParam['maskTextFn']; private maskInputFn: observerParam['maskInputFn']; + private keepIframeSrcFn: observerParam['keepIframeSrcFn']; private recordCanvas: observerParam['recordCanvas']; private inlineImages: observerParam['inlineImages']; private slimDOMOptions: observerParam['slimDOMOptions']; @@ -186,6 +187,7 @@ export default class MutationBuffer { 'maskInputOptions', 'maskTextFn', 'maskInputFn', + 'keepIframeSrcFn', 'recordCanvas', 'inlineImages', 'slimDOMOptions', @@ -493,6 +495,19 @@ export default class MutationBuffer { let item: attributeCursor | undefined = this.attributes.find( (a) => a.node === m.target, ); + if ( + target.tagName === 'IFRAME' && + m.attributeName === 'src' && + !this.keepIframeSrcFn(value as string) + ) { + if (!(target as HTMLIFrameElement).contentDocument) { + // we can't record it directly as we can't see into it + // preserve the src attribute so a decision can be taken at replay time + m.attributeName = 'rr_src'; + } else { + return; + } + } if (!item) { item = { node: m.target, @@ -544,7 +559,7 @@ export default class MutationBuffer { // overwrite attribute if the mutations was triggered in same time item.attributes[m.attributeName!] = transformAttribute( this.doc, - (m.target as HTMLElement).tagName, + target.tagName, m.attributeName!, value!, ); diff --git a/packages/rrweb/src/record/stylesheet-manager.ts b/packages/rrweb/src/record/stylesheet-manager.ts index 81edf4f9..f863bf8b 100644 --- a/packages/rrweb/src/record/stylesheet-manager.ts +++ b/packages/rrweb/src/record/stylesheet-manager.ts @@ -1,7 +1,4 @@ -import type { - Mirror, - serializedNodeWithId, -} from '@highlight-run/rrweb-snapshot'; +import type { Mirror, serializedNodeWithId } from '@highlight-run/rrweb-snapshot'; import type { mutationCallBack } from '../types'; export class StylesheetManager { diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 2cd89852..68ec9796 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -15,7 +15,7 @@ import { buildFromDom, diff, getDefaultSN, -} from '@highlight-run/rrdom/es/virtual-dom'; +} from '@highlight-run/rrdom'; import type { RRNode, RRElement, @@ -26,7 +26,7 @@ import type { ReplayerHandler, Mirror as RRDOMMirror, VirtualStyleRules, -} from '@highlight-run/rrdom/es/virtual-dom'; +} from '@highlight-run/rrdom'; import * as mittProxy from 'mitt'; import { polyfill as smoothscrollPolyfill } from './smoothscroll'; import { Timer } from './timer'; @@ -881,7 +881,7 @@ export class Replayer { ); } if (this.usingVirtualDom) { - const styleEl = this.virtualDom.createElement('style') ; + const styleEl = this.virtualDom.createElement('style'); this.virtualDom.mirror.add( styleEl, getDefaultSN(styleEl, this.virtualDom.unserializedId), @@ -902,10 +902,7 @@ export class Replayer { head as HTMLHeadElement, ); for (let idx = 0; idx < injectStylesRules.length; idx++) { - (styleEl.sheet! ).insertRule( - injectStylesRules[idx], - idx, - ); + styleEl.sheet!.insertRule(injectStylesRules[idx], idx); } } } @@ -1360,7 +1357,7 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } - const styleSheet = ((target ) as HTMLStyleElement).sheet!; + const styleSheet = (target as HTMLStyleElement).sheet!; d.adds?.forEach(({ rule, index: nestedIndex }) => { try { if (Array.isArray(nestedIndex)) { @@ -1842,7 +1839,7 @@ export class Replayer { } } } else if (attributeName === 'style') { - const styleValues = value ; + const styleValues = value; const targetEl = target as HTMLElement | RRElement; for (const s in styleValues) { if (styleValues[s] === false) { @@ -1922,7 +1919,7 @@ export class Replayer { const previousInMap = previousId && map[previousId]; const nextInMap = nextId && map[nextId]; if (previousInMap) { - const { node, mutation } = previousInMap ; + const { node, mutation } = previousInMap; parent.insertBefore(node as Node & RRNode, target as Node & RRNode); delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; @@ -1931,7 +1928,7 @@ export class Replayer { } } if (nextInMap) { - const { node, mutation } = nextInMap ; + const { node, mutation } = nextInMap; parent.insertBefore( node as Node & RRNode, target.nextSibling as Node & RRNode, diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 6b9601ff..709b1d4d 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -11,7 +11,7 @@ import type { PackFn, UnpackFn } from './packer/base'; import type { IframeManager } from './record/iframe-manager'; import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; -import type { RRNode } from '@highlight-run/rrdom/es/virtual-dom'; +import type { RRNode } from '@highlight-run/rrdom'; import type { CanvasManager } from './record/observers/canvas/canvas-manager'; import type { StylesheetManager } from './record/stylesheet-manager'; @@ -279,6 +279,7 @@ export type observerParam = { maskInputOptions: MaskInputOptions; maskInputFn?: MaskInputFn; maskTextFn?: MaskTextFn; + keepIframeSrcFn: KeepIframeSrcFn; inlineStylesheet: boolean; styleSheetRuleCb: styleSheetRuleCallback; styleDeclarationCb: styleDeclarationCallback; @@ -315,6 +316,7 @@ export type MutationBufferParam = Pick< | 'maskInputOptions' | 'maskTextFn' | 'maskInputFn' + | 'keepIframeSrcFn' | 'recordCanvas' | 'inlineImages' | 'slimDOMOptions' diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 9acd5215..a784285f 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -11,7 +11,7 @@ import type { } from './types'; import type { IMirror, Mirror } from '@highlight-run/rrweb-snapshot'; import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from '@highlight-run/rrweb-snapshot'; -import type { RRNode, RRIFrameElement } from '@highlight-run/rrdom/es/virtual-dom'; +import type { RRNode, RRIFrameElement } from '@highlight-run/rrdom'; export function on( type: string, diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 2cd01581..bfbc006b 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -9096,7 +9096,7 @@ exports[`record integration tests should record dynamic CSS changes 1`] = ` ]" `; -exports[`record integration tests should record input userTriggered values if userTriggeredOnInput is enabled 1`] = ` +exports[`record integration tests should record images inside iframe with blob url 1`] = ` "[ { \\"type\\": 0, @@ -9173,21 +9173,6 @@ exports[`record integration tests should record input userTriggered values if us \\"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\\", @@ -9195,24 +9180,24 @@ exports[`record integration tests should record input userTriggered values if us \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"form fields\\", - \\"id\\": 13 + \\"textContent\\": \\"Frame with image\\", + \\"id\\": 11 } ], - \\"id\\": 12 + \\"id\\": 10 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", - \\"id\\": 14 + \\"id\\": 12 } ], \\"id\\": 4 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n\\\\n \\", - \\"id\\": 15 + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 }, { \\"type\\": 2, @@ -9222,620 +9207,1737 @@ exports[`record integration tests should record input userTriggered values if us { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"four\\", + \\"frameborder\\": \\"0\\" + }, + \\"childNodes\\": [], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", \\"id\\": 17 }, { \\"type\\": 2, - \\"tagName\\": \\"form\\", + \\"tagName\\": \\"script\\", \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 20 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"rootId\\": 21, + \\"id\\": 22 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 25 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", + \\"tagName\\": \\"meta\\", \\"attributes\\": { - \\"for\\": \\"text\\" + \\"charset\\": \\"UTF-8\\" }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 21 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"text\\" - }, - \\"childNodes\\": [], - \\"id\\": 22 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 23 - } - ], - \\"id\\": 20 + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 26 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 24 + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 27 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 26 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"radio\\", - \\"name\\": \\"toggle\\", - \\"value\\": \\"on\\" - }, - \\"childNodes\\": [], - \\"id\\": 27 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 28 - } - ], - \\"id\\": 25 + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 28 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, \\"id\\": 29 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 31 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"radio\\", - \\"name\\": \\"toggle\\", - \\"value\\": \\"off\\", - \\"checked\\": true - }, - \\"childNodes\\": [], - \\"id\\": 32 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"Image with blob:url\\", + \\"rootId\\": 21, \\"id\\": 33 } ], - \\"id\\": 30 + \\"rootId\\": 21, + \\"id\\": 32 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, \\"id\\": 34 + } + ], + \\"rootId\\": 21, + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 35 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 37 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"checkbox\\" - }, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 36 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"checkbox\\" - }, - \\"childNodes\\": [], - \\"id\\": 37 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 38 + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 21, + \\"id\\": 39 } ], - \\"id\\": 35 + \\"rootId\\": 21, + \\"id\\": 38 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 39 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"textarea\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 41 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"textarea\\", - \\"attributes\\": { - \\"name\\": \\"\\", - \\"id\\": \\"\\", - \\"cols\\": \\"30\\", - \\"rows\\": \\"10\\" - }, - \\"childNodes\\": [], - \\"id\\": 42 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 43 - } - ], + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 21, \\"id\\": 40 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 44 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"select\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 46 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"select\\", - \\"attributes\\": { - \\"name\\": \\"\\", - \\"id\\": \\"\\", - \\"value\\": \\"1\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 48 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"option\\", - \\"attributes\\": { - \\"value\\": \\"1\\", - \\"selected\\": true - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"1\\", - \\"id\\": 50 - } - ], - \\"id\\": 49 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 51 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"option\\", - \\"attributes\\": { - \\"value\\": \\"2\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"2\\", - \\"id\\": 53 - } - ], - \\"id\\": 52 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 54 - } - ], - \\"id\\": 47 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 55 - } - ], - \\"id\\": 45 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 56 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"password\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 58 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"password\\" - }, - \\"childNodes\\": [], - \\"id\\": 59 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 60 - } - ], - \\"id\\": 57 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 61 - } - ], - \\"id\\": 18 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 62 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 64 } ], - \\"id\\": 63 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 65 + \\"rootId\\": 21, + \\"id\\": 36 } ], - \\"id\\": 16 + \\"rootId\\": 21, + \\"id\\": 23 } ], - \\"id\\": 3 + \\"id\\": 21 } - ], - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 22 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 36, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"src\\": \\"blob:http://localhost:3030/...\\", + \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 41 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"t\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 41, + \\"attributes\\": { + \\"crossorigin\\": \\"anonymous\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"te\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 41, + \\"attributes\\": { + \\"crossorigin\\": null + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } - }, + } +]" +`; + +exports[`record integration tests should record images inside iframe with blob url after iframe was reloaded 1`] = ` +"[ { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"tes\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 - } + \\"type\\": 0, + \\"data\\": {} }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"test\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 1, - \\"id\\": 27 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 22 - } + \\"type\\": 1, + \\"data\\": {} }, { - \\"type\\": 3, + \\"type\\": 4, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 27 + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 } }, { - \\"type\\": 3, + \\"type\\": 2, \\"data\\": { - \\"source\\": 2, - \\"type\\": 0, - \\"id\\": 27 + \\"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\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Frame 2\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n frame 2\\\\n \\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 20 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 21 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 2, - \\"id\\": 27 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"five\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"on\\", - \\"isChecked\\": true, - \\"userTriggered\\": true, - \\"id\\": 27 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 26 + } + ], + \\"rootId\\": 23, + \\"id\\": 24 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 23 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"off\\", - \\"isChecked\\": false, - \\"userTriggered\\": false, - \\"id\\": 32 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 1, - \\"id\\": 37 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 27 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 37 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"rootId\\": 27, + \\"id\\": 28 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 33 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 34 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 35 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 36 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 37 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Image with blob:url\\", + \\"rootId\\": 27, + \\"id\\": 39 + } + ], + \\"rootId\\": 27, + \\"id\\": 38 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 40 + } + ], + \\"rootId\\": 27, + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 43 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 27, + \\"id\\": 45 + } + ], + \\"rootId\\": 27, + \\"id\\": 44 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 27, + \\"id\\": 46 + } + ], + \\"rootId\\": 27, + \\"id\\": 42 + } + ], + \\"rootId\\": 27, + \\"id\\": 29 + } + ], + \\"id\\": 27 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 0, - \\"id\\": 37 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 42, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"src\\": \\"blob:http://localhost:3030/...\\", + \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 47 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 2, - \\"id\\": 37 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 47, + \\"attributes\\": { + \\"crossorigin\\": \\"anonymous\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"on\\", - \\"isChecked\\": true, - \\"userTriggered\\": true, - \\"id\\": 37 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 47, + \\"attributes\\": { + \\"crossorigin\\": null + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } - }, + } +]" +`; + +exports[`record integration tests should record images with blob url 1`] = ` +"[ { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 37 - } + \\"type\\": 0, + \\"data\\": {} }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 59 - } + \\"type\\": 1, + \\"data\\": {} }, { - \\"type\\": 3, + \\"type\\": 4, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"*\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 } }, { - \\"type\\": 3, + \\"type\\": 2, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"**\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"***\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"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\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Image with blob:url\\", + \\"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\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 22 + } + ], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 23 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"****\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"src\\": \\"blob:http://localhost:3030/...\\", + \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + }, + \\"childNodes\\": [], + \\"id\\": 24 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"*****\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 24, + \\"attributes\\": { + \\"crossorigin\\": \\"anonymous\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"******\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 24, + \\"attributes\\": { + \\"crossorigin\\": null + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } - }, + } +]" +`; + +exports[`record integration tests should record input userTriggered values if userTriggeredOnInput is enabled 1`] = ` +"[ { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"*******\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 - } + \\"type\\": 0, + \\"data\\": {} }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"********\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 - } + \\"type\\": 1, + \\"data\\": {} }, { - \\"type\\": 3, + \\"type\\": 4, \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 59 + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 } }, { - \\"type\\": 3, + \\"type\\": 2, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 42 - } - }, + \\"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\\": \\"form fields\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"form\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"text\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + } + ], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 26 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"on\\" + }, + \\"childNodes\\": [], + \\"id\\": 27 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 28 + } + ], + \\"id\\": 25 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"off\\", + \\"checked\\": true + }, + \\"childNodes\\": [], + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 33 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 34 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"checkbox\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"checkbox\\" + }, + \\"childNodes\\": [], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + } + ], + \\"id\\": 35 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 39 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"textarea\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"cols\\": \\"30\\", + \\"rows\\": \\"10\\" + }, + \\"childNodes\\": [], + \\"id\\": 42 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 43 + } + ], + \\"id\\": 40 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 44 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"select\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"select\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"value\\": \\"1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"1\\", + \\"selected\\": true + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 50 + } + ], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"2\\", + \\"id\\": 53 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 54 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 45 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + } + ], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 61 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 62 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 64 + } + ], + \\"id\\": 63 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 65 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"t\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"te\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"tes\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"test\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"userTriggered\\": true, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"off\\", + \\"isChecked\\": false, + \\"userTriggered\\": false, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"userTriggered\\": true, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*****\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"******\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*******\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"********\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 42 + } + }, { \\"type\\": 3, \\"data\\": { @@ -9939,41 +11041,350 @@ exports[`record integration tests should record input userTriggered values if us { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"textarea te\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 42 + \\"source\\": 5, + \\"text\\": \\"textarea te\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea tes\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea test\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"1\\", + \\"isChecked\\": false, + \\"userTriggered\\": false, + \\"id\\": 47 + } + } +]" +`; + +exports[`record integration tests should record mutations in iframes accross pages 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\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Frame 2\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n frame 2\\\\n \\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 20 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 21 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"five\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"textarea tes\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 42 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 26 + } + ], + \\"rootId\\": 23, + \\"id\\": 24 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 23 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"textarea test\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 42 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 30 + } + ], + \\"rootId\\": 27, + \\"id\\": 28 + } + ], + \\"id\\": 27 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"1\\", - \\"isChecked\\": false, - \\"userTriggered\\": false, - \\"id\\": 47 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 30, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 31 + } + } + ] } } ]" diff --git a/packages/rrweb/test/html/assets/robot.png b/packages/rrweb/test/html/assets/robot.png new file mode 100644 index 0000000000000000000000000000000000000000..cc486cc8b70680b0dfd72828b6530e0a4e9183ad GIT binary patch literal 11004 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 qTg~2LrAnpQZV>`Mi|_sS + + + + + Frame with image + + + + + diff --git a/packages/rrweb/test/html/image-blob-url.html b/packages/rrweb/test/html/image-blob-url.html new file mode 100644 index 00000000..4dd3f608 --- /dev/null +++ b/packages/rrweb/test/html/image-blob-url.html @@ -0,0 +1,21 @@ + + + + + + + Image with blob:url + + + + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index fc6d4e4f..36df57a6 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -495,6 +495,57 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('should record images with blob url', async () => { + const page: puppeteer.Page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html`); + page.setContent( + getHtml.call(this, 'image-blob-url.html', { inlineImages: true }), + ); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); + await page.waitForSelector('img'); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + + it('should record images inside iframe with blob url', async () => { + const page: puppeteer.Page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html`); + await page.setContent( + getHtml.call(this, 'frame-image-blob-url.html', { inlineImages: true }), + ); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); + await page.waitForTimeout(50); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + + it('should record images inside iframe with blob url after iframe was reloaded', async () => { + const page: puppeteer.Page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html`); + await page.setContent( + getHtml.call(this, 'frame2.html', { inlineImages: true }), + ); + await page.waitForSelector('iframe'); // wait for iframe to get added + await waitForRAF(page); // wait for iframe to load + page.evaluate(() => { + const iframe = document.querySelector('iframe')!; + iframe.setAttribute('src', '/html/image-blob-url.html'); + }); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); // wait for image to get loaded + await page.waitForTimeout(50); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + it('should record shadow DOM', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); @@ -576,7 +627,36 @@ describe('record integration tests', function (this: ISuite) { ); }); }); - await waitForRAF(page); + await waitForRAF(page); // wait for events to get created + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + + it('should record mutations in iframes accross pages', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto(`${serverURL}/html`); + page.on('console', (msg) => console.log(msg.text())); + await page.setContent(getHtml.call(this, 'frame2.html')); + + await page.waitForSelector('iframe'); // wait for iframe to get added + await waitForRAF(page); // wait for iframe to load + + page.evaluate((serverURL) => { + const iframe = document.querySelector('iframe')!; + iframe.setAttribute('src', `${serverURL}/html`); // load new page + }, serverURL); + + await page.waitForResponse(`${serverURL}/html`); // wait for iframe to load pt1 + await waitForRAF(page); // wait for iframe to load pt2 + + await page.evaluate(() => { + const iframeDocument = document.querySelector('iframe')!.contentDocument!; + const div = iframeDocument.createElement('div'); + iframeDocument.body.appendChild(div); + }); + + await waitForRAF(page); // wait for snapshot to be updated const snapshots = await page.evaluate('window.snapshots'); assertSnapshot(snapshots); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index b89cd3e3..3c23da29 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -153,20 +153,54 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript } } + + // strip blob:urls as they are different every time + console.log( + a.attributes.src, + 'src' in a.attributes && + a.attributes.src && + typeof a.attributes.src === 'string', + ); }); s.data.adds.forEach((add) => { - if ( - add.node.type === NodeType.Element && - 'style' in add.node.attributes && - typeof add.node.attributes.style === 'string' && - coordinatesReg.test(add.node.attributes.style) - ) { - add.node.attributes.style = add.node.attributes.style.replace( - coordinatesReg, - '$1: Npx', - ); + if (add.node.type === NodeType.Element) { + if ( + 'style' in add.node.attributes && + typeof add.node.attributes.style === 'string' && + coordinatesReg.test(add.node.attributes.style) + ) { + add.node.attributes.style = add.node.attributes.style.replace( + coordinatesReg, + '$1: Npx', + ); + } + coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript + + // strip blob:urls as they are different every time + if ( + 'src' in add.node.attributes && + add.node.attributes.src && + typeof add.node.attributes.src === 'string' && + add.node.attributes.src.startsWith('blob:') + ) { + add.node.attributes.src = add.node.attributes.src.replace( + /[\w-]+$/, + '...', + ); + } + + // strip rr_dataURL as they are not consistent + if ( + 'rr_dataURL' in add.node.attributes && + add.node.attributes.rr_dataURL && + typeof add.node.attributes.rr_dataURL === 'string' + ) { + add.node.attributes.rr_dataURL = add.node.attributes.rr_dataURL.replace( + /,.+$/, + ',...', + ); + } } - coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript }); } delete (s as Optional).timestamp; @@ -556,6 +590,7 @@ export function generateRecordSnippet(options: recordOptions) { userTriggeredOnInput: ${options.userTriggeredOnInput}, maskTextFn: ${options.maskTextFn}, recordCanvas: ${options.recordCanvas}, + inlineImages: ${options.inlineImages}, plugins: ${options.plugins} }); `; diff --git a/packages/rrweb/typings/record/mutation.d.ts b/packages/rrweb/typings/record/mutation.d.ts index c69c48f6..9d65ebd6 100644 --- a/packages/rrweb/typings/record/mutation.d.ts +++ b/packages/rrweb/typings/record/mutation.d.ts @@ -19,6 +19,7 @@ export default class MutationBuffer { private maskInputOptions; private maskTextFn; private maskInputFn; + private keepIframeSrcFn; private recordCanvas; private inlineImages; private slimDOMOptions; diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts index 121eb0de..651528b4 100644 --- a/packages/rrweb/typings/replay/index.d.ts +++ b/packages/rrweb/typings/replay/index.d.ts @@ -1,5 +1,5 @@ import { Mirror } from '@highlight-run/rrweb-snapshot'; -import { RRDocument } from '@highlight-run/rrdom/es/virtual-dom'; +import { RRDocument } from '@highlight-run/rrdom'; import { Timer } from './timer'; import { createPlayerService, createSpeedService } from './machine'; import { eventWithTime, playerConfig, playerMetaData, Handler, SessionInterval } from '../types'; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index 8cea4b2a..4e161ae7 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -3,7 +3,7 @@ import type { PackFn, UnpackFn } from './packer/base'; import type { IframeManager } from './record/iframe-manager'; import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; -import type { RRNode } from '@highlight-run/rrdom/es/virtual-dom'; +import type { RRNode } from '@highlight-run/rrdom'; import type { CanvasManager } from './record/observers/canvas/canvas-manager'; import type { StylesheetManager } from './record/stylesheet-manager'; export declare enum EventType { @@ -187,6 +187,7 @@ export declare type observerParam = { maskInputOptions: MaskInputOptions; maskInputFn?: MaskInputFn; maskTextFn?: MaskTextFn; + keepIframeSrcFn: KeepIframeSrcFn; inlineStylesheet: boolean; styleSheetRuleCb: styleSheetRuleCallback; styleDeclarationCb: styleDeclarationCallback; @@ -211,7 +212,7 @@ export declare type observerParam = { options: unknown; }>; }; -export declare type MutationBufferParam = Pick; +export declare type MutationBufferParam = Pick; export declare type hooksParam = { mutation?: mutationCallBack; mousemove?: mousemoveCallBack; diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index c194feb7..96f2b264 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -1,6 +1,6 @@ import type { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, DocumentDimension, IWindow, DeprecatedMirror, textMutation } from './types'; import type { IMirror, Mirror } from '@highlight-run/rrweb-snapshot'; -import type { RRNode, RRIFrameElement } from '@highlight-run/rrdom/es/virtual-dom'; +import type { RRNode, RRIFrameElement } from '@highlight-run/rrdom'; export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler; export declare let _mirror: DeprecatedMirror; export declare function throttle(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void; diff --git a/turbo.json b/turbo.json new file mode 100644 index 00000000..5d1a5823 --- /dev/null +++ b/turbo.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://turborepo.org/schema.json", + "baseBranch": "origin/master", + "pipeline": { + "prepublish": { + "dependsOn": [ + "^prepublish" + ], + "outputs": [ + "lib/**", + "es/**", + "dist/**" + ] + }, + "test": {}, + "test:watch": {}, + "dev": {} + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c431c5e0..60d5042d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1791,10 +1791,10 @@ magic-string "^0.25.7" resolve "^1.17.0" -"@rollup/plugin-node-resolve@^13.0.4": - version "13.0.6" - resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz" - integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA== +"@rollup/plugin-node-resolve@^13.0.4", "@rollup/plugin-node-resolve@^13.2.1": + version "13.2.1" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz" + integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" @@ -1815,34 +1815,6 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-node-resolve@^13.2.1": - version "13.2.1" - resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz" - integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA== - dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" - builtin-modules "^3.1.0" - deepmerge "^4.2.2" - is-module "^1.0.0" - resolve "^1.19.0" - -"@rollup/plugin-typescript@^8.2.5": - version "8.2.5" - resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz" - integrity sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - -"@rollup/plugin-typescript@^8.3.2": - version "8.3.2" - resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz" - integrity sha512-MtgyR5LNHZr3GyN0tM7gNO9D0CS+Y+vflS4v/PHmrX17JCkHUYKvQ5jN5o3cz1YKllM3duXUqu3yOHwMPUxhDg== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - "@rollup/pluginutils@4": version "4.1.1" resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.1.tgz" @@ -1965,12 +1937,12 @@ "@types/cssom@^0.4.1": version "0.4.1" - resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz" + resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz#fb64e145b425bd6c1b0ed78ebd66ba43b6e088ab" integrity sha512-hHGVfUuGZe5FpgCxpTJccH0gD1bui5gWceW0We0TyAzUr6wBaqDnSLG9Yr3xqS4AkGhnclNOwRSXH/LIfki3fQ== "@types/cssstyle@^2.2.1": version "2.2.1" - resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz" + resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz#fa010824006ff47af94a6b9baf9759e031815347" integrity sha512-CSQFKdZc3dmWoZXLAM0pPL6XiYLG8hMGzImM2MwQ9kavB5LnbeMGan94CCj4oxY65xMl5mRMwrFUfKPOWO4WpQ== "@types/estree@*": @@ -2088,7 +2060,7 @@ "@types/nwsapi@^2.2.2": version "2.2.2" - resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz" + resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz#1b1dccfc38b2b7e1b9ea71d5285796878375e862" integrity sha512-C4G47l3cAra4729xbhL9y3PjTpO7LJwXd47Fn1mbnZ6WcTkFPo8iDJPyMGCIudxpc7aeM8K1Fmw+lZfOb5ya9g== "@types/offscreencanvas@^2019.6.4": @@ -3499,7 +3471,7 @@ compare-func@^2.0.0: compare-versions@^4.1.3: version "4.1.3" - resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== concat-map@0.0.1: @@ -10820,6 +10792,90 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +turbo-darwin-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.2.16.tgz#1586fd6e52ffb5ad5e1e3ec8afe1c873f7fd98d9" + integrity sha512-dyitLQJdH3uLVdlH9jAkP4LqEO/K+wOXjUqOzjTciRLjQPzmsNY60/bmFHODADK4eBBl1nxbtn7tmmoT4vS1qA== + +turbo-darwin-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.2.16.tgz#3faf9c657fa5feb16465316ac8d47c4a2790c2f1" + integrity sha512-Ex6uM4HU7rGXdhvJMpzNpp6qxglJ98nWeIi5qR/lBXHLjK3UCvSW8BEALArUJYJTXS9FZBq1a5LowFqXYsfDcA== + +turbo-freebsd-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-freebsd-64/-/turbo-freebsd-64-1.2.16.tgz#2674c2078eaa79200a3b91639e530b287a735cb4" + integrity sha512-onRGKMvog8B3XDssSBIAg+FrEq9pcBoAybP7bpi/uYIH1L/WQ7YMmLn88X9JX19ehYuVOVZrjap4jWH2GIkU8A== + +turbo-freebsd-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-freebsd-arm64/-/turbo-freebsd-arm64-1.2.16.tgz#88cc25733b19880b4a14a9d4f34770da9e898a92" + integrity sha512-S0EqPqxwnJuVNNXRgcHB0r8ai8LSrpHdihVJKRM7WYmIR7isccBEf/G9agrt73sCXwjvenxFs4HDR7cSvGt14Q== + +turbo-linux-32@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-32/-/turbo-linux-32-1.2.16.tgz#7c60a19acd32862085566da0f9389d2db36a7aee" + integrity sha512-ecbqmGOxgTWePGrowtwyvZGfvwaLxFWmPK21cU0PS+fzoZBaVmzYmniTdd/2EkGCw7TOPhtiT22v96fWcnRycA== + +turbo-linux-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.2.16.tgz#d589bf73bbfa38fa73ad8ec786c0b89b678e7af8" + integrity sha512-q6gtdMWCzM0Sktkd73zcaQjNoeM1MjtrbwQBctWN/Sgj0eiPBPnzpIvokvx98x7RLf4qyI99/mlme0Dn5fx21A== + +turbo-linux-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.2.16.tgz#9eaaee4094f71a266553248dfaadc59756696242" + integrity sha512-gUf67tYJ/N09WAZTTmtUWYrqm381tZxiulnRGAIM+iRsaTrweyUKZaYXwJvlPpI/cQOw25wCG9/IyvxLeagL8A== + +turbo-linux-arm@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-arm/-/turbo-linux-arm-1.2.16.tgz#54774c7e6e4f91ebdb211fa48b1b2a46b1e3c61c" + integrity sha512-du7uvExELNb89V3g7iM0XP21fR1Yl3EoHRcOfQz32oUqnS7idCKvbEowM9LtiluQl1dKcOIJjn1nlvvsqzkhOg== + +turbo-linux-mips64le@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-mips64le/-/turbo-linux-mips64le-1.2.16.tgz#fa49b858f8ed399b44cdce285a0feb67c30ee99b" + integrity sha512-U5BM+Ql3z13uRtwMmKH/8eL+9DdTgyijC2gaX4xP0RTlcN7WfAstg8Fg/Tn2Vw9vtpVDdxwpw7dvX4kw2ghhpA== + +turbo-linux-ppc64le@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-ppc64le/-/turbo-linux-ppc64le-1.2.16.tgz#0a419310b938ef5eace46cb6c53b18099caae60c" + integrity sha512-HQWSCmVZyc5chw7Ie2ZcfZPfmM06mbEEu0Wl11Y5QWh1ZzhPNQHs/TsF4I9r146wHi62XgcrKFjkw4ARZiWsLA== + +turbo-windows-32@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-windows-32/-/turbo-windows-32-1.2.16.tgz#8307dea3fab91be4e3e46aa37d76ce8e7ce3fdcb" + integrity sha512-0ZtPz5FK2qZjznMG4vvRyaabrhO8BgbN+tBx1wjXSuoICTAjYi5TwRVVRh59c3x7qQmR21Cv33CrhLBPRfeAlg== + +turbo-windows-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.2.16.tgz#9f5f725d6ab829d3cf7944ba92714e9e5bdd09d0" + integrity sha512-j8iAIixq/rGfBpHNbYOosxMasZrGuMzLILEuQGDxZgKNpYgobJ15QFHQlGR9sit1b8qPU5zZX4CtByRtkgH1Bw== + +turbo-windows-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.2.16.tgz#75894186e9f1d6340a30c436d563c34999e5b87c" + integrity sha512-4GpcJG3B8R9WDhwfT8fu6ZmOOfseCg6Q1cy/G8/zpJQk769yYcSnD8MgQhYgHB58aVFxZcMxBvLL6UA0UrpgWA== + +turbo@^1.2.4: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.2.16.tgz#324c2d586f6fa77aa9173274f3b9d9efbe0735e4" + integrity sha512-PPUa2COKgFkyb6N3uF9AnIY3l9FZkF15QQ3U1K2wpI01D3gyGKQO0Q3DUQ4ipmciP0teBfL7H+l/QTrUA9IVvQ== + optionalDependencies: + turbo-darwin-64 "1.2.16" + turbo-darwin-arm64 "1.2.16" + turbo-freebsd-64 "1.2.16" + turbo-freebsd-arm64 "1.2.16" + turbo-linux-32 "1.2.16" + turbo-linux-64 "1.2.16" + turbo-linux-arm "1.2.16" + turbo-linux-arm64 "1.2.16" + turbo-linux-mips64le "1.2.16" + turbo-linux-ppc64le "1.2.16" + turbo-windows-32 "1.2.16" + turbo-windows-64 "1.2.16" + turbo-windows-arm64 "1.2.16" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" @@ -10899,25 +10955,10 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@*: - version "4.3.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== - -typescript@^3.9.7: - version "3.9.10" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== - -typescript@^4.6.2: - version "4.6.2" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz" - integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== - -typescript@^4.6.4: - version "4.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" - integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== +typescript@*, typescript@^4.7.3: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" From 375134bd88b6cf2c5b249a87cec5574a65e3c567 Mon Sep 17 00:00:00 2001 From: Vadim Korolik Date: Thu, 30 Jun 2022 14:35:17 -0700 Subject: [PATCH 2/2] fix merge conflicts --- package.json | 2 +- packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom-nodejs/src/document-nodejs.ts | 2 +- packages/rrdom-nodejs/test/document-nodejs.test.ts | 2 +- packages/rrdom/package.json | 2 +- packages/rrdom/src/index.ts | 10 ++++------ packages/rrweb/package.json | 4 ++-- packages/rrweb/src/record/index.ts | 4 ---- yarn.lock | 5 +++++ 9 files changed, 18 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 8574443a..7d2c17d2 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "dev": "yarn turbo run dev", "repl": "cd packages/rrweb && npm run repl", "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'", - "clean": "rm -rf dist node_modules packages/**/build packages/**/es packages/**/dist packages/**/lib packages/**/node_modules" + "clean": "rm -rf dist node_modules packages/**/.turbo packages/**/build packages/**/es packages/**/dist packages/**/lib packages/**/node_modules" }, "resolutions": { "**/jsdom/cssom": "^0.5.0" diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 9c9c8903..4401db13 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,5 +1,5 @@ { - "name": "rrdom-nodejs", + "name": "@highlight-run/rrdom-nodejs", "version": "0.1.2", "scripts": { "dev": "rollup -c -w", @@ -49,7 +49,7 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrweb-snapshot": "^1.1.14", - "rrdom": "^0.1.2" + "@highlight-run/rrweb-snapshot": ">=1.1.15", + "@highlight-run/rrdom": ">=0.1.12" } } diff --git a/packages/rrdom-nodejs/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts index 277810ae..daedf801 100644 --- a/packages/rrdom-nodejs/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -13,7 +13,7 @@ import { ClassList, IRRDocument, CSSStyleDeclaration, -} from 'rrdom'; +} from '@highlight-run/rrdom'; const nwsapi = require('nwsapi'); const cssom = require('cssom'); const cssstyle = require('cssstyle'); diff --git a/packages/rrdom-nodejs/test/document-nodejs.test.ts b/packages/rrdom-nodejs/test/document-nodejs.test.ts index ea064c10..81469c87 100644 --- a/packages/rrdom-nodejs/test/document-nodejs.test.ts +++ b/packages/rrdom-nodejs/test/document-nodejs.test.ts @@ -16,7 +16,7 @@ import { RRStyleElement, RRText, } from '../src/document-nodejs'; -import { buildFromDom } from 'rrdom'; +import { buildFromDom } from '@highlight-run/rrdom'; describe('RRDocument for nodejs environment', () => { describe('RRDocument API', () => { diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 5075a649..44489589 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "@highlight-run/rrdom", - "version": "0.1.11", + "version": "0.1.12", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.js", diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index c81f6af6..e9fa16b9 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -1,18 +1,16 @@ import { + IMirror, + Mirror as NodeMirror, NodeType as RRNodeType, createMirror as createNodeMirror, -} from 'rrweb-snapshot'; -import type { - Mirror as NodeMirror, - IMirror, serializedNodeWithId, -} from 'rrweb-snapshot'; +} from '@highlight-run/rrweb-snapshot'; import type { canvasMutationData, canvasEventWithTime, inputData, scrollData, -} from 'rrweb/src/types'; +} from '@highlight-run/rrweb/src/types'; import type { VirtualStyleRules } from './diff'; import { BaseRRNode as RRNode, diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 5abd9bf4..20687a89 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "@highlight-run/rrweb", - "version": "2.0.13", + "version": "2.0.14", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -74,7 +74,7 @@ "typescript": "^4.7.3" }, "dependencies": { - "@highlight-run/rrdom": ">=0.1.10", + "@highlight-run/rrdom": ">=0.1.12", "@highlight-run/rrweb-snapshot": ">1.1.15", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index fe314016..fe4d9509 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -222,10 +222,6 @@ function record( mutationCb: wrappedMutationEmit, }); - const stylesheetManager = new StylesheetManager({ - mutationCb: wrappedMutationEmit, - }); - const canvasManager = new CanvasManager({ recordCanvas, mutationCb: wrappedCanvasMutationEmit, diff --git a/yarn.lock b/yarn.lock index 60d5042d..80614d1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9829,6 +9829,11 @@ rollup@^2.71.1: optionalDependencies: fsevents "~2.3.2" +rrweb-snapshot@^1.1.14: + version "1.1.14" + resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz#9d4d9be54a28a893373428ee4393ec7e5bd83fcc" + integrity sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ== + run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz"