Repository: puppeteer/puppeteer
File: packages/puppeteer-core/src/common/FrameManager.ts (TypeScript)

Code before:
      await swapped.valueOrThrow();
      for (const child of mainFrame.childFrames()) {
        this.#removeFramesRecursively(child);
      }
    } catch (err) {
      this.#removeFramesRecursively(mainFrame);
    }
  }

  /**
   * When the main frame is replaced by another main frame,
   * we maintain the main frame object identity while updating
   * its frame tree and ID.
   */
  async swapFrameTree(client: CDPSession): Promise<void> {
    this.#onExecutionContextsCleared(this.#client);

    this.#client = client;
    assert(
      this.#client instanceof CDPSessionImpl,
      'CDPSession is not an instance of CDPSessionImpl.'
    );
    const frame = this._frameTree.getMainFrame();
    if (frame) {
      this._frameTree.removeFrame(frame);
      frame.updateId(this.#client._target()._targetId);
    }
    this.setupEventListeners(client);
    client.once(CDPSessionEmittedEvents.Disconnected, () => {
      this.#onClientDisconnect().catch(debugError);
    });
    await this.initialize(client);
    await this.#networkManager.updateClient(client);
    if (frame) {
      frame.emit(FrameEmittedEvents.FrameSwappedByActivation);
    }
  }

  private setupEventListeners(session: CDPSession) {
    session.on('Page.frameAttached', event => {
      this.#onFrameAttached(session, event.frameId, event.parentFrameId);
    });
    session.on('Page.frameNavigated', event => {
      this.#frameNavigatedReceived.add(event.frame.id);
      void this.#onFrameNavigated(event.frame);
    });
    session.on('Page.navigatedWithinDocument', event => {
      this.#onFrameNavigatedWithinDocument(event.frameId, event.url);
    });
    session.on(
      'Page.frameDetached',

Review comment (suggestion):
Removing it here means we create a new frame in `await this.initialize(client);` and we have a stale double that way.

Code after:
      swapped.resolve();
    });
    try {
      await swapped.valueOrThrow();
    } catch (err) {
      this.#removeFramesRecursively(mainFrame);
    }
  }

  /**
   * When the main frame is replaced by another main frame,
   * we maintain the main frame object identity while updating
   * its frame tree and ID.
   */
  async swapFrameTree(client: CDPSession): Promise<void> {
    this.#onExecutionContextsCleared(this.#client);

    this.#client = client;
    assert(
      this.#client instanceof CDPSessionImpl,
      'CDPSession is not an instance of CDPSessionImpl.'
    );
    const frame = this._frameTree.getMainFrame();
    if (frame) {
      this.#frameNavigatedReceived.add(this.#client._target()._targetId);
      this._frameTree.removeFrame(frame);
      frame.updateId(this.#client._target()._targetId);
      frame.mainRealm().clearContext();
      frame.isolatedRealm().clearContext();
      this._frameTree.addFrame(frame);
      frame.updateClient(client, true);
    }
    this.setupEventListeners(client);
    client.once(CDPSessionEmittedEvents.Disconnected, () => {
      this.#onClientDisconnect().catch(debugError);
    });
    await this.initialize(client);
    await this.#networkManager.updateClient(client);
    if (frame) {
      frame.emit(FrameEmittedEvents.FrameSwappedByActivation);
    }
  }

  private setupEventListeners(session: CDPSession) {
    session.on('Page.frameAttached', event => {
      this.#onFrameAttached(session, event.frameId, event.parentFrameId);
    });
    session.on('Page.frameNavigated', event => {
      this.#frameNavigatedReceived.add(event.frame.id);
      void this.#onFrameNavigated(event.frame);
    });