static computeNavigationTimingsForFrame(frameEvents, options) {
    const {timeOriginEvt} = options;

    // Find our first paint of this frame
    const firstPaint = frameEvents.find(e => e.name === 'firstPaint' && e.ts > timeOriginEvt.ts);

    // FCP will follow at/after the FP. Used in so many places we require it.
    const firstContentfulPaint = frameEvents.find(
      e => e.name === 'firstContentfulPaint' && e.ts > timeOriginEvt.ts
    );

    if (!firstContentfulPaint) {
      throw this.createNoFirstContentfulPaintError();
    }

    // This function accepts events spanning multiple frames, but this usage will only provide events from the main frame.
    const lcpResult = this.computeValidLCPAllFrames(frameEvents, timeOriginEvt);

    const load = frameEvents.find(e => e.name === 'loadEventEnd' && e.ts > timeOriginEvt.ts);
    const domContentLoaded = frameEvents.find(
      e => e.name === 'domContentLoadedEventEnd' && e.ts > timeOriginEvt.ts
    );

    /** @param {{ts: number}=} event */
    const getTimestamp = (event) => event?.ts;
    /** @type {TraceNavigationTimesForFrame} */
    const timestamps = {
      timeOrigin: timeOriginEvt.ts,
      firstPaint: getTimestamp(firstPaint),
      firstContentfulPaint: firstContentfulPaint.ts,
      largestContentfulPaint: getTimestamp(lcpResult.lcp),
      load: getTimestamp(load),
      domContentLoaded: getTimestamp(domContentLoaded),
    };

    /** @param {number} ts */
    const getTiming = ts => (ts - timeOriginEvt.ts) / 1000;
    /** @param {number=} ts */
    const maybeGetTiming = (ts) => ts === undefined ? undefined : getTiming(ts);
    /** @type {TraceNavigationTimesForFrame} */
    const timings = {
      timeOrigin: 0,
      firstPaint: maybeGetTiming(timestamps.firstPaint),
      firstContentfulPaint: getTiming(timestamps.firstContentfulPaint),
      largestContentfulPaint: maybeGetTiming(timestamps.largestContentfulPaint),
      load: maybeGetTiming(timestamps.load),
      domContentLoaded: maybeGetTiming(timestamps.domContentLoaded),
    };

    return {
      timings,
      timestamps,
      timeOriginEvt: timeOriginEvt,
      firstPaintEvt: firstPaint,
      firstContentfulPaintEvt: firstContentfulPaint,
      largestContentfulPaintEvt: lcpResult.lcp,
      loadEvt: load,
      domContentLoadedEvt: domContentLoaded,
      lcpInvalidated: lcpResult.invalidated,
    };
  }
Computes timings of trace events of key trace events in milliseconds since the time origin
in addition to the standard microsecond monotonic timestamps.
@param {Array<LH.TraceEvent>} frameEvents
@param {{timeOriginEvt: LH.TraceEvent}} options