function waitForFcp(session, pauseAfterFcpMs, maxWaitForFcpMs) {
  /** @type {(() => void)} */
  let cancel = () => {
    throw new Error('waitForFcp.cancel() called before it was defined');
  };

  /** @type {Promise<void>} */
  const promise = new Promise((resolve, reject) => {
    const maxWaitTimeout = setTimeout(() => {
      reject(new LighthouseError(LighthouseError.errors.NO_FCP));
    }, maxWaitForFcpMs);
    /** @type {NodeJS.Timeout|undefined} */
    let loadTimeout;

    /** @param {LH.Crdp.Page.LifecycleEventEvent} e */
    const lifecycleListener = e => {
      if (e.name === 'firstContentfulPaint') {
        loadTimeout = setTimeout(() => {
          resolve();
          cancel();
        }, pauseAfterFcpMs);
      }
    };

    session.on('Page.lifecycleEvent', lifecycleListener);

    let canceled = false;
    cancel = () => {
      if (canceled) return;
      canceled = true;
      session.off('Page.lifecycleEvent', lifecycleListener);
      maxWaitTimeout && clearTimeout(maxWaitTimeout);
      loadTimeout && clearTimeout(loadTimeout);
      reject(new Error('Wait for FCP canceled'));
    };
  });

  return {
    promise,
    cancel,
  };
}
Returns a promise that resolve when a frame has a FCP.
@param {LH.Gatherer.ProtocolSession} session
@param {number} pauseAfterFcpMs
@param {number} maxWaitForFcpMs
@return {CancellableWait}