Bug 1241024 - Make about:performance localizable, r=gandalf,flod.
authorFlorian Quèze <florian@queze.net>
Thu, 18 Oct 2018 18:35:44 +0200
changeset 500428 9a6b1ba0f53b8a8ab5aaa895c2de6c596ef4364a
parent 500427 2d3d9d6f968fa8ebf85064c30e035455ea119aa3
child 500429 fc8ae615f7268108e364437b261bccc2ba65c1bb
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgandalf, flod
bugs1241024
milestone64.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 1241024 - Make about:performance localizable, r=gandalf,flod.
toolkit/components/aboutperformance/content/aboutPerformance.js
toolkit/components/aboutperformance/content/aboutPerformance.xhtml
toolkit/locales/en-US/toolkit/about/aboutPerformance.ftl
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -618,30 +618,31 @@ var State = {
       let name = `${host} (${id})`;
       let image = "chrome://mozapps/skin/places/defaultFavicon.svg";
       let found = tabFinder.get(parseInt(id));
       if (found) {
         if (found.tabbrowser) {
           name = found.tab.getAttribute("label");
           image = found.tab.getAttribute("image");
         } else {
-          name = "Preloaded: " + found.tab.linkedBrowser.contentTitle;
+          name = {id: "preloaded-tab",
+                  title: found.tab.linkedBrowser.contentTitle};
           type = "other";
         }
       } else if (id == 1) {
         name = BRAND_NAME;
         image = "chrome://branding/content/icon32.png";
         type = "browser";
       } else if (/^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(host)) {
         let addon = WebExtensionPolicy.getByHostname(host);
         name = `${addon.name} (${addon.id})`;
         image = "chrome://mozapps/skin/extensions/extensionGeneric-16.svg";
         type = "addon";
       } else if (id == 0 && !tab.isWorker) {
-        name = "Ghost windows";
+        name = {id: "ghost-windows"};
         type = "other";
       }
 
       // Create a map of all the child items from the previous time we read the
       // counters, indexed by counterId so that we can quickly find the previous
       // value for any subitem.
       let prevChildren = new Map();
       if (prev) {
@@ -1000,53 +1001,70 @@ var View = {
       tbody.firstChild.remove();
     tbody.appendChild(this._fragment);
     this._fragment = document.createDocumentFragment();
   },
   insertAfterRow(row) {
     row.parentNode.insertBefore(this._fragment, row.nextSibling);
     this._fragment = document.createDocumentFragment();
   },
-  appendRow(name, value, tooltip, type, image = "") {
+  appendRow(name, energyImpact, tooltip, type, image = "") {
     let row = document.createElement("tr");
 
     let elt = document.createElement("td");
-    elt.textContent = name;
+    if (typeof name == "string") {
+      elt.textContent = name;
+    } else {
+      if (name.title)
+        document.l10n.setAttributes(elt, name.id, {title: name.title});
+      else
+        document.l10n.setAttributes(elt, name.id);
+    }
     if (image)
       elt.style.backgroundImage = `url('${image}')`;
 
     if (["subframe", "tracker", "worker"].includes(type))
       elt.classList.add("indent");
     else
       elt.classList.add("root");
     if (["tracker", "worker"].includes(type))
       elt.classList.add(type);
     row.appendChild(elt);
 
     elt = document.createElement("td");
-    elt.textContent = type;
+    document.l10n.setAttributes(elt, "type-" + type);
     row.appendChild(elt);
 
     elt = document.createElement("td");
-    elt.textContent = value;
+    if (!energyImpact)
+      elt.textContent = "–";
+    else {
+      let impact = "high";
+      if (energyImpact < 1)
+        impact = "low";
+      else if (energyImpact < 25)
+        impact = "medium";
+      document.l10n.setAttributes(elt, "energy-impact-" + impact,
+                                  {value: energyImpact});
+    }
     row.appendChild(elt);
 
     if (tooltip)
-      row.title = tooltip;
+      document.l10n.setAttributes(row, "item", tooltip);
 
     elt = document.createElement("td");
     if (type == "tab") {
       let img = document.createElement("img");
       img.className = "action-icon close-icon";
-      img.title = "Close tab";
+      document.l10n.setAttributes(img, "close-tab");
       elt.appendChild(img);
     } else if (type == "addon") {
       let img = document.createElement("img");
       img.className = "action-icon addon-icon";
-      img.title = "Show in add-on manager";
+      document.l10n.setAttributes(img, "show-addon");
       elt.appendChild(img);
     }
     row.appendChild(elt);
 
     this._fragment.appendChild(row);
     return row;
   },
 };
@@ -1158,37 +1176,52 @@ var Control = {
       let openItems = this._openItems;
       this._openItems = new Set();
 
       let counters = this._sortCounters(State.getCounters());
       for (let {id, name, image, type, totalDispatches, dispatchesSincePrevious,
                 totalDuration, durationSincePrevious, children} of counters) {
         let row =
           View.appendRow(name,
-                         this._formatEnergyImpact(dispatchesSincePrevious, durationSincePrevious),
-                         this._formatTooltip(totalDispatches, totalDuration,
-                                             dispatchesSincePrevious, durationSincePrevious),
+                         this._computeEnergyImpact(dispatchesSincePrevious,
+                                                   durationSincePrevious),
+                         {totalDispatches, totalDuration: Math.ceil(totalDuration / 1000),
+                          dispatchesSincePrevious,
+                          durationSincePrevious: Math.ceil(durationSincePrevious / 1000)},
                          type, image);
         row.windowId = id;
         if (id == selectedId) {
           row.setAttribute("selected", "true");
           this.selectedRow = row;
         }
 
         if (!children.length)
           continue;
 
+        // Show the twisty image.
         let elt = row.firstChild;
         let img = document.createElement("img");
         img.className = "twisty";
         let open = openItems.has(id);
         if (open) {
           img.classList.add("open");
           this._openItems.add(id);
         }
+
+        // If there's an l10n id on our <td> node, any image we add will be
+        // removed during localization, so move the l10n id to a <span>
+        let l10nAttrs = document.l10n.getAttributes(elt);
+        if (l10nAttrs.id) {
+          let span = document.createElement("span");
+          document.l10n.setAttributes(span, l10nAttrs.id, l10nAttrs.args);
+          elt.removeAttribute("data-l10n-id");
+          elt.removeAttribute("data-l10n-args");
+          elt.insertBefore(span, elt.firstChild);
+        }
+
         elt.insertBefore(img, elt.firstChild);
 
         row._children = children;
         if (open)
           this._showChildren(row);
       }
 
       View.commit();
@@ -1205,77 +1238,59 @@ var Control = {
     for (let row of children) {
       let host = row.host.replace(/^blob:https?:\/\//, "");
       let type = "subframe";
       if (State.isTracker(host))
         type = "tracker";
       if (row.isWorker)
         type = "worker";
       View.appendRow(row.host,
-                     this._formatEnergyImpact(row.dispatchesSincePrevious,
-                                              row.durationSincePrevious),
-                     this._formatTooltip(row.dispatchCount, row.duration,
-                                         row.dispatchesSincePrevious,
-                                         row.durationSincePrevious),
+                     this._computeEnergyImpact(row.dispatchesSincePrevious,
+                                               row.durationSincePrevious),
+                     {totalDispatches: row.dispatchCount,
+                      totalDuration: Math.ceil(row.duration / 1000),
+                      dispatchesSincePrevious: row.dispatchesSincePrevious,
+                      durationSincePrevious: Math.ceil(row.durationSincePrevious / 1000)},
                      type);
     }
   },
   _computeEnergyImpact(dispatches, duration) {
     // 'Dispatches' doesn't make sense to users, and it's difficult to present
     // two numbers in a meaningful way, so we need to somehow aggregate the
     // dispatches and duration values we have.
     // The current formula to aggregate the numbers assumes that the cost of
     // a dispatch is equivalent to 1ms of CPU time.
     // Dividing the result by the sampling interval and by 10 gives a number that
     // looks like a familiar percentage to users, as fullying using one core will
     // result in a number close to 100.
-    return Math.max(duration || 0, dispatches * 1000) / UPDATE_INTERVAL_MS / 10;
-  },
-  _formatTooltip(totalDispatches, totalDuration,
-                 dispatchesSincePrevious, durationSincePrevious) {
-    function dispatchesAndDuration(dispatches, duration) {
-      let result = dispatches;
-      if (duration) {
-        duration /= 1000;
-        duration = Math.round(duration);
-        if (duration)
-          result += duration >= 1000 ? ` (${duration / 1000}s)` : ` (${duration}ms)`;
-        else
-          result += " (< 1ms)";
-      }
-      return result;
-    }
-
-    return `${dispatchesAndDuration(totalDispatches, totalDuration)} dispatches since load\n` +
-      `${dispatchesAndDuration(dispatchesSincePrevious, durationSincePrevious)} in the last seconds`;
-  },
-  _formatEnergyImpact(dispatches, duration) {
-    let energyImpact = this._computeEnergyImpact(dispatches, duration);
-    if (!energyImpact)
-      return "None";
-    energyImpact = Math.ceil(energyImpact * 100) / 100;
-    if (energyImpact < 1)
-      return `Low (${energyImpact})`;
-    if (energyImpact < 25)
-      return `Medium (${energyImpact})`;
-    return `High (${energyImpact})`;
+    let energyImpact =
+      Math.max(duration || 0, dispatches * 1000) / UPDATE_INTERVAL_MS / 10;
+    // Keep only 2 digits after the decimal point.
+    return Math.ceil(energyImpact * 100) / 100;
   },
   _sortCounters(counters) {
     return counters.sort((a, b) => {
+      // Force 'Recently Closed Tabs' to be always at the bottom, because it'll
+      // never be actionable.
+      if (a.name.id && a.name.id == "ghost-windows")
+        return 1;
+
       // Note: _computeEnergyImpact uses UPDATE_INTERVAL_MS which doesn't match
       // the time between the most recent sample and the start of the buffer,
       // BUFFER_DURATION_MS would be better, but the values is never displayed
       // so this is OK.
       let aEI = this._computeEnergyImpact(a.dispatchesSinceStartOfBuffer,
                                           a.durationSinceStartOfBuffer);
       let bEI = this._computeEnergyImpact(b.dispatchesSinceStartOfBuffer,
                                           b.durationSinceStartOfBuffer);
       if (aEI != bEI)
         return bEI - aEI;
-      return a.name.localeCompare(b.name);
+
+      // a.name is sometimes an object, so we can't use a.name.localeCompare.
+      return String.prototype.localeCompare.call(a.name, b.name);
     });
   },
   _setOptions(options) {
     dump(`about:performance _setOptions ${JSON.stringify(options)}\n`);
     let eltRefresh = document.getElementById("check-autorefresh");
     if ((options.autoRefresh > 0) != eltRefresh.checked) {
       eltRefresh.click();
     }
--- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
@@ -1,21 +1,22 @@
 <?xml version="1.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/. -->
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
-    <title>about:performance</title>
+    <title data-l10n-id="about-performance-title"/>
     <link rel="icon" type="image/png" id="favicon"
           href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css"
           type="text/css"/>
+    <link rel="localization" href="toolkit/about/aboutPerformance.ftl"/>
     <script type="text/javascript" src="chrome://global/content/aboutPerformance.js"></script>
     <style>
       @import url("chrome://global/skin/in-content/common.css");
 
       html {
         --aboutSupport-table-background: #ebebeb;
         background-color: var(--in-content-page-background);
       }
@@ -277,19 +278,19 @@
       <h2>Performance of Web pages</h2>
       <div id="webpages" class="measuring">
       </div>
     </div>
     <div>
       <table id="dispatch-table">
         <thead id="dispatch-thead">
           <tr>
-            <td>Name</td>
-            <td>Type</td>
-            <td>Energy Impact</td>
+            <td data-l10n-id="column-name"/>
+            <td data-l10n-id="column-type"/>
+            <td data-l10n-id="column-energy-impact"/>
             <td></td><!-- actions -->
           </tr>
         </thead>
         <tbody id="dispatch-tbody"></tbody>
       </table>
     </div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/toolkit/about/aboutPerformance.ftl
@@ -0,0 +1,52 @@
+# 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/.
+
+# Page title
+about-performance-title = Task Manager
+
+## Column headers
+column-name = Name
+column-type = Type
+column-energy-impact = Energy Impact
+
+## Special values for the Name column
+ghost-windows = Recently closed tabs
+# Variables:
+#   $title (String) - the title of the preloaded page, typically 'New Tab'
+preloaded-tab = Preloaded: { $title }
+
+## Values for the Type column
+type-tab = Tab
+type-subframe = Subframe
+type-tracker = Tracker
+type-addon = Add-on
+type-browser = Browser
+type-worker = Worker
+type-other = Other
+
+## Values for the Energy Impact column
+##
+## Variables:
+##   $value (Number) - Value of the energy impact, eg. 0.25 (low),
+##                     5.38 (medium), 105.38 (high)
+energy-impact-high = High ({ $value })
+energy-impact-medium = Medium ({ $value })
+energy-impact-low = Low ({ $value })
+
+## Tooltips for the action buttons
+close-tab =
+    .title = Close tab
+show-addon =
+    .title = Show in Add-ons Manager
+
+# Tooltip when hovering an item of the about:performance table
+# Variables:
+#   $totalDispatches (Number) - how many dispatches occured for this page since it loaded
+#   $totalDuration (Number) - how much CPU time was used by this page since it loaded
+#   $dispatchesSincePrevious (Number) - how many dispatches occured in the last 2 seconds
+#   $durationSincePrevious (Number) - how much CPU time was used in the last 2 seconds
+item =
+    .title =
+        Dispatches since load: { $totalDispatches } ({ $totalDuration }ms)
+        Dispatches in the last seconds: { $dispatchesSincePrevious } ({ $durationSincePrevious }ms)