From 661c746b14373dc3c360b2f6ce3e604e33887678 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Mon, 24 Jan 2022 23:42:36 +0800 Subject: [PATCH] fix #811 expose inlineImages to record --- guide.zh_CN.md | 77 ++++++++++--------- packages/rrweb/src/record/index.ts | 4 + packages/rrweb/src/record/mutation.ts | 7 +- packages/rrweb/src/record/observer.ts | 3 + .../rrweb/src/record/shadow-dom-manager.ts | 2 + packages/rrweb/src/types.ts | 2 + packages/rrweb/typings/record/mutation.d.ts | 3 +- packages/rrweb/typings/record/observer.d.ts | 2 +- .../typings/record/shadow-dom-manager.d.ts | 1 + packages/rrweb/typings/types.d.ts | 8 +- 10 files changed, 67 insertions(+), 42 deletions(-) diff --git a/guide.zh_CN.md b/guide.zh_CN.md index ab3fd66894..43edece2ed 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -131,29 +131,30 @@ setInterval(save, 10 * 1000); `rrweb.record(config)` 的 config 部分接受以下参数 -| key | 默认值 | 功能 | -| -------------------- | ------------------ | ------------------------------------------------------------ | -| emit | 必填 | 获取当前录制的数据 | -| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | -| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | -| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | -| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 | -| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | -| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 | -| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 | -| maskAllInputs | false | 将所有输入内容记录为 \* | +| key | 默认值 | 功能 | +| -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| emit | 必填 | 获取当前录制的数据 | +| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | +| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | +| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | +| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 | +| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | +| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 | +| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 | +| maskAllInputs | false | 将所有输入内容记录为 \* | | maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | -| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 | -| maskTextFn | - | 自定义文字内容的记录逻辑 | -| slimDOMOptions | {} | 去除 DOM 中不必要的部分
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | -| inlineStylesheet | true | 是否将样式表内联 | -| hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | -| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| recordCanvas | false | 是否记录 canvas 内容 | -| collectFonts | false | 是否记录页面中的字体文件 | -| userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | -| plugins | [] | 加载插件以获得额外的录制功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | +| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 | +| maskTextFn | - | 自定义文字内容的记录逻辑 | +| slimDOMOptions | {} | 去除 DOM 中不必要的部分
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | +| inlineStylesheet | true | 是否将样式表内联 | +| hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | +| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| recordCanvas | false | 是否记录 canvas 内容 | +| inlineImages | false | 是否将图片内容记内联录制 | +| collectFonts | false | 是否记录页面中的字体文件 | +| userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | +| plugins | [] | 加载插件以获得额外的录制功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | #### 隐私 @@ -281,23 +282,23 @@ replayer.pause(5000); 可以通过 `new rrweb.Replayer(events, options)` 的方式向 rrweb 传递回放时的配置参数,具体配置如下: -| key | 默认值 | 功能 | -| ------------------- | ------------- | ------------------------------------------------------------ | -| speed | 1 | 回放倍速 | -| root | document.body | 回放时使用的 HTML 元素 | -| loadTimeout | 0 | 加载异步样式表的超时时长 | -| skipInactive | false | 是否快速跳过无用户操作的阶段 | -| showWarning | true | 是否在回放过程中打印警告信息 | -| showDebug | false | 是否在回放过程中打印 debug 信息 | -| blockClass | 'rr-block' | 需要在回放时展示为隐藏区域的元素类名 | -| liveMode | false | 是否开启直播模式 | -| insertStyleRules | [] | 可以传入多个 CSS rule string,用于自定义回放时 iframe 内的样式 | -| triggerFocus | true | 回放时是否回放 focus 交互 | -| UNSAFE_replayCanvas | false | 回放时是否回放 canvas 内容,**开启后将会关闭沙盒策略,导致一定风险** | +| key | 默认值 | 功能 | +| ------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| speed | 1 | 回放倍速 | +| root | document.body | 回放时使用的 HTML 元素 | +| loadTimeout | 0 | 加载异步样式表的超时时长 | +| skipInactive | false | 是否快速跳过无用户操作的阶段 | +| showWarning | true | 是否在回放过程中打印警告信息 | +| showDebug | false | 是否在回放过程中打印 debug 信息 | +| blockClass | 'rr-block' | 需要在回放时展示为隐藏区域的元素类名 | +| liveMode | false | 是否开启直播模式 | +| insertStyleRules | [] | 可以传入多个 CSS rule string,用于自定义回放时 iframe 内的样式 | +| triggerFocus | true | 回放时是否回放 focus 交互 | +| UNSAFE_replayCanvas | false | 回放时是否回放 canvas 内容,**开启后将会关闭沙盒策略,导致一定风险** | | mouseTail | true | 是否在回放时增加鼠标轨迹。传入 false 可关闭,传入对象可以定制轨迹持续时间、样式等,配置详见[类型](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L407) | -| unpackFn | - | 数据解压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| logConfig | - | console logger 数据播放设置,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) | -| plugins | [] | 加载插件以获得额外的回放功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | +| unpackFn | - | 数据解压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| logConfig | - | console logger 数据播放设置,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) | +| plugins | [] | 加载插件以获得额外的回放功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | #### 使用 rrweb-player diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 5930913a8a..e9189c1670 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -59,6 +59,7 @@ function record( recordCanvas = false, userTriggeredOnInput = false, collectFonts = false, + inlineImages = false, plugins, keepIframeSrcFn = () => false, } = options; @@ -197,6 +198,7 @@ function record( maskTextFn, maskInputFn, recordCanvas, + inlineImages, sampling, slimDOMOptions, iframeManager, @@ -228,6 +230,7 @@ function record( maskTextFn, slimDOM: slimDOMOptions, recordCanvas, + inlineImages, onSerialize: (n) => { if (isIframeINode(n)) { iframeManager.addIframe(n); @@ -390,6 +393,7 @@ function record( inlineStylesheet, sampling, recordCanvas, + inlineImages, userTriggeredOnInput, collectFonts, doc, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 924c58cd2c..275843fbb7 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -172,6 +172,7 @@ export default class MutationBuffer { private maskTextFn: MaskTextFn | undefined; private maskInputFn: MaskInputFn | undefined; private recordCanvas: boolean; + private inlineImages: boolean; private slimDOMOptions: SlimDOMOptions; private doc: Document; @@ -190,6 +191,7 @@ export default class MutationBuffer { maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, + inlineImages: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, @@ -205,6 +207,7 @@ export default class MutationBuffer { this.maskTextFn = maskTextFn; this.maskInputFn = maskInputFn; this.recordCanvas = recordCanvas; + this.inlineImages = inlineImages; this.slimDOMOptions = slimDOMOptions; this.emissionCallback = cb; this.doc = doc; @@ -296,6 +299,7 @@ export default class MutationBuffer { maskInputFn: this.maskInputFn, slimDOMOptions: this.slimDOMOptions, recordCanvas: this.recordCanvas, + inlineImages: this.inlineImages, onSerialize: (currentN) => { if (isIframeINode(currentN)) { this.iframeManager.addIframe(currentN); @@ -504,7 +508,8 @@ export default class MutationBuffer { } } for (const pname of Array.from(old.style)) { - if (target.style.getPropertyValue(pname) === '') { // "if not set, returns the empty string" + if (target.style.getPropertyValue(pname) === '') { + // "if not set, returns the empty string" styleObj[pname] = false; // delete } } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index d59e57d4d0..b01ef49e1f 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -97,6 +97,7 @@ export function initMutationObserver( maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, + inlineImages: boolean, slimDOMOptions: SlimDOMOptions, mirror: Mirror, iframeManager: IframeManager, @@ -117,6 +118,7 @@ export function initMutationObserver( maskTextFn, maskInputFn, recordCanvas, + inlineImages, slimDOMOptions, doc, mirror, @@ -951,6 +953,7 @@ export function initObservers( o.maskTextFn, o.maskInputFn, o.recordCanvas, + o.inlineImages, o.slimDOMOptions, o.mirror, o.iframeManager, diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index e068bb645d..6f2ed26a66 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -25,6 +25,7 @@ type BypassOptions = { maskTextFn: MaskTextFn | undefined; maskInputFn: MaskInputFn | undefined; recordCanvas: boolean; + inlineImages: boolean; sampling: SamplingStrategy; slimDOMOptions: SlimDOMOptions; iframeManager: IframeManager; @@ -61,6 +62,7 @@ export class ShadowDomManager { this.bypassOptions.maskTextFn, this.bypassOptions.maskInputFn, this.bypassOptions.recordCanvas, + this.bypassOptions.inlineImages, this.bypassOptions.slimDOMOptions, this.mirror, this.bypassOptions.iframeManager, diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index da904dee13..a84a29edd1 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -230,6 +230,7 @@ export type recordOptions = { recordCanvas?: boolean; userTriggeredOnInput?: boolean; collectFonts?: boolean; + inlineImages?: boolean; plugins?: RecordPlugin[]; // departed, please use sampling options mousemoveWait?: number; @@ -259,6 +260,7 @@ export type observerParam = { fontCb: fontCallback; sampling: SamplingStrategy; recordCanvas: boolean; + inlineImages: boolean; userTriggeredOnInput: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; diff --git a/packages/rrweb/typings/record/mutation.d.ts b/packages/rrweb/typings/record/mutation.d.ts index b67548e30b..e47b119721 100644 --- a/packages/rrweb/typings/record/mutation.d.ts +++ b/packages/rrweb/typings/record/mutation.d.ts @@ -23,12 +23,13 @@ export default class MutationBuffer { private maskTextFn; private maskInputFn; private recordCanvas; + private inlineImages; private slimDOMOptions; private doc; private mirror; private iframeManager; private shadowDomManager; - init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager): void; + init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, inlineImages: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager): void; freeze(): void; unfreeze(): void; isFrozen(): boolean; diff --git a/packages/rrweb/typings/record/observer.d.ts b/packages/rrweb/typings/record/observer.d.ts index ffca5918e6..58965d552d 100644 --- a/packages/rrweb/typings/record/observer.d.ts +++ b/packages/rrweb/typings/record/observer.d.ts @@ -4,7 +4,7 @@ import MutationBuffer from './mutation'; import { IframeManager } from './iframe-manager'; import { ShadowDomManager } from './shadow-dom-manager'; export declare const mutationBuffers: MutationBuffer[]; -export declare function initMutationObserver(cb: mutationCallBack, doc: Document, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager, rootEl: Node): MutationObserver; +export declare function initMutationObserver(cb: mutationCallBack, doc: Document, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, maskInputFn: MaskInputFn | undefined, recordCanvas: boolean, inlineImages: boolean, slimDOMOptions: SlimDOMOptions, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager, rootEl: Node): MutationObserver; export declare function initScrollObserver(cb: scrollCallback, doc: Document, mirror: Mirror, blockClass: blockClass, sampling: SamplingStrategy): listenerHandler; export declare const INPUT_TAGS: string[]; export declare function initObservers(o: observerParam, hooks?: hooksParam): listenerHandler; diff --git a/packages/rrweb/typings/record/shadow-dom-manager.d.ts b/packages/rrweb/typings/record/shadow-dom-manager.d.ts index c3bbaef717..d31d572759 100644 --- a/packages/rrweb/typings/record/shadow-dom-manager.d.ts +++ b/packages/rrweb/typings/record/shadow-dom-manager.d.ts @@ -11,6 +11,7 @@ declare type BypassOptions = { maskTextFn: MaskTextFn | undefined; maskInputFn: MaskInputFn | undefined; recordCanvas: boolean; + inlineImages: boolean; sampling: SamplingStrategy; slimDOMOptions: SlimDOMOptions; iframeManager: IframeManager; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index 7d9c049435..ce27ce4147 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -122,6 +122,7 @@ export declare type SamplingStrategy = Partial<{ mousemoveCallback: number; mouseInteraction: boolean | Record; scroll: number; + media: number; input: 'all' | 'last'; }>; export declare type RecordPlugin = { @@ -150,6 +151,7 @@ export declare type recordOptions = { recordCanvas?: boolean; userTriggeredOnInput?: boolean; collectFonts?: boolean; + inlineImages?: boolean; plugins?: RecordPlugin[]; mousemoveWait?: number; keepIframeSrcFn?: KeepIframeSrcFn; @@ -177,6 +179,7 @@ export declare type observerParam = { fontCb: fontCallback; sampling: SamplingStrategy; recordCanvas: boolean; + inlineImages: boolean; userTriggeredOnInput: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; @@ -349,12 +352,15 @@ export declare type inputCallback = (v: inputValue & { export declare const enum MediaInteractions { Play = 0, Pause = 1, - Seeked = 2 + Seeked = 2, + VolumeChange = 3 } export declare type mediaInteractionParam = { type: MediaInteractions; id: number; currentTime?: number; + volume?: number; + muted?: boolean; }; export declare type mediaInteractionCallback = (p: mediaInteractionParam) => void; export declare type DocumentDimension = {