 if browsing context has been discarded, false otherwise.
   */
  get closed() {
    return this.contentBrowser === null;
  }

  /**
   * Gets the position and dimensions of the top-level browsing context.
   *
   * @returns {Map.<string, number>}
   *     Object with |x|, |y|, |width|, and |height| properties.
   */
  get rect() {
    return {
      x: this.window.screenX,
      y: this.window.screenY,
      width: this.window.outerWidth,
      height: this.window.outerHeight,
    };
  }

  /**
   * Close the current window.
   *
   * @returns {Promise}
   *     A promise which is resolved when the current window has been closed.
   */
  async closeWindow() {
    return lazy.windowManager.closeWindow(this.window);
  }

  /**
   * Focus the current window.
   *
   * @returns {Promise}
   *     A promise which is resolved when the current window has been focused.
   */
  async focusWindow() {
    await lazy.windowManager.focusWindow(this.window);

    // Also focus the currently selected tab if present.
    this.contentBrowser?.focus();
  }

  /**
   * Open a new browser window.
   *
   * @returns {Promise}
   *     A promise resolving to the newly created chrome window.
   */
  openBrowserWindow(focus = false, isPrivate = false) {
    return lazy.windowManager.openBrowserWindow({
      openerWindow: this.window,
      focus,
      isPrivate,
    });
  }

  /**
   * Close the current tab.
   *
   * @returns {Promise}
   *     A promise which is resolved when the current tab has been closed.
   *
   * @throws UnsupportedOperationError
   *     If tab handling for the current application isn't supported.
   */
  async closeTab() {
    // If the current window is not a browser then close it directly. Do the
    // same if only one remaining tab is open, or no tab selected at all.
    //
    // Note: For GeckoView there will always be a single tab only. But for
    // consistency with other platforms a specific condition has been added
    // below as well even it's not really used.
    if (
      !this.tabBrowser ||
      !this.tabBrowser.tabs ||
      this.tabBrowser.tabs.length === 1 ||
      !this.tab
    ) {
      return this.closeWindow();
    }

    let destroyed = new lazy.MessageManagerDestroyedPromise(
      this.messageManager
    );
    let tabClosed;

    if (lazy.AppInfo.isAndroid) {
      await lazy.TabManager.removeTab(this.tab);
    } else if (lazy.AppInfo.isFirefox) {
      tabClosed = new lazy.EventPromise(this.tab, "TabClose");
      await this.tabBrowser.removeTab(this.tab);
    } else {
      throw new lazy.error.UnsupportedOperationError(
        `closeTab() not supported for ${lazy.AppInfo.name}`
      );
    }

    return Promise.all([destroyed, tabClosed]);
  }

  /**
   * Open a new tab in the currently selected chrome window.
   */
  async openTab(focus = false) {
    let tab = null;

    // Bug 1795841 - For Firefox the TabManager cannot be used yet. As such
    // handle opening a tab differently for Android.
    if (lazy.AppInfo.isAndroid) {
      tab = await lazy.TabManager.addTab({ focus, window: this.window });
    } else if (lazy.AppInfo.isFirefox) {
      const opened = new lazy.EventPromise(this.window, "TabOpen");
      this.window.BrowserCommands.openTab({ url: "about:blank" });
      await opened;

      tab = this.tabBrowser.selectedTab;

      // The new tab is always selected by default. If focus is not wanted,
      // the previously tab needs to be selected again.
      if (!focus) {
        await lazy.TabManager.selectTab(this.tab);
      }
    } else {
      throw new lazy.error.UnsupportedOperationError(
        `openTab() not supported for ${lazy.AppInfo.name}`
      );
    }

    return tab;
  }

  /**
   * Set the current tab.
   *
   * @param {number=} index
   *     Tab index to switch to. If the parameter is undefined,
   *     the currently selected tab will be used.
   * @param {ChromeWindow=} window
   *     Switch to this window before selecting the tab.
   * @param {boolean=} focus
   *      A boolean value which determines whether to focus
   *      the window. Defaults to true.
   *
   * @returns {Tab}
   *     The selected tab.
   *
   * @throws UnsupportedOperationError
   *     If tab handling for the current application isn't supported.
   */
  async switchToTab(index, window = undefined, focus = true) {
    if (window) {
      this.window = window;
      this.tabBrowser = lazy.TabManager.getTabBrowser(this.window);
    }

    if (!this.tabBrowser || this.driver.isReftestBrowser(this.tabBrowser)) {
      return null;
    }

    if (typeof index == "undefined") {
      this.tab = this.tabBrowser.selectedTab;
    } else {
      this.tab = this.tabBrowser.tabs[index];
    }

    if (focus) {
      await lazy.TabManager.selectTab(this.tab);
    }

    // By accessing the content browser's message manager a new browsing
    // context is created for browserless tabs, which is needed to successfully
    // run the WebDriver's is browsing context open step. This is temporary
    // until we find a better solution on bug 1812258.
    this.messageManager;

    return this.tab;
  }

  /**
   * Registers a new frame, and sets its current frame id to this frame
   * if it is not already assigned, and if a) we already have a session
   * or b) we're starting a new session and it is the right start frame.
   */
  register() {
    if (!this.tabBrowser) {
      return;
    }

    // If we're setting up a new session on Firefox, we only process the
    // registration for this frame if it belongs to the current tab.
    if (!this.tab) {
      this.switchToTab();
    }
  }
};
PK