Bug 808372 - After bug 807222, the variables view feels sluggish when expanding some nodes, r=past

This commit is contained in:
Victor Porof 2012-11-16 08:34:13 +02:00
parent 4ad390f7e8
commit 68c2283a28
23 changed files with 1355 additions and 327 deletions

View File

@ -52,6 +52,7 @@ let DebuggerController = {
DebuggerView.initialize(function() {
DebuggerView._isInitialized = true;
window.dispatchEvent("Debugger:Loaded");
this._connect();
}.bind(this));
@ -371,6 +372,7 @@ function StackFrames() {
StackFrames.prototype = {
get activeThread() DebuggerController.activeThread,
autoScopeExpand: false,
currentFrame: null,
currentException: null,
@ -493,11 +495,11 @@ StackFrames.prototype = {
if (!frame) {
return;
}
let env = frame.environment;
let environment = frame.environment;
let { url, line } = frame.where;
// Check if the frame does not represent the evaluation of debuggee code.
if (!env) {
if (!environment) {
return;
}
@ -512,78 +514,24 @@ StackFrames.prototype = {
// Clear existing scopes and create each one dynamically.
DebuggerView.Variables.empty();
let self = this;
let name = "";
do {
// Name the outermost scope Global.
if (!env.parent) {
name = L10N.getStr("globalScopeLabel");
}
// Otherwise construct the scope name.
else {
name = env.type.charAt(0).toUpperCase() + env.type.slice(1);
}
let label = L10N.getFormatStr("scopeLabel", [name]);
switch (env.type) {
case "with":
case "object":
label += " [" + env.object.class + "]";
break;
case "function":
label += " [" + env.functionName + "]";
break;
}
// Create a scope to contain all the inspected variables.
let label = this._getScopeLabel(environment);
let scope = DebuggerView.Variables.addScope(label);
// Special additions to the innermost scope.
if (env == frame.environment) {
// Add any thrown exception.
if (aDepth == 0 && this.currentException) {
let excVar = scope.addVar("<exception>", { value: this.currentException });
this._addExpander(excVar, this.currentException);
}
// Add "this".
if (frame.this) {
let thisVar = scope.addVar("this", { value: frame.this });
this._addExpander(thisVar, frame.this);
}
// Expand the innermost scope by default.
scope.expand(true);
if (environment == frame.environment) {
this._insertScopeFrameReferences(scope, frame);
this._fetchScopeVariables(scope, environment);
// Always expand the innermost scope by default.
scope.expand();
}
switch (env.type) {
case "with":
case "object":
// Add nodes for all variables in the environment object scope.
this.activeThread.pauseGrip(env.object).getPrototypeAndProperties(function(aResponse) {
self._addScopeVariables(aResponse.ownProperties, scope);
// Signal that variables have been fetched.
window.dispatchEvent("Debugger:FetchedVariables");
DebuggerView.Variables.commitHierarchy();
});
break;
case "block":
case "function":
// Add nodes for every argument.
for (let variable of env.bindings.arguments) {
let name = Object.getOwnPropertyNames(variable)[0];
let paramVar = scope.addVar(name, variable[name]);
let paramVal = variable[name].value;
this._addExpander(paramVar, paramVal);
}
// Add nodes for every other variable in scope.
this._addScopeVariables(env.bindings.variables, scope);
break;
default:
Cu.reportError("Unknown Debugger.Environment type: " + env.type);
break;
// Lazily add nodes for every other environment scope.
else {
this._addScopeExpander(scope, environment);
this.autoScopeExpand && scope.expand();
}
} while (env = env.parent);
} while (environment = environment.parent);
// Signal that variables have been fetched.
window.dispatchEvent("Debugger:FetchedVariables");
@ -591,30 +539,21 @@ StackFrames.prototype = {
},
/**
* Add nodes for every variable in scope.
* Adds an 'onexpand' callback for a scope, lazily handling
* the addition of new variables.
*
* @param object aVariables
* The map of names to variables, as specified in the Remote
* Debugging Protocol.
* @param Scope aScope
* The scope where the nodes will be placed into.
* The scope where the variables will be placed into.
* @param object aEnv
* The scope's environment.
*/
_addScopeVariables: function SF_addScopeVariables(aVariables, aScope) {
if (!aVariables) {
return;
}
let variableNames = Object.keys(aVariables);
_addScopeExpander: function SF__addScopeExpander(aScope, aEnv) {
let callback = this._fetchScopeVariables.bind(this, aScope, aEnv);
// Sort all of the variables before adding them if preferred.
if (Prefs.variablesSortingEnabled) {
variableNames.sort();
}
// Add the sorted variables to the specified scope.
for (let name of variableNames) {
let paramVar = aScope.addVar(name, aVariables[name]);
let paramVal = aVariables[name].value;
this._addExpander(paramVar, paramVal);
}
// It's a good idea to be prepared in case of an expansion.
aScope.onmouseover = callback;
// Make sure that variables are always available on expansion.
aScope.onexpand = callback;
},
/**
@ -626,28 +565,146 @@ StackFrames.prototype = {
* @param any aGrip
* The grip of the variable.
*/
_addExpander: function SF__addExpander(aVar, aGrip) {
_addVarExpander: function SF__addVarExpander(aVar, aGrip) {
// No need for expansion for primitive values.
if (VariablesView.isPrimitive({ value: aGrip })) {
return;
}
aVar.onexpand = this._addVarProperties.bind(this, aVar, aGrip);
let callback = this._fetchVarProperties.bind(this, aVar, aGrip);
// Some variables are likely to contain a very large number of properties.
// It's a good idea to be prepared in case of an expansion.
if (aVar.name == "window" || aVar.name == "this") {
aVar.onmouseover = callback;
}
// Make sure that properties are always available on expansion.
aVar.onexpand = callback;
},
/**
* Adds variables to a scope in the view. Triggered when a scope is
* expanded or is hovered. It does not expand the scope.
*
* @param Scope aScope
* The scope where the variables will be placed into.
* @param object aEnv
* The scope's environment.
*/
_fetchScopeVariables: function SF__fetchScopeVariables(aScope, aEnv) {
// Retrieve the variables only once.
if (aScope.fetched) {
return;
}
aScope.fetched = true;
switch (aEnv.type) {
case "with":
case "object":
// Add nodes for every variable in scope.
this.activeThread.pauseGrip(aEnv.object).getPrototypeAndProperties(function(aResponse) {
this._insertScopeVariables(aResponse.ownProperties, aScope);
// Signal that variables have been fetched.
window.dispatchEvent("Debugger:FetchedVariables");
DebuggerView.Variables.commitHierarchy();
}.bind(this));
break;
case "block":
case "function":
// Add nodes for every argument and every other variable in scope.
this._insertScopeArguments(aEnv.bindings.arguments, aScope);
this._insertScopeVariables(aEnv.bindings.variables, aScope);
break;
default:
Cu.reportError("Unknown Debugger.Environment type: " + aEnv.type);
break;
}
},
/**
* Add nodes for special frame references in the innermost scope.
*
* @param Scope aScope
* The scope where the references will be placed into.
* @param object aFrame
* The frame to get some references from.
*/
_insertScopeFrameReferences: function SF__insertScopeFrameReferences(aScope, aFrame) {
// Add any thrown exception.
if (this.currentException) {
let excRef = aScope.addVar("<exception>", { value: this.currentException });
this._addVarExpander(excRef, this.currentException);
}
// Add "this".
if (aFrame.this) {
let thisRef = aScope.addVar("this", { value: aFrame.this });
this._addVarExpander(thisRef, aFrame.this);
}
},
/**
* Add nodes for every argument in scope.
*
* @param object aArguments
* The map of names to arguments, as specified in the protocol.
* @param Scope aScope
* The scope where the nodes will be placed into.
*/
_insertScopeArguments: function SF__insertScopeArguments(aArguments, aScope) {
if (!aArguments) {
return;
}
for (let argument of aArguments) {
let name = Object.getOwnPropertyNames(argument)[0];
let argRef = aScope.addVar(name, argument[name]);
let argVal = argument[name].value;
this._addVarExpander(argRef, argVal);
}
},
/**
* Add nodes for every variable in scope.
*
* @param object aVariables
* The map of names to variables, as specified in the protocol.
* @param Scope aScope
* The scope where the nodes will be placed into.
*/
_insertScopeVariables: function SF__insertScopeVariables(aVariables, aScope) {
if (!aVariables) {
return;
}
let variableNames = Object.keys(aVariables);
// Sort all of the variables before adding them if preferred.
if (Prefs.variablesSortingEnabled) {
variableNames.sort();
}
// Add the sorted variables to the specified scope.
for (let name of variableNames) {
let varRef = aScope.addVar(name, aVariables[name]);
let varVal = aVariables[name].value;
this._addVarExpander(varRef, varVal);
}
},
/**
* Adds properties to a variable in the view. Triggered when a variable is
* expanded.
* expanded or certain variables are hovered. It does not expand the variable.
*
* @param Variable aVar
* The variable where the properties will be placed into.
* @param any aGrip
* The grip of the variable.
*/
_addVarProperties: function SF__addVarProperties(aVar, aGrip) {
_fetchVarProperties: function SF__fetchVarProperties(aVar, aGrip) {
// Retrieve the properties only once.
if (aVar.fetched) {
return;
}
aVar.fetched = true;
this.activeThread.pauseGrip(aGrip).getPrototypeAndProperties(function(aResponse) {
let { ownProperties, prototype } = aResponse;
@ -657,7 +714,7 @@ StackFrames.prototype = {
aVar.addProperties(ownProperties);
// Expansion handlers must be set after the properties are added.
for (let name in ownProperties) {
this._addExpander(aVar.get(name), ownProperties[name].value);
this._addVarExpander(aVar.get(name), ownProperties[name].value);
}
}
@ -665,10 +722,10 @@ StackFrames.prototype = {
if (prototype.type != "null") {
aVar.addProperty("__proto__", { value: prototype });
// Expansion handlers must be set after the properties are added.
this._addExpander(aVar.get("__proto__"), prototype);
this._addVarExpander(aVar.get("__proto__"), prototype);
}
aVar.fetched = true;
aVar._retrieved = true;
// Signal that properties have been fetched.
window.dispatchEvent("Debugger:FetchedProperties");
@ -676,6 +733,39 @@ StackFrames.prototype = {
}.bind(this));
},
/**
* Constructs a scope label based on its environment.
*
* @param object aEnv
* The scope's environment.
* @return string
* The scope's label.
*/
_getScopeLabel: function SV__getScopeLabel(aEnv) {
let name = "";
// Name the outermost scope Global.
if (!aEnv.parent) {
name = L10N.getStr("globalScopeLabel");
}
// Otherwise construct the scope name.
else {
name = aEnv.type.charAt(0).toUpperCase() + aEnv.type.slice(1);
}
let label = L10N.getFormatStr("scopeLabel", [name]);
switch (aEnv.type) {
case "with":
case "object":
label += " [" + aEnv.object.class + "]";
break;
case "function":
label += " [" + aEnv.functionName + "]";
break;
}
return label;
},
/**
* Adds the specified stack frame to the list.
*
@ -1041,8 +1131,8 @@ Breakpoints.prototype = {
* @param object aLocation
* The location where you want the breakpoint. This object must have
* two properties:
* - url - the url of the source.
* - line - the line number (starting from 1).
* - url: the url of the source.
* - line: the line number (starting from 1).
* @param function aCallback [optional]
* Optional function to invoke once the breakpoint is added. The
* callback is invoked with two arguments:

View File

@ -1044,7 +1044,7 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
let sourceResultsItem = SourceResults.getItemForElement(target);
let lineResultsItem = LineResults.getItemForElement(target);
sourceResultsItem.instance.expand(true);
sourceResultsItem.instance.expand();
this._currentlyFocusedMatch = LineResults.indexOfElement(target);
this._scrollMatchIntoViewIfNeeded(target);
this._bounceMatch(target);
@ -1084,7 +1084,7 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
let { clientHeight } = this._container._parent;
if (top - height <= clientHeight || this._forceExpandResults) {
sourceResultsItem.instance.expand(true);
sourceResultsItem.instance.expand();
}
},
@ -1303,7 +1303,7 @@ SourceResults.prototype = {
aElementNode.resultsContainer = resultsContainer;
if (aExpandFlag && aMatchCount < GLOBAL_SEARCH_EXPAND_MAX_RESULTS) {
this.expand(true);
this.expand();
}
let resultsBox = document.createElement("vbox");
@ -1528,7 +1528,7 @@ LineResults.indexOfElement = function DVGS_indexOFElement(aElement) {
SourceResults.size =
LineResults.size = function DVGS_size() {
let count = 0;
for (let [_, item] of this._itemsByElement) {
for (let [, item] of this._itemsByElement) {
if (!item.nonenumerable) {
count++;
}

View File

@ -884,6 +884,7 @@ FilterView.prototype = {
// Perform a variable search based on the specified operator.
if (isVariable) {
DebuggerView.Variables.performSearch(token);
DebuggerView.Variables.expandFirstSearchResults();
return;
}
@ -913,7 +914,6 @@ FilterView.prototype = {
_doSearch: function DVF__doSearch(aOperator = "") {
this._searchbox.focus();
this._searchbox.value = aOperator;
DebuggerView.GlobalSearch.clearView();
},
/**
@ -952,6 +952,7 @@ FilterView.prototype = {
* Called when the variable search filter key sequence was pressed.
*/
_doVariableSearch: function DVF__doVariableSearch() {
DebuggerView.Variables.performSearch("");
this._doSearch(SEARCH_VARIABLE_FLAG);
this._searchboxPanel.hidePopup();
},

View File

@ -680,7 +680,7 @@ MenuContainer.prototype = {
this._container.removeAttribute("tooltiptext");
this._container.removeAllItems();
for (let [_, item] of this._itemsByElement) {
for (let [, item] of this._itemsByElement) {
this._untangleItem(item);
}
@ -1057,7 +1057,7 @@ MenuContainer.prototype = {
* A generator-iterator over all the items in this container.
*/
__iterator__: function DVMC_iterator() {
for (let [_, item] of this._itemsByElement) {
for (let [, item] of this._itemsByElement) {
yield item;
}
},

View File

@ -13,14 +13,10 @@
overflow-y: auto;
}
.dbg-results-container {
.dbg-results-container:not([open]) {
display: none;
}
.dbg-results-container[open] {
display: -moz-box;
}
/**
* Stack frames
*/
@ -48,21 +44,18 @@
*/
#variables {
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
/**
* Scope, variable and property elements
*/
#variables .details {
#variables .details:not([open]) {
display: none;
}
#variables .details[open] {
display: -moz-box;
}
.scope[non-header] > .title,
.variable[non-header] > .title,
.property[non-header] > .title {

View File

@ -39,6 +39,9 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_propertyview-filter-03.js \
browser_dbg_propertyview-filter-04.js \
browser_dbg_propertyview-filter-05.js \
browser_dbg_propertyview-filter-06.js \
browser_dbg_propertyview-filter-07.js \
browser_dbg_propertyview-filter-08.js \
browser_dbg_propertyview-reexpand.js \
browser_dbg_reload-same-script.js \
browser_dbg_pane-collapse.js \

View File

@ -36,68 +36,82 @@ function testNonEnumProperties() {
}
});
// Expand the variable.
testScope.expand();
testVar.expand();
let details = testVar._enum;
let nonenum = testVar._nonenum;
executeSoon(function() {
let details = testVar._enum;
let nonenum = testVar._nonenum;
is(details.childNodes.length, 1,
"There should be just one property in the .details container.");
is(details.childNodes.length, 1,
"There should be just one property in the .details container.");
ok(details.hasAttribute("open"),
".details container should be visible.");
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.");
ok(nonenum.hasAttribute("open"),
".nonenum container should be visible.");
is(nonenum.childNodes.length, 1,
"There should be just one property in the .nonenum container.");
// Uncheck 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "false");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
// Uncheck 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "false");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
ok(details.hasAttribute("open"),
".details container should stay visible.");
executeSoon(function() {
ok(details.hasAttribute("open"),
".details container should stay visible.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should become hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should become hidden.");
// Check 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "true");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
// Check 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "true");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
ok(details.hasAttribute("open"),
".details container should stay visible.");
executeSoon(function() {
ok(details.hasAttribute("open"),
".details container should stay visible.");
ok(nonenum.hasAttribute("open"),
".nonenum container should become visible.");
ok(nonenum.hasAttribute("open"),
".nonenum container should become visible.");
testVar.collapse();
// Collapse the variable.
testVar.collapse();
ok(!details.hasAttribute("open"),
".details container should be hidden.");
executeSoon(function() {
ok(!details.hasAttribute("open"),
".details container should be hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should be hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should be hidden.");
// Uncheck 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "false");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
// Uncheck 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "false");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
ok(!details.hasAttribute("open"),
".details container should stay hidden.");
executeSoon(function() {
ok(!details.hasAttribute("open"),
".details container should stay hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should stay hidden.");
ok(!nonenum.hasAttribute("open"),
".nonenum container should stay hidden.");
// Check 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "true");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
// Check 'show hidden properties'.
gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "true");
gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
gDebugger.DebuggerController.activeThread.resume(function() {
closeDebuggerAndFinish();
executeSoon(function() {
gDebugger.DebuggerController.activeThread.resume(function() {
closeDebuggerAndFinish();
});
});
});
});
});
});
});
}}, 0);
});

View File

@ -13,6 +13,8 @@ var gTab = null;
var gDebugger = null;
var gCount = 0;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
@ -20,6 +22,8 @@ function test()
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
gDebugger.DebuggerView.Variables.nonEnumVisible = false;
testWithFrame();
});
}

View File

@ -92,9 +92,9 @@ function testFrameParameters()
window.clearInterval(intervalID);
return resumeAndFinish();
}
if (!thisNode.fetched ||
!argumentsNode.fetched ||
!cNode.fetched) {
if (!thisNode._retrieved ||
!argumentsNode._retrieved ||
!cNode._retrieved) {
return;
}
window.clearInterval(intervalID);

View File

@ -12,6 +12,8 @@ var gPane = null;
var gTab = null;
var gDebugger = null;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
@ -19,6 +21,8 @@ function test()
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
gDebugger.DebuggerView.Variables.nonEnumVisible = false;
testFrameParameters();
});
}

View File

@ -12,6 +12,8 @@ var gPane = null;
var gTab = null;
var gDebugger = null;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
@ -19,6 +21,8 @@ function test()
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
gDebugger.DebuggerView.Variables.nonEnumVisible = false;
testWithFrame();
});
}

View File

@ -3,12 +3,15 @@
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
requestLongerTimeout(2);
function test() {
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
@ -17,6 +20,8 @@ function test() {
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
gDebugger.DebuggerView.Variables.nonEnumVisible = false;
testFrameEval();
});
}

View File

@ -24,6 +24,7 @@ function test()
gDebugger = gPane.contentWindow;
gDebuggee = aDebuggee;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
testSearchbox();
prepareVariables(testVariablesFiltering);
});
@ -121,6 +122,8 @@ function testVariablesFiltering()
"The local scope 'this.window.document.location' should be expanded");
ignoreExtraMatchedProperties();
locationItem.toggle();
locationItem.toggle();
is(innerScope.querySelectorAll(".variable:not([non-match])").length, 1,
"There should be 1 variable displayed in the inner scope");
@ -207,6 +210,8 @@ function testVariablesFiltering()
"The local scope 'this.window.document.location' should be expanded");
ignoreExtraMatchedProperties();
locationItem.toggle();
locationItem.toggle();
is(innerScope.querySelectorAll(".variable:not([non-match])").length, 1,
"There should be 1 variable displayed in the inner scope");
@ -317,6 +322,22 @@ function prepareVariables(aCallback)
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
is(innerScopeItem.expanded, true,
"The innerScope expanded getter should return true");
is(mathScopeItem.expanded, true,
"The mathScope expanded getter should return true");
is(testScopeItem.expanded, true,
"The testScope expanded getter should return true");
is(loadScopeItem.expanded, true,
"The loadScope expanded getter should return true");
is(globalScopeItem.expanded, true,
"The globalScope expanded getter should return true");
mathScopeItem.collapse();
testScopeItem.collapse();
loadScopeItem.collapse();
globalScopeItem.collapse();
is(innerScopeItem.expanded, true,
"The innerScope expanded getter should return true");
is(mathScopeItem.expanded, false,
@ -443,7 +464,7 @@ function prepareVariables(aCallback)
function ignoreExtraMatchedProperties()
{
for (let [_, item] of gDebugger.DebuggerView.Variables._currHierarchy) {
for (let [, item] of gDebugger.DebuggerView.Variables._currHierarchy) {
let name = item.name.toLowerCase();
let value = item._valueString || "";

View File

@ -24,6 +24,7 @@ function test()
gDebugger = gPane.contentWindow;
gDebuggee = aDebuggee;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
testSearchbox();
prepareVariables(testVariablesFiltering);
});
@ -69,6 +70,11 @@ function testVariablesFiltering()
is(locationItem.expanded, true,
"The local scope 'this.window.document.location' should be expanded");
locationItem.toggle();
locationItem.toggle();
documentItem.toggle();
documentItem.toggle();
is(innerScope.querySelectorAll(".variable:not([non-match])").length, 1,
"There should be 1 variable displayed in the inner scope");
is(mathScope.querySelectorAll(".variable:not([non-match])").length, 0,
@ -150,6 +156,19 @@ function testVariablesFiltering()
write("htmldocument");
is(thisItem.expanded, true,
"The local scope 'this' should be expanded");
is(windowItem.expanded, true,
"The local scope 'this.window' should be expanded");
is(documentItem.expanded, true,
"The local scope 'this.window.document' should be expanded");
is(locationItem.expanded, false,
"The local scope 'this.window.document.location' should not be expanded");
documentItem.toggle();
documentItem.toggle();
locationItem.toggle();
is(innerScope.querySelectorAll(".variable:not([non-match])").length, 1,
"There should be 1 variable displayed in the inner scope");
is(mathScope.querySelectorAll(".variable:not([non-match])").length, 0,
@ -265,6 +284,22 @@ function prepareVariables(aCallback)
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
is(innerScopeItem.expanded, true,
"The innerScope expanded getter should return true");
is(mathScopeItem.expanded, true,
"The mathScope expanded getter should return true");
is(testScopeItem.expanded, true,
"The testScope expanded getter should return true");
is(loadScopeItem.expanded, true,
"The loadScope expanded getter should return true");
is(globalScopeItem.expanded, true,
"The globalScope expanded getter should return true");
mathScopeItem.collapse();
testScopeItem.collapse();
loadScopeItem.collapse();
globalScopeItem.collapse();
is(innerScopeItem.expanded, true,
"The innerScope expanded getter should return true");
is(mathScopeItem.expanded, false,

View File

@ -3,7 +3,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure that the property view correctly filters nodes by value.
* Make sure that the property view correctly filters nodes.
*/
const TAB_URL = EXAMPLE_URL + "browser_dbg_with-frame.html";
@ -14,6 +14,8 @@ var gDebugger = null;
var gDebuggee = null;
var gSearchBox = null;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
@ -22,6 +24,7 @@ function test()
gDebugger = gPane.contentWindow;
gDebuggee = aDebuggee;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
prepareVariables(testVariablesFiltering);
});
}
@ -258,7 +261,7 @@ function prepareVariables(aCallback)
function ignoreExtraMatchedProperties()
{
for (let [_, item] of gDebugger.DebuggerView.Variables._currHierarchy) {
for (let [, item] of gDebugger.DebuggerView.Variables._currHierarchy) {
let name = item.name.toLowerCase();
let value = item._valueString || "";

View File

@ -0,0 +1,248 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure that the property view correctly filters nodes.
*/
const TAB_URL = EXAMPLE_URL + "browser_dbg_with-frame.html";
var gPane = null;
var gTab = null;
var gDebugger = null;
var gDebuggee = null;
var gSearchBox = null;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebuggee = aDebuggee;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = false;
prepareVariables(testVariablesFiltering);
});
}
function testVariablesFiltering()
{
let f = {
test1: function()
{
assertExpansion(1, [true, false, false, false, false]);
clear();
},
test2: function()
{
assertExpansion(2, [true, false, false, false, false]);
EventUtils.sendKey("RETURN");
},
test3: function()
{
assertExpansion(3, [true, false, false, false, false]);
gDebugger.editor.focus();
},
test4: function()
{
assertExpansion(4, [true, false, false, false, false]);
write("*");
},
test5: function() {
assertExpansion(5, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test6: function() {
assertExpansion(6, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test7: function() {
assertExpansion(7, [true, true, true, true, true]);
backspace(1);
},
test8: function() {
assertExpansion(8, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test9: function() {
assertExpansion(9, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test10: function() {
assertExpansion(10, [true, true, true, true, true]);
innerScopeItem.collapse();
mathScopeItem.collapse();
testScopeItem.collapse();
loadScopeItem.collapse();
globalScopeItem.collapse();
},
test11: function() {
assertExpansion(11, [false, false, false, false, false]);
clear();
},
test12: function() {
assertExpansion(12, [false, false, false, false, false]);
EventUtils.sendKey("RETURN");
},
test13: function() {
assertExpansion(13, [false, false, false, false, false]);
gDebugger.editor.focus();
},
test14: function() {
assertExpansion(14, [false, false, false, false, false]);
write("*");
},
test15: function() {
assertExpansion(15, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test16: function() {
assertExpansion(16, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test17: function() {
assertExpansion(17, [true, true, true, true, true]);
backspace(1);
},
test18: function() {
assertExpansion(18, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test19: function() {
assertExpansion(19, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test20: function() {
assertExpansion(20, [true, true, true, true, true]);
}
};
function assertExpansion(n, array) {
is(innerScopeItem.expanded, array[0],
"The innerScope should " + (array[0] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(mathScopeItem.expanded, array[1],
"The mathScope should " + (array[1] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(testScopeItem.expanded, array[2],
"The testScope should " + (array[2] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(loadScopeItem.expanded, array[3],
"The loadScope should " + (array[3] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(globalScopeItem.expanded, array[4],
"The globalScope should " + (array[4] ? "" : "not ") +
"be expanded at this point (" + n + ")");
}
var scopes = gDebugger.DebuggerView.Variables._list,
innerScope = scopes.querySelectorAll(".scope")[0],
mathScope = scopes.querySelectorAll(".scope")[1],
testScope = scopes.querySelectorAll(".scope")[2],
loadScope = scopes.querySelectorAll(".scope")[3],
globalScope = scopes.querySelectorAll(".scope")[4];
let innerScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
innerScope.querySelector(".name").getAttribute("value"));
let mathScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
mathScope.querySelector(".name").getAttribute("value"));
let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
testScope.querySelector(".name").getAttribute("value"));
let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
loadScope.querySelector(".name").getAttribute("value"));
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
executeSoon(function() {
for (let i = 1; i <= Object.keys(f).length; i++) {
f["test" + i]();
}
closeDebuggerAndFinish();
});
}
function prepareVariables(aCallback)
{
let count = 0;
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
// We expect 2 Debugger:FetchedVariables events, one from the inner object
// scope and the regular one.
if (++count < 2) {
info("Number of received Debugger:FetchedVariables events: " + count);
return;
}
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._container._list,
scopes = gDebugger.DebuggerView.Variables._list,
innerScope = scopes.querySelectorAll(".scope")[0],
mathScope = scopes.querySelectorAll(".scope")[1],
testScope = scopes.querySelectorAll(".scope")[2],
loadScope = scopes.querySelectorAll(".scope")[3],
globalScope = scopes.querySelectorAll(".scope")[4];
let innerScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
innerScope.querySelector(".name").getAttribute("value"));
let mathScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
mathScope.querySelector(".name").getAttribute("value"));
let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
testScope.querySelector(".name").getAttribute("value"));
let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
loadScope.querySelector(".name").getAttribute("value"));
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
executeSoon(function() {
aCallback();
});
}}, 0);
}, false);
EventUtils.sendMouseEvent({ type: "click" },
gDebuggee.document.querySelector("button"),
gDebuggee.window);
}
function clear() {
gSearchBox.focus();
gSearchBox.value = "";
}
function write(text) {
clear();
append(text);
}
function backspace(times) {
for (let i = 0; i < times; i++) {
EventUtils.sendKey("BACK_SPACE")
}
}
function append(text) {
gSearchBox.focus();
for (let i = 0; i < text.length; i++) {
EventUtils.sendChar(text[i]);
}
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebugger = null;
gDebuggee = null;
gSearchBox = null;
});

View File

@ -0,0 +1,253 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure that the property view correctly filters nodes.
*/
const TAB_URL = EXAMPLE_URL + "browser_dbg_with-frame.html";
var gPane = null;
var gTab = null;
var gDebugger = null;
var gDebuggee = null;
var gSearchBox = null;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebuggee = aDebuggee;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
prepareVariables(testVariablesFiltering);
});
}
function testVariablesFiltering()
{
let f = {
test1: function()
{
assertExpansion(1, [true, false, false, false, false]);
clear();
},
test2: function()
{
assertExpansion(2, [true, false, false, false, false]);
EventUtils.sendKey("RETURN");
},
test3: function()
{
assertExpansion(3, [true, false, false, false, false]);
gDebugger.editor.focus();
},
test4: function()
{
assertExpansion(4, [true, false, false, false, false]);
write("*");
},
test5: function() {
assertExpansion(5, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test6: function() {
assertExpansion(6, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test7: function() {
assertExpansion(7, [true, true, true, true, true]);
backspace(1);
},
test8: function() {
assertExpansion(8, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test9: function() {
assertExpansion(9, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test10: function() {
assertExpansion(10, [true, true, true, true, true]);
innerScopeItem.collapse();
mathScopeItem.collapse();
testScopeItem.collapse();
loadScopeItem.collapse();
globalScopeItem.collapse();
},
test11: function() {
assertExpansion(11, [false, false, false, false, false]);
clear();
},
test12: function() {
assertExpansion(12, [false, false, false, false, false]);
EventUtils.sendKey("RETURN");
},
test13: function() {
assertExpansion(13, [false, false, false, false, false]);
gDebugger.editor.focus();
},
test14: function() {
assertExpansion(14, [false, false, false, false, false]);
write("*");
},
test15: function() {
assertExpansion(15, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test16: function() {
assertExpansion(16, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test17: function() {
assertExpansion(17, [true, true, true, true, true]);
backspace(1);
},
test18: function() {
assertExpansion(18, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
},
test19: function() {
assertExpansion(19, [true, true, true, true, true]);
gDebugger.editor.focus();
},
test20: function() {
assertExpansion(20, [true, true, true, true, true]);
}
};
function assertExpansion(n, array) {
is(innerScopeItem.expanded, array[0],
"The innerScope should " + (array[0] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(mathScopeItem.expanded, array[1],
"The mathScope should " + (array[1] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(testScopeItem.expanded, array[2],
"The testScope should " + (array[2] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(loadScopeItem.expanded, array[3],
"The loadScope should " + (array[3] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(globalScopeItem.expanded, array[4],
"The globalScope should " + (array[4] ? "" : "not ") +
"be expanded at this point (" + n + ")");
}
var scopes = gDebugger.DebuggerView.Variables._list,
innerScope = scopes.querySelectorAll(".scope")[0],
mathScope = scopes.querySelectorAll(".scope")[1],
testScope = scopes.querySelectorAll(".scope")[2],
loadScope = scopes.querySelectorAll(".scope")[3],
globalScope = scopes.querySelectorAll(".scope")[4];
let innerScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
innerScope.querySelector(".name").getAttribute("value"));
let mathScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
mathScope.querySelector(".name").getAttribute("value"));
let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
testScope.querySelector(".name").getAttribute("value"));
let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
loadScope.querySelector(".name").getAttribute("value"));
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
executeSoon(function() {
for (let i = 1; i <= Object.keys(f).length; i++) {
f["test" + i]();
}
closeDebuggerAndFinish();
});
}
function prepareVariables(aCallback)
{
let count = 0;
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
// We expect 2 Debugger:FetchedVariables events, one from the inner object
// scope and the regular one.
if (++count < 2) {
info("Number of received Debugger:FetchedVariables events: " + count);
return;
}
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._container._list,
scopes = gDebugger.DebuggerView.Variables._list,
innerScope = scopes.querySelectorAll(".scope")[0],
mathScope = scopes.querySelectorAll(".scope")[1],
testScope = scopes.querySelectorAll(".scope")[2],
loadScope = scopes.querySelectorAll(".scope")[3],
globalScope = scopes.querySelectorAll(".scope")[4];
let innerScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
innerScope.querySelector(".name").getAttribute("value"));
let mathScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
mathScope.querySelector(".name").getAttribute("value"));
let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
testScope.querySelector(".name").getAttribute("value"));
let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
loadScope.querySelector(".name").getAttribute("value"));
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
executeSoon(function() {
aCallback();
});
}}, 0);
}, false);
EventUtils.sendMouseEvent({ type: "click" },
gDebuggee.document.querySelector("button"),
gDebuggee.window);
}
function clear() {
gSearchBox.focus();
gSearchBox.value = "";
}
function write(text) {
clear();
append(text);
}
function backspace(times) {
for (let i = 0; i < times; i++) {
EventUtils.sendKey("BACK_SPACE")
}
}
function append(text) {
gSearchBox.focus();
for (let i = 0; i < text.length; i++) {
EventUtils.sendChar(text[i]);
}
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebugger = null;
gDebuggee = null;
gSearchBox = null;
});

View File

@ -0,0 +1,323 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure that the property view correctly filters nodes.
*/
const TAB_URL = EXAMPLE_URL + "browser_dbg_with-frame.html";
var gPane = null;
var gTab = null;
var gDebugger = null;
var gDebuggee = null;
var gSearchBox = null;
requestLongerTimeout(2);
function test()
{
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gPane = aPane;
gDebugger = gPane.contentWindow;
gDebuggee = aDebuggee;
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
prepareVariables(testVariablesFiltering);
});
}
function testVariablesFiltering()
{
let f = {
test1: function(aCallback)
{
assertExpansion(1, [true, false, false, false, false]);
write("*arguments");
aCallback();
},
test2: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, false,
"The arguments pseudoarray in the testScope should not be expanded");
is(loadScopeItem.get("arguments").expanded, false,
"The arguments pseudoarray in the testScope should not be expanded");
assertExpansion(1, [true, true, true, true, true]);
EventUtils.sendKey("RETURN");
aCallback();
},
test3: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
is(loadScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
waitForFetchedProperties(2, function() {
is(testScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the testScope should have 4 visible properties");
is(loadScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the loadScope should have 4 visible properties");
assertExpansion(2, [true, true, true, true, true]);
backspace(1);
aCallback();
});
},
test4: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
is(loadScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
waitForFetchedProperties(0, function() {
is(testScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the testScope should have 4 visible properties");
is(loadScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the loadScope should have 4 visible properties");
assertExpansion(3, [true, true, true, true, true]);
backspace(8);
aCallback();
});
},
test5: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
is(loadScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
waitForFetchedProperties(0, function() {
is(testScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the testScope should have 4 visible properties");
is(loadScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the loadScope should have 4 visible properties");
assertExpansion(4, [true, true, true, true, true]);
backspace(1);
aCallback();
});
},
test6: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
is(loadScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
waitForFetchedProperties(0, function() {
is(testScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the testScope should have 4 visible properties");
is(loadScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the loadScope should have 4 visible properties");
assertExpansion(5, [true, true, true, true, true]);
write("*");
aCallback();
});
},
test7: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
is(loadScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
waitForFetchedProperties(0, function() {
is(testScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the testScope should have 4 visible properties");
is(loadScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 4,
"The arguments in the loadScope should have 4 visible properties");
assertExpansion(5, [true, true, true, true, true]);
append("arguments");
aCallback();
});
},
test8: function(aCallback)
{
is(testScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
is(loadScopeItem.get("arguments").expanded, true,
"The arguments pseudoarray in the testScope should now be expanded");
waitForFetchedProperties(0, function() {
is(testScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 0,
"The arguments in the testScope should have 0 visible properties");
is(loadScopeItem.get("arguments").target.querySelectorAll(".property:not([non-match])").length, 0,
"The arguments in the loadScope should have 0 visible properties");
assertExpansion(5, [true, true, true, true, true]);
aCallback();
});
},
};
function assertExpansion(n, array) {
is(innerScopeItem.expanded, array[0],
"The innerScope should " + (array[0] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(mathScopeItem.expanded, array[1],
"The mathScope should " + (array[1] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(testScopeItem.expanded, array[2],
"The testScope should " + (array[2] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(loadScopeItem.expanded, array[3],
"The loadScope should " + (array[3] ? "" : "not ") +
"be expanded at this point (" + n + ")");
is(globalScopeItem.expanded, array[4],
"The globalScope should " + (array[4] ? "" : "not ") +
"be expanded at this point (" + n + ")");
}
function waitForFetchedProperties(n, aCallback) {
if (n == 0) {
aCallback();
return;
}
let count = 0;
gDebugger.addEventListener("Debugger:FetchedProperties", function test() {
// We expect n Debugger:FetchedProperties events.
if (++count < n) {
info("Number of received Debugger:FetchedVariables events: " + count);
return;
}
gDebugger.removeEventListener("Debugger:FetchedProperties", test, false);
Services.tm.currentThread.dispatch({ run: function() {
executeSoon(aCallback);
}}, 0);
}, false);
}
var scopes = gDebugger.DebuggerView.Variables._list,
innerScope = scopes.querySelectorAll(".scope")[0],
mathScope = scopes.querySelectorAll(".scope")[1],
testScope = scopes.querySelectorAll(".scope")[2],
loadScope = scopes.querySelectorAll(".scope")[3],
globalScope = scopes.querySelectorAll(".scope")[4];
let innerScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
innerScope.querySelector(".name").getAttribute("value"));
let mathScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
mathScope.querySelector(".name").getAttribute("value"));
let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
testScope.querySelector(".name").getAttribute("value"));
let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
loadScope.querySelector(".name").getAttribute("value"));
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
executeSoon(function() {
f.test1(function() {
f.test2(function() {
f.test3(function() {
f.test4(function() {
f.test5(function() {
f.test6(function() {
f.test7(function() {
f.test8(function() {
closeDebuggerAndFinish();
});
});
});
});
});
});
});
});
});
}
function prepareVariables(aCallback)
{
let count = 0;
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
// We expect 2 Debugger:FetchedVariables events, one from the inner object
// scope and the regular one.
if (++count < 2) {
info("Number of received Debugger:FetchedVariables events: " + count);
return;
}
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._container._list,
scopes = gDebugger.DebuggerView.Variables._list,
innerScope = scopes.querySelectorAll(".scope")[0],
mathScope = scopes.querySelectorAll(".scope")[1],
testScope = scopes.querySelectorAll(".scope")[2],
loadScope = scopes.querySelectorAll(".scope")[3],
globalScope = scopes.querySelectorAll(".scope")[4];
let innerScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
innerScope.querySelector(".name").getAttribute("value"));
let mathScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
mathScope.querySelector(".name").getAttribute("value"));
let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
testScope.querySelector(".name").getAttribute("value"));
let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
loadScope.querySelector(".name").getAttribute("value"));
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
executeSoon(function() {
aCallback();
});
}}, 0);
}, false);
EventUtils.sendMouseEvent({ type: "click" },
gDebuggee.document.querySelector("button"),
gDebuggee.window);
}
function clear() {
gSearchBox.focus();
gSearchBox.value = "";
}
function write(text) {
clear();
append(text);
}
function backspace(times) {
for (let i = 0; i < times; i++) {
EventUtils.sendKey("BACK_SPACE")
}
}
function append(text) {
gSearchBox.focus();
for (let i = 0; i < text.length; i++) {
EventUtils.sendChar(text[i]);
}
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebugger = null;
gDebuggee = null;
gSearchBox = null;
});

View File

@ -35,7 +35,9 @@ function addBreakpoint()
}, function() {
// Wait for the resume...
gDebugger.gClient.addOneTimeListener("resumed", function() {
gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
gDebugger.DebuggerView.Variables.nonEnumVisible = false;
gDebugger.DebuggerView.Variables.commitHierarchyIgnoredItems = Object.create(null);
testVariablesExpand();
});
});
@ -73,6 +75,44 @@ function testVariablesExpand()
let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
globalScope.querySelector(".name").getAttribute("value"));
is(innerScope.querySelector(".arrow").hasAttribute("open"), true,
"The innerScope arrow should initially be expanded");
is(mathScope.querySelector(".arrow").hasAttribute("open"), true,
"The mathScope arrow should initially be expanded");
is(testScope.querySelector(".arrow").hasAttribute("open"), true,
"The testScope arrow should initially be expanded");
is(loadScope.querySelector(".arrow").hasAttribute("open"), true,
"The loadScope arrow should initially be expanded");
is(globalScope.querySelector(".arrow").hasAttribute("open"), true,
"The globalScope arrow should initially be expanded");
is(innerScope.querySelector(".details").hasAttribute("open"), true,
"The innerScope enumerables should initially be expanded");
is(mathScope.querySelector(".details").hasAttribute("open"), true,
"The mathScope enumerables should initially be expanded");
is(testScope.querySelector(".details").hasAttribute("open"), true,
"The testScope enumerables should initially be expanded");
is(loadScope.querySelector(".details").hasAttribute("open"), true,
"The loadScope enumerables should initially be expanded");
is(globalScope.querySelector(".details").hasAttribute("open"), true,
"The globalScope enumerables should initially be expanded");
is(innerScopeItem.expanded, true,
"The innerScope expanded getter should return true");
is(mathScopeItem.expanded, true,
"The mathScope expanded getter should return true");
is(testScopeItem.expanded, true,
"The testScope expanded getter should return true");
is(loadScopeItem.expanded, true,
"The loadScope expanded getter should return true");
is(globalScopeItem.expanded, true,
"The globalScope expanded getter should return true");
mathScopeItem.collapse();
testScopeItem.collapse();
loadScopeItem.collapse();
globalScopeItem.collapse();
is(innerScope.querySelector(".arrow").hasAttribute("open"), true,
"The innerScope arrow should initially be expanded");
is(mathScope.querySelector(".arrow").hasAttribute("open"), false,

View File

@ -139,7 +139,7 @@ VariablesView.prototype = {
set enumVisible(aFlag) {
this._enumVisible = aFlag;
for (let [_, scope] in this) {
for (let [, scope] in this) {
scope._nonEnumVisible = aFlag;
}
},
@ -151,7 +151,7 @@ VariablesView.prototype = {
set nonEnumVisible(aFlag) {
this._nonEnumVisible = aFlag;
for (let [_, scope] in this) {
for (let [, scope] in this) {
scope._nonEnumVisible = aFlag;
}
},
@ -216,13 +216,17 @@ VariablesView.prototype = {
* The variable or property to search for.
*/
performSearch: function VV_performSerch(aQuery) {
if (!aQuery) {
for (let [_, item] of this._currHierarchy) {
item._match = true;
}
} else {
for (let [_, scope] in this) {
scope._performSearch(aQuery.toLowerCase());
for (let [, scope] in this) {
switch (aQuery) {
case "":
scope.expand();
// fall through
case null:
scope._performSearch("");
break;
default:
scope._performSearch(aQuery.toLowerCase());
break;
}
}
},
@ -231,8 +235,8 @@ VariablesView.prototype = {
* Expands the first search results in this container.
*/
expandFirstSearchResults: function VV_expandFirstSearchResults() {
for (let [_, scope] in this) {
for (let [_, variable] in scope) {
for (let [, scope] in this) {
for (let [, variable] in scope) {
if (variable._isMatch) {
variable.expand();
break;
@ -366,6 +370,8 @@ function Scope(aView, aName, aFlags = {}) {
this.expand = this.expand.bind(this);
this.collapse = this.collapse.bind(this);
this.toggle = this.toggle.bind(this);
this._openEnum = this._openEnum.bind(this);
this._openNonEnum = this._openNonEnum.bind(this);
this.ownerView = aView;
this.eval = aView.eval;
@ -443,24 +449,16 @@ Scope.prototype = {
/**
* Expands the scope, showing all the added details.
*
* @param boolean aSkipAnimationFlag
* Pass true to not show an opening animation.
*/
expand: function S_expand(aSkipAnimationFlag) {
expand: function S_expand() {
if (this._isExpanded || this._locked) {
return;
}
if (this._variablesView._enumVisible) {
this._arrow.setAttribute("open", "");
this._enum.setAttribute("open", "");
this._openEnum();
}
if (this._variablesView._nonEnumVisible) {
this._nonenum.setAttribute("open", "");
}
if (!aSkipAnimationFlag) {
this._enum.setAttribute("animated", "");
this._nonenum.setAttribute("animated", "");
Services.tm.currentThread.dispatch({ run: this._openNonEnum }, 0);
}
this._isExpanded = true;
@ -479,8 +477,6 @@ Scope.prototype = {
this._arrow.removeAttribute("open");
this._enum.removeAttribute("open");
this._nonenum.removeAttribute("open");
this._enum.removeAttribute("animated");
this._nonenum.removeAttribute("animated");
this._isExpanded = false;
if (this.oncollapse) {
@ -495,6 +491,11 @@ Scope.prototype = {
this._wasToggled = true;
this.expanded ^= 1;
// Make sure the scope and its contents are visibile.
for (let [, variable] in this) {
variable.header = true;
variable._match = true;
}
if (this.ontoggle) {
this.ontoggle(this);
}
@ -504,6 +505,9 @@ Scope.prototype = {
* Shows the scope's title header.
*/
showHeader: function S_showHeader() {
if (this._isHeaderVisible) {
return;
}
this._target.removeAttribute("non-header");
this._isHeaderVisible = true;
},
@ -513,6 +517,9 @@ Scope.prototype = {
* This action will automatically expand the scope.
*/
hideHeader: function S_hideHeader() {
if (!this._isHeaderVisible) {
return;
}
this.expand();
this._target.setAttribute("non-header", "");
this._isHeaderVisible = false;
@ -522,6 +529,9 @@ Scope.prototype = {
* Shows the scope's expand/collapse arrow.
*/
showArrow: function S_showArrow() {
if (this._isArrowVisible) {
return;
}
this._arrow.removeAttribute("invisible");
this._isArrowVisible = true;
},
@ -530,6 +540,9 @@ Scope.prototype = {
* Hides the scope's expand/collapse arrow.
*/
hideArrow: function S_hideArrow() {
if (!this._isArrowVisible) {
return;
}
this._arrow.setAttribute("invisible", "");
this._isArrowVisible = false;
},
@ -663,12 +676,35 @@ Scope.prototype = {
this._title.addEventListener("mousedown", this.toggle, false);
},
/**
* Adds an event listener for the mouse over event on the title element.
* @param function aCallback
*/
set onmouseover(aCallback) {
this._title.addEventListener("mouseover", aCallback, false);
},
/**
* Opens the enumerable items container.
*/
_openEnum: function S__openEnum() {
this._arrow.setAttribute("open", "");
this._enum.setAttribute("open", "");
},
/**
* Opens the non-enumerable items container.
*/
_openNonEnum: function S__openNonEnum() {
this._nonenum.setAttribute("open", "");
},
/**
* Specifies if enumerable properties and variables should be displayed.
* @param boolean aFlag
*/
set _enumVisible(aFlag) {
for (let [_, variable] in this) {
for (let [, variable] in this) {
variable._enumVisible = aFlag;
if (!this.expanded) {
@ -687,7 +723,7 @@ Scope.prototype = {
* @param boolean aFlag
*/
set _nonEnumVisible(aFlag) {
for (let [_, variable] in this) {
for (let [, variable] in this) {
variable._nonEnumVisible = aFlag;
if (!this.expanded) {
@ -709,7 +745,7 @@ Scope.prototype = {
* The lowercased name of the variable or property to search for.
*/
_performSearch: function S__performSearch(aLowerCaseQuery) {
for (let [_, variable] in this) {
for (let [, variable] in this) {
let currentObject = variable;
let lowerCaseName = variable._nameString.toLowerCase();
let lowerCaseValue = variable._valueString.toLowerCase();
@ -727,8 +763,11 @@ Scope.prototype = {
// contain some matched properties, so make sure they're visible
// ("expand downwards").
if (variable._wasToggled) {
variable.expand(true);
if (variable._wasToggled && aLowerCaseQuery) {
variable.expand();
}
if (variable._isExpanded && !aLowerCaseQuery) {
variable._wasToggled = true;
}
// If the variable is contained in another scope (variable or property),
@ -742,12 +781,14 @@ Scope.prototype = {
// Show and expand the parent, as it is certainly accessible.
variable._match = true;
variable.expand(true);
aLowerCaseQuery && variable.expand();
}
}
// Proceed with the search recursively inside this variable or property.
if (variable._wasToggled || variable.expanded || variable.getter || variable.setter) {
if (currentObject._wasToggled ||
currentObject.getter ||
currentObject.setter) {
currentObject._performSearch(aLowerCaseQuery);
}
}
@ -836,6 +877,7 @@ Scope.prototype = {
* The variable's descriptor.
*/
function Variable(aScope, aName, aDescriptor) {
this._displayTooltip = this._displayTooltip.bind(this);
this._activateInput = this._activateInput.bind(this);
this._deactivateInput = this._deactivateInput.bind(this);
this._saveInput = this._saveInput.bind(this);
@ -918,6 +960,7 @@ create({ constructor: Variable, proto: Scope.prototype }, {
if (this.fetched) {
return;
}
this.fetched = true;
// Sort all of the properties before adding them.
let sortedPropertyNames = Object.getOwnPropertyNames(aObject).sort();
@ -936,8 +979,6 @@ create({ constructor: Variable, proto: Scope.prototype }, {
if (prototype) {
this._addRawValueProperty("__proto__", {}, prototype);
}
this.fetched = true;
},
/**
@ -1056,7 +1097,7 @@ create({ constructor: Variable, proto: Scope.prototype }, {
this._idString = generateId(this._nameString = aName);
this._createScope(aName, "variable");
this._displayVariable(aDescriptor);
this._displayTooltip();
this._prepareTooltip();
this._setAttributes(aName, aDescriptor);
this._addEventListeners();
@ -1092,16 +1133,24 @@ create({ constructor: Variable, proto: Scope.prototype }, {
if (aDescriptor.get || aDescriptor.set) {
this.addProperty("get", { value: aDescriptor.get });
this.addProperty("set", { value: aDescriptor.set });
this.expand(true);
this.expand();
separatorLabel.hidden = true;
valueLabel.hidden = true;
}
},
/**
* Prepares a tooltip for this variable.
*/
_prepareTooltip: function V__prepareTooltip() {
this._target.addEventListener("mouseover", this._displayTooltip, false);
},
/**
* Creates a tooltip for this variable.
*/
_displayTooltip: function V__displayTooltip() {
this._target.removeEventListener("mouseover", this._displayTooltip, false);
let document = this.document;
let tooltip = document.createElement("tooltip");
@ -1173,6 +1222,8 @@ create({ constructor: Variable, proto: Scope.prototype }, {
if (!this.eval) {
return;
}
let window = this.window;
let document = this.document;
let title = this._title;
let valueLabel = this._valueLabel;
@ -1182,8 +1233,8 @@ create({ constructor: Variable, proto: Scope.prototype }, {
// element's value location.
let input = this.document.createElement("textbox");
input.setAttribute("value", initialString);
input.className = "element-input";
input.width = valueLabel.clientWidth + 1;
input.className = "plain element-input";
input.width = this._target.clientWidth;
title.removeChild(valueLabel);
title.appendChild(input);
@ -1238,9 +1289,11 @@ create({ constructor: Variable, proto: Scope.prototype }, {
this._deactivateInput(e);
if (initialString != currentString) {
this._arrow.setAttribute("invisible", "");
this._separatorLabel.hidden = true;
this._valueLabel.hidden = true;
this.collapse();
this._enum.hidden = true;
this._nonenum.hidden = true;
this.eval("(" + this._symbolicName + "=" + currentString + ")");
}
},
@ -1304,7 +1357,7 @@ create({ constructor: Property, proto: Variable.prototype }, {
this._idString = generateId(this._nameString = aName);
this._createScope(aName, "property");
this._displayVariable(aDescriptor);
this._displayTooltip();
this._prepareTooltip();
this._setAttributes(aName, aDescriptor);
this._addEventListeners();
@ -1350,29 +1403,34 @@ VariablesView.prototype.commitHierarchy = function VV_commitHierarchy() {
if (currVariable._committed) {
continue;
}
// Avoid performing expensive operations.
if (this.commitHierarchyIgnoredItems[currVariable._nameString]) {
continue;
}
// Try to get the previous instance of the inspected variable to
// determine the difference in state.
let prevVariable = prevHierarchy.get(absoluteName);
let expanded = false;
let changed = false;
// If the inspected variable existed in a previous hierarchy, check if
// the displayed value (a representation of the grip) has changed.
// the displayed value (a representation of the grip) has changed and if
// it was previously expanded.
if (prevVariable) {
let prevString = prevVariable._valueString;
let currString = currVariable._valueString;
changed = prevString != currString;
// Re-expand the variable if not previously collapsed.
if (prevVariable.expanded) {
currVariable.expand(true);
}
expanded = prevVariable._isExpanded;
changed = prevVariable._valueString != currVariable._valueString;
}
// Make sure this variable is not handled in ulteror commits for the
// same hierarchy.
currVariable._committed = true;
// Re-expand the variable if not previously collapsed.
if (expanded) {
currVariable._wasToggled = prevVariable._wasToggled;
currVariable.expand();
}
// This variable was either not changed or removed, no need to continue.
if (!changed) {
continue;
@ -1392,6 +1450,13 @@ VariablesView.prototype.commitHierarchy = function VV_commitHierarchy() {
}
};
// Some variables are likely to contain a very large number of properties.
// It would be a bad idea to re-expand them or perform expensive operations.
VariablesView.prototype.commitHierarchyIgnoredItems = Object.create(null, {
"window": { value: true },
"this": { value: true }
});
/**
* Returns true if the descriptor represents an undefined, null or
* primitive value.

View File

@ -145,7 +145,6 @@
}
.dbg-stackframe-name {
-moz-padding-end: 4px;
font-weight: 600;
}
@ -209,22 +208,14 @@
.variable {
-moz-margin-start: 1px;
-moz-margin-end: 1px;
transition: background 1s ease-in-out;
border-bottom: 1px solid #eee;
background: #fff;
}
.variable:not(:last-child) {
border-bottom: 1px dotted #ddd;
border-radius: 8px;
}
.variable:last-child {
margin-bottom: 2px;
transition: background 1s ease-in-out;
}
.variable[changed] {
transition-duration: 0.4s;
background: rgba(255, 255, 0, 0.65);
transition-duration: 0.4s;
}
.variable > .title > .name {
@ -245,14 +236,13 @@
*/
.property {
transition: background 1s ease-in-out;
background: #fff;
border-radius: 8px;
transition: background 1s ease-in-out;
}
.property[changed] {
transition-duration: 0.4s;
background: rgba(255, 255, 0, 0.65);
transition-duration: 0.4s;
}
.property > .title > .name {
@ -326,6 +316,14 @@
text-decoration: line-through;
}
/**
* Variables and properties editing
*/
#variables .element-input {
-moz-margin-start: 5px !important;
}
/**
* Variables and properties searching
*/
@ -386,30 +384,6 @@
visibility: hidden;
}
/**
* Animations
*/
#variables .details[open][animated],
#globalsearch .dbg-results-container[open][animated] {
animation-duration: 0.25s;
animation-name: showblock;
}
@keyframes showblock {
from {
opacity: 0;
transform-origin: top;
transform: scaleY(0);
}
to {
opacity: 1;
transform-origin: top;
transform: scaleY(1);
}
}
/**
* Toolbar Controls
*/

View File

@ -147,7 +147,6 @@
}
.dbg-stackframe-name {
-moz-padding-end: 4px;
font-weight: 600;
}
@ -211,22 +210,14 @@
.variable {
-moz-margin-start: 1px;
-moz-margin-end: 1px;
transition: background 1s ease-in-out;
border-bottom: 1px solid #eee;
background: #fff;
}
.variable:not(:last-child) {
border-bottom: 1px dotted #ddd;
border-radius: 8px;
}
.variable:last-child {
margin-bottom: 2px;
transition: background 1s ease-in-out;
}
.variable[changed] {
transition-duration: 0.4s;
background: rgba(255, 255, 0, 0.65);
transition-duration: 0.4s;
}
.variable > .title > .name {
@ -247,14 +238,13 @@
*/
.property {
transition: background 1s ease-in-out;
background: #fff;
border-radius: 8px;
transition: background 1s ease-in-out;
}
.property[changed] {
transition-duration: 0.4s;
background: rgba(255, 255, 0, 0.65);
transition-duration: 0.4s;
}
.property > .title > .name {
@ -328,6 +318,14 @@
text-decoration: line-through;
}
/**
* Variables and properties editing
*/
#variables .element-input {
-moz-margin-start: 5px !important;
}
/**
* Variables and properties searching
*/
@ -386,30 +384,6 @@
visibility: hidden;
}
/**
* Animations
*/
#variables .details[open][animated],
#globalsearch .dbg-results-container[open][animated] {
animation-duration: 0.25s;
animation-name: showblock;
}
@keyframes showblock {
from {
opacity: 0;
transform-origin: top;
transform: scaleY(0);
}
to {
opacity: 1;
transform-origin: top;
transform: scaleY(1);
}
}
/**
* Toolbar Controls
*/

View File

@ -153,7 +153,6 @@
}
.dbg-stackframe-name {
-moz-padding-end: 4px;
font-weight: 600;
}
@ -217,22 +216,14 @@
.variable {
-moz-margin-start: 1px;
-moz-margin-end: 1px;
transition: background 1s ease-in-out;
border-bottom: 1px solid #eee;
background: #fff;
}
.variable:not(:last-child) {
border-bottom: 1px dotted #ddd;
border-radius: 8px;
}
.variable:last-child {
margin-bottom: 2px;
transition: background 1s ease-in-out;
}
.variable[changed] {
transition-duration: 0.4s;
background: rgba(255, 255, 0, 0.65);
transition-duration: 0.4s;
}
.variable > .title > .name {
@ -253,14 +244,13 @@
*/
.property {
transition: background 1s ease-in-out;
background: #fff;
border-radius: 8px;
transition: background 1s ease-in-out;
}
.property[changed] {
transition-duration: 0.4s;
background: rgba(255, 255, 0, 0.65);
transition-duration: 0.4s;
}
.property > .title > .name {
@ -334,6 +324,14 @@
text-decoration: line-through;
}
/**
* Variables and properties editing
*/
#variables .element-input {
-moz-margin-start: 5px !important;
}
/**
* Variables and properties searching
*/
@ -397,30 +395,6 @@
visibility: hidden;
}
/**
* Animations
*/
#variables .details[open][animated],
#globalsearch .dbg-results-container[open][animated] {
animation-duration: 0.25s;
animation-name: showblock;
}
@keyframes showblock {
from {
opacity: 0;
transform-origin: top;
transform: scaleY(0);
}
to {
opacity: 1;
transform-origin: top;
transform: scaleY(1);
}
}
/**
* Toolbar Controls
*/