Files
rrweb/packages/rrweb-snapshot/src/utils.ts

76 lines
2.2 KiB
TypeScript

import { INode, MaskInputFn, MaskInputOptions } from './types';
export function isElement(n: Node | INode): n is Element {
return n.nodeType === n.ELEMENT_NODE;
}
export function isShadowRoot(n: Node): n is ShadowRoot {
const host: Element | null = (n as ShadowRoot)?.host;
return Boolean(host && host.shadowRoot && host.shadowRoot === n);
}
export function maskInputValue({
maskInputOptions,
tagName,
type,
value,
maskInputFn,
}: {
maskInputOptions: MaskInputOptions;
tagName: string;
type: string | number | boolean | null;
value: string | null;
maskInputFn?: MaskInputFn;
}): string {
let text = value || '';
if (
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
maskInputOptions[type as keyof MaskInputOptions]
) {
if (maskInputFn) {
text = maskInputFn(text);
} else {
text = '*'.repeat(text.length);
}
}
return text;
}
const ORIGINAL_ATTRIBUTE_NAME = '__rrweb_original__';
type PatchedGetImageData = {
[ORIGINAL_ATTRIBUTE_NAME]: CanvasImageData['getImageData'];
} & CanvasImageData['getImageData'];
export function is2DCanvasBlank(canvas: HTMLCanvasElement): boolean {
const ctx = canvas.getContext('2d');
if (!ctx) return true;
const chunkSize = 50;
// get chunks of the canvas and check if it is blank
for (let x = 0; x < canvas.width; x += chunkSize) {
for (let y = 0; y < canvas.height; y += chunkSize) {
const getImageData = ctx.getImageData as PatchedGetImageData;
const originalGetImageData =
ORIGINAL_ATTRIBUTE_NAME in getImageData
? getImageData[ORIGINAL_ATTRIBUTE_NAME]
: getImageData;
// by getting the canvas in chunks we avoid an expensive
// `getImageData` call that retrieves everything
// even if we can already tell from the first chunk(s) that
// the canvas isn't blank
const pixelBuffer = new Uint32Array(
originalGetImageData.call(
ctx,
x,
y,
Math.min(chunkSize, canvas.width - x),
Math.min(chunkSize, canvas.height - y),
).data.buffer,
);
if (pixelBuffer.some((pixel) => pixel !== 0)) return false;
}
}
return true;
}