Bug 840097: Add clipboard copy for about:telemetry histograms. r=vladan
authorAvi Halachmi <avihpit@yahoo.com>
Fri, 29 Nov 2013 19:31:14 +0200
changeset 158155 ec86d240ceed1cf05643062dd7a5bbaa08969c9e
parent 158154 a6f1b8a182edbe13c2cbda2da8baff169c2488c3
child 158156 fe97252f9c2032d387802d69918642414049e623
push id25737
push usercbook@mozilla.com
push dateMon, 02 Dec 2013 11:42:38 +0000
treeherdermozilla-central@5b9a4d273114 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvladan
bugs840097
milestone28.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 840097: Add clipboard copy for about:telemetry histograms. r=vladan
toolkit/content/aboutTelemetry.css
toolkit/content/aboutTelemetry.js
toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
--- a/toolkit/content/aboutTelemetry.css
+++ b/toolkit/content/aboutTelemetry.css
@@ -73,16 +73,17 @@ h2 {
   overflow: hidden;
 }
 
 .histogram {
   float: left;
   border: 1px solid gray;
   white-space: nowrap;
   padding: 10px;
+  position: relative; /* required for position:absolute of the contained .copy-node */
 }
 
 body[dir="rtl"] .histogram {
   float: right;
 }
 
 .histogram-title {
   text-overflow: ellipsis;
@@ -123,8 +124,23 @@ caption {
   white-space: nowrap;
   text-align: left;
   font-size: large;
 }
 
 body[dir="rtl"] caption {
   text-align: right;
 }
+
+.copy-node {
+  visibility: hidden;
+  position: absolute;
+  bottom: 1px;
+  right: 1px;
+}
+
+body[dir="rtl"] .copy-node {
+  left: 1px;
+}
+
+.histogram:hover .copy-node {
+  visibility: visible;
+}
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -14,28 +14,35 @@ Cu.import("resource://gre/modules/Teleme
 const Telemetry = Services.telemetry;
 const bundle = Services.strings.createBundle(
   "chrome://global/locale/aboutTelemetry.properties");
 const brandBundle = Services.strings.createBundle(
   "chrome://branding/locale/brand.properties");
 const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].
   getService(Ci.nsITelemetryPing);
 
-// Maximum height of a histogram bar (in em)
+// Maximum height of a histogram bar (in em for html, in chars for text)
 const MAX_BAR_HEIGHT = 18;
+const MAX_BAR_CHARS = 25;
 const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
 #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
 const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabledPreRelease";
 #else
 const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
 #endif
 const PREF_DEBUG_SLOW_SQL = "toolkit.telemetry.debugSlowSql";
 const PREF_SYMBOL_SERVER_URI = "profiler.symbolicationUrl";
 const DEFAULT_SYMBOL_SERVER_URI = "http://symbolapi.mozilla.org";
 
+#ifdef XP_WIN
+const EOL = "\r\n";
+#else
+const EOL = "\n";
+#endif
+
 // Cached value of document's RTL mode
 let documentRTLMode = "";
 
 /**
  * Helper function for fetching a config pref
  *
  * @param aPrefName Name of config pref to fetch.
  * @param aDefault Default value to return if pref isn't set.
@@ -402,16 +409,18 @@ let ChromeHangs = {
 let Histogram = {
 
   hgramSamplesCaption: bundle.GetStringFromName("histogramSamples"),
 
   hgramAverageCaption: bundle.GetStringFromName("histogramAverage"),
 
   hgramSumCaption: bundle.GetStringFromName("histogramSum"),
 
+  hgramCopyCaption: bundle.GetStringFromName("histogramCopy"),
+
   /**
    * Renders a single Telemetry histogram
    *
    * @param aParent Parent element
    * @param aName Histogram name
    * @param aHgram Histogram information
    */
   render: function Histogram_render(aParent, aName, aHgram) {
@@ -432,17 +441,28 @@ let Histogram = {
 
     let divStats = document.createElement("div");
     divStats.appendChild(document.createTextNode(stats));
     outerDiv.appendChild(divStats);
 
     if (isRTL())
       hgram.values.reverse();
 
-    this.renderValues(outerDiv, hgram.values, hgram.max);
+    let textData = this.renderValues(outerDiv, hgram.values, hgram.max, hgram.sample_count);
+
+    // The 'Copy' button contains the textual data, copied to clipboard on click
+    let copyButton = document.createElement("button");
+    copyButton.className = "copy-node";
+    copyButton.appendChild(document.createTextNode(this.hgramCopyCaption));
+    copyButton.histogramText = aName + EOL + stats + EOL + EOL + textData;
+    copyButton.addEventListener("click", function(){
+      Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper)
+                                                 .copyString(this.histogramText);
+    });
+    outerDiv.appendChild(copyButton);
 
     aParent.appendChild(outerDiv);
   },
 
   /**
    * Unpacks histogram values
    *
    * @param aHgram Packed histogram
@@ -486,24 +506,38 @@ let Histogram = {
       sample_count: sample_count,
       sum: aHgram.sum
     };
 
     return result;
   },
 
   /**
-   * Create histogram bars
+   * Create histogram HTML bars, also returns a textual representation
+   * Both aMaxValue and aSumValues must be positive.
+   * Values are assumed to use 0 as baseline.
    *
    * @param aDiv Outer parent div
    * @param aValues Histogram values
-   * @param aMaxValue Largest histogram value in set
+   * @param aMaxValue Value of the longest bar (length, not label)
+   * @param aSumValues Sum of all bar values
    */
-  renderValues: function Histogram_renderValues(aDiv, aValues, aMaxValue) {
+  renderValues: function Histogram_renderValues(aDiv, aValues, aMaxValue, aSumValues) {
+    let text = "";
+    // If the last label is not the longest string, alignment will break a little
+    let labelPadTo = String(aValues[aValues.length -1][0]).length;
+
     for (let [label, value] of aValues) {
+      // Create a text representation: <right-aligned-label> |<bar-of-#><value>  <percentage>
+      text += EOL
+              + " ".repeat(Math.max(0, labelPadTo - String(label).length)) + label // Right-aligned label
+              + " |" + "#".repeat(Math.round(MAX_BAR_CHARS * value / aMaxValue)) + value // Bars and value
+              + "  " + Math.round(100 * value / aSumValues) + "%"; // Percentage
+
+      // Construct the HTML labels + bars
       let belowEm = Math.round(MAX_BAR_HEIGHT * (value / aMaxValue) * 10) / 10;
       let aboveEm = MAX_BAR_HEIGHT - belowEm;
 
       let barDiv = document.createElement("div");
       barDiv.className = "bar";
       barDiv.style.paddingTop = aboveEm + "em";
 
       // Add value label or an nbsp if no value
@@ -515,16 +549,18 @@ let Histogram = {
       bar.style.height = belowEm + "em";
       barDiv.appendChild(bar);
 
       // Add bucket label
       barDiv.appendChild(document.createTextNode(label));
 
       aDiv.appendChild(barDiv);
     }
+
+    return text.substr(EOL.length); // Trim the EOL before the first line
   }
 };
 
 /*
  * Helper function to render JS objects with white space between top level elements
  * so that they look better in the browser
  * @param   aObject JavaScript object or array to render
  * @return  String
--- a/toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutTelemetry.properties
@@ -33,16 +33,18 @@ memoryMapTitle = Memory map:
 errorFetchingSymbols = An error occurred while fetching symbols. Check that you are connected to the Internet and try again.
 
 histogramSamples = samples
 
 histogramAverage = average
 
 histogramSum = sum
 
+histogramCopy = Copy
+
 disableTelemetry = Disable Telemetry
 
 enableTelemetry = Enable Telemetry
 
 keysHeader = Property
 
 valuesHeader = Value