Skip to content

Conversation

@RohitR311
Copy link
Contributor

@RohitR311 RohitR311 commented Sep 28, 2025

What this PR does?

  1. Fixes the CORS navigation issue that occurs when clicking on links that open a new tab on the browser.
  2. Fixes prevent default console errors for scroll operations
  3. Occasional browser flickering issue

Fixes #808
Fixes #807
Fixes #806

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability when rendering snapshots that include cross‑origin iframes, reducing blank or failed loads.
    • More consistent scroll and touch interactions by aligning event listener behavior.
  • Refactor
    • Simplified loading indicator to show only active loading state.
    • Removed in‑iframe error screen; issues are now reported in the console.
  • Style
    • Minor cleanup of event listener setup/removal to ensure consistent behavior across mount/unmount.

@RohitR311 RohitR311 requested a review from amhsirak September 28, 2025 10:43
@coderabbitai
Copy link

coderabbitai bot commented Sep 28, 2025

Walkthrough

The DOMBrowserRenderer component removes in-iframe error UI and renderError state, updates event listener options to use passive: false, and adds cross-origin handling by recreating the iframe on access failure during rrweb snapshot rendering. Loading indicator logic is simplified to depend solely on isRendered. Cleanup paths mirror the new listener options.

Changes

Cohort / File(s) Summary of changes
DOMBrowserRenderer updates
src/components/recorder/DOMBrowserRenderer.tsx
- Removed renderError state and in-iframe error UI/rendering path
- Reworked event listeners (wheel/touchstart/touchmove) to use { passive: false } for add/remove
- Added iframe cross-origin resilience: on access failure, recreate iframe and retry document access in rrweb snapshot rendering
- Simplified loading indicator to only reflect isRendered
- Updated cleanup to remove listeners with the new options

Sequence Diagram(s)

sequenceDiagram
  participant U as DOMBrowserRenderer
  participant IF as Iframe
  participant DOC as Iframe Document
  participant C as Console

  rect rgba(230,240,255,0.5)
  note over U: Start rrweb snapshot render
  U->>IF: Create/ensure iframe
  U->>IF: Attach listeners (wheel/touch*) with { passive: false }
  end

  alt Access iframe document
    U->>IF: Get contentWindow/document
    IF-->>U: Document reference
    U->>DOC: Inject rrweb snapshot & styles
    U-->>U: Mark isRendered = true
  else Cross-origin access error
    U->>C: console.error(error)
    note over U,IF: Recreate iframe preserving attributes/styles
    U->>IF: Recreate and mount iframe
    U->>IF: Retry get document
    IF-->>U: Document reference (if available)
    U->>DOC: Inject rrweb snapshot & styles
    U-->>U: Mark isRendered = true
  end

  rect rgba(240,240,240,0.5)
  note over U: Unmount/Cleanup
  U->>IF: Remove listeners with { passive: false }
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Type: Bug, Scope: Recorder

Suggested reviewers

  • amhsirak

Poem

I balanced on the rim of an iframe sky,
Nibbled CORS clouds drifting by.
With passive paws I tiptoe slow,
Rebuild the frame—then off I go.
No error carrots on display—
Just snapshots crisp in fields of gray. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title “fix: cors navigation issue” does not accurately summarize the main changes in this pull request, which primarily refactor error handling, update event listener options, and introduce iframe cross-origin resilience rather than addressing navigation via target="_blank" links. Update the title to clearly reflect the core refactors and new iframe cross-origin handling in DOMBrowserRenderer, for example: “refactor(DOMBrowserRenderer): add cross-origin iframe resilience and clean up error handling.”
Linked Issues Check ⚠️ Warning Although the linked issue #808 describes a bug when opening target="_blank" links, the changes in this PR do not implement any link click interception or new-tab navigation fixes and instead focus on removing error UI, adjusting passive event listeners, and retrying iframe access. Implement or restore the logic that intercepts or handles target="_blank" link clicks so that cross-origin navigation errors are prevented, ensuring the PR meets issue #808’s objective.
Out of Scope Changes Check ⚠️ Warning The PR contains several refactors unrelated to the linked CORS navigation bug—such as removing renderError state, simplifying loading indicators, and updating event listener options—that fall outside the stated issue’s objective of fixing cross-origin new-tab navigation. Split unrelated cleanup and UI refactors into a separate pull request or remove them from this PR to keep the scope focused solely on resolving the target="_blank" CORS navigation issue.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cors-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/recorder/DOMBrowserRenderer.tsx (2)

432-441: Don’t permanently strip target on anchor clicks; restore or avoid mutation

Setting linkElement.target = "" alters page semantics and isn’t restored. Preventing default + temporary href removal is enough to suppress navigation/tab opening.

Patch:

-          if (linkElement.target) {
-            linkElement.target = "";
-          }
-
-          const originalHref = linkElement.href;
+          const originalHref = linkElement.href;
+          const originalTarget = linkElement.getAttribute('target');
           linkElement.removeAttribute("href");
 
           setTimeout(() => {
             linkElement.setAttribute("href", originalHref);
+            if (originalTarget !== null) {
+              linkElement.setAttribute('target', originalTarget);
+            }
           }, 100);

876-880: Invalid CSS: quoted cursor value is ignored

cursor: "pointer" is invalid; remove the quotes so the rule applies.

-          * { 
-              cursor: "pointer" !important; 
-          }
+          * {
+              cursor: pointer !important;
+          }
🧹 Nitpick comments (2)
src/components/recorder/DOMBrowserRenderer.tsx (2)

344-347: Deduplicate listener options to avoid drift between add/remove

You compute the options in three places. Factor it to a helper to ensure symmetry and readability.

-          const options: boolean | AddEventListenerOptions = ['wheel', 'touchstart', 'touchmove'].includes(event)
-            ? { passive: false }
-            : false;
+          const options = listenerOptions(event);

Add once (near the component top-level):

function listenerOptions(event: string): boolean | AddEventListenerOptions {
  return ['wheel', 'touchstart', 'touchmove'].includes(event)
    ? { passive: false }
    : false; // capture=false
}

Also applies to: 761-765, 965-972


1009-1045: Add minimal a11y to loading overlay

Expose status to AT without behavioral change.

-      {!isRendered && (
-        <div
+      {!isRendered && (
+        <div
+          role="status"
+          aria-live="polite"
+          aria-busy="true"
           style={{
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2e0324 and d1b6404.

📒 Files selected for processing (1)
  • src/components/recorder/DOMBrowserRenderer.tsx (6 hunks)
🔇 Additional comments (1)
src/components/recorder/DOMBrowserRenderer.tsx (1)

705-709: Good: wheel prevention guarded by capture mode

Ensuring preventDefault only in capture mode (with passive: false) is correct and avoids blocking normal scrolling otherwise.

Comment on lines +812 to +839
try {
iframeDoc = iframe.contentDocument!;
if (!iframeDoc) {
throw new Error("Cannot access iframe document");
}
} catch (crossOriginError) {
console.warn("Cross-origin iframe access blocked, recreating iframe");

const newIframe = document.createElement('iframe');
newIframe.style.cssText = iframe.style.cssText;
newIframe.sandbox = iframe.sandbox.value;
newIframe.title = iframe.title;
newIframe.tabIndex = iframe.tabIndex;
newIframe.id = iframe.id;

iframe.parentNode?.replaceChild(newIframe, iframe);
Object.defineProperty(iframeRef, 'current', {
value: newIframe,
writable: false,
enumerable: true,
configurable: true
});

iframeDoc = newIframe.contentDocument!;
if (!iframeDoc) {
throw new Error("Cannot access new iframe document");
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Recreating the iframe: ref mutation and sandbox handling will crash and/or no-op

  • Don’t redefine refs with Object.defineProperty; it makes current read‑only and React will later fail to update it (on re-render/unmount).
  • HTMLIFrameElement.sandbox is a DOMTokenList (read‑only); assigning a string won’t work reliably. Set the attribute instead.

Fix with this minimal patch:

-          const newIframe = document.createElement('iframe');
-          newIframe.style.cssText = iframe.style.cssText;
-          newIframe.sandbox = iframe.sandbox.value;
+          const newIframe = document.createElement('iframe');
+          newIframe.style.cssText = iframe.style.cssText;
+          newIframe.setAttribute('sandbox', iframe.getAttribute('sandbox') ?? '');
           newIframe.title = iframe.title;
           newIframe.tabIndex = iframe.tabIndex;
           newIframe.id = iframe.id;

           iframe.parentNode?.replaceChild(newIframe, iframe);
-          Object.defineProperty(iframeRef, 'current', {
-            value: newIframe,
-            writable: false,
-            enumerable: true,
-            configurable: true
-          });
+          iframeRef.current = newIframe;

Optional: const newIframe = iframe.cloneNode(false) as HTMLIFrameElement; will also preserve attributes like class, data‑*, etc., then replace and assign iframeRef.current.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
iframeDoc = iframe.contentDocument!;
if (!iframeDoc) {
throw new Error("Cannot access iframe document");
}
} catch (crossOriginError) {
console.warn("Cross-origin iframe access blocked, recreating iframe");
const newIframe = document.createElement('iframe');
newIframe.style.cssText = iframe.style.cssText;
newIframe.sandbox = iframe.sandbox.value;
newIframe.title = iframe.title;
newIframe.tabIndex = iframe.tabIndex;
newIframe.id = iframe.id;
iframe.parentNode?.replaceChild(newIframe, iframe);
Object.defineProperty(iframeRef, 'current', {
value: newIframe,
writable: false,
enumerable: true,
configurable: true
});
iframeDoc = newIframe.contentDocument!;
if (!iframeDoc) {
throw new Error("Cannot access new iframe document");
}
}
try {
iframeDoc = iframe.contentDocument!;
if (!iframeDoc) {
throw new Error("Cannot access iframe document");
}
} catch (crossOriginError) {
console.warn("Cross-origin iframe access blocked, recreating iframe");
const newIframe = document.createElement('iframe');
newIframe.style.cssText = iframe.style.cssText;
newIframe.setAttribute('sandbox', iframe.getAttribute('sandbox') ?? '');
newIframe.title = iframe.title;
newIframe.tabIndex = iframe.tabIndex;
newIframe.id = iframe.id;
iframe.parentNode?.replaceChild(newIframe, iframe);
iframeRef.current = newIframe;
iframeDoc = newIframe.contentDocument!;
if (!iframeDoc) {
throw new Error("Cannot access new iframe document");
}
}
🤖 Prompt for AI Agents
In src/components/recorder/DOMBrowserRenderer.tsx around lines 812 to 839, the
current approach recreates the iframe by using Object.defineProperty to mutate
the ref (making current read‑only) and assigns iframe.sandbox as a string
(DOMTokenList is not a string), which will break React ref updates and sandbox
handling; fix by creating the new iframe (or cloneNode(false) to preserve
attributes), copy needed attributes/styles (style.cssText, title, tabIndex, id,
classes, data-*, etc.), set sandbox using setAttribute('sandbox',
original.getAttribute('sandbox') || '') or manipulate newIframe.sandbox.tokens
properly, replace the old iframe in the DOM, and update the ref by assigning
iframeRef.current = newIframe (not via Object.defineProperty) so React can
update/unmount correctly.

@RohitR311 RohitR311 added Type: Bug Something isn't working Scope: UI/UX Issues/PRs related to UI/UX labels Sep 29, 2025
@amhsirak amhsirak merged commit dc30e15 into develop Sep 29, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: UI/UX Issues/PRs related to UI/UX Type: Bug Something isn't working

Projects

None yet

3 participants