rrweb
                                
                                 rrweb copied to clipboard
                                
                                    rrweb copied to clipboard
                            
                            
                            
                        [Bug]: Injected iframe cleanup on error
Preflight Checklist
- [X] I have searched the issue tracker for a bug report that matches the one I want to file, without success.
What package is this bug report for?
Other (specify below)
Version
v2.0.0-alpha.18
Expected Behavior
@rrweb/record and rrweb-snapshot should clean up any iframes that are injected for the purposes of getting clean prototypes.
Actual Behavior
In @rrweb/record and rrweb-snapshot, getUntaintedPrototype contains code that injects an iframe but doesn't always properly clean it up (in my case, when calling const win = iframeEl.contentWindow; doesn't work within an iframe):
  try {
    const iframeEl = document.createElement("iframe");
    document.body.appendChild(iframeEl);
    const win = iframeEl.contentWindow;
    if (!win) return defaultObj.prototype;
    const untaintedObject = win[key].prototype;
    document.body.removeChild(iframeEl);
    if (!untaintedObject) return defaultPrototype;
    return untaintedBasePrototype[key] = untaintedObject;
  } catch {
    return defaultPrototype;
  }
Steps to Reproduce
Enable @rrweb/record Go to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe Paste the following into the HTML input:
<iframe
  id="inlineFrameExample"
  title="Inline Frame Example"
  width="300"
  name="Example Website"
  src="https://example.com"
  title="Example.com"
  ref={iframeRef}
  sandbox="allow-scripts"
  height="200"
  src="">
</iframe>
Click on "Example Domain" when it renders in the iframe
Note a new blank iframe is injected and not removed
Testcase Gist URL
No response
Additional Information
I suggest adding a finally clause to handle the iframe cleanup:
  let iframeEl;
try {
  iframeEl = document.createElement("iframe");
  document.body.appendChild(iframeEl);
  const win = iframeEl.contentWindow;
  if (!win) return defaultObj.prototype;
  const untaintedObject = win[key].prototype;
  if (!untaintedObject) return defaultPrototype;
  return untaintedBasePrototype[key] = untaintedObject;
} catch {
  return defaultPrototype;
} finally {
  try {
    if (iframeEl && iframeEl.parentNode) {
      document.body.removeChild(iframeEl);
    }
  } catch (cleanupErr) {
    console.error('[getUntaintedPrototype2] cleanup error', cleanupErr);
  }
}