Bug 1081245 - Make the call tree text select and copy-able. r=nfitzgerald
--- 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);
}