Bug 1507595 - Test fixes. r=flod,Gijs
authorJared Wein <jwein@mozilla.com>
Fri, 28 Dec 2018 19:40:33 +0000
changeset 512066 c68ba2c62949ea7bb210666a1e67466205a75603
parent 512065 32fe57d45d1be0278956b9d44c86805c809b65f3
child 512067 8bf9415e1de84e1d4050d3cf2d56d065e94adf5d
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersflod, Gijs
bugs1507595
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1507595 - Test fixes. r=flod,Gijs There are multiple fixes contained in this patch: - fixes for Android/mobile support - adding extra strings to the ftl file that were never defined before, and previously the ID was just printed to the page - update test_l10n.py to not rely on about:support anymore - changing Troubleshoot.jsm to return and ID and args for the strings that should be displayed, so it is compatible with the Fluent API - misc. fixes so strings that are not localized don't go through the Fluent codepath Differential Revision: https://phabricator.services.mozilla.com/D15437
browser/base/content/test/about/browser_aboutSupport.js
browser/base/content/test/static/browser_misused_characters_in_strings.js
mobile/android/chrome/jar.mn
mobile/android/locales/filter.py
mobile/android/locales/jar.mn
mobile/android/locales/l10n.toml
mobile/locales/filter.py
python/l10n/fluent_migrations/bug_1507595_aboutsupport.py
testing/firefox-ui/tests/puppeteer/test_l10n.py
toolkit/content/aboutSupport.js
toolkit/locales/en-US/chrome/global/aboutSupport.properties
toolkit/locales/en-US/toolkit/about/aboutSupport.ftl
toolkit/locales/jar.mn
toolkit/modules/Troubleshoot.jsm
toolkit/modules/tests/browser/browser_Troubleshoot.js
--- a/browser/base/content/test/about/browser_aboutSupport.js
+++ b/browser/base/content/test/about/browser_aboutSupport.js
@@ -5,23 +5,23 @@
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 add_task(async function() {
   await BrowserTestUtils.withNewTab({ gBrowser, url: "about:support" }, async function(browser) {
     let keyGoogleStatus = await ContentTask.spawn(browser, null, async function() {
       let textBox = content.document.getElementById("key-google-box");
-      await ContentTaskUtils.waitForCondition(() => document.l10n.getAttributes(textBox).id,
+      await ContentTaskUtils.waitForCondition(() => content.document.l10n.getAttributes(textBox).id,
         "Google API key status loaded");
-      return document.l10n.getAttributes(textBox).id;
+      return content.document.l10n.getAttributes(textBox).id;
     });
     ok(keyGoogleStatus, "Google API key status shown");
 
     let keyMozillaStatus = await ContentTask.spawn(browser, null, async function() {
       let textBox = content.document.getElementById("key-mozilla-box");
-      await ContentTaskUtils.waitForCondition(() => document.l10n.getAttributes(textBox).id,
+      await ContentTaskUtils.waitForCondition(() => content.document.l10n.getAttributes(textBox).id,
         "Mozilla API key status loaded");
-      return document.l10n.getAttributes(textBox).id;
+      return content.document.l10n.getAttributes(textBox).id;
     });
     ok(keyMozillaStatus, "Mozilla API key status shown");
   });
 });
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -140,24 +140,16 @@ let gWhitelist = [{
     file: "xbl.properties",
     key: "CommandNotInChrome",
     type: "double-quote",
   }, {
     file: "dom.properties",
     key: "PatternAttributeCompileFailure",
     type: "single-quote",
   }, {
-    file: "aboutSupport.dtd",
-    key: "aboutSupport.pageSubtitle",
-    type: "single-quote",
-  }, {
-    file: "aboutSupport.dtd",
-    key: "aboutSupport.userJSDescription",
-    type: "single-quote",
-  }, {
     file: "netError.dtd",
     key: "inadequateSecurityError.longDesc",
     type: "single-quote",
   }, {
     file: "netErrorApp.dtd",
     key: "securityOverride.warningContent",
     type: "single-quote",
   }, {
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -55,18 +55,16 @@ chrome.jar:
 % override chrome://global/locale/aboutReader.properties chrome://browser/locale/overrides/aboutReader.properties
 % override chrome://global/locale/aboutRights.dtd chrome://browser/locale/overrides/aboutRights.dtd
 % override chrome://global/locale/charsetMenu.properties chrome://browser/locale/overrides/charsetMenu.properties
 % override chrome://global/locale/commonDialogs.properties chrome://browser/locale/overrides/commonDialogs.properties
 % override chrome://global/locale/intl.properties chrome://browser/locale/overrides/intl.properties
 % override chrome://global/locale/intl.css chrome://browser/locale/overrides/intl.css
 % override chrome://global/locale/search/search.properties chrome://browser/locale/overrides/search/search.properties
 % override chrome://pluginproblem/locale/pluginproblem.dtd chrome://browser/locale/overrides/plugins/pluginproblem.dtd
-% override chrome://global/locale/aboutSupport.dtd chrome://browser/locale/overrides/global/aboutSupport.dtd
-% override chrome://global/locale/aboutSupport.properties chrome://browser/locale/overrides/global/aboutSupport.properties
 % override chrome://global/locale/mozilla.dtd chrome://browser/locale/overrides/global/mozilla.dtd
 % override chrome://global/locale/aboutTelemetry.dtd chrome://browser/locale/overrides/global/aboutTelemetry.dtd
 % override chrome://global/locale/aboutTelemetry.properties chrome://browser/locale/overrides/global/aboutTelemetry.properties
 % override chrome://global/locale/aboutWebrtc.properties chrome://browser/locale/overrides/global/aboutWebrtc.properties
 
 # overrides for dom l10n, also for en-US
 # keep this file list in sync with filter.py
 % override chrome://global/locale/global.dtd chrome://browser/locale/overrides/global.dtd
--- a/mobile/android/locales/filter.py
+++ b/mobile/android/locales/filter.py
@@ -21,33 +21,34 @@ def test(mod, path, entity=None):
             "chrome/global/aboutReader.properties",
             "chrome/global/aboutRights.dtd",
             "chrome/global/charsetMenu.properties",
             "chrome/global/commonDialogs.properties",
             "chrome/global/intl.properties",
             "chrome/global/intl.css",
             "chrome/search/search.properties",
             "chrome/pluginproblem/pluginproblem.dtd",
-            "chrome/global/aboutSupport.dtd",
-            "chrome/global/aboutSupport.properties",
             "chrome/global/mozilla.dtd",
             "chrome/global/aboutTelemetry.dtd",
             "chrome/global/aboutTelemetry.properties",
             "chrome/global/aboutWebrtc.properties",
         ):
             return "error"
         if re.match(r"crashreporter/[^/]*.ftl", path):
             # error on crashreporter/*.ftl
             return "error"
         if re.match(r"toolkit/about/[^/]*About.ftl", path):
             # error on toolkit/about/*About.ftl
             return "error"
         if re.match(r"toolkit/about/[^/]*Plugins.ftl", path):
             # error on toolkit/about/*Plugins.ftl
             return "error"
+        if re.match(r"toolkit/about/[^/]*Support.ftl", path):
+            # error on toolkit/about/*Support.ftl
+            return "error"
         return "ignore"
 
     if mod == "dom":
         # keep this file list in sync with jar.mn
         if path in (
             "chrome/global.dtd",
             "chrome/accessibility/AccessFu.properties",
             "chrome/dom/dom.properties",
--- a/mobile/android/locales/jar.mn
+++ b/mobile/android/locales/jar.mn
@@ -41,19 +41,16 @@ relativesrcdir toolkit/locales:
   locale/@AB_CD@/browser/overrides/aboutRights.dtd                 (%chrome/global/aboutRights.dtd)
   locale/@AB_CD@/browser/overrides/charsetMenu.properties          (%chrome/global/charsetMenu.properties)
   locale/@AB_CD@/browser/overrides/commonDialogs.properties        (%chrome/global/commonDialogs.properties)
   locale/@AB_CD@/browser/overrides/intl.properties                 (%chrome/global/intl.properties)
   locale/@AB_CD@/browser/overrides/intl.css                        (%chrome/global/intl.css)
   locale/@AB_CD@/browser/overrides/search/search.properties        (%chrome/search/search.properties)
 # plugins
   locale/@AB_CD@/browser/overrides/plugins/pluginproblem.dtd       (%chrome/pluginproblem/pluginproblem.dtd)
-# about:support
-  locale/@AB_CD@/browser/overrides/global/aboutSupport.dtd         (%chrome/global/aboutSupport.dtd)
-  locale/@AB_CD@/browser/overrides/global/aboutSupport.properties  (%chrome/global/aboutSupport.properties)
 #about:mozilla
   locale/@AB_CD@/browser/overrides/global/mozilla.dtd                (%chrome/global/mozilla.dtd)
 #about:telemetry
   locale/@AB_CD@/browser/overrides/global/aboutTelemetry.dtd         (%chrome/global/aboutTelemetry.dtd)
   locale/@AB_CD@/browser/overrides/global/aboutTelemetry.properties  (%chrome/global/aboutTelemetry.properties)
 #about:webrtc
   locale/@AB_CD@/browser/overrides/global/aboutWebrtc.properties  (%chrome/global/aboutWebrtc.properties)
 
@@ -75,8 +72,10 @@ relativesrcdir dom/locales:
 #if AB_CD != EN_US
 [localization] @AB_CD@.jar:
 relativesrcdir toolkit/locales:
 #about:crashes
   crashreporter                                    (%crashreporter/**/*.ftl)
 #endif
 #about:about
   toolkit                                          (%toolkit/about/*About.ftl)
+#about:support
+  toolkit                                          (%toolkit/about/*Support.ftl)
--- a/mobile/android/locales/l10n.toml
+++ b/mobile/android/locales/l10n.toml
@@ -205,16 +205,20 @@ exclude-multi-locale = [
     reference = "toolkit/locales/en-US/chrome/global/aboutSupport.dtd"
     l10n = "{l}toolkit/chrome/global/aboutSupport.dtd"
 
 [[paths]]
     reference = "toolkit/locales/en-US/chrome/global/aboutSupport.properties"
     l10n = "{l}toolkit/chrome/global/aboutSupport.properties"
 
 [[paths]]
+    reference = "toolkit/locales/en-US/toolkit/about/*Support.ftl"
+    l10n = "{l}toolkit/toolkit/about/*Support.ftl"
+
+[[paths]]
     reference = "toolkit/locales/en-US/crashreporter/*.ftl"
     l10n = "{l}toolkit/crashreporter/*.ftl"
 
 [[paths]]
     reference = "toolkit/locales/en-US/crashreporter/crashes.dtd"
     l10n = "{l}toolkit/crashreporter/crashes.dtd"
 
 [[paths]]
--- a/mobile/locales/filter.py
+++ b/mobile/locales/filter.py
@@ -21,34 +21,35 @@ def test(mod, path, entity=None):
             "chrome/global/aboutReader.properties",
             "chrome/global/aboutRights.dtd",
             "chrome/global/charsetMenu.properties",
             "chrome/global/commonDialogs.properties",
             "chrome/global/intl.properties",
             "chrome/global/intl.css",
             "chrome/search/search.properties",
             "chrome/pluginproblem/pluginproblem.dtd",
-            "chrome/global/aboutSupport.dtd",
-            "chrome/global/aboutSupport.properties",
             "chrome/global/mozilla.dtd",
             "chrome/global/aboutTelemetry.dtd",
             "chrome/global/aboutTelemetry.properties",
             "chrome/global/aboutWebrtc.properties",
         ):
             return "error"
         if re.match(r"crashreporter/[^/]*.ftl", path):
             # error on crashreporter/*.ftl
             return "error"
 
         if re.match(r"toolkit/about/[^/]*About.ftl", path):
             # error on toolkit/about/*About.ftl
             return "error"
         if re.match(r"toolkit/about/[^/]*Plugins.ftl", path):
             # error on toolkit/about/*Plugins.ftl
             return "error"
+        if re.match(r"toolkit/about/[^/]*Support.ftl", path):
+            # error on toolkit/about/*Support.ftl
+            return "error"
         return "ignore"
 
     if mod == "dom":
         # keep this file list in sync with jar.mn
         if path in (
             "chrome/global.dtd",
             "chrome/accessibility/AccessFu.properties",
             "chrome/dom/dom.properties",
--- a/python/l10n/fluent_migrations/bug_1507595_aboutsupport.py
+++ b/python/l10n/fluent_migrations/bug_1507595_aboutsupport.py
@@ -195,16 +195,20 @@ wheel-enabled = { COPY("toolkit/chrome/g
 touch-enabled = { COPY("toolkit/chrome/global/aboutSupport.properties", "touchEnabled")}
 drag-enabled = { COPY("toolkit/chrome/global/aboutSupport.properties", "dragEnabled")}
 keyboard-enabled = { COPY("toolkit/chrome/global/aboutSupport.properties", "keyboardEnabled")}
 autoscroll-enabled = { COPY("toolkit/chrome/global/aboutSupport.properties", "autoscrollEnabled")}
 policies-inactive = { COPY("toolkit/chrome/global/aboutSupport.properties", "policies.inactive")}
 policies-active = { COPY("toolkit/chrome/global/aboutSupport.properties", "policies.active")}
 policies-error = { COPY("toolkit/chrome/global/aboutSupport.properties", "policies.error")}
 multi-process-windows = { $remoteWindows }/{ $totalWindows }
+blocked-driver = { COPY("toolkit/chrome/global/aboutSupport.properties", "blockedDriver")}
+blocked-gfx-card = { COPY("toolkit/chrome/global/aboutSupport.properties", "blockedGfxCard")}
+blocked-os-version = { COPY("toolkit/chrome/global/aboutSupport.properties", "blockedOSVersion")}
+blocked-mismatched-version = { COPY("toolkit/chrome/global/aboutSupport.properties", "blockedMismatchedVersion")}
 """)
 )
 
     ctx.add_transforms(
         "toolkit/toolkit/about/aboutSupport.ftl",
         "toolkit/toolkit/about/aboutSupport.ftl",
         [
         FTL.Message(
@@ -417,10 +421,20 @@ multi-process-windows = { $remoteWindows
             value=REPLACE(
                 "toolkit/chrome/global/aboutSupport.properties",
                 "touchWarning",
                 {
                     "%S": VARIABLE_REFERENCE("preferenceKey"),
                 },
             )
         ),
+        FTL.Message(
+            id=FTL.Identifier("try-newer-driver"),
+            value=REPLACE(
+                "toolkit/chrome/global/aboutSupport.properties",
+                "tryNewerDriver",
+                {
+                    "%S": VARIABLE_REFERENCE("driverVersion"),
+                },
+            )
+        ),
    ]
 )
--- a/testing/firefox-ui/tests/puppeteer/test_l10n.py
+++ b/testing/firefox-ui/tests/puppeteer/test_l10n.py
@@ -24,22 +24,22 @@ class TestL10n(PuppeteerMixin, Marionett
         elm = self.marionette.find_element(By.ID, 'helpSafeMode')
         self.assertEqual(value, elm.get_attribute('label'))
 
         self.assertRaises(NoSuchElementException,
                           self.l10n.localize_entity, dtds, 'notExistent')
 
     def test_dtd_entity_content(self):
         dtds = ['chrome://branding/locale/brand.dtd',
-                'chrome://global/locale/aboutSupport.dtd']
+                'chrome://global/locale/aboutRights.dtd']
 
-        value = self.l10n.localize_entity(dtds, 'aboutSupport.pageTitle')
+        value = self.l10n.localize_entity(dtds, 'rights.title')
 
         self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
-        self.marionette.navigate('about:support')
+        self.marionette.navigate('about:rights')
 
         elm = self.marionette.find_element(By.TAG_NAME, 'h1')
         self.assertEqual(value, elm.text)
 
     def test_properties(self):
         properties = ['chrome://global/locale/filepicker.properties',
                       'chrome://global/locale/findbar.properties']
 
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -26,17 +26,21 @@ window.addEventListener("load", function
     setupEventListeners();
   } catch (e) {
     Cu.reportError("stack of load error for about:support: " + e + ": " + e.stack);
   }
 });
 
 // Fluent uses lisp-case IDs so this converts
 // the SentenceCase info IDs to lisp-case.
+const FLUENT_IDENT_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
 function toFluentID(str) {
+  if (!FLUENT_IDENT_REGEX.test(str)) {
+    return null;
+  }
   return str.toString().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
 }
 
 // Each property in this object corresponds to a property in Troubleshoot.jsm's
 // snapshot data.  Each function is passed its property's corresponding data,
 // and it's the function's job to update the page with it.
 var snapshotFormatters = {
 
@@ -101,19 +105,19 @@ var snapshotFormatters = {
         default:
           policiesStrId = "policies-error";
           aboutPolicies += "#errors";
           break;
       }
 
       if (data.policiesStatus != Services.policies.INACTIVE) {
         let activePolicies = $.new("a", null, null, {
-          "data-l10n-id": policiesStrId,
           href: aboutPolicies,
         });
+        document.l10n.setAttributes(activePolicies, policiesStrId);
         $("policies-status").appendChild(activePolicies);
       } else {
         document.l10n.setAttributes($("policies-status"), policiesStrId);
       }
     } else {
       $("policies-status-row").hidden = true;
     }
 
@@ -234,18 +238,24 @@ var snapshotFormatters = {
           $.new("td", String(value).substr(0, 120), "pref-value"),
         ]);
       }
     ));
   },
 
   async graphics(data) {
     function localizedMsg(msg) {
+      if (typeof msg == "object" && msg.key) {
+        return document.l10n.formatValue(msg.key, msg.args);
+      }
       let msgId = toFluentID(msg);
-      return document.l10n.formatValue(msgId);
+      if (msgId) {
+        return document.l10n.formatValue(msgId);
+      }
+      return "";
     }
 
     // Read APZ info out of data.info, stripping it out in the process.
     let apzInfo = [];
     let formatApzInfo = function(info) {
       let out = [];
       for (let type of ["Wheel", "Touch", "Drag", "Keyboard", "Autoscroll"]) {
         let key = "Apz" + type + "Input";
@@ -264,21 +274,28 @@ var snapshotFormatters = {
     // Create a <tr> element with key and value columns.
     //
     // @key      Text in the key column. Localized automatically, unless starts with "#".
     // @value    Text in the value column. Not localized.
     function buildRow(key, value) {
       let title = key[0] == "#" ? key.substr(1) : key;
       let keyStrId = toFluentID(key);
       let valueStrId = Array.isArray(value) ? null : toFluentID(value);
-      let td = $.new("td", value, null, {"data-l10n-id": valueStrId});
+      let td = $.new("td", value);
       td.style["white-space"] = "pre-wrap";
+      if (valueStrId) {
+        document.l10n.setAttributes(td, valueStrId);
+      }
 
+      let th = $.new("th", title, "column");
+      if (!key.startsWith("#")) {
+        document.l10n.setAttributes(th, keyStrId);
+      }
       return $.new("tr", [
-        $.new("th", title, "column", {"data-l10n-id": keyStrId}),
+        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);
@@ -317,31 +334,31 @@ var snapshotFormatters = {
 
         gpuProcessKillButton.addEventListener("click", function() {
           windowUtils.terminateGPUProcess();
         });
 
         document.l10n.setAttributes(gpuProcessKillButton, "gpu-process-kill-button");
       }
 
-      addRow("diagnostics", "GPUProcessPid", gpuProcessPid);
+      addRow("diagnostics", "gpu-process-pid", [new Text(gpuProcessPid)]);
       if (gpuProcessKillButton) {
-        addRow("diagnostics", "GPUProcess", [gpuProcessKillButton]);
+        addRow("diagnostics", "gpu-process", [gpuProcessKillButton]);
       }
     }
 
     if ((AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_DEV_EDITION) && AppConstants.platform != "macosx") {
       let gpuDeviceResetButton = $.new("button");
 
       gpuDeviceResetButton.addEventListener("click", function() {
         windowUtils.triggerDeviceReset();
       });
 
       document.l10n.setAttributes(gpuDeviceResetButton, "gpu-device-reset-button");
-      addRow("diagnostics", "Device Reset", [gpuDeviceResetButton]);
+      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.jsm) but we check anyway:
       if ("indices" in data && data.failures.length == data.indices.length) {
         let combined = [];
@@ -387,32 +404,32 @@ var snapshotFormatters = {
         value = await localizedMsg(data[messageKey]);
         delete data[messageKey];
       } else {
         value = data[key];
       }
       delete data[key];
 
       if (value) {
-        addRow(where, colKey, value);
+        addRow(where, colKey, [new Text(value)]);
       }
     }
 
     // graphics-features-tbody
     let compositor = "";
     if (data.windowLayerManagerRemote) {
       compositor = data.windowLayerManagerType;
       if (data.windowUsingAdvancedLayers) {
         compositor += " (Advanced Layers)";
       }
     } else {
       let noOMTCString = await document.l10n.formatValue("main-thread-no-omtc");
       compositor = "BasicLayers (" + noOMTCString + ")";
     }
-    addRow("features", "compositing", compositor);
+    addRow("features", "compositing", [new Text(compositor)]);
     delete data.windowLayerManagerRemote;
     delete data.windowLayerManagerType;
     delete data.numTotalWindows;
     delete data.numAcceleratedWindows;
     delete data.numAcceleratedWindowsMessage;
     delete data.windowUsingAdvancedLayers;
 
     addRow("features", "asyncPanZoom",
@@ -444,17 +461,17 @@ var snapshotFormatters = {
       }
       await addRowFromKey("features", feature);
     }
 
     if ("directWriteEnabled" in data) {
       let message = data.directWriteEnabled;
       if ("directWriteVersion" in data)
         message += " (" + data.directWriteVersion + ")";
-      await addRow("features", "#DirectWrite", message);
+      await addRow("features", "#DirectWrite", [new Text(message)]);
       delete data.directWriteEnabled;
       delete data.directWriteVersion;
     }
 
     // Adapter tbodies.
     let adapterKeys = [
       ["adapterDescription", "gpu-description"],
       ["adapterVendorID", "gpu-vendor-id"],
@@ -542,17 +559,17 @@ var snapshotFormatters = {
           } else {
             contents = entry.status + " by " + entry.type + ": " + entry.message;
           }
 
           trs.push($.new("tr", [
             $.new("td", contents),
           ]));
         }
-        addRow("decisions", feature.name, [$.new("table", trs)]);
+        addRow("decisions", "#" + feature.name, [$.new("table", trs)]);
       }
     } else {
       $("graphics-decisions-tbody").style.display = "none";
     }
 
     if (featureLog.fallbacks.length) {
       for (let fallback of featureLog.fallbacks) {
         addRow("workarounds", fallback.name, fallback.message);
@@ -588,17 +605,18 @@ var snapshotFormatters = {
       let value = data[key];
       addRow("diagnostics", key, value);
     }
   },
 
   media(data) {
     function insertBasicInfo(key, value) {
       function createRow(key, value) {
-        let th = $.new("th", null, "column", {"data-l10n-id": key});
+        let th = $.new("th", null, "column");
+        document.l10n.setAttributes(th, key);
         let td = $.new("td", value);
         td.style["white-space"] = "pre-wrap";
         td.colSpan = 8;
         return $.new("tr", [th, td]);
       }
       $.append($("media-info-tbody"), [createRow(key, value)]);
     }
 
@@ -754,18 +772,20 @@ var snapshotFormatters = {
           data[key] === data.hasUserNamespaces) {
         continue;
       }
       if (key === "syscallLog") {
         // Not in this table.
         continue;
       }
       let keyStrId = toFluentID(key);
+      let th = $.new("th", null, "column");
+      document.l10n.setAttributes(th, keyStrId);
       tbody.appendChild($.new("tr", [
-        $.new("th", null, "column", {"data-l10n-id": keyStrId}),
+        th,
         $.new("td", data[key]),
       ]));
     }
 
     if ("syscallLog" in data) {
       let syscallBody = $("sandbox-syscalls-tbody");
       let argsHead = $("sandbox-syscalls-argshead");
       for (let syscall of data.syscallLog) {
@@ -812,21 +832,24 @@ var $ = document.getElementById.bind(doc
 
 $.new = function $_new(tag, textContentOrChildren, className, attributes) {
   let elt = document.createElement(tag);
   if (className) {
     elt.className = className;
   }
   if (attributes) {
     if (attributes["data-l10n-id"]) {
+      let args = attributes.hasOwnProperty("data-l10n-args") ?
+        attributes["data-l10n-args"] :
+        undefined;
       document.l10n.setAttributes(elt,
                                   attributes["data-l10n-id"],
-                                  attributes["data-l10n-args"]);
+                                  args);
       delete attributes["data-l10n-id"];
-      if (attributes["data-l10n-args"]) {
+      if (args) {
         delete attributes["data-l10n-args"];
       }
     }
 
     for (let attrName in attributes) {
       elt.setAttribute(attrName, attributes[attrName]);
     }
   }
deleted file mode 100644
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# LOCALIZATION NOTE The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
-blockedDriver = Blocked for your graphics driver version.
-
-# LOCALIZATION NOTE The %S here is a placeholder, leave unchanged, it will get replaced by the driver version string.
-tryNewerDriver = Blocked for your graphics driver version. Try updating your graphics driver to version %S or newer.
-
-# LOCALIZATION NOTE The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
-blockedGfxCard = Blocked for your graphics card because of unresolved driver issues.
-
-# LOCALIZATION NOTE The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
-blockedOSVersion = Blocked for your operating system version.
-
-# LOCALIZATION NOTE The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
-blockedMismatchedVersion = Blocked for your graphics driver version mismatch between registry and DLL.
--- a/toolkit/locales/en-US/toolkit/about/aboutSupport.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutSupport.ftl
@@ -161,32 +161,45 @@ pending-reports =
     { $reports ->
         [one] All Crash Reports (including { $reports } pending crash in the given time range)
        *[other] All Crash Reports (including { $reports } pending crashes in the given time range)
     }
 
 raw-data-copied = Raw data copied to clipboard
 text-copied = Text copied to clipboard
 
+## The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
+blocked-driver = Blocked for your graphics driver version.
+blocked-gfx-card = Blocked for your graphics card because of unresolved driver issues.
+blocked-os-version = Blocked for your operating system version.
+blocked-mismatched-version = Blocked for your graphics driver version mismatch between registry and DLL.
+# Variables
+# $driverVersion - The graphics driver version string
+try-newer-driver = Blocked for your graphics driver version. Try updating your graphics driver to version { $driverVersion } or newer.
+
 # "ClearType" is a proper noun and should not be translated. Feel free to leave English strings if
 # there are no good translations, these are only used in about:support
 clear-type-parameters = ClearType Parameters
 
 compositing = Compositing
 hardware-h264 = Hardware H264 Decoding
 main-thread-no-omtc = main thread, no OMTC
 yes = Yes
 no = No
+unknown = Unknown
+virtual-monitor-disp = Virtual Monitor Display
 
 ## The following strings indicate if an API key has been found.
 ## In some development versions, it's expected for some API keys that they are
 ## not found.
 found = Found
 missing = Missing
 
+gpu-process-pid = GPUProcessPid
+gpu-process = GPUProcess
 gpu-description = Description
 gpu-vendor-id = Vendor ID
 gpu-device-id = Device ID
 gpu-subsys-id = Subsys ID
 gpu-drivers = Drivers
 gpu-ram = RAM
 gpu-driver-version = Driver Version
 gpu-driver-date = Driver Date
@@ -213,16 +226,17 @@ unknown-failure = Blocklisted; failure c
 
 d3d11layers-crash-guard = D3D11 Compositor
 d3d11video-crash-guard = D3D11 Video Decoder
 d3d9video-crash-buard = D3D9 Video Decoder
 glcontext-crash-guard = OpenGL
 
 reset-on-next-restart = Reset on Next Restart
 gpu-process-kill-button = Terminate GPU Process
+gpu-device-reset = Device Reset
 gpu-device-reset-button = Trigger Device Reset
 uses-tiling = Uses Tiling
 content-uses-tiling = Uses Tiling (Content)
 off-main-thread-paint-enabled = Off Main Thread Painting Enabled
 off-main-thread-paint-worker-count = Off Main Thread Painting Worker Count
 
 audio-backend = Audio Backend
 max-audio-channels = Max Channels
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -7,17 +7,16 @@
   crashreporter                                    (%crashreporter/**/*.ftl)
   toolkit                                          (%toolkit/**/*.ftl)
 
 @AB_CD@.jar:
 % locale global @AB_CD@ %locale/@AB_CD@/global/
   locale/@AB_CD@/global/aboutReader.properties          (%chrome/global/aboutReader.properties)
   locale/@AB_CD@/global/aboutRights.dtd                 (%chrome/global/aboutRights.dtd)
   locale/@AB_CD@/global/aboutStudies.properties         (%chrome/global/aboutStudies.properties)
-  locale/@AB_CD@/global/aboutSupport.properties         (%chrome/global/aboutSupport.properties)
   locale/@AB_CD@/global/aboutTelemetry.dtd              (%chrome/global/aboutTelemetry.dtd)
   locale/@AB_CD@/global/aboutTelemetry.properties       (%chrome/global/aboutTelemetry.properties)
   locale/@AB_CD@/global/aboutWebrtc.properties          (%chrome/global/aboutWebrtc.properties)
   locale/@AB_CD@/global/autocomplete.properties         (%chrome/global/autocomplete.properties)
   locale/@AB_CD@/global/appPicker.dtd                   (%chrome/global/appPicker.dtd)
   locale/@AB_CD@/global/browser.properties              (%chrome/global/browser.properties)
   locale/@AB_CD@/global/charsetMenu.dtd                 (%chrome/global/charsetMenu.dtd)
   locale/@AB_CD@/global/charsetMenu.properties          (%chrome/global/charsetMenu.properties)
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -304,43 +304,43 @@ var dataProviders = {
   },
 
   lockedPreferences: function lockedPreferences(done) {
     done(getPrefList(name => Services.prefs.prefIsLocked(name)));
   },
 
   graphics: function graphics(done) {
     function statusMsgForFeature(feature) {
-      // We return an array because in the tryNewerDriver case we need to
+      // We return an object because in the try-newer-driver case we need to
       // include the suggested version, which the consumer likely needs to plug
-      // into a format string from a localization file.  Rather than returning
-      // a string in some cases and an array in others, return an array always.
-      let msg = [""];
+      // into a format string from a localization file. Rather than returning
+      // a string in some cases and an object in others, return an object always.
+      let msg = {key: ""};
       try {
         var status = gfxInfo.getFeatureStatus(feature);
       } catch (e) {}
       switch (status) {
       case Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE:
       case Ci.nsIGfxInfo.FEATURE_DISCOURAGED:
-        msg = ["blockedGfxCard"];
+        msg = {key: "blocked-gfx-card"};
         break;
       case Ci.nsIGfxInfo.FEATURE_BLOCKED_OS_VERSION:
-        msg = ["blockedOSVersion"];
+        msg = {key: "blocked-os-version"};
         break;
       case Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION:
         try {
-          var suggestedDriverVersion =
+          var driverVersion =
             gfxInfo.getFeatureSuggestedDriverVersion(feature);
         } catch (e) {}
-        msg = suggestedDriverVersion ?
-              ["tryNewerDriver", suggestedDriverVersion] :
-              ["blockedDriver"];
+        msg = driverVersion ?
+              {key: "try-newer-driver", args: {driverVersion}} :
+              {key: "blocked-driver"};
         break;
       case Ci.nsIGfxInfo.FEATURE_BLOCKED_MISMATCHED_VERSION:
-        msg = ["blockedMismatchedVersion"];
+        msg = {key: "blocked-mismatched-version"};
         break;
       }
       return msg;
     }
 
     let data = {};
 
     try {
--- a/toolkit/modules/tests/browser/browser_Troubleshoot.js
+++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js
@@ -278,17 +278,27 @@ const SNAPSHOT_SCHEMA = {
         },
         windowLayerManagerRemote: {
           type: "boolean",
         },
         windowUsingAdvancedLayers: {
           type: "boolean",
         },
         numAcceleratedWindowsMessage: {
-          type: "array",
+          type: "object",
+          properties: {
+            key: {
+              required: true,
+              type: "string",
+            },
+            args: {
+              required: false,
+              type: "object",
+            },
+          },
         },
         adapterDescription: {
           type: "string",
         },
         adapterVendorID: {
           type: "string",
         },
         adapterDeviceID: {
@@ -389,35 +399,52 @@ const SNAPSHOT_SCHEMA = {
         },
         webgl2WSIInfo: {
           type: "string",
         },
         info: {
           type: "object",
         },
         failures: {
-          type: "array",
-          items: {
-            type: "string",
+          type: "object",
+          properties: {
+            key: {
+              required: true,
+              type: "string",
+            },
+            args: {
+              required: false,
+              type: "object",
+            },
           },
         },
         indices: {
           type: "array",
           items: {
             type: "number",
           },
         },
         featureLog: {
           type: "object",
         },
         crashGuards: {
           type: "array",
         },
         direct2DEnabledMessage: {
-          type: "array",
+          type: "object",
+          properties: {
+            key: {
+              required: true,
+              type: "string",
+            },
+            args: {
+              required: false,
+              type: "object",
+            },
+          },
         },
       },
     },
     media: {
       required: true,
       type: "object",
       properties: {
         currentAudioBackend: {