Bug 1498185 - Implement the type and action columns in the new about:performance, r=felipe.
authorFlorian Quèze <florian@queze.net>
Wed, 17 Oct 2018 14:12:16 +0200
changeset 500426 8b06ecb59a60ef9f7920fa6e9d307c6592f6a477
parent 500425 edaa4f0b9da959b7a0158e35fbb972eb24cbe708
child 500427 2d3d9d6f968fa8ebf85064c30e035455ea119aa3
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)
reviewersfelipe
bugs1498185
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 1498185 - Implement the type and action columns in the new about:performance, r=felipe.
toolkit/components/aboutperformance/content/aboutPerformance.js
toolkit/components/aboutperformance/content/aboutPerformance.xhtml
toolkit/themes/shared/icons/shortcut.svg
toolkit/themes/shared/jar.inc.mn
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -609,35 +609,40 @@ var State = {
         if (id in this._buffer[index].tabs) {
           oldest = this._buffer[index].tabs[id];
           break;
         }
       }
       let prev = previous[id];
       let host = tab.host;
 
+      let type = "tab";
       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;
+          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";
+        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) {
         for (let child of prev.children) {
@@ -686,17 +691,17 @@ var State = {
           dispatches - prev.dispatchCount - (prev.dispatchesFromFormerChildren || 0);
       }
       if (oldest) {
         dispatchesSinceStartOfBuffer =
           dispatches - oldest.dispatchCount - (oldest.dispatchesFromFormerChildren || 0);
         durationSinceStartOfBuffer =
           duration - oldest.duration - (oldest.durationFromFormerChildren || 0);
       }
-      return ({id, name, image,
+      return ({id, name, image, type,
                totalDispatches: dispatches, totalDuration: duration,
                durationSincePrevious, dispatchesSincePrevious,
                durationSinceStartOfBuffer, dispatchesSinceStartOfBuffer,
                children});
     });
   },
 };
 
@@ -995,36 +1000,57 @@ 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, classes, image = "") {
+  appendRow(name, value, tooltip, type, image = "") {
     let row = document.createElement("tr");
 
     let elt = document.createElement("td");
     elt.textContent = name;
     if (image)
       elt.style.backgroundImage = `url('${image}')`;
-    if (classes)
-      elt.classList.add(...classes);
-    if (!classes || !classes.includes("indent"))
+
+    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;
     row.appendChild(elt);
 
     elt = document.createElement("td");
     elt.textContent = value;
     row.appendChild(elt);
 
     if (tooltip)
       row.title = tooltip;
 
+    elt = document.createElement("td");
+    if (type == "tab") {
+      let img = document.createElement("img");
+      img.className = "action-icon close-icon";
+      img.title = "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";
+      elt.appendChild(img);
+    }
+    row.appendChild(elt);
+
     this._fragment.appendChild(row);
     return row;
   },
 };
 
 var Control = {
   _openItems: new Set(),
   init() {
@@ -1044,16 +1070,39 @@ var Control = {
         } else {
           this._openItems.delete(id);
           while (row.nextSibling.firstChild.classList.contains("indent"))
             row.nextSibling.remove();
         }
         return;
       }
 
+      // Handle closing a tab.
+      if (target.classList.contains("close-icon")) {
+        let row = target.parentNode.parentNode;
+        let id = parseInt(row.windowId);
+        let found = tabFinder.get(id);
+        if (!found || !found.tabbrowser)
+          return;
+        let {tabbrowser, tab} = found;
+        tabbrowser.removeTab(tab);
+        while (row.nextSibling.firstChild.classList.contains("indent"))
+          row.nextSibling.remove();
+        row.remove();
+        return;
+      }
+
+      if (target.classList.contains("addon-icon")) {
+        let row = target.parentNode.parentNode;
+        let id = row.windowId;
+        let parentWin = window.docShell.rootTreeItem.domWindow;
+        parentWin.BrowserOpenAddonsMgr("addons://detail/" + encodeURIComponent(id));
+        return;
+      }
+
       // Handle selection changes
       let row = target.parentNode;
       if (this.selectedRow) {
         this.selectedRow.removeAttribute("selected");
       }
       if (row.windowId) {
         row.setAttribute("selected", "true");
         this.selectedRow = row;
@@ -1105,24 +1154,24 @@ var Control = {
       if (this.selectedRow) {
         selectedId = this.selectedRow.windowId;
         this.selectedRow = null;
       }
       let openItems = this._openItems;
       this._openItems = new Set();
 
       let counters = this._sortCounters(State.getCounters());
-      for (let {id, name, image, totalDispatches, dispatchesSincePrevious,
+      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),
-                         null, image);
+                         type, image);
         row.windowId = id;
         if (id == selectedId) {
           row.setAttribute("selected", "true");
           this.selectedRow = row;
         }
 
         if (!children.length)
           continue;
@@ -1150,28 +1199,28 @@ var Control = {
     // Inform watchers
     Services.obs.notifyObservers(null, UPDATE_COMPLETE_TOPIC, mode);
   },
   _showChildren(row) {
     let children = row._children;
     children.sort((a, b) => b.dispatchesSincePrevious - a.dispatchesSincePrevious);
     for (let row of children) {
       let host = row.host.replace(/^blob:https?:\/\//, "");
-      let classes = ["indent"];
+      let type = "subframe";
       if (State.isTracker(host))
-        classes.push("tracking");
+        type = "tracker";
       if (row.isWorker)
-        classes.push("worker");
+        type = "worker";
       View.appendRow(row.host,
                      this._formatEnergyImpact(row.dispatchesSincePrevious,
                                               row.durationSincePrevious),
                      this._formatTooltip(row.dispatchCount, row.duration,
                                          row.dispatchesSincePrevious,
                                          row.durationSincePrevious),
-                     classes);
+                     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.
--- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
@@ -130,17 +130,61 @@
         height: 2em;
       }
       #dispatch-thead, #dispatch-tbody tr {
         display: table;
         table-layout: fixed;
         width: 100%;
       }
       #dispatch-table td:nth-child(2) {
-        width: 10em;
+        width: 8em;
+      }
+      #dispatch-table td:nth-child(3) {
+        width: 12em;
+      }
+      #dispatch-table td:nth-child(4) {
+        width: 20px;
+      }
+
+      /* Show action icons on selected or hovered rows */
+      tr:-moz-any([selected], :hover) > td > .action-icon {
+        padding: 1px 10px;
+        -moz-context-properties: fill, fill-opacity;
+        fill-opacity: 0;
+        background-repeat: no-repeat;
+        background-position: center;
+        fill: currentColor;
+      }
+      .addon-icon {
+        background-image: url("chrome://global/skin/icons/shortcut.svg");
+        background-size: 16px;
+      }
+      .close-icon {
+        background-image: url("chrome://global/skin/icons/close.svg");
+        background-size: 24px;
+      }
+      .action-icon {
+        position: relative;
+      }
+      /* action icon background */
+      .action-icon::before {
+        background-color: currentColor;
+        opacity: 0;
+        height: 200%;
+        position: absolute;
+        top: -50%;
+        left: -3px;
+        padding-left: 13px;
+        padding-right: 13px;
+      }
+      .action-icon:hover::before {
+        opacity: 0.1;
+      }
+      .action-icon:hover:active::before {
+        opacity: 0.2;
       }
 
       #dispatch-table > tbody {
         border-top: 1px solid var(--in-content-border-color);
       }
       #dispatch-table > thead > tr > td {
         border: none;
         background-color: var(--in-content-box-background-hover);
@@ -152,21 +196,21 @@
         border-image: linear-gradient(transparent 0%, transparent 20%, #c1c1c1 20%, #c1c1c1 80%, transparent 80%, transparent 100%) 1 1;
         border-bottom: 1px solid var(--in-content-border-color);
       }
       #dispatch-tbody > tr > td {
         padding: 5px 10px;
         min-height: 2em;
         color: var(--in-content-text-color);
         max-width: 70vw;
-        text-overflow: ellipsis;
         overflow: hidden;
         white-space: nowrap;
       }
       #dispatch-tbody > tr > td:first-child {
+        text-overflow: ellipsis;
         padding-inline-start: 32px;
         background-repeat: no-repeat;
         background-size: 16px 16px;
       }
       #dispatch-tbody > tr > td.root{
         background-position: 36px;
         padding-inline-start: 62px;
       }
@@ -183,23 +227,23 @@
       }
       .twisty.open {
         background-image: url("chrome://global/skin/icons/twisty-expanded.svg");
       }
       #dispatch-tbody > tr > td.indent {
         padding-inline-start: 88px;
         background-position: 62px;
       }
-      #dispatch-tbody > tr > td.tracking {
-        background-image: url(chrome://browser/skin/controlcenter/trackers.svg);
+      #dispatch-tbody > tr > td.tracker {
+        background-image: url("chrome://browser/skin/controlcenter/trackers.svg");
         -moz-context-properties: fill;
         fill: rgb(224, 41, 29);
       }
       #dispatch-tbody > tr > td.worker {
-        background-image: url(chrome://devtools/skin/images/debugging-workers.svg);
+        background-image: url("chrome://devtools/skin/images/debugging-workers.svg");
         -moz-context-properties: fill;
         fill: #808080;
       }
 
       #dispatch-tbody > tr[selected] > td {
         background-color: var(--in-content-item-selected);
         color: var(--in-content-selected-text);
       }
@@ -234,16 +278,18 @@
       <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></td><!-- actions -->
           </tr>
         </thead>
         <tbody id="dispatch-tbody"></tbody>
       </table>
     </div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/icons/shortcut.svg
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="context-fill" d="M11 2H5a1 1 0 0 0 0 2h3.59L6.05 6.54a7 7 0 0 0-2 5V13a1 1 0 0 0 2 0v-1.51A5 5 0 0 1 7.46 8L10 5.41V9a1 1 0 0 0 2 0V3a1 1 0 0 0-1-1z"/></svg>
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -35,16 +35,17 @@ toolkit.jar:
   skin/classic/global/icons/error.svg                      (../../shared/icons/error.svg)
   skin/classic/global/icons/find-previous-arrow.svg        (../../shared/icons/find-previous-arrow.svg)
   skin/classic/global/icons/find-next-arrow.svg            (../../shared/icons/find-next-arrow.svg)
   skin/classic/global/icons/help.svg                       (../../shared/icons/help.svg)
   skin/classic/global/icons/info.svg                       (../../shared/incontent-icons/info.svg)
   skin/classic/global/icons/loading.png                    (../../shared/icons/loading.png)
   skin/classic/global/icons/loading@2x.png                 (../../shared/icons/loading@2x.png)
   skin/classic/global/icons/resizer.svg                    (../../shared/icons/resizer.svg)
+  skin/classic/global/icons/shortcut.svg                   (../../shared/icons/shortcut.svg)
   skin/classic/global/icons/spinner-arrow-down.svg         (../../shared/icons/spinner-arrow-down.svg)
   skin/classic/global/icons/spinner-arrow-up.svg           (../../shared/icons/spinner-arrow-up.svg)
   skin/classic/global/icons/twisty-collapsed.svg           (../../shared/icons/twisty-collapsed.svg)
   skin/classic/global/icons/twisty-collapsed-rtl.svg       (../../shared/icons/twisty-collapsed-rtl.svg)
   skin/classic/global/icons/twisty-expanded.svg            (../../shared/icons/twisty-expanded.svg)
   skin/classic/global/icons/arrow-dropdown-12.svg          (../../shared/icons/arrow-dropdown-12.svg)
   skin/classic/global/icons/arrow-dropdown-16.svg          (../../shared/icons/arrow-dropdown-16.svg)
   skin/classic/global/icons/warning.svg                    (../../shared/icons/warning.svg)