Skip to content

fix: clear mutation buffer and remove mirror node on iframe pagehide event#1755

Open
megboehlert wants to merge 3 commits intorrweb-io:masterfrom
pendo-io:meg-fix-mutation-observer-leak
Open

fix: clear mutation buffer and remove mirror node on iframe pagehide event#1755
megboehlert wants to merge 3 commits intorrweb-io:masterfrom
pendo-io:meg-fix-mutation-observer-leak

Conversation

@megboehlert
Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings October 27, 2025 20:06
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Oct 27, 2025

🦋 Changeset detected

Latest commit: 7b2416a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
rrweb Patch
rrweb-snapshot Patch
rrdom Patch
rrdom-nodejs Patch
rrweb-player Patch
@rrweb/all Patch
@rrweb/replay Patch
@rrweb/record Patch
@rrweb/types Patch
@rrweb/packer Patch
@rrweb/utils Patch
@rrweb/web-extension Patch
rrvideo Patch
@rrweb/rrweb-plugin-console-record Patch
@rrweb/rrweb-plugin-console-replay Patch
@rrweb/rrweb-plugin-sequential-id-record Patch
@rrweb/rrweb-plugin-sequential-id-replay Patch
@rrweb/rrweb-plugin-canvas-webrtc-record Patch
@rrweb/rrweb-plugin-canvas-webrtc-replay Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a memory leak by ensuring proper cleanup of iframe-related resources when iframes are unloaded. Specifically, it clears mutation buffers and removes mirror nodes when an iframe's pagehide event fires.

Key Changes:

  • Added infrastructure to track and clean up iframe handlers and mutation buffers when iframes are hidden
  • Introduced a pagehide event listener on iframe content windows to trigger cleanup operations
  • Implemented methods to identify and remove mutation buffers associated with specific iframes

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/rrweb/src/record/observer.ts Adds findAndRemoveIframeBuffer function to locate and remove mutation buffers belonging to a specific iframe
packages/rrweb/src/record/mutation.ts Adds bufferBelongsToIframe method to identify if a mutation buffer belongs to a given iframe
packages/rrweb/src/record/index.ts Implements iframeHandlersMap to track iframe handlers and cleanup logic via addPageHideListener
packages/rrweb/src/record/iframe-manager.ts Adds pageHideListener property and addPageHideListener method, attaches pagehide event listener to iframe content windows
.changeset/eleven-knives-rescue.md Documents the memory leak fix in the changeset

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

iframeManager.addLoadListener((iframeEl) => {
try {
handlers.push(observe(iframeEl.contentDocument!));
iframeHandlersMap.set(iframeEl, observe(iframeEl.contentDocument!));
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler is added to iframeHandlersMap but not to the handlers array. This creates an inconsistency where the cleanup in the return function iterates both collections separately. Consider adding the handler to both collections or documenting why this handler should only be in the map.

Suggested change
iframeHandlersMap.set(iframeEl, observe(iframeEl.contentDocument!));
const handler = observe(iframeEl.contentDocument!);
iframeHandlersMap.set(iframeEl, handler);
handlers.push(handler);

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +95
iframeEl.contentWindow?.addEventListener('pagehide', () => {
this.pageHideListener?.(iframeEl);
this.mirror.removeNodeFromMap(iframeEl.contentDocument!);
this.crossOriginIframeMap.delete(iframeEl.contentWindow!);
});
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pagehide event listener is added but never removed, which could cause memory leaks if the iframe element is reused or if the manager is disposed. Consider storing the listener function reference and providing a cleanup mechanism to remove it when the iframe is detached.

Copilot uses AI. Check for mistakes.
pauldambra pushed a commit to PostHog/posthog-rrweb that referenced this pull request Apr 2, 2026
…rruption (#157)

## Summary
- Adopts upstream rrweb
[#1755](rrweb-io/rrweb#1755) to fix silent
recording corruption when iframes navigate
- When an iframe fires `pagehide` (navigating to a new page for
example), we now:
  - Disconnect the iframe's mutation observer
  - Reset and remove its mutation buffer from the global array
  - Clean up mirror and cross-origin iframe map references
- Integrated with our existing `iframeObserverCleanups` mechanism rather
than introducing a separate handler map

## Why
Without this, when an iframe navigates away without being removed from
the DOM, its old mutation buffer stays alive holding stale DOM
references. This can cause silent recording corruption and broken
replays that are hard to debug.

## Test plan
- [x] Verify existing tests pass
- [ ] Test with a page containing iframes that navigate (embedded forms,
OAuth flows)
- [ ] Confirm no memory growth in long-running sessions with iframe
navigation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants