static async computeResults(artifacts, context) {
    const settings = context.settings;
    const gatherContext = artifacts.GatherContext;
    const trace = artifacts.Trace;
    const devtoolsLog = artifacts.DevtoolsLog;
    const SourceMaps = artifacts.SourceMaps;
    const simulatorData = {devtoolsLog, settings: context.settings};
    const simulator = await LoadSimulator.request(simulatorData, context);
    const wastedCssBytes = await RenderBlockingResources.computeWastedCSSBytes(artifacts, context);
    const navInsights = await NavigationInsights.request({trace, settings, SourceMaps}, context);

    const renderBlocking = navInsights.model.RenderBlocking;
    if (renderBlocking instanceof Error) throw renderBlocking;

    /** @type {LH.Audit.Context['settings']} */
    const metricSettings = {
      ...context.settings,
      throttlingMethod: 'simulate',
    };

    const metricComputationData = {trace, devtoolsLog, gatherContext, simulator,
      settings: metricSettings, URL: artifacts.URL, SourceMaps: artifacts.SourceMaps};

    // Cast to just `LanternMetric` since we explicitly set `throttlingMethod: 'simulate'`.
    const fcpSimulation = /** @type {LH.Artifacts.LanternMetric} */
      (await FirstContentfulPaint.request(metricComputationData, context));

    const nodesAndTimingsByRequestId =
      getNodesAndTimingByRequestId(fcpSimulation.optimisticEstimate.nodeTimings);

    const results = [];
    const deferredNodeIds = new Set();
    for (const resource of renderBlocking.renderBlockingRequests) {
      const nodeAndTiming = nodesAndTimingsByRequestId.get(resource.args.data.requestId);
      // TODO: beacon to Sentry, https://github.com/GoogleChrome/lighthouse/issues/7041
      if (!nodeAndTiming) continue;

      const {node, nodeTiming} = nodeAndTiming;

      const stackSpecificTiming = computeStackSpecificTiming(node, nodeTiming, artifacts.Stacks);

      // Mark this node and all its dependents as deferrable
      node.traverse(node => deferredNodeIds.add(node.id));

      // "wastedMs" is the download time of the network request, responseReceived - requestSent
      const wastedMs = Math.round(stackSpecificTiming.duration);
      if (wastedMs < MINIMUM_WASTED_MS) continue;

      results.push({
        url: resource.args.data.url,
        totalBytes: node.request.transferSize,
        wastedMs,
      });
    }

    if (!results.length) {
      return {results, fcpWastedMs: 0, lcpWastedMs: 0};
    }

    const fcpWastedMs = RenderBlockingResources.estimateSavingsWithGraphs(
      simulator,
      fcpSimulation.optimisticGraph,
      deferredNodeIds,
      wastedCssBytes,
      artifacts.Stacks
    );

    const lcpRecord = await LCPImageRecord.request(metricComputationData, context);

    // In most cases if the LCP is an image, render blocking resources don't affect LCP. For these cases we should reduce its impact.
    return {results, fcpWastedMs, lcpWastedMs: lcpRecord ? 0 : fcpWastedMs};
  }
@param {LH.Artifacts} artifacts
@param {LH.Audit.Context} context
@return {Promise<{fcpWastedMs: number, lcpWastedMs: number, results: Array<{url: string, totalBytes: number, wastedMs: number}>}>}