initFeatures(lhr) {
    this.json = lhr;
    this._fullPageScreenshot = Util.getFullPageScreenshot(lhr);

    if (this._topbar) {
      this._topbar.enable(lhr);
      this._topbar.resetUIState();
    }
    this._setupMediaQueryListeners();
    this._setupThirdPartyFilter();
    this._setupElementScreenshotOverlay(this._dom.rootEl);

    // Do not query the system preferences for DevTools - DevTools should only apply dark theme
    // if dark is selected in the settings panel.
    // TODO: set `disableDarkMode` in devtools and delete this special case.
    const disableDarkMode = this._dom.isDevTools() ||
      this._opts.disableDarkMode || this._opts.disableAutoDarkModeAndFireworks;
    if (!disableDarkMode && window.matchMedia('(prefers-color-scheme: dark)').matches) {
      toggleDarkTheme(this._dom, true);
    }

    // Fireworks!
    // To get fireworks you need 100 scores in all core categories.
    const fireworksRequiredCategoryIds = ['performance', 'accessibility', 'best-practices', 'seo'];
    const scoresAll100 = fireworksRequiredCategoryIds.every(id => {
      const cat = lhr.categories[id];
      return cat && cat.score === 1;
    });
    const disableFireworks =
      this._opts.disableFireworks || this._opts.disableAutoDarkModeAndFireworks;
    if (scoresAll100 && !disableFireworks) {
      this._enableFireworks();
      // If dark mode is allowed, force it on because it looks so much better.
      if (!disableDarkMode) toggleDarkTheme(this._dom, true);
    }

    // Show the metric descriptions by default when there is an error.
    const hasMetricError = lhr.categories.performance && lhr.categories.performance.auditRefs
      .some(audit => Boolean(audit.group === 'metrics' && lhr.audits[audit.id].errorMessage));
    if (hasMetricError) {
      const toggleInputEl = this._dom.find('input.lh-metrics-toggle__input', this._dom.rootEl);
      toggleInputEl.checked = true;
    }

    const showTreemapApp =
      this.json.audits['script-treemap-data'] && this.json.audits['script-treemap-data'].details;
    if (showTreemapApp) {
      this.addButton({
        text: Globals.strings.viewTreemapLabel,
        icon: 'treemap',
        onClick: () => openTreemap(this.json),
      });
    }

    if (this._opts.onViewTrace) {
      if (lhr.configSettings.throttlingMethod === 'simulate') {
        // eslint-disable-next-line max-len
        this._dom.find('a[data-action="view-unthrottled-trace"]', this._dom.rootEl).classList.remove('lh-hidden');
      } else {
        this.addButton({
          text: Globals.strings.viewTraceLabel,
          onClick: () => this._opts.onViewTrace?.(),
        });
      }
    }

    if (this._opts.getStandaloneReportHTML) {
      this._dom.find('a[data-action="save-html"]', this._dom.rootEl).classList.remove('lh-hidden');
    }

    // Fill in all i18n data.
    for (const node of this._dom.findAll('[data-i18n]', this._dom.rootEl)) {
      // These strings are guaranteed to (at least) have a default English string in UIStrings,
      // so this cannot be undefined as long as `report-ui-features.data-i18n` test passes.
      const i18nKey = node.getAttribute('data-i18n');
      const i18nAttr = /** @type {keyof typeof Globals.strings} */ (i18nKey);
      node.textContent = Globals.strings[i18nAttr];
    }
  }
Adds tools button, print, and other functionality to the report. The method
should be called whenever the report needs to be re-rendered.
@param {LH.Result} lhr