Skip to content

Commit ac3ea6e

Browse files
committed
optimize mask/unmask rule matching #1097
1 parent 7173936 commit ac3ea6e

File tree

1 file changed

+50
-67
lines changed

1 file changed

+50
-67
lines changed

packages/rrweb-snapshot/src/snapshot.ts

Lines changed: 50 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -275,87 +275,67 @@ export function _isBlockedElement(
275275
return false;
276276
}
277277

278+
function elementClassMatchesRegex(el: HTMLElement, regex: RegExp): boolean {
279+
for (let eIndex = el.classList.length; eIndex--; ) {
280+
const className = el.classList[eIndex];
281+
if (regex.test(className)) {
282+
return true;
283+
}
284+
}
285+
return false;
286+
}
287+
278288
export function classMatchesRegex(
279289
node: Node | null,
280290
regex: RegExp,
281291
checkAncestors: boolean,
282292
): boolean {
283-
return distanceToClassRegexMatch(node, regex, checkAncestors) >= 0;
293+
if (!node) return false;
294+
if (checkAncestors) {
295+
return (
296+
distanceToMatch(node, (node) =>
297+
elementClassMatchesRegex(node as HTMLElement, regex),
298+
) >= 0
299+
);
300+
} else if (node.nodeType === node.ELEMENT_NODE) {
301+
return elementClassMatchesRegex(node as HTMLElement, regex);
302+
}
303+
return false;
284304
}
285305

286-
function distanceToClassRegexMatch(
306+
function distanceToMatch(
287307
node: Node | null,
288-
regex: RegExp,
289-
checkAncestors: boolean,
308+
matchPredicate: (node: Node) => boolean,
309+
limit = Infinity,
290310
distance = 0,
291311
): number {
292312
if (!node) return -1;
293-
if (node.nodeType !== node.ELEMENT_NODE) {
294-
if (!checkAncestors) return -1;
295-
return distanceToClassRegexMatch(node.parentNode, regex, checkAncestors);
296-
}
297-
298-
for (let eIndex = (node as HTMLElement).classList.length; eIndex--; ) {
299-
const className = (node as HTMLElement).classList[eIndex];
300-
if (regex.test(className)) {
301-
return distance;
302-
}
303-
}
304-
if (!checkAncestors) return -1;
305-
return distanceToClassRegexMatch(
306-
node.parentNode,
307-
regex,
308-
checkAncestors,
309-
distance + 1,
310-
);
313+
if (node.nodeType !== node.ELEMENT_NODE) return -1;
314+
if (distance > limit) return -1;
315+
if (matchPredicate(node)) return distance;
316+
return distanceToMatch(node.parentNode, matchPredicate, limit, distance + 1);
311317
}
312318

313-
function distanceToSelectorMatch(el: HTMLElement, selector: string): number {
314-
if (!el) return -1;
315-
if (el.matches(selector)) return 0;
316-
const closestParent = el.closest(selector);
317-
if (closestParent) {
318-
let current = el;
319-
let distance = 0;
320-
while (current && current !== closestParent) {
321-
current = current.parentNode as HTMLElement;
322-
if (!current) {
323-
return -1;
324-
}
325-
distance++;
326-
}
327-
return distance;
328-
}
329-
return -1;
330-
}
331-
332-
function distanceToMatch(
333-
el: HTMLElement,
319+
function createMatchPredicate(
334320
className: string | RegExp | null,
335321
selector: string | null,
336-
): number {
337-
let classDistance = -1;
338-
let selectorDistance = -1;
339-
340-
if (className) {
341-
if (typeof className === 'string') {
342-
classDistance = distanceToSelectorMatch(el, `.${className}`);
343-
} else {
344-
classDistance = distanceToClassRegexMatch(el, className, true);
322+
): (node: Node) => boolean {
323+
return (node: Node) => {
324+
const el = node as HTMLElement;
325+
if (el === null) return false;
326+
327+
if (className) {
328+
if (typeof className === 'string') {
329+
if (el.matches(`.${className}`)) return true;
330+
} else if (elementClassMatchesRegex(el, className)) {
331+
return true;
332+
}
345333
}
346-
}
347334

348-
if (selector) {
349-
selectorDistance = distanceToSelectorMatch(el, selector);
350-
}
335+
if (selector && el.matches(selector)) return true;
351336

352-
return selectorDistance >= 0
353-
? classDistance >= 0
354-
? Math.min(classDistance, selectorDistance)
355-
: selectorDistance
356-
: classDistance >= 0
357-
? classDistance
358-
: -1;
337+
return false;
338+
};
359339
}
360340

361341
export function needMaskingText(
@@ -374,16 +354,19 @@ export function needMaskingText(
374354

375355
const unmaskDistance = distanceToMatch(
376356
el,
377-
unmaskTextClass,
378-
unmaskTextSelector,
357+
createMatchPredicate(unmaskTextClass, unmaskTextSelector),
379358
);
380-
359+
381360
let maskDistance = -1;
382361
if (maskAllText && unmaskDistance < 0) {
383362
return true;
384363
}
385364

386-
maskDistance = distanceToMatch(el, maskTextClass, maskTextSelector);
365+
maskDistance = distanceToMatch(
366+
el,
367+
createMatchPredicate(maskTextClass, maskTextSelector),
368+
unmaskDistance >= 0 ? unmaskDistance : Infinity,
369+
);
387370

388371
return maskDistance >= 0
389372
? unmaskDistance >= 0

0 commit comments

Comments
 (0)