Skip to content

Commit

Permalink
Avoid closest lookup for the full snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Babik committed Oct 24, 2023
1 parent 8444cb2 commit 444618f
Showing 1 changed file with 56 additions and 11 deletions.
67 changes: 56 additions & 11 deletions packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ function serializeNode(
* `newlyAddedElement: true` skips scrollTop and scrollLeft check
*/
newlyAddedElement?: boolean;
maskCurrentNode?: boolean;
},
): serializedNode | false {
const {
Expand All @@ -458,6 +459,7 @@ function serializeNode(
recordCanvas,
keepIframeSrcFn,
newlyAddedElement = false,
maskCurrentNode,
} = options;
// Only record root id when document object is not the base document
const rootId = getRootId(doc, mirror);
Expand Down Expand Up @@ -504,6 +506,7 @@ function serializeNode(
maskTextSelector,
maskTextFn,
rootId,
maskCurrentNode,
});
case n.CDATA_SECTION_NODE:
return {
Expand Down Expand Up @@ -535,9 +538,16 @@ function serializeTextNode(
maskTextSelector: string | null;
maskTextFn: MaskTextFn | undefined;
rootId: number | undefined;
maskCurrentNode?: boolean;
},
): serializedNode {
const { maskTextClass, maskTextSelector, maskTextFn, rootId } = options;
const {
maskTextClass,
maskTextSelector,
maskTextFn,
rootId,
maskCurrentNode,
} = 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;
Expand Down Expand Up @@ -568,15 +578,18 @@ function serializeTextNode(
if (isScript) {
textContent = 'SCRIPT_PLACEHOLDER';
}
if (
!isStyle &&
!isScript &&
textContent &&
needMaskingText(n, maskTextClass, maskTextSelector)
) {
textContent = maskTextFn
? maskTextFn(textContent, n.parentElement)
: textContent.replace(/[\S]/g, '*');
if (!isStyle && !isScript && textContent) {
let mask = false;
if (typeof maskCurrentNode === 'undefined') {
mask = needMaskingText(n, maskTextClass, maskTextSelector);
} else {
mask = maskCurrentNode;
}
if (mask) {
textContent = maskTextFn
? maskTextFn(textContent, n.parentElement)
: textContent.replace(/[\S]/g, '*');
}
}

return {
Expand Down Expand Up @@ -946,6 +959,8 @@ export function serializeNodeWithId(
node: serializedElementNodeWithId,
) => unknown;
stylesheetLoadTimeout?: number;
maskCurrentNode?: boolean;
maskedElements?: Element[];
},
): serializedNodeWithId | null {
const {
Expand All @@ -971,8 +986,12 @@ export function serializeNodeWithId(
stylesheetLoadTimeout = 5000,
keepIframeSrcFn = () => false,
newlyAddedElement = false,
maskedElements = [],
} = options;
let { preserveWhiteSpace = true } = options;
let { preserveWhiteSpace = true, maskCurrentNode } = options;
if (maskCurrentNode === false) {
maskCurrentNode = maskedElements.includes(n as Element);
}
const _serializedNode = serializeNode(n, {
doc,
mirror,
Expand All @@ -989,6 +1008,7 @@ export function serializeNodeWithId(
recordCanvas,
keepIframeSrcFn,
newlyAddedElement,
maskCurrentNode,
});
if (!_serializedNode) {
// TODO: dev only
Expand Down Expand Up @@ -1068,6 +1088,8 @@ export function serializeNodeWithId(
onStylesheetLoad,
stylesheetLoadTimeout,
keepIframeSrcFn,
maskCurrentNode,
maskedElements,
};
for (const childN of Array.from(n.childNodes)) {
const serializedChildNode = serializeNodeWithId(childN, bypassOptions);
Expand Down Expand Up @@ -1128,6 +1150,8 @@ export function serializeNodeWithId(
onStylesheetLoad,
stylesheetLoadTimeout,
keepIframeSrcFn,
maskCurrentNode,
maskedElements,
});

if (serializedIframeNode) {
Expand Down Expand Up @@ -1175,6 +1199,8 @@ export function serializeNodeWithId(
onStylesheetLoad,
stylesheetLoadTimeout,
keepIframeSrcFn,
maskCurrentNode,
maskedElements,
});

if (serializedLinkNode) {
Expand Down Expand Up @@ -1288,6 +1314,23 @@ function snapshot(
: slimDOM === false
? {}
: slimDOM;
// Select elements to mask and pass the masking flag as an argument to a recursive function.
// This should help avoid expensive `closest` look-ups when deciding whether a text node should be masked.
// It makes sense to do this if maskTextClass is a string.
// If maskTextClass is a RegExp the procedure is more complex.
let maskCurrentNode: boolean | undefined;
let maskedElements: Element[] | undefined;
let isMaskTextClassString = typeof maskTextClass === 'string';
if (isMaskTextClassString) {
maskCurrentNode = false;
maskedElements = [];
const elements = n.querySelectorAll('.' + maskTextClass);
maskedElements = maskedElements.concat(Array.from(elements));
if (maskTextSelector) {
const elements = n.querySelectorAll(maskTextSelector);
maskedElements = maskedElements.concat(Array.from(elements));
}
}
return serializeNodeWithId(n, {
doc: n,
mirror,
Expand All @@ -1312,6 +1355,8 @@ function snapshot(
stylesheetLoadTimeout,
keepIframeSrcFn,
newlyAddedElement: false,
maskCurrentNode,
maskedElements,
});
}

Expand Down

0 comments on commit 444618f

Please sign in to comment.