abs());
  }

  async waitUntilReady() {
    await this.#ready;
  }

  async getTaskbarTab(...args) {
    await this.#ready;
    return this.#registry.getTaskbarTab(...args);
  }

  async findOrCreateTaskbarTab(...args) {
    await this.#ready;
    let result = this.#registry.findOrCreateTaskbarTab(...args);

    if (result.created) {
      this.#updateMetrics();

      // Don't wait for the pinning to complete.
      TaskbarTabsPin.pinTaskbarTab(result.taskbarTab, this.#registry);
    }

    return result;
  }

  async findTaskbarTab(...args) {
    await this.#ready;
    return this.#registry.findTaskbarTab(...args);
  }

  /**
   * Moves an existing tab into a new Taskbar Tab window.
   *
   * If there is already a Taskbar Tab for the tab's selected URL and container,
   * opens the existing Taskbar Tab. If not, a new Taskbar Tab is created.
   *
   * @param {MozTabbrowserTab} aTab - The tab to move into a Taskbar Tab window.
   * @returns {{window: DOMWindow, taskbarTab: TaskbarTab}} The created window
   * and the Taskbar Tab it is associated with.
   */
  async moveTabIntoTaskbarTab(aTab) {
    const browser = aTab.linkedBrowser;
    let url = browser.currentURI;
    let userContextId = aTab.userContextId;

    let [, manifest] = await Promise.all([
      this.#ready,
      lazy.ManifestObtainer.browserObtainManifest(browser).catch(e => {
        lazy.logConsole.error(e);
        return {};
      }),
    ]);

    let { taskbarTab } = await this.findOrCreateTaskbarTab(
      url,
      userContextId,
      // 'manifest' can be null if the site doesn't have a manifest.
      manifest ? { manifest } : {}
    );

    let win = await this.replaceTabWithWindow(taskbarTab, aTab);
    return {
      window: win,
      taskbarTab,
    };
  }

  async resetForTests(...args) {
    await this.#ready;
    return this.#registry.resetForTests(...args);
  }

  async removeTaskbarTab(...args) {
    await this.#ready;

    let taskbarTab = this.#registry.removeTaskbarTab(...args);
    this.#updateMetrics();

    // Don't wait for unpinning to finish.
    TaskbarTabsPin.unpinTaskbarTab(taskbarTab, this.#registry);
  }

  async openWindow(...args) {
    await this.#ready;
    return this.#windowManager.openWindow(...args);
  }

  async replaceTabWithWindow(...args) {
    await this.#ready;
    return this.#windowManager.replaceTabWithWindow(...args);
  }

  async ejectWindow(...args) {
    await this.#ready;
    return this.#windowManager.ejectWindow(...args);
  }

  async getCountForId(...args) {
    await this.#ready;
    return this.#windowManager.getCountForId(...args);
  }
})();

/**
 * Taskbar Tabs Registry initialization.
 *
 * @returns {TaskbarTabsRegistry} A registry after loading and hooking saving to persistent storage.
 */
async function initRegistry() {
  const kRegistryFilename = "taskbartabs.json";
  // Construct the path [Profile]/taskbartabs/taskbartabs.json.
  let registryFile = TaskbarTabsUtils.getTaskbarTabsFolder();
  registryFile.append(kRegistryFilename);

  let init = {};
  if (registryFile.exists()) {
    init.loadFile = registryFile;
  }

  let registry = await TaskbarTabsRegistry.create(init);

  // Initialize persistent storage.
  let storage = new TaskbarTabsRegistryStorage(registry, registryFile);
  registry.on(kTaskbarTabsRegistryEvents.created, () => {
    storage.save();
  });
  registry.on(kTaskbarTabsRegistryEvents.patched, () => {
    storage.save();
  });
  registry.on(kTaskbarTabsRegistryEvents.removed, () => {
    storage.save();
  });

  return registry;
}

/**
 * Taskbar Tabs Window Manager initialization.
 *
 * @returns {TaskbarTabsWindowManager} The initialized Window Manager
 */
function initWindowManager() {
  let wm = new TaskbarTabsWindowManager();

  return wm;
}
PK