isBlocked factors in the selector (#894)

* isBlocked factors in the selector

* Ensure contains parameter is a node

* Fix blockSelector blocking for closest nodes

* Fix integration test

* adding ignoreCSSAttributes to ignore the addition of certain css attributes

* tested ignoreCSSAttributes

* Update test snapshot

* swapped the wrapping of htmlelement to be element

* Fix linter errors

* Address MR feedback

* Rebase

Co-authored-by: Filip <filipslatinac@gmail.com>
This commit is contained in:
dbseel
2026-04-01 12:00:00 +08:00
committed by GitHub
parent 9529acb47b
commit c94b5783c4
14 changed files with 74 additions and 28 deletions

View File

@@ -143,6 +143,7 @@ The parameter of `rrweb.record` accepts the following options.
| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter | | blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter |
| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter | | blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter |
| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter | | ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter |
| ignoreCSSAttributes | null | array of CSS attributes that should be ignored |
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter | | maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter |
| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | | maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
| maskAllInputs | false | mask all input content as \* | | maskAllInputs | false | mask all input content as \* |

View File

@@ -139,6 +139,7 @@ setInterval(save, 10 * 1000);
| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | | blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 |
| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 | | blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 |
| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | | ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 |
| ignoreCSSAttributes | null | 应该被忽略的 CSS 属性数组 |
| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 | | maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 |
| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 | | maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 |
| maskAllInputs | false | 将所有输入内容记录为 \* | | maskAllInputs | false | 将所有输入内容记录为 \* |

View File

@@ -70,7 +70,9 @@ function record<T = eventWithTime>(
inlineImages = false, inlineImages = false,
plugins, plugins,
keepIframeSrcFn = () => false, keepIframeSrcFn = () => false,
ignoreCSSAttributes = new Set([]),
} = options; } = options;
// runtime checks for user options // runtime checks for user options
if (!emit) { if (!emit) {
throw new Error('emit function is required'); throw new Error('emit function is required');
@@ -226,6 +228,7 @@ function record<T = eventWithTime>(
mutationCb: wrappedCanvasMutationEmit, mutationCb: wrappedCanvasMutationEmit,
win: window, win: window,
blockClass, blockClass,
blockSelector,
mirror, mirror,
sampling: sampling.canvas, sampling: sampling.canvas,
}); });
@@ -464,6 +467,7 @@ function record<T = eventWithTime>(
stylesheetManager, stylesheetManager,
shadowDomManager, shadowDomManager,
canvasManager, canvasManager,
ignoreCSSAttributes,
plugins: plugins:
plugins plugins
?.filter((p) => p.observer) ?.filter((p) => p.observer)

View File

@@ -273,7 +273,7 @@ export default class MutationBuffer {
rootShadowHost = rootShadowHost =
(rootShadowHost?.getRootNode?.() as ShadowRoot | undefined)?.host || (rootShadowHost?.getRootNode?.() as ShadowRoot | undefined)?.host ||
null; null;
// ensure shadowHost is a Node, or doc.contains will throw an error // ensure contains is passed a Node, or it will throw an error
const notInDoc = const notInDoc =
!this.doc.contains(n) && !this.doc.contains(n) &&
(!rootShadowHost || !this.doc.contains(rootShadowHost)); (!rootShadowHost || !this.doc.contains(rootShadowHost));
@@ -446,7 +446,7 @@ export default class MutationBuffer {
case 'characterData': { case 'characterData': {
const value = m.target.textContent; const value = m.target.textContent;
if ( if (
!isBlocked(m.target, this.blockClass, false) && !isBlocked(m.target, this.blockClass, this.blockSelector, false) &&
value !== m.oldValue value !== m.oldValue
) { ) {
this.texts.push({ this.texts.push({
@@ -478,7 +478,7 @@ export default class MutationBuffer {
}); });
} }
if ( if (
isBlocked(m.target, this.blockClass, false) || isBlocked(m.target, this.blockClass, this.blockSelector, false) ||
value === m.oldValue value === m.oldValue
) { ) {
return; return;
@@ -554,7 +554,7 @@ export default class MutationBuffer {
/** /**
* Parent is blocked, ignore all child mutations * Parent is blocked, ignore all child mutations
*/ */
if (isBlocked(m.target, this.blockClass, true)) return; if (isBlocked(m.target, this.blockClass, this.blockSelector, true)) return;
m.addedNodes.forEach((n) => this.genAdds(n, m.target)); m.addedNodes.forEach((n) => this.genAdds(n, m.target));
m.removedNodes.forEach((n) => { m.removedNodes.forEach((n) => {
@@ -563,7 +563,7 @@ export default class MutationBuffer {
? this.mirror.getId(m.target.host) ? this.mirror.getId(m.target.host)
: this.mirror.getId(m.target); : this.mirror.getId(m.target);
if ( if (
isBlocked(m.target, this.blockClass, false) || isBlocked(m.target, this.blockClass, this.blockSelector, false) ||
isIgnored(n, this.mirror) || isIgnored(n, this.mirror) ||
!isSerialized(n, this.mirror) !isSerialized(n, this.mirror)
) { ) {
@@ -635,7 +635,7 @@ export default class MutationBuffer {
// if this node is blocked `serializeNode` will turn it into a placeholder element // if this node is blocked `serializeNode` will turn it into a placeholder element
// but we have to remove it's children otherwise they will be added as placeholders too // but we have to remove it's children otherwise they will be added as placeholders too
if (!isBlocked(n, this.blockClass, false)) if (!isBlocked(n, this.blockClass, this.blockSelector, false))
n.childNodes.forEach((childN) => this.genAdds(childN)); n.childNodes.forEach((childN) => this.genAdds(childN));
}; };
} }

View File

@@ -210,6 +210,7 @@ function initMouseInteractionObserver({
doc, doc,
mirror, mirror,
blockClass, blockClass,
blockSelector,
sampling, sampling,
}: observerParam): listenerHandler { }: observerParam): listenerHandler {
if (sampling.mouseInteraction === false) { if (sampling.mouseInteraction === false) {
@@ -227,7 +228,7 @@ function initMouseInteractionObserver({
const getHandler = (eventKey: keyof typeof MouseInteractions) => { const getHandler = (eventKey: keyof typeof MouseInteractions) => {
return (event: MouseEvent | TouchEvent) => { return (event: MouseEvent | TouchEvent) => {
const target = getEventTarget(event) as Node; const target = getEventTarget(event) as Node;
if (isBlocked(target, blockClass, true)) { if (isBlocked(target, blockClass, blockSelector, true)) {
return; return;
} }
const e = isTouchEvent(event) ? event.changedTouches[0] : event; const e = isTouchEvent(event) ? event.changedTouches[0] : event;
@@ -266,14 +267,15 @@ export function initScrollObserver({
doc, doc,
mirror, mirror,
blockClass, blockClass,
blockSelector,
sampling, sampling,
}: Pick< }: Pick<
observerParam, observerParam,
'scrollCb' | 'doc' | 'mirror' | 'blockClass' | 'sampling' 'scrollCb' | 'doc' | 'mirror' | 'blockClass' | 'blockSelector' | 'sampling'
>): listenerHandler { >): listenerHandler {
const updatePosition = throttle<UIEvent>((evt) => { const updatePosition = throttle<UIEvent>((evt) => {
const target = getEventTarget(evt); const target = getEventTarget(evt);
if (!target || isBlocked(target as Node, blockClass, true)) { if (!target || isBlocked(target as Node, blockClass, blockSelector, true)) {
return; return;
} }
const id = mirror.getId(target as Node); const id = mirror.getId(target as Node);
@@ -331,6 +333,7 @@ function initInputObserver({
doc, doc,
mirror, mirror,
blockClass, blockClass,
blockSelector,
ignoreClass, ignoreClass,
maskInputOptions, maskInputOptions,
maskInputFn, maskInputFn,
@@ -350,7 +353,7 @@ function initInputObserver({
!target || !target ||
!(target as Element).tagName || !(target as Element).tagName ||
INPUT_TAGS.indexOf((target as Element).tagName) < 0 || INPUT_TAGS.indexOf((target as Element).tagName) < 0 ||
isBlocked(target as Node, blockClass, true) isBlocked(target as Node, blockClass, blockSelector, true)
) { ) {
return; return;
} }
@@ -613,7 +616,7 @@ function initStyleSheetObserver(
} }
function initStyleDeclarationObserver( function initStyleDeclarationObserver(
{ styleDeclarationCb, mirror }: observerParam, { styleDeclarationCb, mirror, ignoreCSSAttributes }: observerParam,
{ win }: { win: IWindow }, { win }: { win: IWindow },
): listenerHandler { ): listenerHandler {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
@@ -624,6 +627,10 @@ function initStyleDeclarationObserver(
value: string, value: string,
priority: string, priority: string,
) { ) {
// ignore this mutation if we do not care about this css attribute
if (ignoreCSSAttributes.has(property)) {
return setProperty.apply(this, [property, value, priority]);
}
const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode); const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode);
if (id !== -1) { if (id !== -1) {
styleDeclarationCb({ styleDeclarationCb({
@@ -645,6 +652,10 @@ function initStyleDeclarationObserver(
this: CSSStyleDeclaration, this: CSSStyleDeclaration,
property: string, property: string,
) { ) {
// ignore this mutation if we do not care about this css attribute
if (ignoreCSSAttributes.has(property)) {
return removeProperty.apply(this, [property]);
}
const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode); const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode);
if (id !== -1) { if (id !== -1) {
styleDeclarationCb({ styleDeclarationCb({
@@ -667,13 +678,14 @@ function initStyleDeclarationObserver(
function initMediaInteractionObserver({ function initMediaInteractionObserver({
mediaInteractionCb, mediaInteractionCb,
blockClass, blockClass,
blockSelector,
mirror, mirror,
sampling, sampling,
}: observerParam): listenerHandler { }: observerParam): listenerHandler {
const handler = (type: MediaInteractions) => const handler = (type: MediaInteractions) =>
throttle((event: Event) => { throttle((event: Event) => {
const target = getEventTarget(event); const target = getEventTarget(event);
if (!target || isBlocked(target as Node, blockClass, true)) { if (!target || isBlocked(target as Node, blockClass, blockSelector, true)) {
return; return;
} }
const { currentTime, volume, muted } = target as HTMLMediaElement; const { currentTime, volume, muted } = target as HTMLMediaElement;
@@ -755,7 +767,7 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler {
} }
function initSelectionObserver(param: observerParam): listenerHandler { function initSelectionObserver(param: observerParam): listenerHandler {
const { doc, mirror, blockClass, selectionCb } = param; const { doc, mirror, blockClass, blockSelector, selectionCb } = param;
let collapsed = true; let collapsed = true;
const updateSelection = () => { const updateSelection = () => {
@@ -774,8 +786,8 @@ function initSelectionObserver(param: observerParam): listenerHandler {
const { startContainer, startOffset, endContainer, endOffset } = range; const { startContainer, startOffset, endContainer, endOffset } = range;
const blocked = const blocked =
isBlocked(startContainer, blockClass, true) || isBlocked(startContainer, blockClass, blockSelector, true) ||
isBlocked(endContainer, blockClass, true); isBlocked(endContainer, blockClass, blockSelector, true);
if (blocked) continue; if (blocked) continue;

View File

@@ -13,6 +13,7 @@ export default function initCanvas2DMutationObserver(
cb: canvasManagerMutationCallback, cb: canvasManagerMutationCallback,
win: IWindow, win: IWindow,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
mirror: Mirror, mirror: Mirror,
): listenerHandler { ): listenerHandler {
const handlers: listenerHandler[] = []; const handlers: listenerHandler[] = [];
@@ -41,7 +42,7 @@ export default function initCanvas2DMutationObserver(
this: CanvasRenderingContext2D, this: CanvasRenderingContext2D,
...args: Array<unknown> ...args: Array<unknown>
) { ) {
if (!isBlocked(this.canvas, blockClass, true)) { if (!isBlocked(this.canvas, blockClass, blockSelector, true)) {
// Using setTimeout as toDataURL can be heavy // Using setTimeout as toDataURL can be heavy
// and we'd rather not block the main thread // and we'd rather not block the main thread
setTimeout(() => { setTimeout(() => {

View File

@@ -60,17 +60,18 @@ export class CanvasManager {
mutationCb: canvasMutationCallback; mutationCb: canvasMutationCallback;
win: IWindow; win: IWindow;
blockClass: blockClass; blockClass: blockClass;
blockSelector: string | null,
mirror: Mirror; mirror: Mirror;
sampling?: 'all' | number; sampling?: 'all' | number;
}) { }) {
const { sampling = 'all', win, blockClass, recordCanvas } = options; const { sampling = 'all', win, blockClass, blockSelector, recordCanvas } = options;
this.mutationCb = options.mutationCb; this.mutationCb = options.mutationCb;
this.mirror = options.mirror; this.mirror = options.mirror;
if (recordCanvas && sampling === 'all') if (recordCanvas && sampling === 'all')
this.initCanvasMutationObserver(win, blockClass); this.initCanvasMutationObserver(win, blockClass, blockSelector);
if (recordCanvas && typeof sampling === 'number') if (recordCanvas && typeof sampling === 'number')
this.initCanvasFPSObserver(sampling, win, blockClass); this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector);
} }
private processMutation: canvasManagerMutationCallback = ( private processMutation: canvasManagerMutationCallback = (
@@ -94,8 +95,9 @@ export class CanvasManager {
fps: number, fps: number,
win: IWindow, win: IWindow,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
) { ) {
const canvasContextReset = initCanvasContextObserver(win, blockClass); const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector);
const snapshotInProgressMap: Map<number, boolean> = new Map(); const snapshotInProgressMap: Map<number, boolean> = new Map();
const worker = new ImageBitmapDataURLWorker() as ImageBitmapDataURLRequestWorker; const worker = new ImageBitmapDataURLWorker() as ImageBitmapDataURLRequestWorker;
worker.onmessage = (e) => { worker.onmessage = (e) => {
@@ -141,7 +143,7 @@ export class CanvasManager {
const getCanvas = (): HTMLCanvasElement[] => { const getCanvas = (): HTMLCanvasElement[] => {
const matchedCanvas: HTMLCanvasElement[] = []; const matchedCanvas: HTMLCanvasElement[] = [];
win.document.querySelectorAll('canvas').forEach(canvas => { win.document.querySelectorAll('canvas').forEach(canvas => {
if (!isBlocked(canvas, blockClass, true)) { if (!isBlocked(canvas, blockClass, blockSelector, true)) {
matchedCanvas.push(canvas); matchedCanvas.push(canvas);
} }
}) })
@@ -208,15 +210,17 @@ export class CanvasManager {
private initCanvasMutationObserver( private initCanvasMutationObserver(
win: IWindow, win: IWindow,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
): void { ): void {
this.startRAFTimestamping(); this.startRAFTimestamping();
this.startPendingCanvasMutationFlusher(); this.startPendingCanvasMutationFlusher();
const canvasContextReset = initCanvasContextObserver(win, blockClass); const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector);
const canvas2DReset = initCanvas2DMutationObserver( const canvas2DReset = initCanvas2DMutationObserver(
this.processMutation.bind(this), this.processMutation.bind(this),
win, win,
blockClass, blockClass,
blockSelector,
this.mirror, this.mirror,
); );
@@ -224,6 +228,7 @@ export class CanvasManager {
this.processMutation.bind(this), this.processMutation.bind(this),
win, win,
blockClass, blockClass,
blockSelector,
this.mirror, this.mirror,
); );

View File

@@ -5,6 +5,7 @@ import { isBlocked, patch } from '../../../utils';
export default function initCanvasContextObserver( export default function initCanvasContextObserver(
win: IWindow, win: IWindow,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
): listenerHandler { ): listenerHandler {
const handlers: listenerHandler[] = []; const handlers: listenerHandler[] = [];
try { try {
@@ -23,7 +24,7 @@ export default function initCanvasContextObserver(
contextType: string, contextType: string,
...args: Array<unknown> ...args: Array<unknown>
) { ) {
if (!isBlocked(this, blockClass, true)) { if (!isBlocked(this, blockClass, blockSelector, true)) {
if (!('__context' in this)) this.__context = contextType; if (!('__context' in this)) this.__context = contextType;
} }
return original.apply(this, [contextType, ...args]); return original.apply(this, [contextType, ...args]);

View File

@@ -15,6 +15,7 @@ function patchGLPrototype(
type: CanvasContext, type: CanvasContext,
cb: canvasManagerMutationCallback, cb: canvasManagerMutationCallback,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
mirror: Mirror, mirror: Mirror,
win: IWindow, win: IWindow,
): listenerHandler[] { ): listenerHandler[] {
@@ -36,7 +37,7 @@ function patchGLPrototype(
return function (this: typeof prototype, ...args: Array<unknown>) { return function (this: typeof prototype, ...args: Array<unknown>) {
const result = original.apply(this, args); const result = original.apply(this, args);
saveWebGLVar(result, win, prototype); saveWebGLVar(result, win, prototype);
if (!isBlocked(this.canvas, blockClass, true)) { if (!isBlocked(this.canvas, blockClass, blockSelector, true)) {
const recordArgs = serializeArgs([...args], win, prototype); const recordArgs = serializeArgs([...args], win, prototype);
const mutation: canvasMutationWithType = { const mutation: canvasMutationWithType = {
type, type,
@@ -76,6 +77,7 @@ export default function initCanvasWebGLMutationObserver(
cb: canvasManagerMutationCallback, cb: canvasManagerMutationCallback,
win: IWindow, win: IWindow,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
mirror: Mirror, mirror: Mirror,
): listenerHandler { ): listenerHandler {
const handlers: listenerHandler[] = []; const handlers: listenerHandler[] = [];
@@ -86,6 +88,7 @@ export default function initCanvasWebGLMutationObserver(
CanvasContext.WebGL, CanvasContext.WebGL,
cb, cb,
blockClass, blockClass,
blockSelector,
mirror, mirror,
win, win,
), ),
@@ -98,6 +101,7 @@ export default function initCanvasWebGLMutationObserver(
CanvasContext.WebGL2, CanvasContext.WebGL2,
cb, cb,
blockClass, blockClass,
blockSelector,
mirror, mirror,
win, win,
), ),

View File

@@ -39,7 +39,7 @@ export class ShadowDomManager {
const manager = this; const manager = this;
this.restorePatches.push( this.restorePatches.push(
patch( patch(
HTMLElement.prototype, Element.prototype,
'attachShadow', 'attachShadow',
function (original: (init: ShadowRootInit) => ShadowRoot) { function (original: (init: ShadowRootInit) => ShadowRoot) {
return function (this: HTMLElement, option: ShadowRootInit) { return function (this: HTMLElement, option: ShadowRootInit) {

View File

@@ -245,6 +245,7 @@ export type recordOptions<T> = {
maskInputFn?: MaskInputFn; maskInputFn?: MaskInputFn;
maskTextFn?: MaskTextFn; maskTextFn?: MaskTextFn;
slimDOMOptions?: SlimDOMOptions | 'all' | true; slimDOMOptions?: SlimDOMOptions | 'all' | true;
ignoreCSSAttributes?:Set<string>;
inlineStylesheet?: boolean; inlineStylesheet?: boolean;
hooks?: hooksParam; hooks?: hooksParam;
packFn?: PackFn; packFn?: PackFn;
@@ -294,6 +295,7 @@ export type observerParam = {
stylesheetManager: StylesheetManager; stylesheetManager: StylesheetManager;
shadowDomManager: ShadowDomManager; shadowDomManager: ShadowDomManager;
canvasManager: CanvasManager; canvasManager: CanvasManager;
ignoreCSSAttributes:Set<string>;
plugins: Array<{ plugins: Array<{
observer: ( observer: (
cb: (...arg: Array<unknown>) => void, cb: (...arg: Array<unknown>) => void,

View File

@@ -187,12 +187,14 @@ export function getWindowWidth(): number {
* Checks if the given element set to be blocked by rrweb * Checks if the given element set to be blocked by rrweb
* @param node - node to check * @param node - node to check
* @param blockClass - class name to check * @param blockClass - class name to check
* @param ignoreParents - whether to search through parent nodes for the block class * @param blockSelector - css selectors to check
* @param checkAncestors - whether to search through parent nodes for the block class
* @returns true/false if the node was blocked or not * @returns true/false if the node was blocked or not
*/ */
export function isBlocked( export function isBlocked(
node: Node | null, node: Node | null,
blockClass: blockClass, blockClass: blockClass,
blockSelector: string | null,
checkAncestors: boolean, checkAncestors: boolean,
): boolean { ): boolean {
if (!node) { if (!node) {
@@ -210,6 +212,10 @@ export function isBlocked(
} else { } else {
if (classMatchesRegex(el, blockClass, checkAncestors)) return true; if (classMatchesRegex(el, blockClass, checkAncestors)) return true;
} }
if (blockSelector) {
if ((node as HTMLElement).matches(blockSelector)) return true;
if (checkAncestors && el.closest(blockSelector) !== null) return true;
}
return false; return false;
} }

View File

@@ -581,7 +581,7 @@ exports[`record captures style property changes 1`] = `
\\"source\\": 13, \\"source\\": 13,
\\"id\\": 9, \\"id\\": 9,
\\"set\\": { \\"set\\": {
\\"property\\": \\"color\\", \\"property\\": \\"border-color\\",
\\"value\\": \\"green\\" \\"value\\": \\"green\\"
}, },
\\"index\\": [ \\"index\\": [

View File

@@ -385,6 +385,7 @@ describe('record', function (this: ISuite) {
record({ record({
emit: ((window as unknown) as IWindow).emit, emit: ((window as unknown) as IWindow).emit,
ignoreCSSAttributes: new Set(['color']),
}); });
const styleElement = document.createElement('style'); const styleElement = document.createElement('style');
@@ -393,10 +394,18 @@ describe('record', function (this: ISuite) {
const styleSheet = <CSSStyleSheet>styleElement.sheet; const styleSheet = <CSSStyleSheet>styleElement.sheet;
styleSheet.insertRule('body { background: #000; }'); styleSheet.insertRule('body { background: #000; }');
setTimeout(() => { setTimeout(() => {
// should be ignored
(styleSheet.cssRules[0] as CSSStyleRule).style.setProperty( (styleSheet.cssRules[0] as CSSStyleRule).style.setProperty(
'color', 'color',
'green', 'green',
); );
// should be captured because we did not block it
(styleSheet.cssRules[0] as CSSStyleRule).style.setProperty(
'border-color',
'green',
);
(styleSheet.cssRules[0] as CSSStyleRule).style.removeProperty( (styleSheet.cssRules[0] as CSSStyleRule).style.removeProperty(
'background', 'background',
); );