From debf8988a5132ff9400ffda975d7ec83e1d5f34a Mon Sep 17 00:00:00 2001 From: Michael Dellanoce Date: Wed, 20 Sep 2023 13:43:06 -0400 Subject: [PATCH 1/3] rrweb sends message to cross-origin iframe to force snapshot --- .changeset/rotten-squids-run.md | 5 ++++ packages/rrweb/src/record/iframe-manager.ts | 28 +++++++++++++++++++-- packages/rrweb/src/record/index.ts | 1 + packages/rrweb/src/types.ts | 1 + 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 .changeset/rotten-squids-run.md 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; From 4c5513e2e4ead4ca7373a46d41629106fdc9d861 Mon Sep 17 00:00:00 2001 From: Meg Boehlert Date: Tue, 14 May 2024 10:40:59 -0400 Subject: [PATCH 2/3] define takeFullSnapshot before use + setter for takeFullSnapshot --- packages/rrweb/src/record/iframe-manager.ts | 4 ++++ packages/rrweb/src/record/index.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 0a1da385ff..099f8437a4 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -50,6 +50,10 @@ export class IframeManager { } } + public setTakeFullSnapshot (takeFullSnapshot: (isCheckout?: boolean) => void) { + this.takeFullSnapshot = takeFullSnapshot; + } + public addIframe(iframeEl: HTMLIFrameElement) { this.iframes.set(iframeEl, true); if (iframeEl.contentWindow) diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 9062af9954..853159501e 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -41,8 +41,7 @@ import { } from './error-handler'; let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void; - -let takeFullSnapshot!: (isCheckout?: boolean) => void; +let takeFullSnapshot: (isCheckout?: boolean) => void = () => { /* no-op */ }; let canvasManager!: CanvasManager; let recording = false; @@ -412,6 +411,7 @@ function record( mirror.getId(document), ); }; + iframeManager.setTakeFullSnapshot(takeFullSnapshot); try { const handlers: listenerHandler[] = []; From 01d107f17d8db603e23895a2e7dd2fbfeb4f6e15 Mon Sep 17 00:00:00 2001 From: megboehlert Date: Wed, 15 May 2024 18:24:14 +0000 Subject: [PATCH 3/3] Apply formatting changes --- packages/rrweb/src/record/iframe-manager.ts | 2 +- packages/rrweb/src/record/index.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 099f8437a4..8ad46940f9 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -50,7 +50,7 @@ export class IframeManager { } } - public setTakeFullSnapshot (takeFullSnapshot: (isCheckout?: boolean) => void) { + public setTakeFullSnapshot(takeFullSnapshot: (isCheckout?: boolean) => void) { this.takeFullSnapshot = takeFullSnapshot; } diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 853159501e..a95f7893c9 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -41,7 +41,9 @@ import { } from './error-handler'; let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void; -let takeFullSnapshot: (isCheckout?: boolean) => void = () => { /* no-op */ }; +let takeFullSnapshot: (isCheckout?: boolean) => void = () => { + /* no-op */ +}; let canvasManager!: CanvasManager; let recording = false;