Mask value attribute changes for elements in maskInputOptions (#602)
* mask value attribute changes for elements in maskInputOptions * refactor initInputObserver to use maskInputValue * add todo * Fix typo * upgrade rrweb-snapshot to 1.1.6 * move maskInputValue to rrweb-snapshot
This commit is contained in:
@@ -66,6 +66,6 @@
|
||||
"@xstate/fsm": "^1.4.0",
|
||||
"fflate": "^0.4.4",
|
||||
"mitt": "^1.1.3",
|
||||
"rrweb-snapshot": "^1.1.5"
|
||||
"rrweb-snapshot": "^1.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +194,7 @@ function record<T = eventWithTime>(
|
||||
inlineStylesheet,
|
||||
maskInputOptions,
|
||||
maskTextFn,
|
||||
maskInputFn,
|
||||
recordCanvas,
|
||||
sampling,
|
||||
slimDOMOptions,
|
||||
|
||||
@@ -7,6 +7,9 @@ import {
|
||||
IGNORED_NODE,
|
||||
isShadowRoot,
|
||||
needMaskingText,
|
||||
maskInputValue,
|
||||
MaskTextFn,
|
||||
MaskInputFn,
|
||||
} from 'rrweb-snapshot';
|
||||
import {
|
||||
mutationRecord,
|
||||
@@ -17,7 +20,6 @@ import {
|
||||
attributeCursor,
|
||||
removedNodeMutation,
|
||||
addedNodeMutation,
|
||||
MaskTextFn,
|
||||
Mirror,
|
||||
} from '../types';
|
||||
import {
|
||||
@@ -167,6 +169,7 @@ export default class MutationBuffer {
|
||||
private inlineStylesheet: boolean;
|
||||
private maskInputOptions: MaskInputOptions;
|
||||
private maskTextFn: MaskTextFn | undefined;
|
||||
private maskInputFn: MaskInputFn | undefined;
|
||||
private recordCanvas: boolean;
|
||||
private slimDOMOptions: SlimDOMOptions;
|
||||
private doc: Document;
|
||||
@@ -184,6 +187,7 @@ export default class MutationBuffer {
|
||||
inlineStylesheet: boolean,
|
||||
maskInputOptions: MaskInputOptions,
|
||||
maskTextFn: MaskTextFn | undefined,
|
||||
maskInputFn: MaskInputFn | undefined,
|
||||
recordCanvas: boolean,
|
||||
slimDOMOptions: SlimDOMOptions,
|
||||
doc: Document,
|
||||
@@ -198,6 +202,7 @@ export default class MutationBuffer {
|
||||
this.inlineStylesheet = inlineStylesheet;
|
||||
this.maskInputOptions = maskInputOptions;
|
||||
this.maskTextFn = maskTextFn;
|
||||
this.maskInputFn = maskInputFn;
|
||||
this.recordCanvas = recordCanvas;
|
||||
this.slimDOMOptions = slimDOMOptions;
|
||||
this.emissionCallback = cb;
|
||||
@@ -287,6 +292,7 @@ export default class MutationBuffer {
|
||||
inlineStylesheet: this.inlineStylesheet,
|
||||
maskInputOptions: this.maskInputOptions,
|
||||
maskTextFn: this.maskTextFn,
|
||||
maskInputFn: this.maskInputFn,
|
||||
slimDOMOptions: this.slimDOMOptions,
|
||||
recordCanvas: this.recordCanvas,
|
||||
onSerialize: (currentN) => {
|
||||
@@ -443,7 +449,16 @@ export default class MutationBuffer {
|
||||
break;
|
||||
}
|
||||
case 'attributes': {
|
||||
const value = (m.target as HTMLElement).getAttribute(m.attributeName!);
|
||||
let value = (m.target as HTMLElement).getAttribute(m.attributeName!);
|
||||
if (m.attributeName === 'value') {
|
||||
value = maskInputValue({
|
||||
maskInputOptions: this.maskInputOptions,
|
||||
tagName: (m.target as HTMLElement).tagName,
|
||||
type: (m.target as HTMLElement).getAttribute('type'),
|
||||
value,
|
||||
maskInputFn: this.maskInputFn,
|
||||
});
|
||||
}
|
||||
if (isBlocked(m.target, this.blockClass) || value === m.oldValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import {
|
||||
INode,
|
||||
MaskInputOptions,
|
||||
SlimDOMOptions,
|
||||
maskInputValue,
|
||||
MaskInputFn,
|
||||
MaskTextFn,
|
||||
} from 'rrweb-snapshot';
|
||||
import { FontFaceDescriptors, FontFaceSet } from 'css-font-loading-module';
|
||||
import {
|
||||
throttle,
|
||||
@@ -35,8 +42,6 @@ import {
|
||||
canvasMutationCallback,
|
||||
fontCallback,
|
||||
fontParam,
|
||||
MaskInputFn,
|
||||
MaskTextFn,
|
||||
Mirror,
|
||||
} from '../types';
|
||||
import MutationBuffer from './mutation';
|
||||
@@ -83,6 +88,7 @@ export function initMutationObserver(
|
||||
inlineStylesheet: boolean,
|
||||
maskInputOptions: MaskInputOptions,
|
||||
maskTextFn: MaskTextFn | undefined,
|
||||
maskInputFn: MaskInputFn | undefined,
|
||||
recordCanvas: boolean,
|
||||
slimDOMOptions: SlimDOMOptions,
|
||||
mirror: Mirror,
|
||||
@@ -102,6 +108,7 @@ export function initMutationObserver(
|
||||
inlineStylesheet,
|
||||
maskInputOptions,
|
||||
maskTextFn,
|
||||
maskInputFn,
|
||||
recordCanvas,
|
||||
slimDOMOptions,
|
||||
doc,
|
||||
@@ -366,11 +373,13 @@ function initInputObserver(
|
||||
] ||
|
||||
maskInputOptions[type as keyof MaskInputOptions]
|
||||
) {
|
||||
if (maskInputFn) {
|
||||
text = maskInputFn(text);
|
||||
} else {
|
||||
text = '*'.repeat(text.length);
|
||||
}
|
||||
text = maskInputValue({
|
||||
maskInputOptions,
|
||||
tagName: (target as HTMLElement).tagName,
|
||||
type,
|
||||
value: text,
|
||||
maskInputFn,
|
||||
});
|
||||
}
|
||||
cbWithDedup(target, { text, isChecked });
|
||||
// if a radio was checked
|
||||
@@ -476,7 +485,7 @@ function initMediaInteractionObserver(
|
||||
blockClass: blockClass,
|
||||
mirror: Mirror,
|
||||
): listenerHandler {
|
||||
const handler = (type: MediaInteractions ) => (event: Event) => {
|
||||
const handler = (type: MediaInteractions) => (event: Event) => {
|
||||
const target = getEventTarget(event);
|
||||
if (!target || isBlocked(target as Node, blockClass)) {
|
||||
return;
|
||||
@@ -484,13 +493,13 @@ function initMediaInteractionObserver(
|
||||
mediaInteractionCb({
|
||||
type,
|
||||
id: mirror.getId(target as INode),
|
||||
currentTime: (target as HTMLMediaElement).currentTime
|
||||
currentTime: (target as HTMLMediaElement).currentTime,
|
||||
});
|
||||
};
|
||||
const handlers = [
|
||||
on('play', handler(MediaInteractions.Play)),
|
||||
on('pause', handler(MediaInteractions.Pause)),
|
||||
on('seeked', handler(MediaInteractions.Seeked))
|
||||
on('seeked', handler(MediaInteractions.Seeked)),
|
||||
];
|
||||
return () => {
|
||||
handlers.forEach((h) => h());
|
||||
@@ -716,6 +725,7 @@ export function initObservers(
|
||||
o.inlineStylesheet,
|
||||
o.maskInputOptions,
|
||||
o.maskTextFn,
|
||||
o.maskInputFn,
|
||||
o.recordCanvas,
|
||||
o.slimDOMOptions,
|
||||
o.mirror,
|
||||
|
||||
@@ -2,12 +2,16 @@ import {
|
||||
mutationCallBack,
|
||||
blockClass,
|
||||
maskTextClass,
|
||||
MaskTextFn,
|
||||
Mirror,
|
||||
scrollCallback,
|
||||
SamplingStrategy,
|
||||
} from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import {
|
||||
MaskInputOptions,
|
||||
SlimDOMOptions,
|
||||
MaskTextFn,
|
||||
MaskInputFn,
|
||||
} from 'rrweb-snapshot';
|
||||
import { IframeManager } from './iframe-manager';
|
||||
import { initMutationObserver, initScrollObserver } from './observer';
|
||||
|
||||
@@ -19,6 +23,7 @@ type BypassOptions = {
|
||||
inlineStylesheet: boolean;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskTextFn: MaskTextFn | undefined;
|
||||
maskInputFn: MaskInputFn | undefined;
|
||||
recordCanvas: boolean;
|
||||
sampling: SamplingStrategy;
|
||||
slimDOMOptions: SlimDOMOptions;
|
||||
@@ -54,6 +59,7 @@ export class ShadowDomManager {
|
||||
this.bypassOptions.inlineStylesheet,
|
||||
this.bypassOptions.maskInputOptions,
|
||||
this.bypassOptions.maskTextFn,
|
||||
this.bypassOptions.maskInputFn,
|
||||
this.bypassOptions.recordCanvas,
|
||||
this.bypassOptions.slimDOMOptions,
|
||||
this.mirror,
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
INode,
|
||||
MaskInputOptions,
|
||||
SlimDOMOptions,
|
||||
MaskInputFn,
|
||||
MaskTextFn,
|
||||
} from 'rrweb-snapshot';
|
||||
import { PackFn, UnpackFn } from './packer/base';
|
||||
import { FontFaceDescriptors } from 'css-font-loading-module';
|
||||
@@ -544,10 +546,6 @@ export enum ReplayerEvents {
|
||||
PlayBack = 'play-back',
|
||||
}
|
||||
|
||||
export type MaskInputFn = (text: string) => string;
|
||||
|
||||
export type MaskTextFn = (text: string) => string;
|
||||
|
||||
// store the state that would be changed during the process(unmount from dom and mount again)
|
||||
export type ElementState = {
|
||||
// [scrollLeft,scrollTop]
|
||||
|
||||
24
src/utils.ts
24
src/utils.ts
@@ -53,7 +53,7 @@ export function createMirror(): Mirror {
|
||||
delete this.map[id];
|
||||
if (n.childNodes) {
|
||||
n.childNodes.forEach((child) =>
|
||||
this.removeNodeFromMap(child as Node as INode),
|
||||
this.removeNodeFromMap((child as Node) as INode),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -275,7 +275,7 @@ export function isAncestorRemoved(target: INode, mirror: Mirror): boolean {
|
||||
if (!target.parentNode) {
|
||||
return true;
|
||||
}
|
||||
return isAncestorRemoved(target.parentNode as unknown as INode, mirror);
|
||||
return isAncestorRemoved((target.parentNode as unknown) as INode, mirror);
|
||||
}
|
||||
|
||||
export function isTouchEvent(
|
||||
@@ -286,13 +286,13 @@ export function isTouchEvent(
|
||||
|
||||
export function polyfill(win = window) {
|
||||
if ('NodeList' in win && !win.NodeList.prototype.forEach) {
|
||||
win.NodeList.prototype.forEach = Array.prototype
|
||||
.forEach as unknown as NodeList['forEach'];
|
||||
win.NodeList.prototype.forEach = (Array.prototype
|
||||
.forEach as unknown) as NodeList['forEach'];
|
||||
}
|
||||
|
||||
if ('DOMTokenList' in win && !win.DOMTokenList.prototype.forEach) {
|
||||
win.DOMTokenList.prototype.forEach = Array.prototype
|
||||
.forEach as unknown as DOMTokenList['forEach'];
|
||||
win.DOMTokenList.prototype.forEach = (Array.prototype
|
||||
.forEach as unknown) as DOMTokenList['forEach'];
|
||||
}
|
||||
|
||||
// https://github.com/Financial-Times/polyfill-service/pull/183
|
||||
@@ -396,7 +396,7 @@ export class TreeIndex {
|
||||
const node = mirror.getNode(id);
|
||||
node?.childNodes.forEach((childNode) => {
|
||||
if ('__sn' in childNode) {
|
||||
deepRemoveFromMirror((childNode as unknown as INode).__sn.id);
|
||||
deepRemoveFromMirror(((childNode as unknown) as INode).__sn.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -460,8 +460,12 @@ export class TreeIndex {
|
||||
scrollMap: TreeIndex['scrollMap'];
|
||||
inputMap: TreeIndex['inputMap'];
|
||||
} {
|
||||
const { tree, removeNodeMutations, textMutations, attributeMutations } =
|
||||
this;
|
||||
const {
|
||||
tree,
|
||||
removeNodeMutations,
|
||||
textMutations,
|
||||
attributeMutations,
|
||||
} = this;
|
||||
|
||||
const batchMutationData: mutationData = {
|
||||
source: IncrementalSource.Mutation,
|
||||
@@ -650,5 +654,5 @@ export function getBaseDimension(
|
||||
export function hasShadowRoot<T extends Node>(
|
||||
n: T,
|
||||
): n is T & { shadowRoot: ShadowRoot } {
|
||||
return Boolean((n as unknown as Element)?.shadowRoot);
|
||||
return Boolean(((n as unknown) as Element)?.shadowRoot);
|
||||
}
|
||||
|
||||
@@ -5461,6 +5461,360 @@ exports[`maskInputOptions 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`maskPassword 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\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {
|
||||
\\"lang\\": \\"en\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"charset\\": \\"UTF-8\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 5
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"http-equiv\\": \\"X-UA-Compatible\\",
|
||||
\\"content\\": \\"IE=edge\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"name\\": \\"viewport\\",
|
||||
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 9
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"title\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Document\\",
|
||||
\\"id\\": 12
|
||||
}
|
||||
],
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 13
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 14
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 16
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"input\\",
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"password\\",
|
||||
\\"id\\": \\"password\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 17
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\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
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 23
|
||||
}
|
||||
],
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 24
|
||||
}
|
||||
],
|
||||
\\"id\\": 15
|
||||
}
|
||||
],
|
||||
\\"id\\": 2
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 17,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"*\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"**\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 17,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"**\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"***\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 17,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"***\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"****\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 17,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"****\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*****\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 17,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"*****\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"******\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 17,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"******\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`move-node-1 1`] = `
|
||||
"[
|
||||
{
|
||||
|
||||
17
test/html/password.html
Normal file
17
test/html/password.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="password" id="password" />
|
||||
<script>
|
||||
const password = document.getElementById('password');
|
||||
password.addEventListener('keyup', (event) => {
|
||||
password.setAttribute('value', password.value);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -267,6 +267,23 @@ describe('record integration tests', function (this: ISuite) {
|
||||
assertSnapshot(snapshots, __filename, 'maskInputOptions');
|
||||
});
|
||||
|
||||
it('should mask value attribute with maskInputOptions', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(
|
||||
getHtml.call(this, 'password.html', {
|
||||
maskInputOptions: {
|
||||
password: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await page.type('input[type="password"]', 'secr3t');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'maskPassword');
|
||||
});
|
||||
|
||||
it('should not record blocked elements and its child nodes', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
|
||||
7
typings/record/mutation.d.ts
vendored
7
typings/record/mutation.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import { mutationRecord, blockClass, maskTextClass, mutationCallBack, MaskTextFn, Mirror } from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions, MaskTextFn, MaskInputFn } from 'rrweb-snapshot';
|
||||
import { mutationRecord, blockClass, maskTextClass, mutationCallBack, Mirror } from '../types';
|
||||
import { IframeManager } from './iframe-manager';
|
||||
import { ShadowDomManager } from './shadow-dom-manager';
|
||||
export default class MutationBuffer {
|
||||
@@ -21,13 +21,14 @@ export default class MutationBuffer {
|
||||
private inlineStylesheet;
|
||||
private maskInputOptions;
|
||||
private maskTextFn;
|
||||
private maskInputFn;
|
||||
private recordCanvas;
|
||||
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, 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, slimDOMOptions: SlimDOMOptions, doc: Document, mirror: Mirror, iframeManager: IframeManager, shadowDomManager: ShadowDomManager): void;
|
||||
freeze(): void;
|
||||
unfreeze(): void;
|
||||
isFrozen(): boolean;
|
||||
|
||||
6
typings/record/observer.d.ts
vendored
6
typings/record/observer.d.ts
vendored
@@ -1,10 +1,10 @@
|
||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import { mutationCallBack, observerParam, listenerHandler, scrollCallback, blockClass, maskTextClass, hooksParam, SamplingStrategy, MaskTextFn, Mirror } from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot';
|
||||
import { mutationCallBack, observerParam, listenerHandler, scrollCallback, blockClass, maskTextClass, hooksParam, SamplingStrategy, Mirror } from '../types';
|
||||
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, 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, 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;
|
||||
|
||||
5
typings/record/shadow-dom-manager.d.ts
vendored
5
typings/record/shadow-dom-manager.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn, Mirror, scrollCallback, SamplingStrategy } from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import { mutationCallBack, blockClass, maskTextClass, Mirror, scrollCallback, SamplingStrategy } from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions, MaskTextFn, MaskInputFn } from 'rrweb-snapshot';
|
||||
import { IframeManager } from './iframe-manager';
|
||||
declare type BypassOptions = {
|
||||
blockClass: blockClass;
|
||||
@@ -9,6 +9,7 @@ declare type BypassOptions = {
|
||||
inlineStylesheet: boolean;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskTextFn: MaskTextFn | undefined;
|
||||
maskInputFn: MaskInputFn | undefined;
|
||||
recordCanvas: boolean;
|
||||
sampling: SamplingStrategy;
|
||||
slimDOMOptions: SlimDOMOptions;
|
||||
|
||||
4
typings/types.d.ts
vendored
4
typings/types.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import { serializedNodeWithId, idNodeMap, INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import { serializedNodeWithId, idNodeMap, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot';
|
||||
import { PackFn, UnpackFn } from './packer/base';
|
||||
import { FontFaceDescriptors } from 'css-font-loading-module';
|
||||
import { IframeManager } from './record/iframe-manager';
|
||||
@@ -413,8 +413,6 @@ export declare enum ReplayerEvents {
|
||||
StateChange = "state-change",
|
||||
PlayBack = "play-back"
|
||||
}
|
||||
export declare type MaskInputFn = (text: string) => string;
|
||||
export declare type MaskTextFn = (text: string) => string;
|
||||
export declare type ElementState = {
|
||||
scroll?: [number, number];
|
||||
};
|
||||
|
||||
@@ -2845,10 +2845,10 @@ rollup@^2.3.3:
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
rrweb-snapshot@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.5.tgz#7784a01e7d2ccd067ec56cdec38a1fc2a77cbe86"
|
||||
integrity sha512-U5mfiySoK85WkLCljFTh+ShgTDrOMXWrh1qpou8WwW3AEXba7CSv/sTtoXp3QfG5oxe1YOc+fC3tB4kHMdI00w==
|
||||
rrweb-snapshot@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.6.tgz#e598510e387b9b2c62ce0cfc586a37c89549b228"
|
||||
integrity sha512-twvqV1WRuFwodO5UV0tzS4Q2w+ZYi5kwOx/uwbRkplfazcnbphDLFvy8x9IwMOZFhyRg1jDJjbnlC8wy4xq5bQ==
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.4.1"
|
||||
|
||||
Reference in New Issue
Block a user