Skip to content

Commit

Permalink
apply text mask settings to inputs rrweb-io#1096
Browse files Browse the repository at this point in the history
  • Loading branch information
mdellanoce authored and jeffdnguyen committed Jul 16, 2024
1 parent 2d53c07 commit 2ca12d1
Show file tree
Hide file tree
Showing 8 changed files with 743 additions and 4 deletions.
25 changes: 24 additions & 1 deletion packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,12 @@ export function needMaskingText(
el = node.parentElement;
}
try {
const el: HTMLElement | null =
node.nodeType === node.ELEMENT_NODE
? (node as HTMLElement)
: node.parentElement;
if (el === null) return false;
if (maskTextSelector === '*') return true;
if (typeof maskTextClass === 'string') {
if (checkAncestors) {
if (el.closest(`.${maskTextClass}`)) return true;
Expand Down Expand Up @@ -519,12 +525,15 @@ function serializeNode(
keepIframeSrcFn,
newlyAddedElement,
rootId,
needsMask,
});
case n.TEXT_NODE:
return serializeTextNode(n as Text, {
doc,
needsMask,
maskTextFn,
maskInputOptions,
maskInputFn,
rootId,
});
case n.CDATA_SECTION_NODE:
Expand Down Expand Up @@ -556,16 +565,20 @@ function serializeTextNode(
doc: Document;
needsMask: boolean;
maskTextFn: MaskTextFn | undefined;
maskInputOptions: MaskInputOptions;
maskInputFn: MaskInputFn | undefined;
rootId: number | undefined;
},
): serializedNode {
const { needsMask, maskTextFn, rootId } = options;
const { needsMask, maskTextFn, maskInputOptions, maskInputFn, rootId } =
options;
// The parent node may not be a html element which has a tagName attribute.
// So just let it be undefined which is ok in this use case.
const parentTagName = n.parentNode && (n.parentNode as HTMLElement).tagName;
let textContent = n.textContent;
const isStyle = parentTagName === 'STYLE' ? true : undefined;
const isScript = parentTagName === 'SCRIPT' ? true : undefined;
const isTextarea = parentTagName === 'TEXTAREA' ? true : undefined;
if (isStyle && textContent) {
try {
// try to read style sheet
Expand Down Expand Up @@ -595,6 +608,11 @@ function serializeTextNode(
? maskTextFn(textContent, n.parentElement)
: textContent.replace(/[\S]/g, '*');
}
if (isTextarea && textContent && maskInputOptions.textarea) {
textContent = maskInputFn
? maskInputFn(textContent, n.parentNode as HTMLElement)
: textContent.replace(/[\S]/g, '*');
}

return {
type: NodeType.Text,
Expand Down Expand Up @@ -622,6 +640,7 @@ function serializeElementNode(
*/
newlyAddedElement?: boolean;
rootId: number | undefined;
needsMask?: boolean;
},
): serializedNode | false {
const {
Expand All @@ -637,6 +656,7 @@ function serializeElementNode(
keepIframeSrcFn,
newlyAddedElement = false,
rootId,
needsMask,
} = options;
const needBlock = _isBlockedElement(n, blockClass, blockSelector);
const tagName = getValidTagName(n);
Expand Down Expand Up @@ -693,13 +713,16 @@ function serializeElementNode(
attributes.type !== 'button' &&
value
) {
const type = getInputType(n);

attributes.value = maskInputValue({
element: n,
type: getInputType(n),
tagName,
value,
maskInputOptions,
maskInputFn,
needsMask,
});
} else if (checked) {
attributes.checked = checked;
Expand Down
5 changes: 4 additions & 1 deletion packages/rrweb-snapshot/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,20 +248,23 @@ export function maskInputValue({
type,
value,
maskInputFn,
needsMask,
}: {
element: HTMLElement;
maskInputOptions: MaskInputOptions;
tagName: string;
type: string | null;
value: string | null;
maskInputFn?: MaskInputFn;
needsMask?: boolean;
}): string {
let text = value || '';
const actualType = type && toLowerCase(type);

if (
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
(actualType && maskInputOptions[actualType as keyof MaskInputOptions])
(actualType && maskInputOptions[actualType as keyof MaskInputOptions]) ||
needsMask
) {
if (maskInputFn) {
text = maskInputFn(text, element);
Expand Down
1 change: 1 addition & 0 deletions packages/rrweb/src/record/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ function record<T = eventWithTime>(
maskTextSelector,
inlineStylesheet,
maskAllInputs: maskInputOptions,
maskInputFn,
maskTextFn,
maskInputFn,
slimDOM: slimDOMOptions,
Expand Down
8 changes: 8 additions & 0 deletions packages/rrweb/src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,13 +566,21 @@ export default class MutationBuffer {
if (attributeName === 'value') {
const type = getInputType(target);

const needsMask = needMaskingText(
m.target,
this.maskTextClass,
this.maskTextSelector,
true,
);

value = maskInputValue({
element: target,
maskInputOptions: this.maskInputOptions,
tagName: target.tagName,
type,
value,
maskInputFn: this.maskInputFn,
needsMask,
});
}
if (
Expand Down
14 changes: 13 additions & 1 deletion packages/rrweb/src/record/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
toLowerCase,
getNative,
nativeSetTimeout,
needMaskingText,
} from 'rrweb-snapshot';
import type { FontFaceSet } from 'css-font-loading-module';
import {
Expand Down Expand Up @@ -406,6 +407,8 @@ function initInputObserver({
maskInputFn,
sampling,
userTriggeredOnInput,
maskTextClass,
maskTextSelector,
}: observerParam): listenerHandler {
function eventHandler(event: Event) {
let target = getEventTarget(event) as HTMLElement | null;
Expand Down Expand Up @@ -438,11 +441,19 @@ function initInputObserver({
let isChecked = false;
const type: Lowercase<string> = getInputType(target) || '';

const needsMask = needMaskingText(
target as Node,
maskTextClass,
maskTextSelector,
true,
);

if (type === 'radio' || type === 'checkbox') {
isChecked = (target as HTMLInputElement).checked;
} else if (
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
maskInputOptions[type as keyof MaskInputOptions]
maskInputOptions[type as keyof MaskInputOptions] ||
needsMask
) {
text = maskInputValue({
element: target,
Expand All @@ -451,6 +462,7 @@ function initInputObserver({
type,
value: text,
maskInputFn,
needsMask,
});
}
cbWithDedup(
Expand Down
Loading

0 comments on commit 2ca12d1

Please sign in to comment.