Add 'Show hidden properties' checkbox (Bug 786070); r=past f=gavin,rcampbell,vporof

This commit is contained in:
Anton Kovalyov 2012-10-05 11:20:47 -07:00
parent dbaabd3439
commit 427c365cb3
13 changed files with 270 additions and 44 deletions

View File

@ -1035,6 +1035,7 @@ pref("devtools.debugger.ui.stackframes-width", 200);
pref("devtools.debugger.ui.stackframes-pane-visible", true);
pref("devtools.debugger.ui.variables-width", 300);
pref("devtools.debugger.ui.variables-pane-visible", true);
pref("devtools.debugger.ui.non-enum-visible", true);
// Enable the style inspector
pref("devtools.styleinspector.enabled", true);

View File

@ -1733,6 +1733,7 @@ const REMOTE_HOST = "devtools.debugger.remote-host";
const REMOTE_PORT = "devtools.debugger.remote-port";
const REMOTE_CONNECTION_RETRIES = "devtools.debugger.remote-connection-retries";
const REMOTE_TIMEOUT = "devtools.debugger.remote-timeout";
const NON_ENUM_VISIBLE = "devtools.debugger.ui.non-enum-visible";
/**
* Shortcuts for accessing various debugger preferences.
@ -1820,7 +1821,7 @@ let Prefs = {
},
/**
* Gets a flag specifying if the the debugger should automatically connect to
* Gets a flag specifying if the debugger should automatically connect to
* the default host and port number.
* @return boolean
*/
@ -1832,13 +1833,35 @@ let Prefs = {
},
/**
* Sets a flag specifying if the the debugger should automatically connect to
* Sets a flag specifying if the debugger should automatically connect to
* the default host and port number.
* @param boolean value
*/
set remoteAutoConnect(value) {
Services.prefs.setBoolPref(REMOTE_AUTO_CONNECT, value);
this._autoConnect = value;
},
/**
* Gets a flag specifying if the debugger should show non-enumerable
* properties and variables in the scope view.
* @return boolean
*/
get nonEnumVisible() {
if (this._nonEnumVisible === undefined) {
this._nonEnumVisible = Services.prefs.getBoolPref(NON_ENUM_VISIBLE);
}
return this._nonEnumVisible;
},
/**
* Sets a flag specifying if the debugger should show non-enumerable
* properties and variables in the scope view.
* @param boolean value
*/
set nonEnumVisible(value) {
Services.prefs.setBoolPref(NON_ENUM_VISIBLE, value);
this._nonEnumVisible = value;
}
};

View File

@ -2585,9 +2585,16 @@ PropertiesView.prototype = {
// Compute the id of the element if not specified.
aId = aId || (aScope.id + "->" + aName + "-variable");
let parent;
if (aFlags && !aFlags.enumerable) {
parent = aScope.childNodes[2];
}
else {
parent = aScope.childNodes[1];
}
// Contains generic nodes and functionality.
let element = this._createPropertyElement(aName, aId, "variable",
aScope.getElementsByClassName("details")[0]);
let element = this._createPropertyElement(aName, aId, "variable", parent);
// Make sure the element was created successfully.
if (!element) {
@ -2789,6 +2796,7 @@ PropertiesView.prototype = {
if (value !== undefined) {
this._addProperty(aVar, [i, value], desc);
}
if (getter !== undefined || setter !== undefined) {
let prop = this._addProperty(aVar, [i]).expand();
prop.getter = this._addProperty(prop, ["get", getter], desc);
@ -2834,9 +2842,16 @@ PropertiesView.prototype = {
// Compute the id of the element if not specified.
aId = aId || (aVar.id + "->" + aProperty[0] + "-property");
let parent;
if (aFlags && !aFlags.enumerable) {
parent = aVar.childNodes[2];
}
else {
parent = aVar.childNodes[1];
}
// Contains generic nodes and functionality.
let element = this._createPropertyElement(aName, aId, "property",
aVar.getElementsByClassName("details")[0]);
let element = this._createPropertyElement(aName, aId, "property", parent);
// Make sure the element was created successfully.
if (!element) {
@ -3112,6 +3127,7 @@ PropertiesView.prototype = {
let title = document.createElement("box");
let details = document.createElement("vbox");
let nonEnum = document.createElement("vbox");
// Create a scope node to contain all the elements.
element.id = aId;
@ -3131,6 +3147,7 @@ PropertiesView.prototype = {
// The node element which will contain any added scope variables.
details.className = "details";
nonEnum.className = "details nonenum";
// Add the click event handler for the title, or arrow and name.
if (aClass === "scope") {
@ -3146,6 +3163,7 @@ PropertiesView.prototype = {
element.appendChild(title);
element.appendChild(details);
element.appendChild(nonEnum);
aParent.appendChild(element);
@ -3192,12 +3210,19 @@ PropertiesView.prototype = {
arrow.setAttribute("open", "");
details.setAttribute("open", "");
if (Prefs.nonEnumVisible) {
nonEnum.setAttribute("open", "");
}
if (!aSkipAnimationFlag) {
details.setAttribute("animated", "");
nonEnum.setAttribute("animated", "");
}
if ("function" === typeof element.onexpand) {
element.onexpand(element);
}
return element;
};
@ -3210,9 +3235,12 @@ PropertiesView.prototype = {
if (element._preventCollapse) {
return;
}
arrow.removeAttribute("open");
details.removeAttribute("open");
details.removeAttribute("animated");
nonEnum.removeAttribute("open");
nonEnum.removeAttribute("animated");
if ("function" === typeof element.oncollapse) {
element.oncollapse(element);
@ -3240,9 +3268,12 @@ PropertiesView.prototype = {
* The same element.
*/
element.showArrow = function DVP_element_showArrow() {
if (element._forceShowArrow || details.childNodes.length) {
let len = details.childNodes.length + nonEnum.childNodes.length;
if (element._forceShowArrow || len) {
arrow.style.visibility = "visible";
}
return element;
};
@ -3327,13 +3358,19 @@ PropertiesView.prototype = {
element.empty = function DVP_element_empty() {
// This details node won't have any elements, so hide the arrow.
arrow.style.visibility = "hidden";
while (details.firstChild) {
details.removeChild(details.firstChild);
}
while (nonEnum.firstChild) {
nonEnum.removeChild(nonEnum.firstChild);
}
if ("function" === typeof element.onempty) {
element.onempty(element);
}
return element;
};
@ -3431,7 +3468,7 @@ PropertiesView.prototype = {
let node = aParent.parentNode;
let arrow = node.getElementsByClassName("arrow")[0];
let children = node.getElementsByClassName("details")[0].childNodes.length;
let children = node.querySelectorAll(".details > vbox").length;
// If the parent details node has at least one element, set the
// expand/collapse arrow visible.
@ -3549,10 +3586,31 @@ PropertiesView.prototype = {
*/
_vars: null,
_onShowNonEnums: function DVP__onShowNonEnums() {
let option = document.getElementById("show-nonenum");
Prefs.nonEnumVisible = option.checked;
let els = document.getElementsByClassName("nonenum").iterator();
for (let el of els) {
if (el.parentNode.expanded) {
if (Prefs.nonEnumVisible) {
el.setAttribute("open", "");
} else {
el.removeAttribute("open");
el.removeAttribute("animated");
}
}
}
},
/**
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVP_initialize() {
let showNonEnums = document.getElementById("show-nonenum");
showNonEnums.addEventListener("click", this._onShowNonEnums, false);
showNonEnums.checked = Prefs.nonEnumVisible;
this._vars = DebuggerView._variables;
this.emptyText();

View File

@ -90,6 +90,10 @@
type="checkbox"
tabindex="0"
label="&debuggerUI.pauseExceptions;"/>
<checkbox id="show-nonenum"
type="checkbox"
tabindex="0"
label="&debuggerUI.showNonEnums;"/>
<spacer flex="1"/>
#ifndef XP_MACOSX
<toolbarbutton id="close"

View File

@ -63,6 +63,7 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_bug723069_editor-breakpoints.js \
browser_dbg_bug723071_editor-breakpoints-pane.js \
browser_dbg_bug731394_editor-contextmenu.js \
browser_dbg_bug786070_hide_nonenums.js \
browser_dbg_displayName.js \
browser_dbg_iframes.js \
browser_dbg_pause-exceptions.js \

View File

@ -0,0 +1,107 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
function test() {
debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.contentWindow;
testNonEnumProperties();
});
}
function testNonEnumProperties() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({ run: function() {
let testScope = gDebugger.DebuggerView.Properties._addScope("test-scope");
let testVar = testScope.addVar("foo");
testVar.addProperties({
foo: {
value: "bar",
enumerable: true
},
bar: {
value: "foo",
enumerable: false
}
});
testVar.expand();
let details = testVar.childNodes[1];
let nonenum = testVar.childNodes[2];
is(details.childNodes.length, 1,
"There should be just one property in the .details container.");
ok(details.hasAttribute("open"),
".details container should be visible.");
is(nonenum.childNodes.length, 1,
"There should be just one property in the .nonenum container.");
ok(nonenum.hasAttribute("open"),
".nonenum container should be visible.");
let option = gDebugger.document.getElementById("show-nonenum");
// Uncheck 'show hidden properties'.
EventUtils.sendMouseEvent({ type: "click" }, option, gDebugger);
ok(details.hasAttribute("open"),
".details container should stay visible.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should become hidden.");
// Check 'show hidden properties'.
EventUtils.sendMouseEvent({ type: "click" }, option, gDebugger);
ok(details.hasAttribute("open"),
".details container should stay visible.");
ok(nonenum.hasAttribute("open"),
".nonenum container should become visible.");
testVar.collapse();
ok(!details.hasAttribute("open"),
".details container should be hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should be hidden.");
EventUtils.sendMouseEvent({ type: "click" }, option, gDebugger);
ok(!details.hasAttribute("open"),
".details container should stay hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should stay hidden.");
gDebugger.DebuggerController.activeThread.resume(function() {
closeDebuggerAndFinish();
});
}}, 0);
});
gDebuggee.simpleCall();
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
});

View File

@ -26,8 +26,16 @@ function testSimpleCall() {
let testScope = gDebugger.DebuggerView.Properties._addScope("test");
let testVar = testScope.addVar("something");
let properties = testVar.addProperties({ "child": { "value": { "type": "object",
"class": "Object" } } });
let properties = testVar.addProperties({
"child": {
"value": {
"type": "object",
"class": "Object"
},
"enumerable": true
}
});
is(testVar.querySelector(".details").childNodes.length, 1,
"A new detail node should have been added in the variable tree.");
@ -39,8 +47,16 @@ function testSimpleCall() {
"Adding a detail property should return that exact property.");
let properties2 = testVar.child.addProperties({ "grandchild": { "value": { "type": "object",
"class": "Object" } } });
let properties2 = testVar.child.addProperties({
"grandchild": {
"value": {
"type": "object",
"class": "Object"
},
"enumerable": true
}
});
is(testVar.child.querySelector(".details").childNodes.length, 1,
"A new detail node should have been added in the variable tree.");

View File

@ -44,28 +44,32 @@ function testSimpleCall() {
"The information for the variable wasn't set correctly.");
testVar.addProperties({ "helloWorld": { "value": "hello world" } });
testVar.addProperties({ "helloWorld": { "value": "hello world", "enumerable": true } });
is(testVar.querySelector(".details").childNodes.length, 1,
"A new detail node should have been added in the variable tree.");
testVar.addProperties({ "helloWorld": { "value": "hello jupiter" } });
testVar.addProperties({ "helloWorld": { "value": "hello jupiter", "enumerable": true } });
is(testVar.querySelector(".details").childNodes.length, 1,
"Shouldn't be able to duplicate nodes added in the variable tree.");
testVar.addProperties({ "someProp0": { "value": "random string" },
"someProp1": { "value": "another string" } });
testVar.addProperties({ "someProp0": { "value": "random string", "enumerable": true },
"someProp1": { "value": "another string", "enumerable": true } });
is(testVar.querySelector(".details").childNodes.length, 3,
"Two new detail nodes should have been added in the variable tree.");
testVar.addProperties({ "someProp2": { "value": { "type": "null" } },
"someProp3": { "value": { "type": "undefined" } },
"someProp4": { "value": { "type": "object", "class": "Object" } } });
testVar.addProperties({ "someProp2": { "value": { "type": "null" }, "enumerable": true },
"someProp3": { "value": { "type": "undefined" }, "enumerable": true },
"someProp4": {
"value": { "type": "object", "class": "Object" },
"enumerable": true
}
});
is(testVar.querySelector(".details").childNodes.length, 6,
"Three new detail nodes should have been added in the variable tree.");

View File

@ -45,20 +45,25 @@ function testSimpleCall() {
localVar4.setGrip({ "type": "null" });
localVar5.setGrip({ "type": "object", "class": "Object" });
localVar5.addProperties({ "someProp0": { "value": 42 },
"someProp1": { "value": true },
"someProp2": { "value": "nasu" },
"someProp3": { "value": { "type": "undefined" } },
"someProp4": { "value": { "type": "null" } },
"someProp5": { "value": { "type": "object", "class": "Object" } } });
localVar5.addProperties({ "someProp0": { "value": 42, "enumerable": true },
"someProp1": { "value": true , "enumerable": true},
"someProp2": { "value": "nasu", "enumerable": true},
"someProp3": { "value": { "type": "undefined" }, "enumerable": true},
"someProp4": { "value": { "type": "null" }, "enumerable": true },
"someProp5": {
"value": { "type": "object", "class": "Object" },
"enumerable": true
}
});
localVar5.someProp5.addProperties({ "someProp0": { "value": 42 },
"someProp1": { "value": true },
"someProp2": { "value": "nasu" },
"someProp3": { "value": { "type": "undefined" } },
"someProp4": { "value": { "type": "null" } },
localVar5.someProp5.addProperties({ "someProp0": { "value": 42, "enumerable": true },
"someProp1": { "value": true, "enumerable": true },
"someProp2": { "value": "nasu", "enumerable": true },
"someProp3": { "value": { "type": "undefined" }, "enumerable": true },
"someProp4": { "value": { "type": "null" }, "enumerable": true },
"someAccessor": { "get": { "type": "object", "class": "Function" },
"set": { "type": "undefined" } } });
"set": { "type": "undefined" },
"enumerable": true } });
windowVar.setGrip({ "type": "object", "class": "Window" });
windowVar.addProperties({ "helloWorld": { "value": "hello world" } });

View File

@ -37,12 +37,14 @@ function testFrameParameters()
var frames = gDebugger.DebuggerView.StackFrames._frames,
localScope = gDebugger.DebuggerView.Properties._vars.firstChild,
localNodes = localScope.querySelector(".details").childNodes;
localNodes = localScope.childNodes[1].childNodes,
localNonEnums = localScope.childNodes[2].childNodes; // .nonenums
dump("Got our variables:\n");
dump("frames - " + frames.constructor + "\n");
dump("localScope - " + localScope.constructor + "\n");
dump("localNodes - " + localNodes.constructor + "\n");
dump("localNonEnums - " + localNonEnums.constructor + "\n");
is(gDebugger.DebuggerController.activeThread.state, "paused",
"Should only be getting stack frames while paused.");
@ -50,8 +52,8 @@ function testFrameParameters()
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes.length + localNonEnums.length, 11,
"The localScope and localNonEnums should contain all the created variable elements.");
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
"Should have the right property value for 'this'.");
@ -79,8 +81,8 @@ function testFrameParameters()
}
window.clearInterval(intervalID);
is(localNodes[0].querySelector(".property > .title > .key")
.getAttribute("value"), "Array",
"Should have the right property name for Array.");
.getAttribute("value"), "InstallTrigger",
"Should have the right property name for InstallTrigger.");
ok(localNodes[0].querySelector(".property > .title > .value")
.getAttribute("value").search(/object/) != -1,
"Array should be an object.");

View File

@ -38,7 +38,7 @@ function testFrameParameters()
var frames = gDebugger.DebuggerView.StackFrames._frames,
globalScope = gDebugger.DebuggerView.Properties._vars.lastChild,
globalNodes = globalScope.querySelector(".details").childNodes;
globalNodes = globalScope.childNodes[2].childNodes;
globalScope.expand();
@ -55,11 +55,11 @@ function testFrameParameters()
"Should have the right property value for |Array|.");
let len = globalNodes.length - 1;
is(globalNodes[len].querySelector(".name").getAttribute("value"), "window",
"Should have the right property name for |window|.");
is(globalNodes[len].querySelector(".name").getAttribute("value"), "uneval",
"Should have the right property name for |uneval|.");
is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Proxy]",
"Should have the right property value for |window|.");
is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Function]",
"Should have the right property value for |uneval|.");
resumeAndFinish();
}}, 0);

View File

@ -59,11 +59,11 @@ function testWithFrame()
is(innerNodes[1].querySelector(".value").getAttribute("value"), "1",
"Should have the right property value for |one|.");
is(globalNodes[0].querySelector(".name").getAttribute("value"), "Array",
is(globalNodes[0].querySelector(".name").getAttribute("value"), "InstallTrigger",
"Should have the right property name for |Array|.");
is(globalNodes[0].querySelector(".value").getAttribute("value"), "[object Function]",
"Should have the right property value for |Array|.");
is(globalNodes[0].querySelector(".value").getAttribute("value"), "undefined",
"Should have the right property value for |InstallTrigger|.");
let len = globalNodes.length - 1;
is(globalNodes[len].querySelector(".name").getAttribute("value"), "window",

View File

@ -39,6 +39,11 @@
- checkbox that toggles pausing on exceptions. -->
<!ENTITY debuggerUI.pauseExceptions "Pause on exceptions">
<!-- LOCALIZATION NOTE (debuggerUI.showNonEnums): This is the label for the
- checkbox that toggles visibility of hidden (non-enumerable) variables and
- properties in stack views. -->
<!ENTITY debuggerUI.showNonEnums "Show hidden properties">
<!-- LOCALIZATION NOTE (debuggerUI.searchPanelTitle): This is the text that
- appears in the filter panel popup as a description. -->
<!ENTITY debuggerUI.searchPanelTitle "Operators">