Bug 1218674 - Style heap view states and overall tree. r=fitzgen
authorJordan Santell <jsantell@mozilla.com>
Tue, 27 Oct 2015 17:27:59 -0700
changeset 305000 6b5fe9c588b8b2d27140c76c739f39995d6278c0
parent 304999 408b0efe2d0cd3a9dd864793ba61b32727e197bc
child 305001 4a412e7f72b1b39b1f8413d6c4c7c7fdc48a2172
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1218674
milestone44.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 1218674 - Style heap view states and overall tree. r=fitzgen
devtools/client/memory/components/heap.js
devtools/client/memory/utils.js
devtools/client/themes/memory.css
devtools/client/themes/toolbars.inc.css
--- a/devtools/client/memory/components/heap.js
+++ b/devtools/client/memory/components/heap.js
@@ -1,17 +1,17 @@
 /* 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/. */
 
+const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
 const { safeErrorString } = require("devtools/shared/DevToolsUtils");
-const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
 const Tree = createFactory(require("./tree"));
 const TreeItem = createFactory(require("./tree-item"));
-const { getSnapshotStatusText } = require("../utils");
+const { getSnapshotStatusTextFull } = require("../utils");
 const { snapshotState: states } = require("../constants");
 const { snapshot: snapshotModel } = require("../models");
 const TAKE_SNAPSHOT_TEXT = "Take snapshot";
 // If HEAP_TREE_ROW_HEIGHT changes, be sure to change `var(--heap-tree-row-height)`
 // in `devtools/client/themes/memory.css`
 const HEAP_TREE_ROW_HEIGHT = 14;
 
 /**
@@ -36,18 +36,16 @@ function createParentMap (node, aggregat
  *
  * @param {CensusTreeNode} census
  * @return {Object}
  */
 function createTreeProperties (census) {
   let map = createParentMap(census);
 
   return {
-    // getParent only used for focusing parents when child selected;
-    // handle this later?
     getParent: node => map(node.id),
     getChildren: node => node.children || [],
     renderItem: (item, depth, focused, arrow) => new TreeItem({ item, depth, focused, arrow }),
     getRoots: () => census.children,
     getKey: node => node.id,
     itemHeight: HEAP_TREE_ROW_HEIGHT,
   };
 }
@@ -63,50 +61,57 @@ const Heap = module.exports = createClas
 
   propTypes: {
     onSnapshotClick: PropTypes.func.isRequired,
     snapshot: snapshotModel,
   },
 
   render() {
     let { snapshot, onSnapshotClick } = this.props;
-    let pane;
     let census = snapshot ? snapshot.census : null;
     let state = snapshot ? snapshot.state : "initial";
+    let statusText = snapshot ? getSnapshotStatusTextFull(snapshot) : "";
+    let content;
 
     switch (state) {
       case "initial":
-        pane = dom.div({ className: "heap-view-panel", "data-state": "initial" },
-          dom.button({ className: "take-snapshot", onClick: onSnapshotClick }, TAKE_SNAPSHOT_TEXT)
-        );
+        content = dom.button({
+          className: "devtools-toolbarbutton take-snapshot",
+          onClick: onSnapshotClick,
+          // Want to use the [standalone] tag to leverage our styles,
+          // but React hates that evidently
+          "data-standalone": true,
+          "data-text-only": true,
+        }, TAKE_SNAPSHOT_TEXT)
         break;
       case states.ERROR:
-        pane = dom.div({ className: "heap-view-panel" },
-                       dom.h2({}, "There was an error processing this snapshot"),
-                       dom.pre({}, safeErrorString(snapshot.error)));
+        content = [
+          dom.span({ className: "snapshot-status error" }, statusText),
+          dom.pre({}, safeErrorString(snapshot.error || new Error("blahblah"))),
+        ];
         break;
       case states.SAVING:
       case states.SAVED:
       case states.READING:
       case states.READ:
       case states.SAVING_CENSUS:
-        pane = dom.div({ className: "heap-view-panel", "data-state": state },
-          getSnapshotStatusText(snapshot));
+        content = dom.span({ className: "snapshot-status devtools-throbber" }, statusText)
         break;
       case states.SAVED_CENSUS:
-        pane = dom.div({ className: "heap-view-panel", "data-state": "loaded" },
+        content = [
           dom.div({ className: "header" },
             dom.span({ className: "heap-tree-item-bytes" }, "Bytes"),
             dom.span({ className: "heap-tree-item-count" }, "Count"),
             dom.span({ className: "heap-tree-item-total-bytes" }, "Total Bytes"),
             dom.span({ className: "heap-tree-item-total-count" }, "Total Count"),
             dom.span({ className: "heap-tree-item-name" }, "Name")
           ),
           Tree(createTreeProperties(snapshot.census))
-        );
+        ];
         break;
     }
+    let pane = dom.div({ className: "heap-view-panel", "data-state": state }, content);
 
     return (
       dom.div({ id: "heap-view", "data-state": state }, pane)
     )
   }
 });
--- a/devtools/client/memory/utils.js
+++ b/devtools/client/memory/utils.js
@@ -2,16 +2,17 @@
  * 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/. */
 
 const { assert } = require("devtools/shared/DevToolsUtils");
 const { Preferences } = require("resource://gre/modules/Preferences.jsm");
 const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { snapshotState: states, breakdowns } = require("./constants");
+const FULL_ERROR_TEXT = "There was an error processing this snapshot.";
 const ERROR_SNAPSHOT_TEXT = "⚠ Error!";
 const SAVING_SNAPSHOT_TEXT = "Saving snapshot...";
 const READING_SNAPSHOT_TEXT = "Reading snapshot...";
 const SAVING_CENSUS_TEXT = "Taking heap census...";
 
 /**
  * Returns an array of objects with the unique key `name`
  * and `displayName` for each breakdown.
@@ -81,16 +82,17 @@ exports.breakdownNameToSpec = function (
   else if (name in breakdowns) {
     return breakdowns[name].breakdown;
   }
   return Object.create(null);
 };
 
 /**
  * Returns a string representing a readable form of the snapshot's state.
+ * More brief than `getSnapshotStatusText`.
  *
  * @param {Snapshot} snapshot
  * @return {String}
  */
 exports.getSnapshotStatusText = function (snapshot) {
   assert((snapshot || {}).state,
     `Snapshot must have expected state, found ${(snapshot || {}).state}.`);
 
@@ -112,16 +114,33 @@ exports.getSnapshotStatusText = function
       return "";
   }
 
   assert(false, `Snapshot in unexpected state: ${snapshot.state}`);
   return "";
 }
 
 /**
+ * Returns a string representing a readable form of the snapshot's state;
+ * more verbose than `getSnapshotStatusText`.
+ *
+ * @param {Snapshot} snapshot
+ * @return {String}
+ */
+exports.getSnapshotStatusTextFull = function (snapshot) {
+  assert((snapshot || {}).state,
+    `Snapshot must have expected state, found ${(snapshot || {}).state}.`);
+  switch (snapshot.state) {
+    case states.ERROR:
+      return FULL_ERROR_TEXT;
+  }
+  return exports.getSnapshotStatusText(snapshot);
+}
+
+/**
  * Takes an array of snapshots and a snapshot and returns
  * the snapshot instance in `snapshots` that matches
  * the snapshot passed in.
  *
  * @param {Array<Snapshot>} snapshots
  * @param {Snapshot}
  * @return ?Snapshot
  */
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -20,17 +20,18 @@
 }
 
 html, .theme-body, #app, #memory-tool, #memory-tool-container {
   height: 100%;
 }
 
 .theme-body {
   overflow: hidden;
-  background-color: var(--theme-body-background);
+  /* Not sure why .theme-body declares it as `background` and overrides */
+  background-color: var(--theme-toolbar-background) !important;
 }
 
 #memory-tool-container {
   display: flex;
   flex-direction: row;
   --toolbar-height: 20px;
   /**
    * If --heap-tree-row-height changes, be sure to change HEAP_TREE_ROW_HEIGHT
@@ -40,37 +41,37 @@ html, .theme-body, #app, #memory-tool, #
 }
 
 /**
  * TODO bug 1213100
  * should generalize toolbar buttons with images in them
  * toolbars.inc.css contains definitions for .devtools-button,
  * I wager that many of the below styles can be rolled into that
  */
-.devtools-button.take-snapshot {
+.devtools-toolbar .devtools-button.take-snapshot {
   margin: 2px 1px;
   padding: 1px;
   border-width: 0px;
   /* [standalone] buttons override min-height from 18px to 24px -- why? */
   min-height: 18px;
   /* not sure why this is needed for positioning */
   display: -moz-box;
 }
 
-.devtools-button.take-snapshot::before {
+.devtools-toolbar .devtools-button.take-snapshot::before {
   background-image: url(images/command-screenshot.png);
   -moz-appearance: none;
   width: 16px;
   height: 16px;
   background-size: 64px 16px;
   background-position: 0 center;
   background-repeat: no-repeat;
 }
 @media (min-resolution: 1.1dppx) {
-  .devtools-button.take-snapshot::before {
+  .devtools-toolbar .devtools-button.take-snapshot::before {
     background-image: url(images/command-screenshot@2x.png);
   }
 }
 
 /**
  * TODO bug 1213100
  * Should this be codified in .devtools-toolbar itself?
  */
@@ -104,19 +105,20 @@ html, .theme-body, #app, #memory-tool, #
   margin: 0;
   padding: 0;
   width: 186px;
   list-style-type: none;
   font-size: 12px;
   height: 100%;
   overflow-y: scroll;
   background-color: var(--theme-toolbar-background);
+  color: var(--theme-body-color-alt);
   border-color: var(--theme-splitter-color);
-  color: var(--theme-body-color-alt);
-  border-left-width: 1px;
+  border-style: solid;
+  border-width: 0px 1px 0px 0px;
 }
 
 .list > li {
   height: 40px;
   color: var(--theme-body-color);
   border-bottom: 1px solid rgba(128,128,128,0.15);
   padding: 8px;
   cursor: pointer;
@@ -144,39 +146,61 @@ html, .theme-body, #app, #memory-tool, #
 
 /**
  * Main panel
  */
 
 #heap-view {
   flex: 1 1 auto;
   border-color: var(--theme-splitter-color);
-  color: var(--theme-body-color)
+  color: var(--theme-body-color);
   border-left-width: 1px;
 }
 
 #heap-view .heap-view-panel {
   width: 100%;
   height: 100%;
+  background-color: var(--theme-toolbar-background)
+}
+
+#heap-view .heap-view-panel .snapshot-status, #heap-view .take-snapshot {
+  font-size: 120%;
+  display: block;
+  margin: 65px auto;
+}
+
+#heap-view .heap-view-panel .snapshot-status {
+  width: 500px;
+  text-align: center;
 }
 
 #heap-view .take-snapshot {
+  padding: 5px;
+}
+
+#heap-view .heap-view-panel[data-state="snapshot-state-error"] pre {
+  background-color: var(--theme-body-background);
+  overflow-y: scroll;
+  height: 100px;
+  margin: 20px;
+  padding: 20px;
 }
 
 /**
  * Heap Tree View
  */
 
 #heap-view .theme-twisty {
   float: left;
 }
 
 .tree {
   height: 100%;
   overflow-y: scroll;
+  background-color: var(--theme-body-background);
 }
 
 .tree .header {
   height: 17px;
   overflow: hidden;
   color: var(--theme-body-color);
   background-color: var(--theme-tab-toolbar-background);
 }
@@ -241,8 +265,25 @@ html, .theme-body, #app, #memory-tool, #
 .heap-tree-item-bytes,
 .heap-tree-item-total-bytes {
   width: 7vw;
 }
 
 .heap-tree-item-name {
   padding-left: 10px;
 }
+
+.error::before {
+  content: "";
+  background-image: url(chrome://devtools/skin/themes/images/webconsole.svg);
+  background-repeat: no-repeat;
+  background-size: 72px 60px;
+  width: 12px;
+  height: 12px;
+  display: inline-block;
+
+  background-position: -24px -24px;
+  margin: 2px 5px 0 0;
+  max-height: 12px;
+}
+.theme-light .error::before {
+  background-image: url(chrome://devtools/skin/themes/images/webconsole.svg#light-icons);
+}
--- a/devtools/client/themes/toolbars.inc.css
+++ b/devtools/client/themes/toolbars.inc.css
@@ -74,17 +74,17 @@
 }
 
 .devtools-menulist:-moz-focusring,
 .devtools-toolbarbutton:-moz-focusring {
   outline: 1px dotted hsla(210,30%,85%,0.7);
   outline-offset: -4px;
 }
 
-.devtools-toolbarbutton[standalone] {
+.devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
   -moz-margin-end: 5px;
   border-width: 1px;
   border-style: solid;
 }
 .devtools-toolbarbutton[label][standalone] {
   min-height: 2em;
 }
 
@@ -159,20 +159,22 @@
 }
 .theme-light .devtools-menulist,
 .theme-light .devtools-toolbarbutton {
   border-color: rgba(170, 170, 170, .5); /* Splitters */
 }
 
 /* Text-only buttons */
 .theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
+.theme-light .devtools-toolbarbutton[data-text-only],
 .theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   background-color: rgba(170, 170, 170, .2); /* Splitter */
 }
 .theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
+.theme-dark .devtools-toolbarbutton[data-text-only],
 .theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   background-color: rgba(0, 0, 0, .2); /* Splitter */
 }
 
 /* Button States */
 .theme-dark .devtools-toolbarbutton:not([disabled]):hover,
 .theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
 .theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
@@ -265,17 +267,17 @@
   margin: 0;
   padding: 0;
   min-width: 32px;
   min-height: 18px;
   /* The icon is absolutely positioned in the button using ::before */
   position: relative;
 }
 
-.devtools-button[standalone] {
+.devtools-button[standalone], .devtools-button[data-standalone] {
   min-height: 32px;
   border-width: 1px;
 }
 
 /* Button States */
 .theme-dark .devtools-button:not([disabled]):hover {
   background: rgba(0, 0, 0, .3); /* Splitters */
 }