diff --git a/src/rebuild.ts b/src/rebuild.ts index be8cd8f0..ecf94cc5 100644 --- a/src/rebuild.ts +++ b/src/rebuild.ts @@ -154,9 +154,7 @@ function buildNode( node.appendChild(child); continue; } - if (tagName === 'iframe' && name === 'src') { - continue; - } + try { if (n.isSVG && name === 'xlink:href') { node.setAttributeNS('http://www.w3.org/1999/xlink', name, value); diff --git a/src/snapshot.ts b/src/snapshot.ts index 6d2fe8ff..2a80d755 100644 --- a/src/snapshot.ts +++ b/src/snapshot.ts @@ -8,6 +8,7 @@ import { MaskInputOptions, SlimDOMOptions, MaskTextFn, + KeepIframeSrcFn, } from './types'; import { isElement, isShadowRoot } from './utils'; @@ -348,6 +349,7 @@ function serializeNode( maskInputOptions: MaskInputOptions; maskTextFn: MaskTextFn | undefined; recordCanvas: boolean; + keepIframeSrcFn: KeepIframeSrcFn; }, ): serializedNode | false { const { @@ -360,6 +362,7 @@ function serializeNode( maskInputOptions = {}, maskTextFn, recordCanvas, + keepIframeSrcFn } = options; // Only record root id when document object is not the base document let rootId: number | undefined; @@ -483,7 +486,7 @@ function serializeNode( }; } // iframe - if (tagName === 'iframe') { + if (tagName === 'iframe' && !keepIframeSrcFn(attributes.src as string)) { delete attributes.src; } return { @@ -648,6 +651,7 @@ export function serializeNodeWithId( maskInputOptions?: MaskInputOptions; maskTextFn: MaskTextFn | undefined; slimDOMOptions: SlimDOMOptions; + keepIframeSrcFn?: KeepIframeSrcFn; recordCanvas?: boolean; preserveWhiteSpace?: boolean; onSerialize?: (n: INode) => unknown; @@ -671,6 +675,7 @@ export function serializeNodeWithId( onSerialize, onIframeLoad, iframeLoadTimeout = 5000, + keepIframeSrcFn = () => false, } = options; let { preserveWhiteSpace = true } = options; const _serializedNode = serializeNode(n, { @@ -683,6 +688,7 @@ export function serializeNodeWithId( maskInputOptions, maskTextFn, recordCanvas, + keepIframeSrcFn, }); if (!_serializedNode) { // TODO: dev only @@ -750,6 +756,7 @@ export function serializeNodeWithId( onSerialize, onIframeLoad, iframeLoadTimeout, + keepIframeSrcFn, }; for (const childN of Array.from(n.childNodes)) { const serializedChildNode = serializeNodeWithId(childN, bypassOptions); @@ -800,6 +807,7 @@ export function serializeNodeWithId( onSerialize, onIframeLoad, iframeLoadTimeout, + keepIframeSrcFn, }); if (serializedIframeNode) { @@ -830,6 +838,7 @@ function snapshot( onSerialize?: (n: INode) => unknown; onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; iframeLoadTimeout?: number; + keepIframeSrcFn?: KeepIframeSrcFn; }, ): [serializedNodeWithId | null, idNodeMap] { const { @@ -846,6 +855,7 @@ function snapshot( onSerialize, onIframeLoad, iframeLoadTimeout, + keepIframeSrcFn = () => false, } = options || {}; const idNodeMap: idNodeMap = {}; const maskInputOptions: MaskInputOptions = @@ -909,6 +919,7 @@ function snapshot( onSerialize, onIframeLoad, iframeLoadTimeout, + keepIframeSrcFn, }), idNodeMap, ]; diff --git a/src/types.ts b/src/types.ts index 0a3d2ce2..9b2a6286 100644 --- a/src/types.ts +++ b/src/types.ts @@ -108,3 +108,6 @@ export type SlimDOMOptions = Partial<{ }>; export type MaskTextFn = (text: string) => string; + +export type KeepIframeSrcFn = (src: string) => boolean; + diff --git a/typings/snapshot.d.ts b/typings/snapshot.d.ts index 0581feda..6db21b71 100644 --- a/typings/snapshot.d.ts +++ b/typings/snapshot.d.ts @@ -1,4 +1,4 @@ -import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, MaskTextFn } from './types'; +import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, MaskTextFn, KeepIframeSrcFn } from './types'; export declare const IGNORED_NODE = -2; export declare function absoluteToStylesheet(cssText: string | null, href: string): string; export declare function absoluteToDoc(doc: Document, attributeValue: string): string; @@ -17,6 +17,7 @@ export declare function serializeNodeWithId(n: Node | INode, options: { maskInputOptions?: MaskInputOptions; maskTextFn: MaskTextFn | undefined; slimDOMOptions: SlimDOMOptions; + keepIframeSrcFn?: KeepIframeSrcFn; recordCanvas?: boolean; preserveWhiteSpace?: boolean; onSerialize?: (n: INode) => unknown; @@ -37,6 +38,7 @@ declare function snapshot(n: Document, options?: { onSerialize?: (n: INode) => unknown; onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; iframeLoadTimeout?: number; + keepIframeSrcFn?: KeepIframeSrcFn; }): [serializedNodeWithId | null, idNodeMap]; export declare function visitSnapshot(node: serializedNodeWithId, onVisit: (node: serializedNodeWithId) => unknown): void; export declare function cleanupSnapshot(): void; diff --git a/typings/types.d.ts b/typings/types.d.ts index c43cccbe..93267fa2 100644 --- a/typings/types.d.ts +++ b/typings/types.d.ts @@ -88,3 +88,4 @@ export declare type SlimDOMOptions = Partial<{ headMetaVerification: boolean; }>; export declare type MaskTextFn = (text: string) => string; +export declare type KeepIframeSrcFn = (src: string) => boolean;