fix: wrong rootId value in special iframes (#1100)
1. When some same-origin iframes are nested in cross-origin iframes, their `rootId`s are wrong. 2. The property `rootId` is missing in serialized cross-origin iframes
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot';
|
import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot';
|
||||||
import { genId } from 'rrweb-snapshot';
|
import { genId, NodeType } from 'rrweb-snapshot';
|
||||||
import type { CrossOriginIframeMessageEvent } from '../types';
|
import type { CrossOriginIframeMessageEvent } from '../types';
|
||||||
import CrossOriginIframeMirror from './cross-origin-iframe-mirror';
|
import CrossOriginIframeMirror from './cross-origin-iframe-mirror';
|
||||||
import { EventType, IncrementalSource } from '@rrweb/types';
|
import { EventType, IncrementalSource } from '@rrweb/types';
|
||||||
@@ -14,6 +14,10 @@ export class IframeManager {
|
|||||||
> = new WeakMap();
|
> = new WeakMap();
|
||||||
public crossOriginIframeMirror = new CrossOriginIframeMirror(genId);
|
public crossOriginIframeMirror = new CrossOriginIframeMirror(genId);
|
||||||
public crossOriginIframeStyleMirror: CrossOriginIframeMirror;
|
public crossOriginIframeStyleMirror: CrossOriginIframeMirror;
|
||||||
|
public crossOriginIframeRootIdMap: WeakMap<
|
||||||
|
HTMLIFrameElement,
|
||||||
|
number
|
||||||
|
> = new WeakMap();
|
||||||
private mirror: Mirror;
|
private mirror: Mirror;
|
||||||
private mutationCb: mutationCallBack;
|
private mutationCb: mutationCallBack;
|
||||||
private wrappedEmit: (e: eventWithTime, isCheckout?: boolean) => void;
|
private wrappedEmit: (e: eventWithTime, isCheckout?: boolean) => void;
|
||||||
@@ -121,6 +125,9 @@ export class IframeManager {
|
|||||||
* Replaces the original id of the iframe with a new set of unique ids
|
* Replaces the original id of the iframe with a new set of unique ids
|
||||||
*/
|
*/
|
||||||
this.replaceIdOnNode(e.data.node, iframeEl);
|
this.replaceIdOnNode(e.data.node, iframeEl);
|
||||||
|
const rootId = e.data.node.id;
|
||||||
|
this.crossOriginIframeRootIdMap.set(iframeEl, rootId);
|
||||||
|
this.patchRootIdOnNode(e.data.node, rootId);
|
||||||
return {
|
return {
|
||||||
timestamp: e.timestamp,
|
timestamp: e.timestamp,
|
||||||
type: EventType.IncrementalSnapshot,
|
type: EventType.IncrementalSnapshot,
|
||||||
@@ -171,6 +178,8 @@ export class IframeManager {
|
|||||||
'previousId',
|
'previousId',
|
||||||
]);
|
]);
|
||||||
this.replaceIdOnNode(n.node, iframeEl);
|
this.replaceIdOnNode(n.node, iframeEl);
|
||||||
|
const rootId = this.crossOriginIframeRootIdMap.get(iframeEl);
|
||||||
|
rootId && this.patchRootIdOnNode(n.node, rootId);
|
||||||
});
|
});
|
||||||
e.data.removes.forEach((n) => {
|
e.data.removes.forEach((n) => {
|
||||||
this.replaceIds(n, iframeEl, ['parentId', 'id']);
|
this.replaceIds(n, iframeEl, ['parentId', 'id']);
|
||||||
@@ -273,11 +282,20 @@ export class IframeManager {
|
|||||||
node: serializedNodeWithId,
|
node: serializedNodeWithId,
|
||||||
iframeEl: HTMLIFrameElement,
|
iframeEl: HTMLIFrameElement,
|
||||||
) {
|
) {
|
||||||
this.replaceIds(node, iframeEl, ['id']);
|
this.replaceIds(node, iframeEl, ['id', 'rootId']);
|
||||||
if ('childNodes' in node) {
|
if ('childNodes' in node) {
|
||||||
node.childNodes.forEach((child) => {
|
node.childNodes.forEach((child) => {
|
||||||
this.replaceIdOnNode(child, iframeEl);
|
this.replaceIdOnNode(child, iframeEl);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private patchRootIdOnNode(node: serializedNodeWithId, rootId: number) {
|
||||||
|
if (node.type !== NodeType.Document && !node.rootId) node.rootId = rootId;
|
||||||
|
if ('childNodes' in node) {
|
||||||
|
node.childNodes.forEach((child) => {
|
||||||
|
this.patchRootIdOnNode(child, rootId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import type { Page } from 'puppeteer';
|
import type { Page } from 'puppeteer';
|
||||||
import type { eventWithTime, recordOptions } from '../../src/types';
|
import type { eventWithTime } from '@rrweb/types';
|
||||||
|
import type { recordOptions } from '../../src/types';
|
||||||
import { startServer, launchPuppeteer, ISuite, getServerURL } from '../utils';
|
import { startServer, launchPuppeteer, ISuite, getServerURL } from '../utils';
|
||||||
|
|
||||||
const suites: Array<
|
const suites: Array<
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import type { eventWithTime, recordOptions } from '../../src/types';
|
import type { eventWithTime } from '@rrweb/types';
|
||||||
|
import type { recordOptions } from '../../src/types';
|
||||||
import { launchPuppeteer, ISuite } from '../utils';
|
import { launchPuppeteer, ISuite } from '../utils';
|
||||||
|
|
||||||
const suites: Array<{
|
const suites: Array<{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -517,6 +517,24 @@ describe('cross origin iframes', function (this: ISuite) {
|
|||||||
serverBURL: string;
|
serverBURL: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
it('should record same-origin iframe in cross-origin iframe', async () => {
|
||||||
|
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||||
|
await frame.evaluate(() => {
|
||||||
|
const iframe2 = document.createElement('iframe');
|
||||||
|
// Append a same-origin iframe in a cross-origin iframe.
|
||||||
|
document.body.appendChild(iframe2);
|
||||||
|
iframe2.contentDocument!.body.appendChild(
|
||||||
|
document.createTextNode('Same-origin iframe in cross-origin iframe'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForRAF(ctx.page);
|
||||||
|
const snapshots = (await ctx.page.evaluate(
|
||||||
|
'window.snapshots',
|
||||||
|
)) as eventWithTime[];
|
||||||
|
assertSnapshot(snapshots);
|
||||||
|
});
|
||||||
|
|
||||||
it('should filter out forwarded cross origin rrweb messages', async () => {
|
it('should filter out forwarded cross origin rrweb messages', async () => {
|
||||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||||
const iframe2URL = `${ctx.serverBURL}/html/blank.html`;
|
const iframe2URL = `${ctx.serverBURL}/html/blank.html`;
|
||||||
@@ -533,10 +551,11 @@ describe('cross origin iframes', function (this: ISuite) {
|
|||||||
|
|
||||||
// Wait for iframe2 to load
|
// Wait for iframe2 to load
|
||||||
await ctx.page.waitForFrame(iframe2URL);
|
await ctx.page.waitForFrame(iframe2URL);
|
||||||
|
const iframe2 = frame.childFrames()[0];
|
||||||
// Record iframe2
|
// Record iframe2
|
||||||
await injectRecordScript(frame.childFrames()[0]);
|
await injectRecordScript(iframe2);
|
||||||
|
|
||||||
await waitForRAF(frame.childFrames()[0]);
|
await waitForRAF(iframe2);
|
||||||
const snapshots = (await ctx.page.evaluate(
|
const snapshots = (await ctx.page.evaluate(
|
||||||
'window.snapshots',
|
'window.snapshots',
|
||||||
)) as eventWithTime[];
|
)) as eventWithTime[];
|
||||||
|
|||||||
Reference in New Issue
Block a user