}
    // Flip the flag immediately before waiting for the download list.
    this.#listening = true;

    // Wait for the download list to be retrieved.
    await this.#waitForDownloadList();

    // Check if we are still listening since the previous step is async.
    if (this.#listening) {
      // Prior to adding the view, track all existing downloads to avoid sending
      // unexpected backfill download-started events for existing downloads.
      const downloads = await this.#downloadList.getAll();
      for (const download of downloads) {
        this.#trackedDownloads.set(download, {
          started: download.startTime !== null,
        });
      }
      // Bug 1983012: addView/removeView are flagged as async but are fully sync.
      this.#downloadList.addView(this.#downloadView);
    }
  }

  stopListening() {
    if (!this.#listening) {
      return;
    }

    // Note: this.#downloadList might be null since its initialization is async.
    if (this.#downloadList) {
      // Remove this object as a view from the download list
      this.#downloadList.removeView(this.#downloadView);
    }

    // Clear tracked downloads
    this.#trackedDownloads = new WeakMap();

    this.#listening = false;
  }

  /**
   * Retrieve the internal listener state for a given download.
   *
   * @param {Download} download
   *     The download object for which we want to retrieve the listener state.
   *
   * @returns {object}
   *     The state for the download object.
   */
  #getDownloadState(download) {
    if (!this.#trackedDownloads.has(download)) {
      this.#trackedDownloads.set(download, {
        started: false,
      });
    }

    return this.#trackedDownloads.get(download);
  }

  #maybeEmitDownloadStarted(state, download) {
    if (!state.started && download.startTime) {
      state.started = true;
      this.emit("download-started", {
        download,
      });
    }
  }

  /**
   * DownloadList view callback triggered when a download is added.
   * Note that this will be triggered for all existing downloads when a new view
   * is added to the DownloadList.
   *
   * @param {Download} download
   *     The download object added to the list.
   */
  #onDownloadAdded = download => {
    const state = this.#getDownloadState(download);
    this.#maybeEmitDownloadStarted(state, download);
  };

  /**
   * DownloadList view callback triggered when a download status changed.
   *
   * @param {Download} download
   *     The download object which changed.
   */
  #onDownloadChanged = download => {
    const state = this.#getDownloadState(download);
    this.#maybeEmitDownloadStarted(state, download);

    // canceled + hasPartialData corresponds to a paused download.
    const paused = download.canceled && download.hasPartialData;
    if (!state.stopped && download.stopped && !paused) {
      state.stopped = true;
      this.emit("download-stopped", {
        download,
      });
    }
  };

  /**
   * DownloadList view callback triggered when a download is removed from the
   * list.
   *
   * @param {Download} download
   *     The download object which was removed from the list.
   */
  #onDownloadRemoved = download => {
    this.#trackedDownloads.delete(download);
  };

  async #waitForDownloadList() {
    if (this.#downloadListPromise == null) {
      const { promise, resolve } = Promise.withResolvers();
      this.#downloadListPromise = promise;
      const downloadList = await lazy.Downloads.getList(lazy.Downloads.ALL);
      if (this.#destroyed) {
        // Bail out before setting the downloadList if the listener was already
        // destroyed.
        return;
      }
      this.#downloadList = downloadList;
      resolve();
    }

    await this.#downloadListPromise;
  }
}
PK