Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/purple-carrots-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"rrweb": patch
---

Fix: some nested cross-origin iframes can't be recorded
2 changes: 1 addition & 1 deletion .changeset/silent-plants-perform.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'rrweb': patch
"rrweb": patch
---

Return early for child same origin frames
2 changes: 1 addition & 1 deletion packages/rrweb-player/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ declare global {
}
}

import { EventType, IncrementalSource } from 'rrweb';
import { EventType, IncrementalSource } from '@rrweb/types';
import type { eventWithTime } from '@rrweb/types';

export function inlineCss(cssObj: Record<string, string>): string {
Expand Down
8 changes: 8 additions & 0 deletions packages/rrweb/src/record/iframe-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5538,3 +5538,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
}
}
]"
`;
24 changes: 24 additions & 0 deletions packages/rrweb/test/record/cross-origin-iframes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,4 +604,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);
});
});