diff --git a/.changeset/rotten-squids-run.md b/.changeset/rotten-squids-run.md new file mode 100644 index 0000000000..98b4cfa0ae --- /dev/null +++ b/.changeset/rotten-squids-run.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +rrweb sends message to cross-origin iframe to force snapshot diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 2d9205c0cb..0a1da385ff 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -21,6 +21,7 @@ export class IframeManager { private mirror: Mirror; private mutationCb: mutationCallBack; private wrappedEmit: (e: eventWithoutTime, isCheckout?: boolean) => void; + private takeFullSnapshot: (isCheckout?: boolean) => void; private loadListener?: (iframeEl: HTMLIFrameElement) => unknown; private stylesheetManager: StylesheetManager; private recordCrossOriginIframes: boolean; @@ -31,9 +32,11 @@ export class IframeManager { stylesheetManager: StylesheetManager; recordCrossOriginIframes: boolean; wrappedEmit: (e: eventWithoutTime, isCheckout?: boolean) => void; + takeFullSnapshot: (isCheckout?: boolean) => void; }) { this.mutationCb = options.mutationCb; this.wrappedEmit = options.wrappedEmit; + this.takeFullSnapshot = options.takeFullSnapshot; this.stylesheetManager = options.stylesheetManager; this.recordCrossOriginIframes = options.recordCrossOriginIframes; this.crossOriginIframeStyleMirror = new CrossOriginIframeMirror( @@ -51,6 +54,16 @@ export class IframeManager { this.iframes.set(iframeEl, true); if (iframeEl.contentWindow) this.crossOriginIframeMap.set(iframeEl.contentWindow, iframeEl); + + if (!iframeEl.contentDocument && iframeEl.contentWindow) + iframeEl.contentWindow.postMessage( + { + type: 'rrweb', + origin: window.location.origin, + snapshot: true, + }, + '*', + ); } public addLoadListener(cb: (iframeEl: HTMLIFrameElement) => unknown) { @@ -95,10 +108,21 @@ export class IframeManager { ) return; - const iframeSourceWindow = message.source; + const iframeSourceWindow = crossOriginMessageEvent.source; if (!iframeSourceWindow) return; - const iframeEl = this.crossOriginIframeMap.get(message.source); + if ( + iframeSourceWindow == window.parent && + window != window.parent && + crossOriginMessageEvent.data.snapshot + ) { + this.takeFullSnapshot(); + return; + } + + const iframeEl = this.crossOriginIframeMap.get( + crossOriginMessageEvent.source, + ); if (!iframeEl) return; const transformedEvent = this.transformCrossOriginEvent( diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 7be978199d..9062af9954 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -278,6 +278,7 @@ function record( stylesheetManager: stylesheetManager, recordCrossOriginIframes, wrappedEmit, + takeFullSnapshot, }); /** diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 10ffb62b43..d4da925c23 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -216,6 +216,7 @@ export type CrossOriginIframeMessageEventContent = { // The origin of the iframe which originally emits this message. It is used to check the integrity of message and to filter out the rrweb messages which are forwarded by some sites. origin: string; isCheckout?: boolean; + snapshot?: boolean; }; export type CrossOriginIframeMessageEvent = MessageEvent;