Bug 1081245 - Make the call tree text select and copy-able. r=nfitzgerald
☠☠ backed out by fda060cdf44a ☠ ☠
authorHernan Rodriguez Colmeiro <colmeiro@gmail.com>
Fri, 30 Oct 2015 02:57:00 -0300
changeset 308687 5c98c715b5c7354522da176a19185f47f87e6adc
parent 308686 ea102921641595c8cf67c3296580ecb6700529ef
child 308688 cfce9960cdb18258a6455b2dc607ae43f5aa2c2f
push id7514
push users.kaspari@gmail.com
push dateFri, 13 Nov 2015 14:12:41 +0000
reviewersnfitzgerald
bugs1081245
milestone45.0a1
Bug 1081245 - Make the call tree text select and copy-able. r=nfitzgerald
devtools/client/performance/modules/widgets/tree-view.js
devtools/client/shared/widgets/AbstractTreeItem.jsm
devtools/client/themes/performance.css
--- a/devtools/client/performance/modules/widgets/tree-view.js
+++ b/devtools/client/performance/modules/widgets/tree-view.js
@@ -230,17 +230,18 @@ CallView.prototype = Heritage.extend(Abs
    * Functions creating each cell in this call view.
    * Invoked by `_displaySelf`.
    */
   _createCell: function (doc, value, type) {
     let cell = doc.createElement("description");
     cell.className = "plain call-tree-cell";
     cell.setAttribute("type", type);
     cell.setAttribute("crop", "end");
-    cell.setAttribute("value", value);
+    // Add a tabulation to the cell text in case it's is selected and copied.
+    cell.textContent = value + "\t";
     return cell;
   },
 
   _createFunctionCell: function(doc, arrowNode, frameName, frameInfo, frameLevel) {
     let cell = doc.createElement("hbox");
     cell.className = "call-tree-cell";
     cell.style.MozMarginStart = (frameLevel * CALL_TREE_INDENTATION) + "px";
     cell.setAttribute("type", "function");
@@ -256,76 +257,87 @@ CallView.prototype = Heritage.extend(Abs
 
     // Don't render a name label node if there's no function name. A different
     // location label node will be rendered instead.
     if (frameName) {
       let nameNode = doc.createElement("description");
       nameNode.className = "plain call-tree-name";
       nameNode.setAttribute("flex", "1");
       nameNode.setAttribute("crop", "end");
-      nameNode.setAttribute("value", frameName);
+      nameNode.textContent = frameName;
       cell.appendChild(nameNode);
     }
 
     // Don't render detailed labels for meta category frames
     if (!frameInfo.isMetaCategory) {
       this._appendFunctionDetailsCells(doc, cell, frameInfo);
     }
 
     // Don't render an expando-arrow for leaf nodes.
     let hasDescendants = Object.keys(this.frame.calls).length > 0;
     if (!hasDescendants) {
       arrowNode.setAttribute("invisible", "");
     }
 
+    // Add a line break to the last description of the row in case it's selected
+    // and copied.
+    let lastDescription = cell.querySelector('description:last-of-type');
+    lastDescription.textContent = lastDescription.textContent + "\n";
+
+    // Add spaces as frameLevel indicators in case the row is selected and
+    // copied. These spaces won't be displayed in the cell content.
+    let firstDescription = cell.querySelector('description:first-of-type');
+    let levelIndicator = frameLevel > 0 ? " ".repeat(frameLevel) : "";
+    firstDescription.textContent = levelIndicator + firstDescription.textContent;
+
     return cell;
   },
 
   _appendFunctionDetailsCells: function(doc, cell, frameInfo) {
     if (frameInfo.fileName) {
       let urlNode = doc.createElement("description");
       urlNode.className = "plain call-tree-url";
       urlNode.setAttribute("flex", "1");
       urlNode.setAttribute("crop", "end");
-      urlNode.setAttribute("value", frameInfo.fileName);
+      urlNode.textContent = frameInfo.fileName;
       urlNode.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + frameInfo.url);
       urlNode.addEventListener("mousedown", this._onUrlClick);
       cell.appendChild(urlNode);
     }
 
     if (frameInfo.line) {
       let lineNode = doc.createElement("description");
       lineNode.className = "plain call-tree-line";
-      lineNode.setAttribute("value", ":" + frameInfo.line);
+      lineNode.textContent = ":" + frameInfo.line;
       cell.appendChild(lineNode);
     }
 
     if (frameInfo.column) {
       let columnNode = doc.createElement("description");
       columnNode.className = "plain call-tree-column";
-      columnNode.setAttribute("value", ":" + frameInfo.column);
+      columnNode.textContent = ":" + frameInfo.column;
       cell.appendChild(columnNode);
     }
 
     if (frameInfo.host) {
       let hostNode = doc.createElement("description");
       hostNode.className = "plain call-tree-host";
-      hostNode.setAttribute("value", frameInfo.host);
+      hostNode.textContent = frameInfo.host;
       cell.appendChild(hostNode);
     }
 
     let spacerNode = doc.createElement("spacer");
     spacerNode.setAttribute("flex", "10000");
     cell.appendChild(spacerNode);
 
     if (frameInfo.categoryData.label) {
       let categoryNode = doc.createElement("description");
       categoryNode.className = "plain call-tree-category";
       categoryNode.style.color = frameInfo.categoryData.color;
-      categoryNode.setAttribute("value", frameInfo.categoryData.label);
+      categoryNode.textContent = frameInfo.categoryData.label;
       cell.appendChild(categoryNode);
     }
   },
 
   /**
    * Gets the data displayed about this tree item, based on the FrameNode
    * model associated with this view.
    *
--- a/devtools/client/shared/widgets/AbstractTreeItem.jsm
+++ b/devtools/client/shared/widgets/AbstractTreeItem.jsm
@@ -516,17 +516,16 @@ AbstractTreeItem.prototype = {
       this.collapse();
     }
   },
 
   /**
    * Handler for the "click" event on the element displaying this tree item.
    */
   _onClick: function(e) {
-    e.preventDefault();
     e.stopPropagation();
     this.focus();
   },
 
   /**
    * Handler for the "dblclick" event on the element displaying this tree item.
    */
   _onDoubleClick: function(e) {
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -264,16 +264,26 @@
 .call-tree-cell:not(:last-child) {
   text-align: end;
 }
 
 .call-tree-header {
   background-color: var(--theme-tab-toolbar-background);
 }
 
+.call-tree-item .call-tree-cell,
+.call-tree-item .call-tree-cell[type=function] description {
+  -moz-user-select: text;
+}
+
+.call-tree-item .call-tree-cell::-moz-selection,
+.call-tree-item .call-tree-cell[type=function] description::-moz-selection {
+  background-color: var(--theme-highlight-orange);
+}
+
 .call-tree-item:last-child {
   border-bottom: 1px solid var(--cell-border-color);
 }
 
 .call-tree-item:nth-child(2n) {
   background-color: var(--row-alt-background-color);
 }