Bug 753677 - Visually distinguish between configurable, enumerable and writable properties; r=past
authorVictor Porof <vporof@mozilla.com>
Wed, 30 May 2012 16:13:05 +0300
changeset 95399 1bc2c4e1493b3db5f2e260d01acfc64f0ee90d8a
parent 95398 3f0594ee130da2a94ea9a6674d8f777bc109b61d
child 95400 85c153eea9d4f26a1c459c1bc152e11cc5f1e4b9
push id816
push userpastithas@mozilla.com
push dateFri, 01 Jun 2012 16:36:26 +0000
treeherderfx-team@1bc2c4e1493b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast
bugs753677
milestone15.0a1
Bug 753677 - Visually distinguish between configurable, enumerable and writable properties; r=past
browser/devtools/debugger/debugger-controller.js
browser/devtools/debugger/debugger-view.js
browser/themes/gnomestripe/devtools/debugger.css
browser/themes/pinstripe/devtools/debugger.css
browser/themes/winstripe/devtools/debugger.css
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -579,17 +579,17 @@ StackFrames.prototype = {
             }.bind(this));
             break;
           case "block":
           case "function":
             // Add nodes for every argument.
             let variables = env.bindings.arguments;
             for each (let variable in variables) {
               let name = Object.getOwnPropertyNames(variable)[0];
-              let paramVar = scope.addVar(name);
+              let paramVar = scope.addVar(name, variable[name]);
               let paramVal = variable[name].value;
               paramVar.setGrip(paramVal);
               this._addExpander(paramVar, paramVal);
             }
             // Add nodes for every other variable in scope.
             this._addScopeVariables(env.bindings.variables, scope);
             break;
           default:
@@ -623,17 +623,17 @@ StackFrames.prototype = {
     // Sort all of the variables before adding them, for better UX.
     let variables = {};
     for each (let prop in Object.keys(aVariables).sort()) {
       variables[prop] = aVariables[prop];
     }
 
     // Add the sorted variables to the specified scope.
     for (let variable in variables) {
-      let paramVar = aScope.addVar(variable);
+      let paramVar = aScope.addVar(variable, variables[variable]);
       let paramVal = variables[variable].value;
       paramVar.setGrip(paramVal);
       this._addExpander(paramVar, paramVal);
     }
   },
 
   /**
    * Adds an 'onexpand' callback for a variable, lazily handling the addition of
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -875,22 +875,24 @@ PropertiesView.prototype = {
    * Adds a variable to a specified scope.
    * If the optional id is not specified, the variable html node will have a
    * default id set as aScope.id->aName-variable.
    *
    * @param object aScope
    *        The parent scope element.
    * @param string aName
    *        The variable name.
+   * @param object aFlags
+   *        Optional, contains configurable, enumerable or writable flags.
    * @param string aId
    *        Optional, an id for the variable html node.
    * @return object
    *         The newly created html node representing the added var.
    */
-  _addVar: function DVP__addVar(aScope, aName, aId) {
+  _addVar: function DVP__addVar(aScope, aName, aFlags, aId) {
     // Make sure the scope container exists.
     if (!aScope) {
       return null;
     }
 
     // Compute the id of the element if not specified.
     aId = aId || (aScope.id + "->" + aName + "-variable");
 
@@ -922,16 +924,31 @@ PropertiesView.prototype = {
 
       // Separator between the variable name and its value.
       separatorLabel.className = "plain";
       separatorLabel.setAttribute("value", ":");
 
       // The variable information (type, class and/or value).
       valueLabel.className = "value plain";
 
+      if (aFlags) {
+        // Use attribute flags to specify the element type and tooltip text.
+        let tooltip = [];
+
+        !aFlags.configurable ? element.setAttribute("non-configurable", "")
+                             : tooltip.push("configurable");
+        !aFlags.enumerable   ? element.setAttribute("non-enumerable", "")
+                             : tooltip.push("enumerable");
+        !aFlags.writable     ? element.setAttribute("non-writable", "")
+                             : tooltip.push("writable");
+
+        element.setAttribute("tooltiptext", tooltip.join(", "));
+      }
+      if (aName === "this") { element.setAttribute("self", ""); }
+
       // Handle the click event when pressing the element value label.
       valueLabel.addEventListener("click", this._activateElementInputMode.bind({
         scope: this,
         element: element,
         valueLabel: valueLabel
       }));
 
       // Maintain the symbolic name of the variable.
@@ -1088,17 +1105,17 @@ PropertiesView.prototype = {
    *        is not specified, it will be inferred from the value).
    *        e.g. ["someProp0", 42]
    *             ["someProp1", true]
    *             ["someProp2", "nasu"]
    *             ["someProp3", { type: "undefined" }]
    *             ["someProp4", { type: "null" }]
    *             ["someProp5", { type: "object", class: "Object" }]
    * @param object aFlags
-   *        Contans configurable, enumberable or writable flags.
+   *        Contains configurable, enumerable or writable flags.
    * @param string aName
    *        Optional, the property name.
    * @paarm string aId
    *        Optional, an id for the property html node.
    * @return object
    *         The newly created html node representing the added prop.
    */
   _addProperty: function DVP__addProperty(aVar, aProperty, aFlags, aName, aId) {
@@ -1134,41 +1151,48 @@ PropertiesView.prototype = {
     element.refresh(function(pKey, pGrip) {
       let title = element.getElementsByClassName("title")[0];
       let nameLabel = title.getElementsByClassName("name")[0];
       let separatorLabel = document.createElement("label");
       let valueLabel = document.createElement("label");
 
       if ("undefined" !== typeof pKey) {
         // Use a key element to specify the property name.
-        let className = "";
-        if (aFlags) {
-          if (aFlags.configurable === false) { className += "non-configurable "; }
-          if (aFlags.enumerable === false) { className += "non-enumerable "; }
-          if (aFlags.writable === false) { className += "non-writable "; }
-        }
-        if (pKey === "__proto__ ") { className += "proto "; }
-
-        nameLabel.className = className + "key plain";
+        nameLabel.className = "key plain";
         nameLabel.setAttribute("value", pKey.trim());
         title.appendChild(nameLabel);
       }
       if ("undefined" !== typeof pGrip) {
         // Separator between the variable name and its value.
         separatorLabel.className = "plain";
         separatorLabel.setAttribute("value", ":");
 
         // Use a value element to specify the property value.
         valueLabel.className = "value plain";
         this._applyGrip(valueLabel, pGrip);
 
         title.appendChild(separatorLabel);
         title.appendChild(valueLabel);
       }
 
+      if (aFlags) {
+        // Use attribute flags to specify the element type and tooltip text.
+        let tooltip = [];
+
+        !aFlags.configurable ? element.setAttribute("non-configurable", "")
+                             : tooltip.push("configurable");
+        !aFlags.enumerable   ? element.setAttribute("non-enumerable", "")
+                             : tooltip.push("enumerable");
+        !aFlags.writable     ? element.setAttribute("non-writable", "")
+                             : tooltip.push("writable");
+
+        element.setAttribute("tooltiptext", tooltip.join(", "));
+      }
+      if (pKey === "__proto__ ") { element.setAttribute("proto", ""); }
+
       // Handle the click event when pressing the element value label.
       valueLabel.addEventListener("click", this._activateElementInputMode.bind({
         scope: this,
         element: element,
         valueLabel: valueLabel
       }));
 
       // Maintain the symbolic name of the property.
--- a/browser/themes/gnomestripe/devtools/debugger.css
+++ b/browser/themes/gnomestripe/devtools/debugger.css
@@ -94,17 +94,17 @@
 /**
  * Variable element
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
-  border-bottom: 1px dotted #ccc;
+  border-bottom: 1px dotted #ddd;
   border-radius: 8px;
   -moz-transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
   -moz-transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
@@ -131,19 +131,44 @@
 .property > .title > .arrow {
   margin-top: -2px;
 }
 
 .property > .title > .key {
   color: #881090;
 }
 
-.property > .title > .non-enumerable.key,
-.property > .title > .proto.key {
-  color: #c48bc8;
+/**
+ * Non enumerable, configurable and writable variables and properties.
+ */
+
+.property[proto] > .title > .key,
+.variable[non-enumerable] > .title > .name,
+.property[non-enumerable] > .title > .key {
+  opacity: 0.5;
+}
+
+.variable[non-configurable] > .title > .name,
+.property[non-configurable] > .title > .key {
+  border-bottom: 1px dashed #99f;
+}
+
+.variable[non-writable] > .title > .name,
+.property[non-writable] > .title > .key {
+  border-bottom: 1px dashed #f99;
+}
+
+.variable[non-writable] > .title:after,
+.property[non-writable] > .title:after {
+  content: " ";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
+  opacity: 0.5;
 }
 
 /**
  * Property values colors
  */
 
 .token-undefined {
   -moz-padding-start: 6px;
--- a/browser/themes/pinstripe/devtools/debugger.css
+++ b/browser/themes/pinstripe/devtools/debugger.css
@@ -96,17 +96,17 @@
 /**
  * Variable element
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
-  border-bottom: 1px dotted #ccc;
+  border-bottom: 1px dotted #ddd;
   border-radius: 8px;
   -moz-transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
   -moz-transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
@@ -133,19 +133,44 @@
 .property > .title > .arrow {
   margin-top: -2px;
 }
 
 .property > .title > .key {
   color: #881090;
 }
 
-.property > .title > .non-enumerable.key,
-.property > .title > .proto.key {
-  color: #c48bc8;
+/**
+ * Non enumerable, configurable and writable variables and properties.
+ */
+
+.property[proto] > .title > .key,
+.variable[non-enumerable] > .title > .name,
+.property[non-enumerable] > .title > .key {
+  opacity: 0.5;
+}
+
+.variable[non-configurable] > .title > .name,
+.property[non-configurable] > .title > .key {
+  border-bottom: 1px dashed #99f;
+}
+
+.variable[non-writable] > .title > .name,
+.property[non-writable] > .title > .key {
+  border-bottom: 1px dashed #f99;
+}
+
+.variable[non-writable] > .title:after,
+.property[non-writable] > .title:after {
+  content: " ";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
+  opacity: 0.5;
 }
 
 /**
  * Property values colors
  */
 
 .token-undefined {
   -moz-padding-start: 6px;
--- a/browser/themes/winstripe/devtools/debugger.css
+++ b/browser/themes/winstripe/devtools/debugger.css
@@ -94,17 +94,17 @@
 /**
  * Variable element
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
-  border-bottom: 1px dotted #ccc;
+  border-bottom: 1px dotted #ddd;
   border-radius: 8px;
   -moz-transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
   -moz-transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
@@ -131,19 +131,44 @@
 .property > .title > .arrow {
   margin-top: -2px;
 }
 
 .property > .title > .key {
   color: #881090;
 }
 
-.property > .title > .non-enumerable.key,
-.property > .title > .proto.key {
-  color: #c48bc8;
+/**
+ * Non enumerable, configurable and writable variables and properties.
+ */
+
+.property[proto] > .title > .key,
+.variable[non-enumerable] > .title > .name,
+.property[non-enumerable] > .title > .key {
+  opacity: 0.5;
+}
+
+.variable[non-configurable] > .title > .name,
+.property[non-configurable] > .title > .key {
+  border-bottom: 1px dashed #99f;
+}
+
+.variable[non-writable] > .title > .name,
+.property[non-writable] > .title > .key {
+  border-bottom: 1px dashed #f99;
+}
+
+.variable[non-writable] > .title:after,
+.property[non-writable] > .title:after {
+  content: " ";
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
+  opacity: 0.5;
 }
 
 /**
  * Property values colors
  */
 
 .token-undefined {
   -moz-padding-start: 6px;