* docs: revamp installation docs for esm and umd Document recommended install paths across the main guides and package READMEs for rrweb, @rrweb/all, @rrweb/record, @rrweb/replay, and rrweb-player. Clarify three usage modes: bundler/npm, browser no-build with import maps and +esm, and legacy UMD fallback. * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply formatting changes * Apply suggestion from @eoghanmurray Co-authored-by: Eoghan Murray <eoghan@getthere.ie> * Apply formatting changes * docs(all): streamline README usage section Move the guide link next to the import example and remove the duplicated Usage section to keep docs concise and easier to scan. * docs(readme): update gzip size badges in zh-cn readme * docs(plugins): update readme imports to scoped esm packages Replace `rrweb` default imports and `rrweb.Replayer` usage with `@rrweb/record` `record` and `@rrweb/replay` `Replayer` in plugin usage examples. Also update canvas WebRTC plugin imports to scoped `@rrweb/*` package names to keep docs aligned with current package structure. * docs: update docs to prefer scoped esm packages replace `rrweb` default import examples with `@rrweb/record` and `@rrweb/replay` across recipes and guides in en/zh-CN. clarify package selection for new integrations, add `@rrweb/all` convenience guidance, and refresh CDN/style import snippets for ESM and legacy UMD compatibility. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Eoghan Murray <eoghan@getthere.ie>
121 lines
3.8 KiB
Markdown
121 lines
3.8 KiB
Markdown
# Cross origin iframes
|
|
|
|
By default browsers make it difficult to access the contents of an iframe that is hosted on a different domain. This is a security feature to prevent malicious sites from accessing sensitive information on other sites. It is possible to work around this security feature, but it is not recommended unless you [are very strict](https://stackoverflow.com/a/21629575) about allowing only the sites you trust to embed your website inside of an iframe.
|
|
Since if you allow recording cross origin iframes, any malicious website can embed your website and as long as they have rrweb running they can record all the contents of your website.
|
|
|
|
## How to record cross origin iframes
|
|
|
|
Enable recording cross-origin iframes in your parent page:
|
|
|
|
```js
|
|
import { record } from '@rrweb/record';
|
|
|
|
record({
|
|
emit(event) {}, // all events will be emitted here, including events from cross origin iframes
|
|
recordCrossOriginIframes: true,
|
|
});
|
|
```
|
|
|
|
Enable replaying cross-origin iframes in your child page:
|
|
|
|
```js
|
|
import { record } from '@rrweb/record';
|
|
|
|
record({
|
|
emit(event) {}, // this is required for rrweb, but the child page will not emit any events
|
|
recordCrossOriginIframes: true,
|
|
});
|
|
```
|
|
|
|
## Considerations
|
|
|
|
When cross origin iframe recording is turned on rrweb will check to see if it is being run in a top level window.
|
|
If it isn't it'll send the events to the parent window via `postMessage`.
|
|
|
|
If you don't have rrweb running in the top level window, the events will be lost when `recordCrossOriginIframes` is turned on.
|
|
|
|
If the top level window is a malicious website it can listen to the events and send them to a server of its choosing.
|
|
|
|
Or if a malicious script is running in on your page they can listen in on `postMessage` and as communication between the child and parent window is not encrypted. And they can see the events.
|
|
|
|
## Options for injecting rrweb into cross origin iframes
|
|
|
|
### 1. Website owners, add rrweb in the iframes
|
|
|
|
If you own the website that with the iframe and the website that is being embedded in an iframe, you can add rrweb to both pages via a script tag.
|
|
|
|
### 2. Browser extension
|
|
|
|
See https://developer.chrome.com/docs/extensions/mv3/content_scripts/#functionality
|
|
|
|
### 3. Puppeteer script
|
|
|
|
```js
|
|
import puppeteer from 'puppeteer';
|
|
|
|
async function injectRecording(frame) {
|
|
await frame.evaluate((rrwebCode) => {
|
|
if (window.__IS_RECORDING__) return;
|
|
window.__IS_RECORDING__ = true;
|
|
|
|
(async () => {
|
|
function loadScript(code) {
|
|
const s = document.createElement('script');
|
|
s.type = 'text/javascript';
|
|
s.innerHTML = code;
|
|
if (document.head) {
|
|
document.head.append(s);
|
|
} else {
|
|
requestAnimationFrame(() => {
|
|
document.head.append(s);
|
|
});
|
|
}
|
|
}
|
|
loadScript(rrwebCode);
|
|
|
|
window.rrweb.record({
|
|
emit: (event) => {
|
|
window._captureEvent(event);
|
|
},
|
|
recordCrossOriginIframes: true,
|
|
});
|
|
})();
|
|
}, code);
|
|
}
|
|
|
|
const browser = await puppeteer.launch();
|
|
const page = (await browser.pages())[0];
|
|
|
|
const events = []; // contains all events from all frames
|
|
|
|
await page.exposeFunction('_captureEvent', (event) => {
|
|
events.push(event);
|
|
});
|
|
|
|
page.on('framenavigated', async (frame) => {
|
|
await injectRecording(frame); // injects rrweb into the iframe
|
|
});
|
|
|
|
await page.goto('https://example.com');
|
|
|
|
// your events are in the events array
|
|
```
|
|
|
|
### 4. Electron
|
|
|
|
```ts
|
|
const win = new BrowserWindow({
|
|
width: 800,
|
|
height: 600,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'rrweb-recording-script.js'),
|
|
// this turns on preload inside iframes, but disables node integration
|
|
nodeIntegrationInSubFrames: true,
|
|
nodeIntegration: false,
|
|
},
|
|
});
|
|
```
|
|
|
|
See https://www.electronjs.org/docs/latest/tutorial/tutorial-preload
|
|
And https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
|