Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text masking settings apply to inputs #1097

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/brave-spoons-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'rrweb-snapshot': patch
'rrweb': patch
---

text masking settings apply to inputs
20 changes: 18 additions & 2 deletions packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@
export function ignoreAttribute(
tagName: string,
name: string,
_value: unknown,

Check warning on line 258 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L258

[@typescript-eslint/no-unused-vars] '_value' is defined but never used.
): boolean {
return (tagName === 'video' || tagName === 'audio') && name === 'autoplay';
}
Expand Down Expand Up @@ -321,6 +321,7 @@
? (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 @@ -393,7 +394,7 @@
iframeEl.addEventListener('load', listener);
}

function isStylesheetLoaded(link: HTMLLinkElement) {

Check warning on line 397 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L397

[@typescript-eslint/no-unused-vars] 'isStylesheetLoaded' is defined but never used.
if (!link.getAttribute('href')) return true; // nothing to load
return link.sheet !== null;
}
Expand Down Expand Up @@ -503,11 +504,14 @@
keepIframeSrcFn,
newlyAddedElement,
rootId,
needsMask,
});
case n.TEXT_NODE:
return serializeTextNode(n as Text, {
needsMask,
maskTextFn,
maskInputOptions,
maskInputFn,
rootId,
});
case n.CDATA_SECTION_NODE:
Expand Down Expand Up @@ -538,16 +542,20 @@
options: {
needsMask: boolean | undefined;
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 All @@ -558,7 +566,7 @@
// So we'll be conservative and keep textContent as-is.
} else if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) {
textContent = stringifyStylesheet(
(n.parentNode as HTMLStyleElement).sheet!,

Check warning on line 569 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L569

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
);
}
} catch (err) {
Expand All @@ -577,6 +585,11 @@
? 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 @@ -604,6 +617,7 @@
*/
newlyAddedElement?: boolean;
rootId: number | undefined;
needsMask?: boolean;
},
): serializedNode | false {
const {
Expand All @@ -619,6 +633,7 @@
keepIframeSrcFn,
newlyAddedElement = false,
rootId,
needsMask,
} = options;
const needBlock = _isBlockedElement(n, blockClass, blockSelector);
const tagName = getValidTagName(n);
Expand Down Expand Up @@ -647,7 +662,7 @@
if (cssText) {
delete attributes.rel;
delete attributes.href;
attributes._cssText = absoluteToStylesheet(cssText, stylesheet!.href!);

Check warning on line 665 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L665

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.

Check warning on line 665 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L665

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
}
}
// dynamic stylesheet
Expand Down Expand Up @@ -682,6 +697,7 @@
value,
maskInputOptions,
maskInputFn,
needsMask,
});
} else if (checked) {
attributes.checked = checked;
Expand Down Expand Up @@ -740,10 +756,10 @@
const recordInlineImage = () => {
image.removeEventListener('load', recordInlineImage);
try {
canvasService!.width = image.naturalWidth;

Check warning on line 759 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L759

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
canvasService!.height = image.naturalHeight;

Check warning on line 760 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L760

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
canvasCtx!.drawImage(image, 0, 0);

Check warning on line 761 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L761

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
attributes.rr_dataURL = canvasService!.toDataURL(

Check warning on line 762 in packages/rrweb-snapshot/src/snapshot.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/snapshot.ts#L762

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
dataURLOptions.type,
dataURLOptions.quality,
);
Expand Down Expand Up @@ -1246,7 +1262,7 @@
inlineStylesheet?: boolean;
maskAllInputs?: boolean | MaskInputOptions;
maskTextFn?: MaskTextFn;
maskInputFn?: MaskTextFn;
maskInputFn?: MaskInputFn;
slimDOM?: 'all' | boolean | SlimDOMOptions;
dataURLOptions?: DataURLOptions;
inlineImages?: boolean;
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 @@ -71,7 +71,7 @@
* Browsers sometimes incorrectly escape `@import` on `.cssText` statements.
* This function tries to correct the escaping.
* more info: https://bugs.chromium.org/p/chromium/issues/detail?id=1472259
* @param cssImportRule

Check warning on line 74 in packages/rrweb-snapshot/src/utils.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb-snapshot/src/utils.ts#L74

[tsdoc/syntax] tsdoc-param-tag-missing-hyphen: The @param block should be followed by a parameter name and then a hyphen
* @returns `cssText` with browser inconsistencies fixed, or null if not applicable.
*/
export function escapeImportStatement(rule: CSSImportRule): string {
Expand Down Expand Up @@ -219,20 +219,23 @@
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 @@ -362,6 +362,7 @@
maskTextSelector,
inlineStylesheet,
maskAllInputs: maskInputOptions,
maskInputFn,
maskTextFn,
slimDOM: slimDOMOptions,
dataURLOptions,
Expand Down Expand Up @@ -534,7 +535,7 @@
plugins
?.filter((p) => p.observer)
?.map((p) => ({
observer: p.observer!,

Check warning on line 538 in packages/rrweb/src/record/index.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb/src/record/index.ts#L538

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
options: p.options,
callback: (payload: object) =>
wrappedEmit({
Expand All @@ -552,7 +553,7 @@

iframeManager.addLoadListener((iframeEl) => {
try {
handlers.push(observe(iframeEl.contentDocument!));

Check warning on line 556 in packages/rrweb/src/record/index.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb/src/record/index.ts#L556

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
} catch (error) {
// TODO: handle internal error
console.warn(error);
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 @@ -348,13 +348,13 @@
};

while (this.mapRemoves.length) {
this.mirror.removeNodeFromMap(this.mapRemoves.shift()!);

Check warning on line 351 in packages/rrweb/src/record/mutation.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb/src/record/mutation.ts#L351

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
}

for (const n of this.movedSet) {
if (
isParentRemoved(this.removes, n, this.mirror) &&
!this.movedSet.has(n.parentNode!)

Check warning on line 357 in packages/rrweb/src/record/mutation.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb/src/record/mutation.ts#L357

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
) {
continue;
}
Expand Down Expand Up @@ -566,13 +566,21 @@
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 @@ -4,6 +4,7 @@
Mirror,
getInputType,
toLowerCase,
needMaskingText,
} from 'rrweb-snapshot';
import type { FontFaceSet } from 'css-font-loading-module';
import {
Expand Down Expand Up @@ -161,7 +162,7 @@
| IncrementalSource.TouchMove
| IncrementalSource.Drag,
) => {
const totalOffset = Date.now() - timeBaseline!;

Check warning on line 165 in packages/rrweb/src/record/observer.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/rrweb/src/record/observer.ts#L165

[@typescript-eslint/no-non-null-assertion] Forbidden non-null assertion.
mousemoveCb(
positions.map((p) => {
p.timeOffset -= totalOffset;
Expand Down Expand Up @@ -420,6 +421,8 @@
maskInputFn,
sampling,
userTriggeredOnInput,
maskTextClass,
maskTextSelector,
}: observerParam): listenerHandler {
function eventHandler(event: Event) {
let target = getEventTarget(event) as HTMLElement | null;
Expand Down Expand Up @@ -452,11 +455,19 @@
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 @@ -465,6 +476,7 @@
type,
value: text,
maskInputFn,
needsMask,
});
}
cbWithDedup(
Expand Down
Loading
Loading