pick #286, export slim DOM options

This commit is contained in:
Yanzhen Yu
2026-04-01 12:00:00 +08:00
parent e9a5aeed06
commit ec5b7a4635
5 changed files with 78 additions and 21 deletions

View File

@@ -1,4 +1,4 @@
import { snapshot, MaskInputOptions } from 'rrweb-snapshot';
import { snapshot, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import { initObservers, mutationBuffer } from './observer';
import {
mirror,
@@ -38,6 +38,7 @@ function record<T = eventWithTime>(
inlineStylesheet = true,
maskAllInputs,
maskInputOptions: _maskInputOptions,
slimDOMOptions: _slimDOMOptions,
maskInputFn,
hooks,
packFn,
@@ -78,6 +79,26 @@ function record<T = eventWithTime>(
? _maskInputOptions
: {};
const slimDOMOptions: SlimDOMOptions =
_slimDOMOptions === true || _slimDOMOptions === 'all'
? {
script: true,
comment: true,
headFavicon: true,
headWhitespace: true,
headMetaSocial: true,
headMetaRobots: true,
headMetaHttpEquiv: true,
headMetaVerification: true,
// the following are off for slimDOMOptions === true,
// as they destroy some (hidden) info:
headMetaAuthorship: _slimDOMOptions === 'all',
headMetaDescKeywords: _slimDOMOptions === 'all',
}
: _slimDOMOptions === false
? {}
: _slimDOMOptions;
polyfill();
let lastFullSnapshotEvent: eventWithTime;
@@ -134,6 +155,7 @@ function record<T = eventWithTime>(
blockSelector,
inlineStylesheet,
maskAllInputs: maskInputOptions,
slimDOM: slimDOMOptions,
recordCanvas,
});
@@ -299,6 +321,7 @@ function record<T = eventWithTime>(
sampling,
recordCanvas,
collectFonts,
slimDOMOptions,
},
hooks,
),

View File

@@ -3,6 +3,8 @@ import {
serializeNodeWithId,
transformAttribute,
MaskInputOptions,
SlimDOMOptions,
IGNORED_NODE,
} from 'rrweb-snapshot';
import {
mutationRecord,
@@ -13,7 +15,7 @@ import {
removedNodeMutation,
addedNodeMutation,
} from '../types';
import { mirror, isBlocked, isAncestorRemoved } from '../utils';
import { mirror, isBlocked, isAncestorRemoved, isIgnored } from '../utils';
type DoubleLinkedListNode = {
previous: DoubleLinkedListNode | null;
@@ -145,6 +147,7 @@ export default class MutationBuffer {
private inlineStylesheet: boolean;
private maskInputOptions: MaskInputOptions;
private recordCanvas: boolean;
private slimDOMOptions: SlimDOMOptions;
public init(
cb: mutationCallBack,
@@ -153,12 +156,14 @@ export default class MutationBuffer {
inlineStylesheet: boolean,
maskInputOptions: MaskInputOptions,
recordCanvas: boolean,
slimDOMOptions: SlimDOMOptions,
) {
this.blockClass = blockClass;
this.blockSelector = blockSelector;
this.inlineStylesheet = inlineStylesheet;
this.maskInputOptions = maskInputOptions;
this.recordCanvas = recordCanvas;
this.slimDOMOptions = slimDOMOptions;
this.emissionCallback = cb;
}
@@ -193,8 +198,12 @@ export default class MutationBuffer {
*/
const addList = new DoubleLinkedList();
const getNextId = (n: Node): number | null => {
let nextId =
n.nextSibling && mirror.getId((n.nextSibling as unknown) as INode);
let ns: Node | null = n;
let nextId: number | null = IGNORED_NODE; // slimDOM: ignored
while (nextId === IGNORED_NODE) {
ns = ns && ns.nextSibling;
nextId = ns && mirror.getId((ns as unknown) as INode);
}
if (nextId === -1 && isBlocked(n.nextSibling, this.blockClass)) {
nextId = null;
}
@@ -209,21 +218,24 @@ export default class MutationBuffer {
if (parentId === -1 || nextId === -1) {
return addList.addNode(n);
}
adds.push({
parentId,
nextId,
node: serializeNodeWithId(n, {
doc: document,
map: mirror.map,
blockClass: this.blockClass,
blockSelector: this.blockSelector,
skipChild: true,
inlineStylesheet: this.inlineStylesheet,
maskInputOptions: this.maskInputOptions,
slimDOMOptions: {},
recordCanvas: this.recordCanvas,
})!,
let sn = serializeNodeWithId(n, {
doc: document,
map: mirror.map,
blockClass: this.blockClass,
blockSelector: this.blockSelector,
skipChild: true,
inlineStylesheet: this.inlineStylesheet,
maskInputOptions: this.maskInputOptions,
slimDOMOptions: this.slimDOMOptions,
recordCanvas: this.recordCanvas,
});
if (sn) {
adds.push({
parentId,
nextId,
node: sn,
});
}
};
while (this.mapRemoves.length) {
@@ -332,6 +344,9 @@ export default class MutationBuffer {
};
private processMutation = (m: mutationRecord) => {
if (isIgnored(m.target)) {
return;
}
switch (m.type) {
case 'characterData': {
const value = m.target.textContent;
@@ -373,7 +388,8 @@ export default class MutationBuffer {
const parentId = mirror.getId(m.target as INode);
if (
isBlocked(n, this.blockClass) ||
isBlocked(m.target, this.blockClass)
isBlocked(m.target, this.blockClass) ||
isIgnored(n)
) {
return;
}
@@ -421,6 +437,9 @@ export default class MutationBuffer {
return;
}
if (isINode(n)) {
if (isIgnored(n)) {
return;
}
this.movedSet.add(n);
let targetId: number | null = null;
if (target && isINode(target)) {

View File

@@ -1,4 +1,4 @@
import { INode, MaskInputOptions } from 'rrweb-snapshot';
import { INode, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
import { FontFaceDescriptors, FontFaceSet } from 'css-font-loading-module';
import {
mirror,
@@ -48,6 +48,7 @@ function initMutationObserver(
inlineStylesheet: boolean,
maskInputOptions: MaskInputOptions,
recordCanvas: boolean,
slimDOMOptions: SlimDOMOptions,
): MutationObserver {
// see mutation.ts for details
mutationBuffer.init(
@@ -57,6 +58,7 @@ function initMutationObserver(
inlineStylesheet,
maskInputOptions,
recordCanvas,
slimDOMOptions,
);
const observer = new MutationObserver(
mutationBuffer.processMutations.bind(mutationBuffer),
@@ -584,6 +586,7 @@ export function initObservers(
o.inlineStylesheet,
o.maskInputOptions,
o.recordCanvas,
o.slimDOMOptions,
);
const mousemoveHandler = initMoveObserver(o.mousemoveCb, o.sampling);
const mouseInteractionHandler = initMouseInteractionObserver(

View File

@@ -3,6 +3,7 @@ import {
idNodeMap,
INode,
MaskInputOptions,
SlimDOMOptions,
} from 'rrweb-snapshot';
import { PackFn, UnpackFn } from './packer/base';
import { FontFaceDescriptors } from 'css-font-loading-module';
@@ -176,6 +177,7 @@ export type recordOptions<T> = {
maskAllInputs?: boolean;
maskInputOptions?: MaskInputOptions;
maskInputFn?: MaskInputFn;
slimDOMOptions?: SlimDOMOptions;
inlineStylesheet?: boolean;
hooks?: hooksParam;
packFn?: PackFn;
@@ -206,6 +208,7 @@ export type observerParam = {
sampling: SamplingStrategy;
recordCanvas: boolean;
collectFonts: boolean;
slimDOMOptions: SlimDOMOptions;
};
export type hooksParam = {

View File

@@ -15,7 +15,7 @@ import {
scrollData,
inputData,
} from './types';
import { INode } from 'rrweb-snapshot';
import { INode, IGNORED_NODE } from 'rrweb-snapshot';
export function on(
type: string,
@@ -197,6 +197,15 @@ export function isBlocked(node: Node | null, blockClass: blockClass): boolean {
return isBlocked(node.parentNode, blockClass);
}
export function isIgnored(n: Node | INode): boolean {
if ('__sn' in n) {
return (n as INode).__sn.id === IGNORED_NODE;
}
// The main part of the slimDOM check happens in
// rrweb-snapshot::serializeNodeWithId
return false;
}
export function isAncestorRemoved(target: INode): boolean {
const id = mirror.getId(target);
if (!mirror.has(id)) {