;
    }

    // Ideally, this should be done with a WeakMap() with xpcomBlocker
    // as a key, but XPCWrappedNative objects cannot serve as WeakMap keys, see
    // bug 1834365.
    const moduleBlocker = () =>
      new Promise(
        // This promise is never resolved. By opposition to AsyncShutdown
        // blockers, `nsIAsyncShutdownBlocker`s are always lifted by calling
        // `removeBlocker`.
        () => xpcomBlocker.blockShutdown(this)
      );

    this._byXpcomBlocker.set(xpcomBlocker, moduleBlocker);
    this._moduleClient.addBlocker(xpcomBlocker.name, moduleBlocker, {
      fetchState: () =>
        new PropertyBagConverter().propertyBagToJsValue(xpcomBlocker.state),
      filename: fileName,
      lineNumber,
      stack,
    });
  },

  removeBlocker(xpcomBlocker) {
    let moduleBlocker = this._byXpcomBlocker.get(xpcomBlocker);
    if (!moduleBlocker) {
      return false;
    }
    this._byXpcomBlocker.delete(xpcomBlocker);
    return this._moduleClient.removeBlocker(moduleBlocker);
  },

  QueryInterface: ChromeUtils.generateQI(["nsIAsyncShutdownClient"]),
};

/**
 * Construct an instance of nsIAsyncShutdownBarrier from an instance
 * of AsyncShutdown.Barrier.
 *
 * @param {object} moduleBarrier an instance if
 * `AsyncShutdown.Barrier`. This instance will serve as back-end for
 * all methods.
 * @class
 */
function nsAsyncShutdownBarrier(moduleBarrier) {
  this._client = new nsAsyncShutdownClient(moduleBarrier.client);
  this._moduleBarrier = moduleBarrier;
}
nsAsyncShutdownBarrier.prototype = {
  get state() {
    return new PropertyBagConverter().jsValueToPropertyBag(
      this._moduleBarrier.state
    );
  },
  get client() {
    return this._client;
  },
  wait(onReady) {
    this._moduleBarrier.wait().then(() => {
      onReady.done();
    });
    // By specification, _moduleBarrier.wait() cannot reject.
  },

  QueryInterface: ChromeUtils.generateQI(["nsIAsyncShutdownBarrier"]),
};

export function nsAsyncShutdownService() {
  // Cache for the getters

  for (let _k of [
    // Parent process
    "appShutdownConfirmed",
    "profileBeforeChange",
    "profileChangeTeardown",
    "sendTelemetry",

    // Child processes
    // TODO: This seems obsolete from AsyncShutdown.sys.mjs ?
    "contentChildShutdown",

    // All processes
    "webWorkersShutdown",
    "xpcomWillShutdown",
  ]) {
    let k = _k;
    Object.defineProperty(this, k, {
      configurable: true,
      get() {
        delete this[k];
        let wrapped = lazy.AsyncShutdown[k]; // May be undefined, if we're on the wrong process.
        let result = wrapped ? new nsAsyncShutdownClient(wrapped) : undefined;
        Object.defineProperty(this, k, {
          value: result,
        });
        return result;
      },
    });
  }

  // Hooks for testing purpose
  this.wrappedJSObject = {
    _propertyBagConverter: PropertyBagConverter,
  };
}

nsAsyncShutdownService.prototype = {
  makeBarrier(name) {
    return new nsAsyncShutdownBarrier(new lazy.AsyncShutdown.Barrier(name));
  },

  QueryInterface: ChromeUtils.generateQI(["nsIAsyncShutdownService"]),
};
PK