Bug 1498185 - Implement the type and action columns in the new about:performance, r=felipe.
☠☠ backed out by 2e10a4e70394 ☠ ☠
authorFlorian Quèze <florian@queze.net>
Wed, 17 Oct 2018 22:24:14 +0200
changeset 490041 37647eaadbc29b3f744b225825414bca6bad5b1c
parent 490040 3a58f94c3f5ad656c0c96abf87d0522c3066690e
child 490042 4c0be70aed863561a6c9d7ee84bf63b9acfb51eb
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersfelipe
bugs1498185
milestone64.0a1
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,17 +227,17 @@
       }
       .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 {
+      #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);
         -moz-context-properties: fill;
         fill: #808080;
@@ -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)