fix: some nested cross-origin iframes can't be recorded (#1353)

* fix: some nested cross-origin iframes can't be recorded

* fix building error in rrweb-player

* add test case for this special case

* Apply formatting changes

---------

Co-authored-by: YunFeng0817 <YunFeng0817@users.noreply.github.com>
This commit is contained in:
Yun Feng
2024-06-07 02:28:22 -07:00
committed by GitHub
parent 7261c43f60
commit 5c27b76319
4 changed files with 307 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
"rrweb": patch
---
Fix: some nested cross-origin iframes can't be recorded

View File

@@ -74,6 +74,14 @@ export class IframeManager {
attributes: [],
isAttachIframe: true,
});
// Receive messages (events) coming from cross-origin iframes that are nested in this same-origin iframe.
if (this.recordCrossOriginIframes)
iframeEl.contentWindow?.addEventListener(
'message',
this.handleMessage.bind(this),
);
this.loadListener?.(iframeEl);
if (

View File

@@ -5369,3 +5369,273 @@ exports[`same origin iframes > should emit contents of iframe once 1`] = `
}
]"
`;
exports[`same origin iframes should record cross-origin iframe in same-origin iframe 1`] = `
"[
{
\\"type\\": 4,
\\"data\\": {
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
}
},
{
\\"type\\": 2,
\\"data\\": {
\\"node\\": {
\\"type\\": 0,
\\"childNodes\\": [
{
\\"type\\": 1,
\\"name\\": \\"html\\",
\\"publicId\\": \\"\\",
\\"systemId\\": \\"\\",
\\"id\\": 2
},
{
\\"type\\": 2,
\\"tagName\\": \\"html\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"head\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"script\\",
\\"attributes\\": {
\\"type\\": \\"text/javascript\\"
},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
\\"id\\": 6
}
],
\\"id\\": 5
}
],
\\"id\\": 4
},
{
\\"type\\": 2,
\\"tagName\\": \\"body\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 8
},
{
\\"type\\": 2,
\\"tagName\\": \\"iframe\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"id\\": 9
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
\\"id\\": 10
}
],
\\"id\\": 7
}
],
\\"id\\": 3
}
],
\\"id\\": 1
},
\\"initialOffset\\": {
\\"left\\": 0,
\\"top\\": 0
}
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"adds\\": [
{
\\"parentId\\": 9,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 0,
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"html\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"head\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"rootId\\": 11,
\\"id\\": 13
},
{
\\"type\\": 2,
\\"tagName\\": \\"body\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"rootId\\": 11,
\\"id\\": 14
}
],
\\"rootId\\": 11,
\\"id\\": 12
}
],
\\"compatMode\\": \\"BackCompat\\",
\\"id\\": 11
}
}
],
\\"removes\\": [],
\\"texts\\": [],
\\"attributes\\": [],
\\"isAttachIframe\\": true
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [],
\\"removes\\": [],
\\"adds\\": [
{
\\"parentId\\": 13,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 2,
\\"tagName\\": \\"script\\",
\\"attributes\\": {
\\"type\\": \\"text/javascript\\"
},
\\"childNodes\\": [],
\\"rootId\\": 11,
\\"id\\": 15
}
},
{
\\"parentId\\": 15,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 3,
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
\\"rootId\\": 11,
\\"id\\": 16
}
}
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [],
\\"removes\\": [],
\\"adds\\": [
{
\\"parentId\\": 14,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 2,
\\"tagName\\": \\"iframe\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"rootId\\": 11,
\\"id\\": 17
}
}
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"adds\\": [
{
\\"parentId\\": 17,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 0,
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"html\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"head\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"script\\",
\\"attributes\\": {
\\"type\\": \\"text/javascript\\"
},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
\\"rootId\\": 18,
\\"id\\": 22
}
],
\\"rootId\\": 18,
\\"id\\": 21
}
],
\\"rootId\\": 18,
\\"id\\": 20
},
{
\\"type\\": 2,
\\"tagName\\": \\"body\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n\\\\n\\",
\\"rootId\\": 18,
\\"id\\": 24
}
],
\\"rootId\\": 18,
\\"id\\": 23
}
],
\\"rootId\\": 18,
\\"id\\": 19
}
],
\\"compatMode\\": \\"BackCompat\\",
\\"id\\": 18
}
}
],
\\"removes\\": [],
\\"texts\\": [],
\\"attributes\\": [],
\\"isAttachIframe\\": true
}
}
]"
`;

View File

@@ -594,4 +594,28 @@ describe('same origin iframes', function (this: ISuite) {
expect(events.length).toBe(4);
assertSnapshot(events);
});
it('should record cross-origin iframe in same-origin iframe', async () => {
const sameOriginIframe = ctx.page.mainFrame().childFrames()[0];
await sameOriginIframe.evaluate((serverUrl) => {
/**
* Create a cross-origin iframe in this same-origin iframe.
*/
const crossOriginIframe = document.createElement('iframe');
document.body.appendChild(crossOriginIframe);
crossOriginIframe.src = `${serverUrl}/html/blank.html`;
return new Promise((resolve) => {
crossOriginIframe.onload = resolve;
});
}, ctx.serverURL);
const crossOriginIframe = sameOriginIframe.childFrames()[0];
// Inject recording script into this cross-origin iframe
await injectRecordScript(crossOriginIframe);
await waitForRAF(ctx.page);
const snapshots = (await ctx.page.evaluate(
'window.snapshots',
)) as eventWithTime[];
assertSnapshot(snapshots);
});
});