async graphics()

in mail/components/about-support/content/aboutSupport.js [485:892]


  async graphics(data) {
    function localizedMsg(msg) {
      if (typeof msg == "object" && msg.key) {
        return document.l10n.formatValue(msg.key, msg.args);
      }
      const msgId = toFluentID(msg);
      if (msgId) {
        return document.l10n.formatValue(msgId);
      }
      return "";
    }

    // Read APZ info out of data.info, stripping it out in the process.
    let apzInfo = [];
    const formatApzInfo = function (info) {
      const out = [];
      for (const type of [
        "Wheel",
        "Touch",
        "Drag",
        "Keyboard",
        "Autoscroll",
        "Zooming",
      ]) {
        const key = "Apz" + type + "Input";

        if (!(key in info)) {
          continue;
        }

        delete info[key];

        out.push(toFluentID(type.toLowerCase() + "Enabled"));
      }

      return out;
    };

    // Create a <tr> element with key and value columns.
    //
    // @key      Text in the key column. Localized automatically, unless starts with "#".
    // @value    Fluent ID for text in the value column, or array of children.
    function buildRow(key, value) {
      const title = key[0] == "#" ? key.substr(1) : key;
      const keyStrId = toFluentID(key);
      const valueStrId = Array.isArray(value) ? null : toFluentID(value);
      const td = $.new("td", value);
      td.style["white-space"] = "pre-wrap";
      if (valueStrId) {
        document.l10n.setAttributes(td, valueStrId);
      }

      const th = $.new("th", title, "column");
      if (!key.startsWith("#")) {
        document.l10n.setAttributes(th, keyStrId);
      }
      return $.new("tr", [th, td]);
    }

    // @where    The name in "graphics-<name>-tbody", of the element to append to.
    // @trs      Array of row elements.
    function addRows(where, trs) {
      $.append($("graphics-" + where + "-tbody"), trs);
    }

    // Build and append a row.
    //
    // @where    The name in "graphics-<name>-tbody", of the element to append to.
    function addRow(where, key, value) {
      addRows(where, [buildRow(key, value)]);
    }
    if ("info" in data) {
      apzInfo = formatApzInfo(data.info);

      const trs = sortedArrayFromObject(data.info).map(function ([prop, val]) {
        const td = $.new("td", String(val));
        td.style["word-break"] = "break-all";
        return $.new("tr", [$.new("th", prop, "column"), td]);
      });
      addRows("diagnostics", trs);

      delete data.info;
    }

    const windowUtils = window.windowUtils;
    const gpuProcessPid = windowUtils.gpuProcessPid;

    if (gpuProcessPid != -1) {
      let gpuProcessKillButton = null;
      if (AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_DEV_EDITION) {
        gpuProcessKillButton = $.new("button");

        gpuProcessKillButton.addEventListener("click", function () {
          windowUtils.terminateGPUProcess();
        });

        document.l10n.setAttributes(
          gpuProcessKillButton,
          "gpu-process-kill-button"
        );
      }

      addRow("diagnostics", "gpu-process-pid", [new Text(gpuProcessPid)]);
      if (gpuProcessKillButton) {
        addRow("diagnostics", "gpu-process", [gpuProcessKillButton]);
      }
    }

    if (
      (AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_DEV_EDITION) &&
      AppConstants.platform != "macosx"
    ) {
      const gpuDeviceResetButton = $.new("button");

      gpuDeviceResetButton.addEventListener("click", function () {
        windowUtils.triggerDeviceReset();
      });

      document.l10n.setAttributes(
        gpuDeviceResetButton,
        "gpu-device-reset-button"
      );
      addRow("diagnostics", "gpu-device-reset", [gpuDeviceResetButton]);
    }

    // graphics-failures-tbody tbody
    if ("failures" in data) {
      // If indices is there, it should be the same length as failures,
      // (see Troubleshoot.sys.mjs) but we check anyway:
      if ("indices" in data && data.failures.length == data.indices.length) {
        const combined = [];
        for (let i = 0; i < data.failures.length; i++) {
          const assembled = assembleFromGraphicsFailure(i, data);
          combined.push(assembled);
        }
        combined.sort(function (a, b) {
          if (a.index < b.index) {
            return -1;
          }
          if (a.index > b.index) {
            return 1;
          }
          return 0;
        });
        $.append(
          $("graphics-failures-tbody"),
          combined.map(function (val) {
            return $.new("tr", [
              $.new("th", val.header, "column"),
              $.new("td", val.message),
            ]);
          })
        );
        delete data.indices;
      } else {
        $.append($("graphics-failures-tbody"), [
          $.new("tr", [
            $.new("th", "LogFailure", "column"),
            $.new(
              "td",
              data.failures.map(function (val) {
                return $.new("p", val);
              })
            ),
          ]),
        ]);
      }
      delete data.failures;
    } else {
      $("graphics-failures-tbody").style.display = "none";
    }

    // Add a new row to the table, and take the key (or keys) out of data.
    //
    // @where        Table section to add to.
    // @key          Data key to use.
    // @colKey       The localization key to use, if different from key.
    async function addRowFromKey(where, key, colKey) {
      if (!(key in data)) {
        return;
      }
      colKey = colKey || key;

      let value;
      const messageKey = key + "Message";
      if (messageKey in data) {
        value = await localizedMsg(data[messageKey]);
        delete data[messageKey];
      } else {
        value = data[key];
      }
      delete data[key];

      if (value) {
        addRow(where, colKey, [new Text(value)]);
      }
    }

    // graphics-features-tbody
    let compositor = "";
    if (data.windowLayerManagerRemote) {
      compositor = data.windowLayerManagerType;
    } else {
      const noOMTCString = await document.l10n.formatValue(
        "main-thread-no-omtc"
      );
      compositor = "BasicLayers (" + noOMTCString + ")";
    }
    addRow("features", "compositing", [new Text(compositor)]);
    addRow("features", "supportFontDetermination", [
      new Text(data.supportFontDetermination),
    ]);
    delete data.windowLayerManagerRemote;
    delete data.windowLayerManagerType;
    delete data.numTotalWindows;
    delete data.numAcceleratedWindows;
    delete data.numAcceleratedWindowsMessage;

    addRow(
      "features",
      "asyncPanZoom",
      apzInfo.length
        ? [
            new Text(
              (
                await document.l10n.formatValues(
                  apzInfo.map(id => {
                    return { id };
                  })
                )
              ).join("; ")
            ),
          ]
        : "apz-none"
    );
    const featureKeys = [
      "webgl1WSIInfo",
      "webgl1Renderer",
      "webgl1Version",
      "webgl1DriverExtensions",
      "webgl1Extensions",
      "webgl2WSIInfo",
      "webgl2Renderer",
      "webgl2Version",
      "webgl2DriverExtensions",
      "webgl2Extensions",
      ["supportsHardwareH264", "hardware-h264"],
      ["direct2DEnabled", "#Direct2D"],
      ["windowProtocol", "graphics-window-protocol"],
      ["desktopEnvironment", "graphics-desktop-environment"],
      "usesTiling",
      "targetFrameRate",
    ];
    for (const feature of featureKeys) {
      if (Array.isArray(feature)) {
        await addRowFromKey("features", feature[0], feature[1]);
        continue;
      }
      await addRowFromKey("features", feature);
    }

    if ("directWriteEnabled" in data) {
      let message = data.directWriteEnabled;
      if ("directWriteVersion" in data) {
        message += " (" + data.directWriteVersion + ")";
      }
      await addRow("features", "#DirectWrite", [new Text(message)]);
      delete data.directWriteEnabled;
      delete data.directWriteVersion;
    }

    // Adapter tbodies.
    const adapterKeys = [
      ["adapterDescription", "gpu-description"],
      ["adapterVendorID", "gpu-vendor-id"],
      ["adapterDeviceID", "gpu-device-id"],
      ["driverVendor", "gpu-driver-vendor"],
      ["driverVersion", "gpu-driver-version"],
      ["driverDate", "gpu-driver-date"],
      ["adapterDrivers", "gpu-drivers"],
      ["adapterSubsysID", "gpu-subsys-id"],
      ["adapterRAM", "gpu-ram"],
    ];

    function showGpu(id, suffix) {
      function get(prop) {
        return data[prop + suffix];
      }

      const trs = [];
      for (const [prop, key] of adapterKeys) {
        const value = get(prop);
        if (value === undefined || value === "") {
          continue;
        }
        trs.push(buildRow(key, [new Text(value)]));
      }

      if (trs.length == 0) {
        $("graphics-" + id + "-tbody").style.display = "none";
        return;
      }

      let active = "yes";
      if ("isGPU2Active" in data && (suffix == "2") != data.isGPU2Active) {
        active = "no";
      }

      addRow(id, "gpu-active", active);
      addRows(id, trs);
    }
    showGpu("gpu-1", "");
    showGpu("gpu-2", "2");

    // Remove adapter keys.
    for (const [prop /* key */] of adapterKeys) {
      delete data[prop];
      delete data[prop + "2"];
    }
    delete data.isGPU2Active;

    const featureLog = data.featureLog;
    delete data.featureLog;

    if (featureLog.features.length) {
      for (const feature of featureLog.features) {
        const trs = [];
        for (const entry of feature.log) {
          let contents;
          if (!entry.hasOwnProperty("message")) {
            // This is a default entry.
            contents = entry.status + " by " + entry.type;
          } else if (entry.message.length && entry.message[0] == "#") {
            // This is a failure ID. See nsIGfxInfo.idl.
            const m = /#BLOCKLIST_FEATURE_FAILURE_BUG_(\d+)/.exec(
              entry.message
            );
            if (m) {
              const bugSpan = $.new("span");

              const bugHref = $.new("a");
              bugHref.href =
                "https://bugzilla.mozilla.org/show_bug.cgi?id=" + m[1];
              bugHref.setAttribute("data-l10n-name", "bug-link");
              bugSpan.append(bugHref);
              document.l10n.setAttributes(bugSpan, "support-blocklisted-bug", {
                bugNumber: m[1],
              });

              contents = [bugSpan];
            } else {
              const unknownFailure = $.new("span");
              document.l10n.setAttributes(unknownFailure, "unknown-failure", {
                failureCode: entry.message.substr(1),
              });
              contents = [unknownFailure];
            }
          } else {
            contents =
              entry.status + " by " + entry.type + ": " + entry.message;
          }

          trs.push($.new("tr", [$.new("td", contents)]));
        }
        addRow("decisions", "#" + feature.name, [$.new("table", trs)]);
      }
    } else {
      $("graphics-decisions-tbody").style.display = "none";
    }

    if (featureLog.fallbacks.length) {
      for (const fallback of featureLog.fallbacks) {
        addRow("workarounds", "#" + fallback.name, [
          new Text(fallback.message),
        ]);
      }
    } else {
      $("graphics-workarounds-tbody").style.display = "none";
    }

    const crashGuards = data.crashGuards;
    delete data.crashGuards;

    if (crashGuards.length) {
      for (const guard of crashGuards) {
        const resetButton = $.new("button");
        const onClickReset = function () {
          Services.prefs.setIntPref(guard.prefName, 0);
          resetButton.removeEventListener("click", onClickReset);
          resetButton.disabled = true;
        };

        document.l10n.setAttributes(resetButton, "reset-on-next-restart");
        resetButton.addEventListener("click", onClickReset);

        addRow("crashguards", guard.type + "CrashGuard", [resetButton]);
      }
    } else {
      $("graphics-crashguards-tbody").style.display = "none";
    }

    // Now that we're done, grab any remaining keys in data and drop them into
    // the diagnostics section.
    for (const key in data) {
      const value = data[key];
      addRow("diagnostics", key, [new Text(value)]);
    }
  },