create mirror during record
This commit is contained in:
11
src/index.ts
11
src/index.ts
@@ -1,6 +1,6 @@
|
|||||||
import record from './record';
|
import record from './record';
|
||||||
import { Replayer } from './replay';
|
import { Replayer } from './replay';
|
||||||
import { mirror } from './utils';
|
import { _mirror } from './utils';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -13,4 +13,11 @@ export {
|
|||||||
const { addCustomEvent } = record;
|
const { addCustomEvent } = record;
|
||||||
const { freezePage } = record;
|
const { freezePage } = record;
|
||||||
|
|
||||||
export { record, addCustomEvent, freezePage, Replayer, mirror, utils };
|
export {
|
||||||
|
record,
|
||||||
|
addCustomEvent,
|
||||||
|
freezePage,
|
||||||
|
Replayer,
|
||||||
|
_mirror as mirror,
|
||||||
|
utils,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { snapshot, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
import { snapshot, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||||
import { initObservers, mutationBuffers } from './observer';
|
import { initObservers, mutationBuffers } from './observer';
|
||||||
import {
|
import {
|
||||||
mirror,
|
|
||||||
on,
|
on,
|
||||||
getWindowWidth,
|
getWindowWidth,
|
||||||
getWindowHeight,
|
getWindowHeight,
|
||||||
polyfill,
|
polyfill,
|
||||||
isIframeINode,
|
isIframeINode,
|
||||||
hasShadowRoot,
|
hasShadowRoot,
|
||||||
|
createMirror,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import {
|
import {
|
||||||
EventType,
|
EventType,
|
||||||
@@ -33,6 +33,7 @@ let wrappedEmit!: (e: eventWithTime, isCheckout?: boolean) => void;
|
|||||||
|
|
||||||
let takeFullSnapshot!: (isCheckout?: boolean) => void;
|
let takeFullSnapshot!: (isCheckout?: boolean) => void;
|
||||||
|
|
||||||
|
const mirror = createMirror();
|
||||||
function record<T = eventWithTime>(
|
function record<T = eventWithTime>(
|
||||||
options: recordOptions<T> = {},
|
options: recordOptions<T> = {},
|
||||||
): listenerHandler | undefined {
|
): listenerHandler | undefined {
|
||||||
@@ -215,6 +216,7 @@ function record<T = eventWithTime>(
|
|||||||
slimDOMOptions,
|
slimDOMOptions,
|
||||||
iframeManager,
|
iframeManager,
|
||||||
},
|
},
|
||||||
|
mirror,
|
||||||
});
|
});
|
||||||
|
|
||||||
takeFullSnapshot = (isCheckout = false) => {
|
takeFullSnapshot = (isCheckout = false) => {
|
||||||
@@ -418,6 +420,7 @@ function record<T = eventWithTime>(
|
|||||||
logOptions,
|
logOptions,
|
||||||
blockSelector,
|
blockSelector,
|
||||||
slimDOMOptions,
|
slimDOMOptions,
|
||||||
|
mirror,
|
||||||
iframeManager,
|
iframeManager,
|
||||||
shadowDomManager,
|
shadowDomManager,
|
||||||
},
|
},
|
||||||
@@ -490,4 +493,6 @@ record.takeFullSnapshot = (isCheckout?: boolean) => {
|
|||||||
takeFullSnapshot(isCheckout);
|
takeFullSnapshot(isCheckout);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
record.mirror = mirror;
|
||||||
|
|
||||||
export default record;
|
export default record;
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import {
|
|||||||
removedNodeMutation,
|
removedNodeMutation,
|
||||||
addedNodeMutation,
|
addedNodeMutation,
|
||||||
MaskTextFn,
|
MaskTextFn,
|
||||||
|
Mirror,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import {
|
||||||
mirror,
|
|
||||||
isBlocked,
|
isBlocked,
|
||||||
isAncestorRemoved,
|
isAncestorRemoved,
|
||||||
isIgnored,
|
isIgnored,
|
||||||
@@ -171,6 +171,7 @@ export default class MutationBuffer {
|
|||||||
private slimDOMOptions: SlimDOMOptions;
|
private slimDOMOptions: SlimDOMOptions;
|
||||||
private doc: Document;
|
private doc: Document;
|
||||||
|
|
||||||
|
private mirror: Mirror;
|
||||||
private iframeManager: IframeManager;
|
private iframeManager: IframeManager;
|
||||||
private shadowDomManager: ShadowDomManager;
|
private shadowDomManager: ShadowDomManager;
|
||||||
|
|
||||||
@@ -186,6 +187,7 @@ export default class MutationBuffer {
|
|||||||
recordCanvas: boolean,
|
recordCanvas: boolean,
|
||||||
slimDOMOptions: SlimDOMOptions,
|
slimDOMOptions: SlimDOMOptions,
|
||||||
doc: Document,
|
doc: Document,
|
||||||
|
mirror: Mirror,
|
||||||
iframeManager: IframeManager,
|
iframeManager: IframeManager,
|
||||||
shadowDomManager: ShadowDomManager,
|
shadowDomManager: ShadowDomManager,
|
||||||
) {
|
) {
|
||||||
@@ -200,6 +202,7 @@ export default class MutationBuffer {
|
|||||||
this.slimDOMOptions = slimDOMOptions;
|
this.slimDOMOptions = slimDOMOptions;
|
||||||
this.emissionCallback = cb;
|
this.emissionCallback = cb;
|
||||||
this.doc = doc;
|
this.doc = doc;
|
||||||
|
this.mirror = mirror;
|
||||||
this.iframeManager = iframeManager;
|
this.iframeManager = iframeManager;
|
||||||
this.shadowDomManager = shadowDomManager;
|
this.shadowDomManager = shadowDomManager;
|
||||||
}
|
}
|
||||||
@@ -251,7 +254,7 @@ export default class MutationBuffer {
|
|||||||
let nextId: number | null = IGNORED_NODE; // slimDOM: ignored
|
let nextId: number | null = IGNORED_NODE; // slimDOM: ignored
|
||||||
while (nextId === IGNORED_NODE) {
|
while (nextId === IGNORED_NODE) {
|
||||||
ns = ns && ns.nextSibling;
|
ns = ns && ns.nextSibling;
|
||||||
nextId = ns && mirror.getId((ns as unknown) as INode);
|
nextId = ns && this.mirror.getId((ns as unknown) as INode);
|
||||||
}
|
}
|
||||||
if (nextId === -1 && isBlocked(n.nextSibling, this.blockClass)) {
|
if (nextId === -1 && isBlocked(n.nextSibling, this.blockClass)) {
|
||||||
nextId = null;
|
nextId = null;
|
||||||
@@ -267,15 +270,15 @@ export default class MutationBuffer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const parentId = isShadowRoot(n.parentNode)
|
const parentId = isShadowRoot(n.parentNode)
|
||||||
? mirror.getId((shadowHost as unknown) as INode)
|
? this.mirror.getId((shadowHost as unknown) as INode)
|
||||||
: mirror.getId((n.parentNode as Node) as INode);
|
: this.mirror.getId((n.parentNode as Node) as INode);
|
||||||
const nextId = getNextId(n);
|
const nextId = getNextId(n);
|
||||||
if (parentId === -1 || nextId === -1) {
|
if (parentId === -1 || nextId === -1) {
|
||||||
return addList.addNode(n);
|
return addList.addNode(n);
|
||||||
}
|
}
|
||||||
let sn = serializeNodeWithId(n, {
|
let sn = serializeNodeWithId(n, {
|
||||||
doc: this.doc,
|
doc: this.doc,
|
||||||
map: mirror.map,
|
map: this.mirror.map,
|
||||||
blockClass: this.blockClass,
|
blockClass: this.blockClass,
|
||||||
blockSelector: this.blockSelector,
|
blockSelector: this.blockSelector,
|
||||||
maskTextClass: this.maskTextClass,
|
maskTextClass: this.maskTextClass,
|
||||||
@@ -308,7 +311,7 @@ export default class MutationBuffer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
while (this.mapRemoves.length) {
|
while (this.mapRemoves.length) {
|
||||||
mirror.removeNodeFromMap(this.mapRemoves.shift() as INode);
|
this.mirror.removeNodeFromMap(this.mapRemoves.shift() as INode);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const n of this.movedSet) {
|
for (const n of this.movedSet) {
|
||||||
@@ -338,7 +341,7 @@ export default class MutationBuffer {
|
|||||||
while (addList.length) {
|
while (addList.length) {
|
||||||
let node: DoubleLinkedListNode | null = null;
|
let node: DoubleLinkedListNode | null = null;
|
||||||
if (candidate) {
|
if (candidate) {
|
||||||
const parentId = mirror.getId(
|
const parentId = this.mirror.getId(
|
||||||
(candidate.value.parentNode as Node) as INode,
|
(candidate.value.parentNode as Node) as INode,
|
||||||
);
|
);
|
||||||
const nextId = getNextId(candidate.value);
|
const nextId = getNextId(candidate.value);
|
||||||
@@ -349,7 +352,7 @@ export default class MutationBuffer {
|
|||||||
if (!node) {
|
if (!node) {
|
||||||
for (let index = addList.length - 1; index >= 0; index--) {
|
for (let index = addList.length - 1; index >= 0; index--) {
|
||||||
const _node = addList.get(index)!;
|
const _node = addList.get(index)!;
|
||||||
const parentId = mirror.getId(
|
const parentId = this.mirror.getId(
|
||||||
(_node.value.parentNode as Node) as INode,
|
(_node.value.parentNode as Node) as INode,
|
||||||
);
|
);
|
||||||
const nextId = getNextId(_node.value);
|
const nextId = getNextId(_node.value);
|
||||||
@@ -378,18 +381,18 @@ export default class MutationBuffer {
|
|||||||
const payload = {
|
const payload = {
|
||||||
texts: this.texts
|
texts: this.texts
|
||||||
.map((text) => ({
|
.map((text) => ({
|
||||||
id: mirror.getId(text.node as INode),
|
id: this.mirror.getId(text.node as INode),
|
||||||
value: text.value,
|
value: text.value,
|
||||||
}))
|
}))
|
||||||
// text mutation's id was not in the mirror map means the target node has been removed
|
// text mutation's id was not in the mirror map means the target node has been removed
|
||||||
.filter((text) => mirror.has(text.id)),
|
.filter((text) => this.mirror.has(text.id)),
|
||||||
attributes: this.attributes
|
attributes: this.attributes
|
||||||
.map((attribute) => ({
|
.map((attribute) => ({
|
||||||
id: mirror.getId(attribute.node as INode),
|
id: this.mirror.getId(attribute.node as INode),
|
||||||
attributes: attribute.attributes,
|
attributes: attribute.attributes,
|
||||||
}))
|
}))
|
||||||
// attribute mutation's id was not in the mirror map means the target node has been removed
|
// attribute mutation's id was not in the mirror map means the target node has been removed
|
||||||
.filter((attribute) => mirror.has(attribute.id)),
|
.filter((attribute) => this.mirror.has(attribute.id)),
|
||||||
removes: this.removes,
|
removes: this.removes,
|
||||||
adds,
|
adds,
|
||||||
};
|
};
|
||||||
@@ -466,10 +469,10 @@ export default class MutationBuffer {
|
|||||||
case 'childList': {
|
case 'childList': {
|
||||||
m.addedNodes.forEach((n) => this.genAdds(n, m.target));
|
m.addedNodes.forEach((n) => this.genAdds(n, m.target));
|
||||||
m.removedNodes.forEach((n) => {
|
m.removedNodes.forEach((n) => {
|
||||||
const nodeId = mirror.getId(n as INode);
|
const nodeId = this.mirror.getId(n as INode);
|
||||||
const parentId = isShadowRoot(m.target)
|
const parentId = isShadowRoot(m.target)
|
||||||
? mirror.getId((m.target.host as unknown) as INode)
|
? this.mirror.getId((m.target.host as unknown) as INode)
|
||||||
: mirror.getId(m.target as INode);
|
: this.mirror.getId(m.target as INode);
|
||||||
if (
|
if (
|
||||||
isBlocked(n, this.blockClass) ||
|
isBlocked(n, this.blockClass) ||
|
||||||
isBlocked(m.target, this.blockClass) ||
|
isBlocked(m.target, this.blockClass) ||
|
||||||
@@ -489,7 +492,7 @@ export default class MutationBuffer {
|
|||||||
* newly added node will be serialized without child nodes.
|
* newly added node will be serialized without child nodes.
|
||||||
* TODO: verify this
|
* TODO: verify this
|
||||||
*/
|
*/
|
||||||
} else if (isAncestorRemoved(m.target as INode)) {
|
} else if (isAncestorRemoved(m.target as INode, this.mirror)) {
|
||||||
/**
|
/**
|
||||||
* If parent id was not in the mirror map any more, it
|
* If parent id was not in the mirror map any more, it
|
||||||
* means the parent node has already been removed. So
|
* means the parent node has already been removed. So
|
||||||
@@ -560,7 +563,7 @@ function isParentRemoved(removes: removedNodeMutation[], n: Node): boolean {
|
|||||||
if (!parentNode) {
|
if (!parentNode) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const parentId = mirror.getId((parentNode as Node) as INode);
|
const parentId = this.mirror.getId((parentNode as Node) as INode);
|
||||||
if (removes.some((r) => r.id === parentId)) {
|
if (removes.some((r) => r.id === parentId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
import { INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||||
import { FontFaceDescriptors, FontFaceSet } from 'css-font-loading-module';
|
import { FontFaceDescriptors, FontFaceSet } from 'css-font-loading-module';
|
||||||
import {
|
import {
|
||||||
mirror,
|
|
||||||
throttle,
|
throttle,
|
||||||
on,
|
on,
|
||||||
hookSetter,
|
hookSetter,
|
||||||
@@ -42,6 +41,7 @@ import {
|
|||||||
LogRecordOptions,
|
LogRecordOptions,
|
||||||
Logger,
|
Logger,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
|
Mirror,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import MutationBuffer from './mutation';
|
import MutationBuffer from './mutation';
|
||||||
import { stringify } from './stringify';
|
import { stringify } from './stringify';
|
||||||
@@ -72,6 +72,7 @@ export function initMutationObserver(
|
|||||||
maskTextFn: MaskTextFn | undefined,
|
maskTextFn: MaskTextFn | undefined,
|
||||||
recordCanvas: boolean,
|
recordCanvas: boolean,
|
||||||
slimDOMOptions: SlimDOMOptions,
|
slimDOMOptions: SlimDOMOptions,
|
||||||
|
mirror: Mirror,
|
||||||
iframeManager: IframeManager,
|
iframeManager: IframeManager,
|
||||||
shadowDomManager: ShadowDomManager,
|
shadowDomManager: ShadowDomManager,
|
||||||
rootEl: Node,
|
rootEl: Node,
|
||||||
@@ -91,6 +92,7 @@ export function initMutationObserver(
|
|||||||
recordCanvas,
|
recordCanvas,
|
||||||
slimDOMOptions,
|
slimDOMOptions,
|
||||||
doc,
|
doc,
|
||||||
|
mirror,
|
||||||
iframeManager,
|
iframeManager,
|
||||||
shadowDomManager,
|
shadowDomManager,
|
||||||
);
|
);
|
||||||
@@ -137,6 +139,7 @@ function initMoveObserver(
|
|||||||
cb: mousemoveCallBack,
|
cb: mousemoveCallBack,
|
||||||
sampling: SamplingStrategy,
|
sampling: SamplingStrategy,
|
||||||
doc: Document,
|
doc: Document,
|
||||||
|
mirror: Mirror,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
if (sampling.mousemove === false) {
|
if (sampling.mousemove === false) {
|
||||||
return () => {};
|
return () => {};
|
||||||
@@ -212,6 +215,7 @@ function initMoveObserver(
|
|||||||
function initMouseInteractionObserver(
|
function initMouseInteractionObserver(
|
||||||
cb: mouseInteractionCallBack,
|
cb: mouseInteractionCallBack,
|
||||||
doc: Document,
|
doc: Document,
|
||||||
|
mirror: Mirror,
|
||||||
blockClass: blockClass,
|
blockClass: blockClass,
|
||||||
sampling: SamplingStrategy,
|
sampling: SamplingStrategy,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
@@ -264,6 +268,7 @@ function initMouseInteractionObserver(
|
|||||||
function initScrollObserver(
|
function initScrollObserver(
|
||||||
cb: scrollCallback,
|
cb: scrollCallback,
|
||||||
doc: Document,
|
doc: Document,
|
||||||
|
mirror: Mirror,
|
||||||
blockClass: blockClass,
|
blockClass: blockClass,
|
||||||
sampling: SamplingStrategy,
|
sampling: SamplingStrategy,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
@@ -315,6 +320,7 @@ const lastInputValueMap: WeakMap<EventTarget, inputValue> = new WeakMap();
|
|||||||
function initInputObserver(
|
function initInputObserver(
|
||||||
cb: inputCallback,
|
cb: inputCallback,
|
||||||
doc: Document,
|
doc: Document,
|
||||||
|
mirror: Mirror,
|
||||||
blockClass: blockClass,
|
blockClass: blockClass,
|
||||||
ignoreClass: string,
|
ignoreClass: string,
|
||||||
maskInputOptions: MaskInputOptions,
|
maskInputOptions: MaskInputOptions,
|
||||||
@@ -419,7 +425,10 @@ function initInputObserver(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function initStyleSheetObserver(cb: styleSheetRuleCallback): listenerHandler {
|
function initStyleSheetObserver(
|
||||||
|
cb: styleSheetRuleCallback,
|
||||||
|
mirror: Mirror,
|
||||||
|
): listenerHandler {
|
||||||
const insertRule = CSSStyleSheet.prototype.insertRule;
|
const insertRule = CSSStyleSheet.prototype.insertRule;
|
||||||
CSSStyleSheet.prototype.insertRule = function (rule: string, index?: number) {
|
CSSStyleSheet.prototype.insertRule = function (rule: string, index?: number) {
|
||||||
const id = mirror.getId(this.ownerNode as INode);
|
const id = mirror.getId(this.ownerNode as INode);
|
||||||
@@ -453,6 +462,7 @@ function initStyleSheetObserver(cb: styleSheetRuleCallback): listenerHandler {
|
|||||||
function initMediaInteractionObserver(
|
function initMediaInteractionObserver(
|
||||||
mediaInteractionCb: mediaInteractionCallback,
|
mediaInteractionCb: mediaInteractionCallback,
|
||||||
blockClass: blockClass,
|
blockClass: blockClass,
|
||||||
|
mirror: Mirror,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
const handler = (type: 'play' | 'pause') => (event: Event) => {
|
const handler = (type: 'play' | 'pause') => (event: Event) => {
|
||||||
const { target } = event;
|
const { target } = event;
|
||||||
@@ -473,6 +483,7 @@ function initMediaInteractionObserver(
|
|||||||
function initCanvasMutationObserver(
|
function initCanvasMutationObserver(
|
||||||
cb: canvasMutationCallback,
|
cb: canvasMutationCallback,
|
||||||
blockClass: blockClass,
|
blockClass: blockClass,
|
||||||
|
mirror: Mirror,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
const props = Object.getOwnPropertyNames(CanvasRenderingContext2D.prototype);
|
const props = Object.getOwnPropertyNames(CanvasRenderingContext2D.prototype);
|
||||||
const handlers: listenerHandler[] = [];
|
const handlers: listenerHandler[] = [];
|
||||||
@@ -779,20 +790,28 @@ export function initObservers(
|
|||||||
o.maskTextFn,
|
o.maskTextFn,
|
||||||
o.recordCanvas,
|
o.recordCanvas,
|
||||||
o.slimDOMOptions,
|
o.slimDOMOptions,
|
||||||
|
o.mirror,
|
||||||
o.iframeManager,
|
o.iframeManager,
|
||||||
o.shadowDomManager,
|
o.shadowDomManager,
|
||||||
o.doc,
|
o.doc,
|
||||||
);
|
);
|
||||||
const mousemoveHandler = initMoveObserver(o.mousemoveCb, o.sampling, o.doc);
|
const mousemoveHandler = initMoveObserver(
|
||||||
|
o.mousemoveCb,
|
||||||
|
o.sampling,
|
||||||
|
o.doc,
|
||||||
|
o.mirror,
|
||||||
|
);
|
||||||
const mouseInteractionHandler = initMouseInteractionObserver(
|
const mouseInteractionHandler = initMouseInteractionObserver(
|
||||||
o.mouseInteractionCb,
|
o.mouseInteractionCb,
|
||||||
o.doc,
|
o.doc,
|
||||||
|
o.mirror,
|
||||||
o.blockClass,
|
o.blockClass,
|
||||||
o.sampling,
|
o.sampling,
|
||||||
);
|
);
|
||||||
const scrollHandler = initScrollObserver(
|
const scrollHandler = initScrollObserver(
|
||||||
o.scrollCb,
|
o.scrollCb,
|
||||||
o.doc,
|
o.doc,
|
||||||
|
o.mirror,
|
||||||
o.blockClass,
|
o.blockClass,
|
||||||
o.sampling,
|
o.sampling,
|
||||||
);
|
);
|
||||||
@@ -800,6 +819,7 @@ export function initObservers(
|
|||||||
const inputHandler = initInputObserver(
|
const inputHandler = initInputObserver(
|
||||||
o.inputCb,
|
o.inputCb,
|
||||||
o.doc,
|
o.doc,
|
||||||
|
o.mirror,
|
||||||
o.blockClass,
|
o.blockClass,
|
||||||
o.ignoreClass,
|
o.ignoreClass,
|
||||||
o.maskInputOptions,
|
o.maskInputOptions,
|
||||||
@@ -809,10 +829,14 @@ export function initObservers(
|
|||||||
const mediaInteractionHandler = initMediaInteractionObserver(
|
const mediaInteractionHandler = initMediaInteractionObserver(
|
||||||
o.mediaInteractionCb,
|
o.mediaInteractionCb,
|
||||||
o.blockClass,
|
o.blockClass,
|
||||||
|
o.mirror,
|
||||||
|
);
|
||||||
|
const styleSheetObserver = initStyleSheetObserver(
|
||||||
|
o.styleSheetRuleCb,
|
||||||
|
o.mirror,
|
||||||
);
|
);
|
||||||
const styleSheetObserver = initStyleSheetObserver(o.styleSheetRuleCb);
|
|
||||||
const canvasMutationObserver = o.recordCanvas
|
const canvasMutationObserver = o.recordCanvas
|
||||||
? initCanvasMutationObserver(o.canvasMutationCb, o.blockClass)
|
? initCanvasMutationObserver(o.canvasMutationCb, o.blockClass, o.mirror)
|
||||||
: () => {};
|
: () => {};
|
||||||
const fontObserver = o.collectFonts ? initFontObserver(o.fontCb) : () => {};
|
const fontObserver = o.collectFonts ? initFontObserver(o.fontCb) : () => {};
|
||||||
const logObserver = o.logOptions
|
const logObserver = o.logOptions
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn } from '../types';
|
import {
|
||||||
|
mutationCallBack,
|
||||||
|
blockClass,
|
||||||
|
maskTextClass,
|
||||||
|
MaskTextFn,
|
||||||
|
Mirror,
|
||||||
|
} from '../types';
|
||||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||||
import { IframeManager } from './iframe-manager';
|
import { IframeManager } from './iframe-manager';
|
||||||
import { initMutationObserver } from './observer';
|
import { initMutationObserver } from './observer';
|
||||||
@@ -19,13 +25,16 @@ type BypassOptions = {
|
|||||||
export class ShadowDomManager {
|
export class ShadowDomManager {
|
||||||
private mutationCb: mutationCallBack;
|
private mutationCb: mutationCallBack;
|
||||||
private bypassOptions: BypassOptions;
|
private bypassOptions: BypassOptions;
|
||||||
|
private mirror: Mirror;
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
mutationCb: mutationCallBack;
|
mutationCb: mutationCallBack;
|
||||||
bypassOptions: BypassOptions;
|
bypassOptions: BypassOptions;
|
||||||
|
mirror: Mirror;
|
||||||
}) {
|
}) {
|
||||||
this.mutationCb = options.mutationCb;
|
this.mutationCb = options.mutationCb;
|
||||||
this.bypassOptions = options.bypassOptions;
|
this.bypassOptions = options.bypassOptions;
|
||||||
|
this.mirror = this.mirror;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addShadowRoot(shadowRoot: ShadowRoot, doc: Document) {
|
public addShadowRoot(shadowRoot: ShadowRoot, doc: Document) {
|
||||||
@@ -41,6 +50,7 @@ export class ShadowDomManager {
|
|||||||
this.bypassOptions.maskTextFn,
|
this.bypassOptions.maskTextFn,
|
||||||
this.bypassOptions.recordCanvas,
|
this.bypassOptions.recordCanvas,
|
||||||
this.bypassOptions.slimDOMOptions,
|
this.bypassOptions.slimDOMOptions,
|
||||||
|
this.mirror,
|
||||||
this.bypassOptions.iframeManager,
|
this.bypassOptions.iframeManager,
|
||||||
this,
|
this,
|
||||||
shadowRoot,
|
shadowRoot,
|
||||||
|
|||||||
@@ -779,7 +779,7 @@ export class Replayer {
|
|||||||
d.adds.forEach((m) => this.treeIndex.add(m));
|
d.adds.forEach((m) => this.treeIndex.add(m));
|
||||||
d.texts.forEach((m) => this.treeIndex.text(m));
|
d.texts.forEach((m) => this.treeIndex.text(m));
|
||||||
d.attributes.forEach((m) => this.treeIndex.attribute(m));
|
d.attributes.forEach((m) => this.treeIndex.attribute(m));
|
||||||
d.removes.forEach((m) => this.treeIndex.remove(m));
|
d.removes.forEach((m) => this.treeIndex.remove(m, this.mirror));
|
||||||
}
|
}
|
||||||
this.applyMutation(d, isSync);
|
this.applyMutation(d, isSync);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ export type observerParam = {
|
|||||||
collectFonts: boolean;
|
collectFonts: boolean;
|
||||||
slimDOMOptions: SlimDOMOptions;
|
slimDOMOptions: SlimDOMOptions;
|
||||||
doc: Document;
|
doc: Document;
|
||||||
|
mirror: Mirror;
|
||||||
iframeManager: IframeManager;
|
iframeManager: IframeManager;
|
||||||
shadowDomManager: ShadowDomManager;
|
shadowDomManager: ShadowDomManager;
|
||||||
};
|
};
|
||||||
|
|||||||
18
src/utils.ts
18
src/utils.ts
@@ -68,8 +68,12 @@ export function createMirror(): Mirror {
|
|||||||
|
|
||||||
// https://github.com/rrweb-io/rrweb/pull/407
|
// https://github.com/rrweb-io/rrweb/pull/407
|
||||||
const DEPARTED_MIRROR_ACCESS_WARNING =
|
const DEPARTED_MIRROR_ACCESS_WARNING =
|
||||||
'Please stop import mirror directly. Instead of that, now you can use replayer.getMirror() to access the mirror instance of a replayer.';
|
'Please stop import mirror directly. Instead of that,' +
|
||||||
export let mirror: Mirror = {
|
'\r\n' +
|
||||||
|
'now you can use replayer.getMirror() to access the mirror instance of a replayer,' +
|
||||||
|
'\r\n' +
|
||||||
|
'or you can use record.mirror to access the mirror instance during recording.';
|
||||||
|
export let _mirror: Mirror = {
|
||||||
map: {},
|
map: {},
|
||||||
getId() {
|
getId() {
|
||||||
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
||||||
@@ -90,8 +94,8 @@ export let mirror: Mirror = {
|
|||||||
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (window.Proxy && window.Reflect) {
|
if (typeof window !== 'undefined' && window.Proxy && window.Reflect) {
|
||||||
mirror = new Proxy(mirror, {
|
_mirror = new Proxy(_mirror, {
|
||||||
get(target, prop, receiver) {
|
get(target, prop, receiver) {
|
||||||
if (prop === 'map') {
|
if (prop === 'map') {
|
||||||
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
console.error(DEPARTED_MIRROR_ACCESS_WARNING);
|
||||||
@@ -253,7 +257,7 @@ export function isIgnored(n: Node | INode): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAncestorRemoved(target: INode): boolean {
|
export function isAncestorRemoved(target: INode, mirror: Mirror): boolean {
|
||||||
if (isShadowRoot(target)) {
|
if (isShadowRoot(target)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -271,7 +275,7 @@ export function isAncestorRemoved(target: INode): boolean {
|
|||||||
if (!target.parentNode) {
|
if (!target.parentNode) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return isAncestorRemoved((target.parentNode as unknown) as INode);
|
return isAncestorRemoved((target.parentNode as unknown) as INode, mirror);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTouchEvent(
|
export function isTouchEvent(
|
||||||
@@ -382,7 +386,7 @@ export class TreeIndex {
|
|||||||
this.indexes.set(treeNode.id, treeNode);
|
this.indexes.set(treeNode.id, treeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(mutation: removedNodeMutation) {
|
public remove(mutation: removedNodeMutation, mirror: Mirror) {
|
||||||
const parentTreeNode = this.indexes.get(mutation.parentId);
|
const parentTreeNode = this.indexes.get(mutation.parentId);
|
||||||
const treeNode = this.indexes.get(mutation.id);
|
const treeNode = this.indexes.get(mutation.id);
|
||||||
|
|
||||||
|
|||||||
@@ -142,10 +142,10 @@ describe('record', function (this: ISuite) {
|
|||||||
}
|
}
|
||||||
await this.page.waitFor(300);
|
await this.page.waitFor(300);
|
||||||
expect(this.events.length).to.equal(33); // before first automatic snapshot
|
expect(this.events.length).to.equal(33); // before first automatic snapshot
|
||||||
await this.page.waitFor(200); // could be 33 or 35 events by now depending on speed of test env
|
await this.page.waitFor(200); // could be 33 or 35 events by now depending on speed of test env
|
||||||
await this.page.type('input', 'a');
|
await this.page.type('input', 'a');
|
||||||
await this.page.waitFor(10);
|
await this.page.waitFor(10);
|
||||||
expect(this.events.length).to.equal(36); // additionally includes the 2 checkout events
|
expect(this.events.length).to.equal(36); // additionally includes the 2 checkout events
|
||||||
expect(
|
expect(
|
||||||
this.events.filter(
|
this.events.filter(
|
||||||
(event: eventWithTime) => event.type === EventType.Meta,
|
(event: eventWithTime) => event.type === EventType.Meta,
|
||||||
|
|||||||
4
typings/index.d.ts
vendored
4
typings/index.d.ts
vendored
@@ -1,8 +1,8 @@
|
|||||||
import record from './record';
|
import record from './record';
|
||||||
import { Replayer } from './replay';
|
import { Replayer } from './replay';
|
||||||
import { mirror } from './utils';
|
import { _mirror } from './utils';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
export { EventType, IncrementalSource, MouseInteractions, ReplayerEvents, } from './types';
|
export { EventType, IncrementalSource, MouseInteractions, ReplayerEvents, } from './types';
|
||||||
declare const addCustomEvent: <T>(tag: string, payload: T) => void;
|
declare const addCustomEvent: <T>(tag: string, payload: T) => void;
|
||||||
declare const freezePage: () => void;
|
declare const freezePage: () => void;
|
||||||
export { record, addCustomEvent, freezePage, Replayer, mirror, utils };
|
export { record, addCustomEvent, freezePage, Replayer, _mirror as mirror, utils, };
|
||||||
|
|||||||
1
typings/record/index.d.ts
vendored
1
typings/record/index.d.ts
vendored
@@ -4,5 +4,6 @@ declare namespace record {
|
|||||||
var addCustomEvent: <T>(tag: string, payload: T) => void;
|
var addCustomEvent: <T>(tag: string, payload: T) => void;
|
||||||
var freezePage: () => void;
|
var freezePage: () => void;
|
||||||
var takeFullSnapshot: (isCheckout?: boolean | undefined) => void;
|
var takeFullSnapshot: (isCheckout?: boolean | undefined) => void;
|
||||||
|
var mirror: import("../types").Mirror;
|
||||||
}
|
}
|
||||||
export default record;
|
export default record;
|
||||||
|
|||||||
5
typings/record/mutation.d.ts
vendored
5
typings/record/mutation.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||||
import { mutationRecord, blockClass, maskTextClass, mutationCallBack, MaskTextFn } from '../types';
|
import { mutationRecord, blockClass, maskTextClass, mutationCallBack, MaskTextFn, Mirror } from '../types';
|
||||||
import { IframeManager } from './iframe-manager';
|
import { IframeManager } from './iframe-manager';
|
||||||
import { ShadowDomManager } from './shadow-dom-manager';
|
import { ShadowDomManager } from './shadow-dom-manager';
|
||||||
export default class MutationBuffer {
|
export default class MutationBuffer {
|
||||||
@@ -24,9 +24,10 @@ export default class MutationBuffer {
|
|||||||
private recordCanvas;
|
private recordCanvas;
|
||||||
private slimDOMOptions;
|
private slimDOMOptions;
|
||||||
private doc;
|
private doc;
|
||||||
|
private mirror;
|
||||||
private iframeManager;
|
private iframeManager;
|
||||||
private shadowDomManager;
|
private shadowDomManager;
|
||||||
init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, maskTextClass: maskTextClass, maskTextSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, maskTextFn: MaskTextFn | undefined, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, 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, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager): void;
|
||||||
freeze(): void;
|
freeze(): void;
|
||||||
unfreeze(): void;
|
unfreeze(): void;
|
||||||
isFrozen(): boolean;
|
isFrozen(): boolean;
|
||||||
|
|||||||
4
typings/record/observer.d.ts
vendored
4
typings/record/observer.d.ts
vendored
@@ -1,9 +1,9 @@
|
|||||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||||
import { mutationCallBack, observerParam, listenerHandler, blockClass, maskTextClass, hooksParam, MaskTextFn } from '../types';
|
import { mutationCallBack, observerParam, listenerHandler, blockClass, maskTextClass, hooksParam, MaskTextFn, Mirror } from '../types';
|
||||||
import MutationBuffer from './mutation';
|
import MutationBuffer from './mutation';
|
||||||
import { IframeManager } from './iframe-manager';
|
import { IframeManager } from './iframe-manager';
|
||||||
import { ShadowDomManager } from './shadow-dom-manager';
|
import { ShadowDomManager } from './shadow-dom-manager';
|
||||||
export declare const mutationBuffers: MutationBuffer[];
|
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, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, 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, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager, rootEl: Node): MutationObserver;
|
||||||
export declare const INPUT_TAGS: string[];
|
export declare const INPUT_TAGS: string[];
|
||||||
export declare function initObservers(o: observerParam, hooks?: hooksParam): listenerHandler;
|
export declare function initObservers(o: observerParam, hooks?: hooksParam): listenerHandler;
|
||||||
|
|||||||
4
typings/record/shadow-dom-manager.d.ts
vendored
4
typings/record/shadow-dom-manager.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn } from '../types';
|
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn, Mirror } from '../types';
|
||||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||||
import { IframeManager } from './iframe-manager';
|
import { IframeManager } from './iframe-manager';
|
||||||
declare type BypassOptions = {
|
declare type BypassOptions = {
|
||||||
@@ -16,9 +16,11 @@ declare type BypassOptions = {
|
|||||||
export declare class ShadowDomManager {
|
export declare class ShadowDomManager {
|
||||||
private mutationCb;
|
private mutationCb;
|
||||||
private bypassOptions;
|
private bypassOptions;
|
||||||
|
private mirror;
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
mutationCb: mutationCallBack;
|
mutationCb: mutationCallBack;
|
||||||
bypassOptions: BypassOptions;
|
bypassOptions: BypassOptions;
|
||||||
|
mirror: Mirror;
|
||||||
});
|
});
|
||||||
addShadowRoot(shadowRoot: ShadowRoot, doc: Document): void;
|
addShadowRoot(shadowRoot: ShadowRoot, doc: Document): void;
|
||||||
}
|
}
|
||||||
|
|||||||
1
typings/types.d.ts
vendored
1
typings/types.d.ts
vendored
@@ -168,6 +168,7 @@ export declare type observerParam = {
|
|||||||
collectFonts: boolean;
|
collectFonts: boolean;
|
||||||
slimDOMOptions: SlimDOMOptions;
|
slimDOMOptions: SlimDOMOptions;
|
||||||
doc: Document;
|
doc: Document;
|
||||||
|
mirror: Mirror;
|
||||||
iframeManager: IframeManager;
|
iframeManager: IframeManager;
|
||||||
shadowDomManager: ShadowDomManager;
|
shadowDomManager: ShadowDomManager;
|
||||||
};
|
};
|
||||||
|
|||||||
6
typings/utils.d.ts
vendored
6
typings/utils.d.ts
vendored
@@ -2,7 +2,7 @@ import { Mirror, throttleOptions, listenerHandler, hookResetter, blockClass, eve
|
|||||||
import { INode, serializedNodeWithId } from 'rrweb-snapshot';
|
import { INode, serializedNodeWithId } from 'rrweb-snapshot';
|
||||||
export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | Window): listenerHandler;
|
export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | Window): listenerHandler;
|
||||||
export declare function createMirror(): Mirror;
|
export declare function createMirror(): Mirror;
|
||||||
export declare let mirror: Mirror;
|
export declare let _mirror: Mirror;
|
||||||
export declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void;
|
export declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void;
|
||||||
export declare function hookSetter<T>(target: T, key: string | number | symbol, d: PropertyDescriptor, isRevoked?: boolean, win?: Window & typeof globalThis): hookResetter;
|
export declare function hookSetter<T>(target: T, key: string | number | symbol, d: PropertyDescriptor, isRevoked?: boolean, win?: Window & typeof globalThis): hookResetter;
|
||||||
export declare function patch(source: {
|
export declare function patch(source: {
|
||||||
@@ -12,7 +12,7 @@ export declare function getWindowHeight(): number;
|
|||||||
export declare function getWindowWidth(): number;
|
export declare function getWindowWidth(): number;
|
||||||
export declare function isBlocked(node: Node | null, blockClass: blockClass): boolean;
|
export declare function isBlocked(node: Node | null, blockClass: blockClass): boolean;
|
||||||
export declare function isIgnored(n: Node | INode): boolean;
|
export declare function isIgnored(n: Node | INode): boolean;
|
||||||
export declare function isAncestorRemoved(target: INode): boolean;
|
export declare function isAncestorRemoved(target: INode, mirror: Mirror): boolean;
|
||||||
export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent;
|
export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent;
|
||||||
export declare function polyfill(win?: Window & typeof globalThis): void;
|
export declare function polyfill(win?: Window & typeof globalThis): void;
|
||||||
export declare function needCastInSyncMode(event: eventWithTime): boolean;
|
export declare function needCastInSyncMode(event: eventWithTime): boolean;
|
||||||
@@ -35,7 +35,7 @@ export declare class TreeIndex {
|
|||||||
private inputMap;
|
private inputMap;
|
||||||
constructor();
|
constructor();
|
||||||
add(mutation: addedNodeMutation): void;
|
add(mutation: addedNodeMutation): void;
|
||||||
remove(mutation: removedNodeMutation): void;
|
remove(mutation: removedNodeMutation, mirror: Mirror): void;
|
||||||
text(mutation: textMutation): void;
|
text(mutation: textMutation): void;
|
||||||
attribute(mutation: attributeMutation): void;
|
attribute(mutation: attributeMutation): void;
|
||||||
scroll(d: scrollData): void;
|
scroll(d: scrollData): void;
|
||||||
|
|||||||
Reference in New Issue
Block a user