mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Bug 828987 - The Variables View should be keyboard accessible, r=past
This commit is contained in:
parent
a6f86fbbdc
commit
81f35d94fb
@ -1018,6 +1018,14 @@ FilterView.prototype = {
|
||||
this._searchboxPanel.hidePopup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the variables focus key sequence was pressed.
|
||||
*/
|
||||
_doVariablesFocus: function DVG__doVariablesFocus() {
|
||||
DebuggerView.showPanesSoon();
|
||||
DebuggerView.Variables.focusFirstVisibleNode();
|
||||
},
|
||||
|
||||
_searchbox: null,
|
||||
_searchboxPanel: null,
|
||||
_globalOperatorButton: null,
|
||||
|
@ -61,21 +61,20 @@
|
||||
* Scope, variable and property elements
|
||||
*/
|
||||
|
||||
#variables .details:not([open]) {
|
||||
.details:not([open]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scope,
|
||||
.variable,
|
||||
.property {
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
.scope[non-header] > .title,
|
||||
.variable[non-header] > .title,
|
||||
.property[non-header] > .title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables and properties searching
|
||||
*/
|
||||
|
||||
.variable[non-match] > .title,
|
||||
.property[non-header] > .title,
|
||||
.property[non-match] > .title {
|
||||
display: none;
|
||||
}
|
||||
|
@ -43,6 +43,8 @@
|
||||
oncommand="DebuggerView.Filtering._doLineSearch()"/>
|
||||
<command id="variableSearchCommand"
|
||||
oncommand="DebuggerView.Filtering._doVariableSearch()"/>
|
||||
<command id="variablesFocusCommand"
|
||||
oncommand="DebuggerView.Filtering._doVariablesFocus()"/>
|
||||
<command id="addBreakpointCommand"
|
||||
oncommand="DebuggerView.Breakpoints._onCmdAddBreakpoint()"/>
|
||||
<command id="addConditionalBreakpointCommand"
|
||||
@ -108,6 +110,11 @@
|
||||
accesskey="&debuggerUI.searchVariable.key;"
|
||||
key="variableSearchKey"
|
||||
command="variableSearchCommand"/>
|
||||
<menuitem id="se-dbg-cMenu-focusVariables"
|
||||
label="&debuggerUI.focusVariables;"
|
||||
accesskey="&debuggerUI.focusVariables.key;"
|
||||
key="variablesFocusKey"
|
||||
command="variablesFocusCommand"/>
|
||||
</menupopup>
|
||||
|
||||
<menupopup id="debuggerWatchExpressionsContextMenu">
|
||||
@ -184,6 +191,10 @@
|
||||
key="&debuggerUI.searchVariable.key;"
|
||||
modifiers="accel alt"
|
||||
command="variableSearchCommand"/>
|
||||
<key id="variablesFocusKey"
|
||||
key="&debuggerUI.focusVariables.key;"
|
||||
modifiers="accel shift"
|
||||
command="variablesFocusCommand"/>
|
||||
<key id="addBreakpointKey"
|
||||
key="&debuggerUI.seMenuBreak.key;"
|
||||
modifiers="accel"
|
||||
|
@ -139,8 +139,8 @@ function testSimpleCall() {
|
||||
testVar.target.querySelector(".title"),
|
||||
gDebugger);
|
||||
|
||||
ok(!testVar.expanded,
|
||||
"Clicking the testVar title div shouldn't expand it.");
|
||||
ok(testVar.expanded,
|
||||
"Clicking the testVar title div should expand it again.");
|
||||
|
||||
|
||||
testScope.show();
|
||||
@ -185,8 +185,8 @@ function testSimpleCall() {
|
||||
testVar.get("child").target.querySelector(".title"),
|
||||
gDebugger);
|
||||
|
||||
ok(!testVar.get("child").expanded,
|
||||
"Clicking the testVar child property title div shouldn't expand it.");
|
||||
ok(testVar.get("child").expanded,
|
||||
"Clicking the testVar child property title div should expand it again.");
|
||||
|
||||
|
||||
gDebugger.DebuggerView.Variables.empty();
|
||||
|
@ -74,9 +74,9 @@ function testFrameParameters()
|
||||
"The global scope should be collapsed by default.");
|
||||
|
||||
|
||||
let thisNode = gVars.getVariableOrPropertyForNode(localNodes[0]);
|
||||
let argumentsNode = gVars.getVariableOrPropertyForNode(localNodes[8]);
|
||||
let cNode = gVars.getVariableOrPropertyForNode(localNodes[10]);
|
||||
let thisNode = gVars.getItemForNode(localNodes[0]);
|
||||
let argumentsNode = gVars.getItemForNode(localNodes[8]);
|
||||
let cNode = gVars.getItemForNode(localNodes[10]);
|
||||
|
||||
is(thisNode.expanded, false,
|
||||
"The thisNode should not be expanded at this point.");
|
||||
@ -180,20 +180,20 @@ function testFrameParameters()
|
||||
"Should have the right value for 'c.c'.");
|
||||
|
||||
|
||||
is(gVars.getVariableOrPropertyForNode(
|
||||
is(gVars.getItemForNode(
|
||||
cNode.target.querySelectorAll(".property")[0]).target,
|
||||
cNode.target.querySelectorAll(".property")[0],
|
||||
"getVariableOrPropertyForNode([0]) didn't return the expected property.");
|
||||
"getItemForNode([0]) didn't return the expected property.");
|
||||
|
||||
is(gVars.getVariableOrPropertyForNode(
|
||||
is(gVars.getItemForNode(
|
||||
cNode.target.querySelectorAll(".property")[1]).target,
|
||||
cNode.target.querySelectorAll(".property")[1],
|
||||
"getVariableOrPropertyForNode([1]) didn't return the expected property.");
|
||||
"getItemForNode([1]) didn't return the expected property.");
|
||||
|
||||
is(gVars.getVariableOrPropertyForNode(
|
||||
is(gVars.getItemForNode(
|
||||
cNode.target.querySelectorAll(".property")[2]).target,
|
||||
cNode.target.querySelectorAll(".property")[2],
|
||||
"getVariableOrPropertyForNode([2]) didn't return the expected property.");
|
||||
"getItemForNode([2]) didn't return the expected property.");
|
||||
|
||||
|
||||
is(cNode.find(
|
||||
|
@ -21,6 +21,7 @@ function test()
|
||||
gDebugger = gPane.panelWin;
|
||||
gVariablesView = gDebugger.DebuggerView.Variables;
|
||||
|
||||
gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
|
||||
testVariablesView();
|
||||
});
|
||||
}
|
||||
@ -61,6 +62,9 @@ function testVariablesView()
|
||||
set someProp7(value) { arr[0] = value }
|
||||
};
|
||||
|
||||
gVariablesView.eval = function() {};
|
||||
gVariablesView.switch = function() {};
|
||||
gVariablesView.delete = function() {};
|
||||
gVariablesView.rawObject = test;
|
||||
|
||||
testHierarchy();
|
||||
@ -70,10 +74,6 @@ function testVariablesView()
|
||||
testThirdLevelContents();
|
||||
testIntegrity(arr, obj);
|
||||
|
||||
gVariablesView.eval = function() {};
|
||||
gVariablesView.switch = function() {};
|
||||
gVariablesView.delete = function() {};
|
||||
|
||||
let fooScope = gVariablesView.addScope("foo");
|
||||
let anonymousVar = fooScope.addVar();
|
||||
|
||||
@ -83,9 +83,14 @@ function testVariablesView()
|
||||
|
||||
testAnonymousHeaders(fooScope, anonymousVar, anonymousScope, barVar, bazProperty);
|
||||
testPropertyInheritance(fooScope, anonymousVar, anonymousScope, barVar, bazProperty);
|
||||
testClearHierarchy();
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
executeSoon(function() {
|
||||
testKeyboardAccessibility(function() {
|
||||
testClearHierarchy();
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testHierarchy() {
|
||||
@ -582,11 +587,268 @@ function testPropertyInheritance(fooScope, anonymousVar, anonymousScope, barVar,
|
||||
"The eval and switch functions got mixed up in the property.");
|
||||
}
|
||||
|
||||
function testKeyboardAccessibility(callback) {
|
||||
gDebugger.DebuggerView.Filtering._doVariablesFocus();
|
||||
gDebugger.DebuggerView.Variables.pageSize = 5;
|
||||
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should be focused.");
|
||||
|
||||
gVariablesView.focusNextItem();
|
||||
is(gVariablesView.getFocusedItem().name, "someProp1",
|
||||
"The someProp1 item should be focused.");
|
||||
|
||||
gVariablesView.focusPrevItem();
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should be focused again.");
|
||||
|
||||
|
||||
ok(!gVariablesView._list.querySelector(".element-value-input"),
|
||||
"There shouldn't be a value input element created.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_ENTER", {}, gDebugger);
|
||||
waitForElement(".element-value-input", true, function() {
|
||||
|
||||
ok(gVariablesView._list.querySelector(".element-value-input"),
|
||||
"There should be a value input element created.");
|
||||
|
||||
EventUtils.sendKey("ESCAPE", gDebugger);
|
||||
waitForElement(".element-value-input", false, function() {
|
||||
|
||||
ok(!gVariablesView._list.querySelector(".element-value-input"),
|
||||
"There shouldn't be a value input element anymore.");
|
||||
|
||||
ok(!gVariablesView._list.querySelector(".element-name-input"),
|
||||
"There shouldn't be a name input element created.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_ENTER", { shiftKey: true }, gDebugger);
|
||||
waitForElement(".element-name-input", true, function() {
|
||||
|
||||
ok(gVariablesView._list.querySelector(".element-name-input"),
|
||||
"There should be a name input element created.");
|
||||
|
||||
EventUtils.sendKey("ESCAPE", gDebugger);
|
||||
waitForElement(".element-name-input", false, function() {
|
||||
|
||||
ok(!gVariablesView._list.querySelector(".element-name-input"),
|
||||
"There shouldn't be a name input element anymore.");
|
||||
|
||||
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
executeSoon(function() {
|
||||
is(gVariablesView._parent.scrollTop, 0,
|
||||
"The variables view shouldn't scroll when pressing the DOWN key.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
executeSoon(function() {
|
||||
is(gVariablesView._parent.scrollTop, 0,
|
||||
"The variables view shouldn't scroll when pressing the UP key.");
|
||||
|
||||
|
||||
EventUtils.sendKey("PAGE_DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp5",
|
||||
"The someProp5 item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "0",
|
||||
"The 0 item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("END", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo item should still be focused now.");
|
||||
|
||||
EventUtils.sendKey("RIGHT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "bar",
|
||||
"The bar item should still be focused now.");
|
||||
|
||||
EventUtils.sendKey("PAGE_DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo item should still be focused now.");
|
||||
|
||||
|
||||
EventUtils.sendKey("PAGE_UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "__proto__",
|
||||
"The __proto__ item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "set",
|
||||
"The set item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "get",
|
||||
"The get item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "p8",
|
||||
"The p8 item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("HOME", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should be focused now.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should still be focused now.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should still be focused now.");
|
||||
|
||||
EventUtils.sendKey("PAGE_UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should still be focused now.");
|
||||
|
||||
|
||||
for (let i = 0; i < 16; i++) {
|
||||
// Advance to the first collapsed __proto__ property.
|
||||
EventUtils.sendKey("RIGHT", gDebugger);
|
||||
}
|
||||
is(gVariablesView.getFocusedItem().name, "__proto__",
|
||||
"The __proto__ item should be focused now.");
|
||||
is(gVariablesView.getFocusedItem().expanded, false,
|
||||
"The __proto__ item shouldn't be expanded yet.");
|
||||
|
||||
EventUtils.sendKey("RIGHT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "__proto__",
|
||||
"The __proto__ item should still be focused.");
|
||||
is(gVariablesView.getFocusedItem().expanded, true,
|
||||
"The __proto__ item should be expanded now.");
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// Advance to the fifth top-level someProp5 property.
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
}
|
||||
is(gVariablesView.getFocusedItem().name, "5",
|
||||
"The fifth array item should be focused.");
|
||||
is(gVariablesView.getFocusedItem().expanded, false,
|
||||
"The fifth array item should not be expanded now.");
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
// Advance to the fifth top-level someProp5 property.
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
}
|
||||
is(gVariablesView.getFocusedItem().name, "someProp5",
|
||||
"The someProp5 item should be focused now.");
|
||||
is(gVariablesView.getFocusedItem().expanded, true,
|
||||
"The someProp5 item should already be expanded.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp5",
|
||||
"The someProp5 item should still be focused.");
|
||||
is(gVariablesView.getFocusedItem().expanded, false,
|
||||
"The someProp5 item should not be expanded now.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp4",
|
||||
"The someProp4 item should be focused.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp3",
|
||||
"The someProp3 item should be focused.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp2",
|
||||
"The someProp2 item should be focused.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp1",
|
||||
"The someProp1 item should be focused.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should be focused.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "someProp0",
|
||||
"The someProp0 item should still be focused.");
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
// Advance to the last property in this scope.
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
}
|
||||
is(gVariablesView.getFocusedItem().name, "__proto__",
|
||||
"The top-level __proto__ item should be focused.");
|
||||
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo scope should be focused now.");
|
||||
is(gVariablesView.getFocusedItem().expanded, true,
|
||||
"The foo scope should already be expanded yet.");
|
||||
|
||||
EventUtils.sendKey("LEFT", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo scope should be focused now.");
|
||||
is(gVariablesView.getFocusedItem().expanded, false,
|
||||
"The foo scope shouldn't be expanded now.");
|
||||
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo scope should still be focused.");
|
||||
is(gVariablesView.getFocusedItem().expanded, true,
|
||||
"The foo scope should be expanded now.");
|
||||
|
||||
EventUtils.sendKey("DOWN", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "bar",
|
||||
"The bar variable should still be focused.");
|
||||
is(gVariablesView.getFocusedItem().expanded, false,
|
||||
"The bar variable shouldn't be expanded.");
|
||||
is(gVariablesView.getFocusedItem().visible, true,
|
||||
"The bar variable shouldn't be hidden.");
|
||||
|
||||
EventUtils.sendKey("BACK_SPACE", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "bar",
|
||||
"The bar variable should still be focused.");
|
||||
is(gVariablesView.getFocusedItem().expanded, false,
|
||||
"The bar variable should still not be expanded.");
|
||||
is(gVariablesView.getFocusedItem().visible, false,
|
||||
"The bar variable should be hidden.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "foo",
|
||||
"The foo scope should be focused.");
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
is(gVariablesView.getFocusedItem().name, "__proto__",
|
||||
"The top-level __proto__ item should be focused.");
|
||||
|
||||
executeSoon(callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForElement(selector, exists, callback)
|
||||
{
|
||||
// Poll every few milliseconds until the element are retrieved.
|
||||
let count = 0;
|
||||
let intervalID = window.setInterval(function() {
|
||||
info("count: " + count + " ");
|
||||
if (++count > 50) {
|
||||
ok(false, "Timed out while polling for the element.");
|
||||
window.clearInterval(intervalID);
|
||||
return closeDebuggerAndFinish();
|
||||
}
|
||||
if (!!gVariablesView._list.querySelector(selector) != exists) {
|
||||
return;
|
||||
}
|
||||
// We got the element, it's safe to callback.
|
||||
window.clearInterval(intervalID);
|
||||
callback();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function testClearHierarchy() {
|
||||
gVariablesView.clearHierarchy();
|
||||
is (gVariablesView._prevHierarchy.size, 0,
|
||||
ok(!gVariablesView._prevHierarchy.size,
|
||||
"The previous hierarchy should have been cleared.");
|
||||
is (gVariablesView._currHierarchy.size, 0,
|
||||
ok(!gVariablesView._currHierarchy.size,
|
||||
"The current hierarchy should have been cleared.");
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ function testFrameEval() {
|
||||
ok(scope, "There should be a wach expressions scope in the variables view");
|
||||
is(scope._store.size, 5, "There should be 5 evaluations availalble");
|
||||
|
||||
is(scope.get("this")._isShown, true,
|
||||
is(scope.get("this")._isContentVisible, true,
|
||||
"Should have the right visibility state for 'this'.");
|
||||
is(scope.get("this").target.querySelectorAll(".dbg-variable-delete").length, 1,
|
||||
"Should have the one close button visible for 'this'.");
|
||||
@ -79,7 +79,7 @@ function testFrameEval() {
|
||||
is(scope.get("this").value.class, "Proxy",
|
||||
"Should have the right value type for 'this'.");
|
||||
|
||||
is(scope.get("ermahgerd")._isShown, true,
|
||||
is(scope.get("ermahgerd")._isContentVisible, true,
|
||||
"Should have the right visibility state for 'ermahgerd'.");
|
||||
is(scope.get("ermahgerd").target.querySelectorAll(".dbg-variable-delete").length, 1,
|
||||
"Should have the one close button visible for 'ermahgerd'.");
|
||||
@ -90,7 +90,7 @@ function testFrameEval() {
|
||||
is(scope.get("ermahgerd").value.class, "Function",
|
||||
"Should have the right value type for 'ermahgerd'.");
|
||||
|
||||
is(scope.get("aArg")._isShown, true,
|
||||
is(scope.get("aArg")._isContentVisible, true,
|
||||
"Should have the right visibility state for 'aArg'.");
|
||||
is(scope.get("aArg").target.querySelectorAll(".dbg-variable-delete").length, 1,
|
||||
"Should have the one close button visible for 'aArg'.");
|
||||
@ -99,7 +99,7 @@ function testFrameEval() {
|
||||
is(scope.get("aArg").value, undefined,
|
||||
"Should have the right value for 'aArg'.");
|
||||
|
||||
is(scope.get("document.title")._isShown, true,
|
||||
is(scope.get("document.title")._isContentVisible, true,
|
||||
"Should have the right visibility state for 'document.title'.");
|
||||
is(scope.get("document.title").target.querySelectorAll(".dbg-variable-delete").length, 1,
|
||||
"Should have the one close button visible for 'document.title'.");
|
||||
@ -110,7 +110,7 @@ function testFrameEval() {
|
||||
is(typeof scope.get("document.title").value, "string",
|
||||
"Should have the right value type for 'document.title'.");
|
||||
|
||||
is(scope.get("document.title = 42")._isShown, true,
|
||||
is(scope.get("document.title = 42")._isContentVisible, true,
|
||||
"Should have the right visibility state for 'document.title = 42'.");
|
||||
is(scope.get("document.title = 42").target.querySelectorAll(".dbg-variable-delete").length, 1,
|
||||
"Should have the one close button visible for 'document.title = 42'.");
|
||||
|
@ -69,7 +69,7 @@ function testModification(aVar, aCallback, aNewValue, aNewResult) {
|
||||
EventUtils.sendKey("RETURN", gDebugger);
|
||||
}
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
aVar.querySelector(".value"),
|
||||
gDebugger);
|
||||
|
||||
|
@ -5,15 +5,20 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
|
||||
const LAZY_EMPTY_DELAY = 150; // ms
|
||||
const LAZY_EXPAND_DELAY = 50; // ms
|
||||
const LAZY_APPEND_DELAY = 100; // ms
|
||||
const LAZY_APPEND_BATCH = 100; // nodes
|
||||
const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
|
||||
const PAGE_SIZE_MAX_JUMPS = 30;
|
||||
const SEARCH_ACTION_MAX_DELAY = 1000; // ms
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this,
|
||||
"WebConsoleUtils", "resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
@ -40,6 +45,7 @@ const STR = Services.strings.createBundle(DBG_STRINGS_URI);
|
||||
*/
|
||||
this.VariablesView = function VariablesView(aParentNode) {
|
||||
this._store = new Map();
|
||||
this._itemsByElement = new WeakMap();
|
||||
this._prevHierarchy = new Map();
|
||||
this._currHierarchy = new Map();
|
||||
|
||||
@ -48,9 +54,12 @@ this.VariablesView = function VariablesView(aParentNode) {
|
||||
|
||||
this._onSearchboxInput = this._onSearchboxInput.bind(this);
|
||||
this._onSearchboxKeyPress = this._onSearchboxKeyPress.bind(this);
|
||||
this._onViewKeyPress = this._onViewKeyPress.bind(this);
|
||||
|
||||
// Create an internal list container.
|
||||
this._list = this.document.createElement("vbox");
|
||||
this._list = this.document.createElement("scrollbox");
|
||||
this._list.setAttribute("orient", "vertical");
|
||||
this._list.addEventListener("keypress", this._onViewKeyPress, false);
|
||||
this._parent.appendChild(this._list);
|
||||
};
|
||||
|
||||
@ -82,6 +91,7 @@ VariablesView.prototype = {
|
||||
let scope = new Scope(this, aName);
|
||||
this._store.set(scope.id, scope);
|
||||
this._currHierarchy.set(aName, scope);
|
||||
this._itemsByElement.set(scope._target, scope);
|
||||
scope.header = !!aName;
|
||||
return scope;
|
||||
},
|
||||
@ -112,6 +122,8 @@ VariablesView.prototype = {
|
||||
}
|
||||
|
||||
this._store = new Map();
|
||||
this._itemsByElement = new WeakMap();
|
||||
|
||||
this._appendEmptyNotice();
|
||||
this._toggleSearchVisibility(false);
|
||||
},
|
||||
@ -133,12 +145,18 @@ VariablesView.prototype = {
|
||||
*/
|
||||
_emptySoon: function VV__emptySoon(aTimeout) {
|
||||
let prevList = this._list;
|
||||
let currList = this._list = this.document.createElement("vbox");
|
||||
let currList = this._list = this.document.createElement("scrollbox");
|
||||
|
||||
this._store = new Map();
|
||||
this._itemsByElement = new WeakMap();
|
||||
|
||||
this._emptyTimeout = this.window.setTimeout(function() {
|
||||
this._emptyTimeout = null;
|
||||
|
||||
prevList.removeEventListener("keypress", this._onViewKeyPress, false);
|
||||
currList.addEventListener("keypress", this._onViewKeyPress, false);
|
||||
currList.setAttribute("orient", "vertical");
|
||||
|
||||
this._parent.removeChild(prevList);
|
||||
this._parent.appendChild(currList);
|
||||
|
||||
@ -468,15 +486,74 @@ VariablesView.prototype = {
|
||||
*/
|
||||
expandFirstSearchResults: function VV_expandFirstSearchResults() {
|
||||
for (let [, scope] of this._store) {
|
||||
for (let [, variable] of scope._store) {
|
||||
if (variable._isMatch) {
|
||||
variable.expand();
|
||||
break;
|
||||
}
|
||||
let match = scope._firstMatch;
|
||||
if (match) {
|
||||
match.expand();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the first visible variable or property in this container.
|
||||
*/
|
||||
focusFirstVisibleNode: function VV_focusFirstVisibleNode() {
|
||||
let property, variable, scope;
|
||||
|
||||
for (let [, item] of this._currHierarchy) {
|
||||
if (!item.focusable) {
|
||||
continue;
|
||||
}
|
||||
if (item instanceof Property) {
|
||||
property = item;
|
||||
break;
|
||||
} else if (item instanceof Variable) {
|
||||
variable = item;
|
||||
break;
|
||||
} else if (item instanceof Scope) {
|
||||
scope = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (scope) {
|
||||
this._focusItem(scope);
|
||||
} else if (variable) {
|
||||
this._focusItem(variable);
|
||||
} else if (property) {
|
||||
this._focusItem(property);
|
||||
}
|
||||
this._parent.scrollTop = 0;
|
||||
this._parent.scrollLeft = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the last visible variable or property in this container.
|
||||
*/
|
||||
focusLastVisibleNode: function VV_focusLastVisibleNode() {
|
||||
let property, variable, scope;
|
||||
|
||||
for (let [, item] of this._currHierarchy) {
|
||||
if (!item.focusable) {
|
||||
continue;
|
||||
}
|
||||
if (item instanceof Property) {
|
||||
property = item;
|
||||
} else if (item instanceof Variable) {
|
||||
variable = item;
|
||||
} else if (item instanceof Scope) {
|
||||
scope = item;
|
||||
}
|
||||
}
|
||||
if (property && (!variable || property.isDescendantOf(variable))) {
|
||||
this._focusItem(property);
|
||||
} else if (variable && (!scope || variable.isDescendantOf(scope))) {
|
||||
this._focusItem(variable);
|
||||
} else if (scope) {
|
||||
this._focusItem(scope);
|
||||
this._parent.scrollTop = this._parent.scrollHeight;
|
||||
this._parent.scrollLeft = 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for the scope in this container displayed by the specified node.
|
||||
*
|
||||
@ -486,33 +563,250 @@ VariablesView.prototype = {
|
||||
* The matched scope, or null if nothing is found.
|
||||
*/
|
||||
getScopeForNode: function VV_getScopeForNode(aNode) {
|
||||
for (let [, scope] of this._store) {
|
||||
if (scope._target == aNode) {
|
||||
return scope;
|
||||
}
|
||||
let item = this._itemsByElement.get(aNode);
|
||||
if (item && !(item instanceof Variable) && !(item instanceof Property)) {
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively searches all the scopes for the variable or property
|
||||
* Recursively searches this container for the scope, variable or property
|
||||
* displayed by the specified node.
|
||||
*
|
||||
* @param nsIDOMNode aNode
|
||||
* The node to search for.
|
||||
* @return Variable | Property
|
||||
* The matched variable or property, or null if nothing is found.
|
||||
* @return Scope | Variable | Property
|
||||
* The matched scope, variable or property, or null if nothing is found.
|
||||
*/
|
||||
getVariableOrPropertyForNode: function VV_getVariableOrPropertyForNode(aNode) {
|
||||
for (let [, scope] of this._store) {
|
||||
let match = scope.find(aNode);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
getItemForNode: function VV_getItemForNode(aNode) {
|
||||
return this._itemsByElement.get(aNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the currently focused scope, variable or property in this view.
|
||||
*
|
||||
* @return Scope | Variable | Property
|
||||
* The focused scope, variable or property, or null if nothing is found.
|
||||
*/
|
||||
getFocusedItem: function VV_getFocusedItem() {
|
||||
let focused = this.document.commandDispatcher.focusedElement;
|
||||
return this.getItemForNode(focused);
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the next scope, variable or property in this view.
|
||||
* @see VariablesView.prototype._focusChange
|
||||
*/
|
||||
focusNextItem: function VV_focusNextItem(aMaintainViewFocusedFlag)
|
||||
this._focusChange("advanceFocus", aMaintainViewFocusedFlag),
|
||||
|
||||
/**
|
||||
* Focuses the previous scope, variable or property in this view.
|
||||
* @see VariablesView.prototype._focusChange
|
||||
*/
|
||||
focusPrevItem: function VV_focusPrevItem(aMaintainViewFocusedFlag)
|
||||
this._focusChange("rewindFocus", aMaintainViewFocusedFlag),
|
||||
|
||||
/**
|
||||
* Focuses the next or previous scope, variable or property in this view.
|
||||
*
|
||||
* @param string aDirection
|
||||
* Either "advanceFocus" or "rewindFocus".
|
||||
* @param boolean aMaintainViewFocusedFlag
|
||||
* True too keep this view focused if the element is out of bounds.
|
||||
* @return boolean
|
||||
* True if the focus went out of bounds and the first or last element
|
||||
* in this view was focused instead.
|
||||
*/
|
||||
_focusChange: function VV__changeFocus(aDirection, aMaintainViewFocusedFlag) {
|
||||
let commandDispatcher = this.document.commandDispatcher;
|
||||
let item;
|
||||
|
||||
do {
|
||||
commandDispatcher[aDirection]();
|
||||
|
||||
// If maintaining this view focused is not mandatory, a simple
|
||||
// "advanceFocus" or "rewindFocus" command dispatch is sufficient.
|
||||
if (!aMaintainViewFocusedFlag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the newly focused target is a part of this view.
|
||||
item = this.getFocusedItem();
|
||||
if (!item) {
|
||||
if (aDirection == "advanceFocus") {
|
||||
this.focusLastVisibleNode();
|
||||
} else {
|
||||
this.focusFirstVisibleNode();
|
||||
}
|
||||
// Focus went out of bounds so the first or last element in this view
|
||||
// was focused instead.
|
||||
return true;
|
||||
}
|
||||
} while (!item.focusable);
|
||||
|
||||
// Focus remained within bounds.
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses a scope, variable or property and makes sure it's visible.
|
||||
*
|
||||
* @param aItem Scope | Variable | Property
|
||||
* The item to focus.
|
||||
* @param boolean aCollapseFlag
|
||||
* True if the focused item should also be collapsed.
|
||||
* @return boolean
|
||||
* True if the item was successfully focused.
|
||||
*/
|
||||
_focusItem: function VV__focusItem(aItem, aCollapseFlag) {
|
||||
if (!aItem.focusable) {
|
||||
return false;
|
||||
}
|
||||
if (aCollapseFlag) {
|
||||
aItem.collapse();
|
||||
}
|
||||
aItem._target.focus();
|
||||
|
||||
let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
||||
boxObject.ensureElementIsVisible(aItem._title);
|
||||
boxObject.scrollBy(-this._list.clientWidth, 0);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Listener handling a key press event on the view.
|
||||
*/
|
||||
_onViewKeyPress: function VV__onViewKeyPress(e) {
|
||||
let item = this.getFocusedItem();
|
||||
|
||||
switch (e.keyCode) {
|
||||
case e.DOM_VK_UP:
|
||||
case e.DOM_VK_DOWN:
|
||||
case e.DOM_VK_LEFT:
|
||||
case e.DOM_VK_RIGHT:
|
||||
case e.DOM_VK_PAGE_UP:
|
||||
case e.DOM_VK_PAGE_DOWN:
|
||||
case e.DOM_VK_HOME:
|
||||
case e.DOM_VK_END:
|
||||
// Prevent scrolling when pressing navigation keys.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case e.DOM_VK_UP:
|
||||
// Always rewind focus.
|
||||
this.focusPrevItem(true);
|
||||
return;
|
||||
|
||||
case e.DOM_VK_DOWN:
|
||||
// Only expand scopes before advancing focus.
|
||||
if (!(item instanceof Variable) &&
|
||||
!(item instanceof Property) &&
|
||||
!item._isExpanded && item._isArrowVisible) {
|
||||
item.expand();
|
||||
} else {
|
||||
this.focusNextItem(true);
|
||||
}
|
||||
return;
|
||||
|
||||
case e.DOM_VK_LEFT:
|
||||
// If this is a collapsed or un-expandable item that has an expandable
|
||||
// variable or property parent, collapse and focus the owner view.
|
||||
if (!item._isExpanded || !item._isArrowVisible) {
|
||||
let ownerView = item.ownerView;
|
||||
if ((ownerView instanceof Variable ||
|
||||
ownerView instanceof Property) &&
|
||||
ownerView._isExpanded && ownerView._isArrowVisible) {
|
||||
if (this._focusItem(ownerView, true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collapse scopes, variables and properties before rewinding focus.
|
||||
if (item._isExpanded && item._isArrowVisible) {
|
||||
item.collapse();
|
||||
} else {
|
||||
this.focusPrevItem(true);
|
||||
}
|
||||
return;
|
||||
|
||||
case e.DOM_VK_RIGHT:
|
||||
// Expand scopes, variables and properties before advancing focus.
|
||||
if (!item._isExpanded && item._isArrowVisible) {
|
||||
item.expand();
|
||||
} else {
|
||||
this.focusNextItem(true);
|
||||
}
|
||||
return;
|
||||
|
||||
case e.DOM_VK_PAGE_UP:
|
||||
// Rewind a certain number of elements based on the container height.
|
||||
var jumps = this.pageSize || Math.min(Math.floor(this._list.scrollHeight /
|
||||
PAGE_SIZE_SCROLL_HEIGHT_RATIO),
|
||||
PAGE_SIZE_MAX_JUMPS);
|
||||
|
||||
while (jumps--) {
|
||||
if (this.focusPrevItem(true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case e.DOM_VK_PAGE_DOWN:
|
||||
// Advance a certain number of elements based on the container height.
|
||||
var jumps = this.pageSize || Math.min(Math.floor(this._list.scrollHeight /
|
||||
PAGE_SIZE_SCROLL_HEIGHT_RATIO),
|
||||
PAGE_SIZE_MAX_JUMPS);
|
||||
|
||||
while (jumps--) {
|
||||
if (this.focusNextItem(true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case e.DOM_VK_HOME:
|
||||
this.focusFirstVisibleNode();
|
||||
return;
|
||||
|
||||
case e.DOM_VK_END:
|
||||
this.focusLastVisibleNode();
|
||||
return;
|
||||
|
||||
case e.DOM_VK_RETURN:
|
||||
case e.DOM_VK_ENTER:
|
||||
// Start editing the value or name of the variable or property.
|
||||
if (item instanceof Variable ||
|
||||
item instanceof Property) {
|
||||
if (e.metaKey || e.altKey || e.shiftKey) {
|
||||
item._activateNameInput();
|
||||
} else {
|
||||
item._activateValueInput();
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
case e.DOM_VK_DELETE:
|
||||
case e.DOM_VK_BACK_SPACE:
|
||||
// Delete the variable or property if allowed.
|
||||
if (item instanceof Variable ||
|
||||
item instanceof Property) {
|
||||
item._onDelete(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of elements in this container to jump when Page Up or Page Down
|
||||
* keys are pressed. If falsy, then the page size will be based on the
|
||||
* container height.
|
||||
*/
|
||||
pageSize: 0,
|
||||
|
||||
/**
|
||||
* Sets the text displayed in this container when there are no available items.
|
||||
* @param string aValue
|
||||
@ -604,8 +898,7 @@ VariablesView.prototype = {
|
||||
function Scope(aView, aName, aFlags = {}) {
|
||||
this.ownerView = aView;
|
||||
|
||||
this.expand = this.expand.bind(this);
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._openEnum = this._openEnum.bind(this);
|
||||
this._openNonEnum = this._openNonEnum.bind(this);
|
||||
this._batchAppend = this._batchAppend.bind(this);
|
||||
@ -656,6 +949,7 @@ Scope.prototype = {
|
||||
let variable = new Variable(this, aName, aDescriptor);
|
||||
this._store.set(aName, variable);
|
||||
this._variablesView._currHierarchy.set(variable._absoluteName, variable);
|
||||
this._variablesView._itemsByElement.set(variable._target, variable);
|
||||
variable.header = !!aName;
|
||||
return variable;
|
||||
},
|
||||
@ -696,12 +990,45 @@ Scope.prototype = {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if this scope is a direct child of a parent variables view,
|
||||
* scope, variable or property.
|
||||
*
|
||||
* @param VariablesView | Scope | Variable | Property
|
||||
* The parent to check.
|
||||
* @return boolean
|
||||
* True if the specified item is a direct child, false otherwise.
|
||||
*/
|
||||
isChildOf: function S_isChildOf(aParent) {
|
||||
return this.ownerView == aParent;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if this scope is a descendant of a parent variables view,
|
||||
* scope, variable or property.
|
||||
*
|
||||
* @param VariablesView | Scope | Variable | Property
|
||||
* The parent to check.
|
||||
* @return boolean
|
||||
* True if the specified item is a descendant, false otherwise.
|
||||
*/
|
||||
isDescendantOf: function S_isDescendantOf(aParent) {
|
||||
if (this.isChildOf(aParent)) {
|
||||
return true;
|
||||
}
|
||||
if (this.ownerView instanceof Scope ||
|
||||
this.ownerView instanceof Variable ||
|
||||
this.ownerView instanceof Property) {
|
||||
return this.ownerView.isDescendantOf(aParent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the scope.
|
||||
*/
|
||||
show: function S_show() {
|
||||
this._target.hidden = false;
|
||||
this._isShown = true;
|
||||
this._isContentVisible = true;
|
||||
|
||||
if (this.onshow) {
|
||||
this.onshow(this);
|
||||
@ -713,7 +1040,7 @@ Scope.prototype = {
|
||||
*/
|
||||
hide: function S_hide() {
|
||||
this._target.hidden = true;
|
||||
this._isShown = false;
|
||||
this._isContentVisible = false;
|
||||
|
||||
if (this.onhide) {
|
||||
this.onhide(this);
|
||||
@ -738,7 +1065,7 @@ Scope.prototype = {
|
||||
// Start spinning a throbber in this scope's title and allow a few
|
||||
// milliseconds for it to be painted.
|
||||
this._startThrobber();
|
||||
this.window.setTimeout(this.expand, LAZY_EXPAND_DELAY);
|
||||
this.window.setTimeout(this.expand.bind(this), LAZY_EXPAND_DELAY);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -787,7 +1114,7 @@ Scope.prototype = {
|
||||
// Make sure the scope and its contents are visibile.
|
||||
for (let [, variable] of this._store) {
|
||||
variable.header = true;
|
||||
variable._match = true;
|
||||
variable._matched = true;
|
||||
}
|
||||
if (this.ontoggle) {
|
||||
this.ontoggle(this);
|
||||
@ -844,7 +1171,7 @@ Scope.prototype = {
|
||||
* Gets the visibility state.
|
||||
* @return boolean
|
||||
*/
|
||||
get visible() this._isShown,
|
||||
get visible() this._isContentVisible,
|
||||
|
||||
/**
|
||||
* Gets the expanded state.
|
||||
@ -864,6 +1191,12 @@ Scope.prototype = {
|
||||
*/
|
||||
get twisty() this._isArrowVisible,
|
||||
|
||||
/**
|
||||
* Gets the expand lock state.
|
||||
* @return boolean
|
||||
*/
|
||||
get locked() this._locked,
|
||||
|
||||
/**
|
||||
* Sets the visibility state.
|
||||
* @param boolean aFlag
|
||||
@ -888,18 +1221,37 @@ Scope.prototype = {
|
||||
*/
|
||||
set twisty(aFlag) aFlag ? this.showArrow() : this.hideArrow(),
|
||||
|
||||
/**
|
||||
* Gets the expand lock state.
|
||||
* @return boolean
|
||||
*/
|
||||
get locked() this._locked,
|
||||
|
||||
/**
|
||||
* Sets the expand lock state.
|
||||
* @param boolean aFlag
|
||||
*/
|
||||
set locked(aFlag) this._locked = aFlag,
|
||||
|
||||
/**
|
||||
* Specifies if this target node may be focused.
|
||||
* @return boolean
|
||||
*/
|
||||
get focusable() {
|
||||
// Check if this target node is actually visibile.
|
||||
if (!this._nameString ||
|
||||
!this._isContentVisible ||
|
||||
!this._isHeaderVisible ||
|
||||
!this._isMatch) {
|
||||
return false;
|
||||
}
|
||||
// Check if all parent objects are expanded.
|
||||
let item = this;
|
||||
while ((item = item.ownerView) && /* Parent object exists. */
|
||||
(item instanceof Scope ||
|
||||
item instanceof Variable ||
|
||||
item instanceof Property)) {
|
||||
if (!item._isExpanded) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an event listener for a certain event on this scope's title.
|
||||
* @param string aName
|
||||
@ -998,7 +1350,15 @@ Scope.prototype = {
|
||||
* Adds the necessary event listeners for this scope.
|
||||
*/
|
||||
_addEventListeners: function S__addEventListeners() {
|
||||
this._title.addEventListener("mousedown", this.toggle, false);
|
||||
this._title.addEventListener("mousedown", this._onClick, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* The click listener for this scope's title.
|
||||
*/
|
||||
_onClick: function S__onClick() {
|
||||
this.toggle();
|
||||
this._variablesView._focusItem(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1157,11 +1517,11 @@ Scope.prototype = {
|
||||
// Non-matched variables or properties require a corresponding attribute.
|
||||
if (!lowerCaseName.contains(aLowerCaseQuery) &&
|
||||
!lowerCaseValue.contains(aLowerCaseQuery)) {
|
||||
variable._match = false;
|
||||
variable._matched = false;
|
||||
}
|
||||
// Variable or property is matched.
|
||||
else {
|
||||
variable._match = true;
|
||||
variable._matched = true;
|
||||
|
||||
// If the variable was ever expanded, there's a possibility it may
|
||||
// contain some matched properties, so make sure they're visible
|
||||
@ -1184,7 +1544,7 @@ Scope.prototype = {
|
||||
variable instanceof Property)) {
|
||||
|
||||
// Show and expand the parent, as it is certainly accessible.
|
||||
variable._match = true;
|
||||
variable._matched = true;
|
||||
aLowerCaseQuery && variable.expand();
|
||||
}
|
||||
}
|
||||
@ -1199,10 +1559,10 @@ Scope.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets if this object instance is a match or non-match.
|
||||
* Sets if this object instance is a matched or non-matched item.
|
||||
* @param boolean aStatus
|
||||
*/
|
||||
set _match(aStatus) {
|
||||
set _matched(aStatus) {
|
||||
if (this._isMatch == aStatus) {
|
||||
return;
|
||||
}
|
||||
@ -1215,6 +1575,25 @@ Scope.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the first search results match in this scope.
|
||||
* @return Variable | Property
|
||||
*/
|
||||
get _firstMatch() {
|
||||
for (let [, variable] of this._store) {
|
||||
let match;
|
||||
if (variable._isMatch) {
|
||||
match = variable;
|
||||
} else {
|
||||
match = variable._firstMatch;
|
||||
}
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets top level variables view instance.
|
||||
* @return VariablesView
|
||||
@ -1269,10 +1648,10 @@ Scope.prototype = {
|
||||
_batchItems: null,
|
||||
_batchTimeout: null,
|
||||
_locked: false,
|
||||
_isShown: true,
|
||||
_isExpanding: false,
|
||||
_isExpanded: false,
|
||||
_wasToggled: false,
|
||||
_isContentVisible: true,
|
||||
_isHeaderVisible: true,
|
||||
_isArrowVisible: true,
|
||||
_isMatch: true,
|
||||
@ -1342,6 +1721,7 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
let property = new Property(this, aName, aDescriptor);
|
||||
this._store.set(aName, property);
|
||||
this._variablesView._currHierarchy.set(property._absoluteName, property);
|
||||
this._variablesView._itemsByElement.set(property._target, property);
|
||||
property.header = !!aName;
|
||||
return property;
|
||||
},
|
||||
@ -1536,7 +1916,7 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
*/
|
||||
_init: function V__init(aName, aDescriptor) {
|
||||
this._idString = generateId(this._nameString = aName);
|
||||
this._displayScope(aName, "variable");
|
||||
this._displayScope(aName, "variable variable-or-property");
|
||||
|
||||
// Don't allow displaying variable information there's no name available.
|
||||
if (this._nameString) {
|
||||
@ -1694,10 +2074,9 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
* Adds the necessary event listeners for this variable.
|
||||
*/
|
||||
_addEventListeners: function V__addEventListeners() {
|
||||
this._arrow.addEventListener("mousedown", this.toggle, false);
|
||||
this._name.addEventListener("mousedown", this.toggle, false);
|
||||
this._name.addEventListener("dblclick", this._activateNameInput, false);
|
||||
this._valueLabel.addEventListener("click", this._activateValueInput, false);
|
||||
this._valueLabel.addEventListener("mousedown", this._activateValueInput, false);
|
||||
this._title.addEventListener("mousedown", this._onClick, false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1774,6 +2153,11 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
if (!this.ownerView.switch) {
|
||||
return;
|
||||
}
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
this._activateInput(this._name, "element-name-input", {
|
||||
onKeypress: this._onNameInputKeyPress,
|
||||
onBlur: this._deactivateNameInput
|
||||
@ -1805,6 +2189,11 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
if (!this.ownerView.eval) {
|
||||
return;
|
||||
}
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
this._activateInput(this._valueLabel, "element-value-input", {
|
||||
onKeypress: this._onValueInputKeyPress,
|
||||
onBlur: this._deactivateValueInput
|
||||
@ -1867,13 +2256,17 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
* The key press listener for this variable's editable name textbox.
|
||||
*/
|
||||
_onNameInputKeyPress: function V__onNameInputKeyPress(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
switch(e.keyCode) {
|
||||
case e.DOM_VK_RETURN:
|
||||
case e.DOM_VK_ENTER:
|
||||
this._saveNameInput(e);
|
||||
this._variablesView._focusItem(this);
|
||||
return;
|
||||
case e.DOM_VK_ESCAPE:
|
||||
this._deactivateNameInput(e);
|
||||
this._variablesView._focusItem(this);
|
||||
return;
|
||||
}
|
||||
},
|
||||
@ -1882,13 +2275,17 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
* The key press listener for this variable's editable value textbox.
|
||||
*/
|
||||
_onValueInputKeyPress: function V__onValueInputKeyPress(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
switch(e.keyCode) {
|
||||
case e.DOM_VK_RETURN:
|
||||
case e.DOM_VK_ENTER:
|
||||
this._saveValueInput(e);
|
||||
this._variablesView._focusItem(this);
|
||||
return;
|
||||
case e.DOM_VK_ESCAPE:
|
||||
this._deactivateValueInput(e);
|
||||
this._variablesView._focusItem(this);
|
||||
return;
|
||||
}
|
||||
},
|
||||
@ -1896,11 +2293,13 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
/**
|
||||
* The click listener for the delete button.
|
||||
*/
|
||||
_onDelete: function V__onDelete() {
|
||||
this.hide();
|
||||
_onDelete: function V__onDelete(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.ownerView.delete) {
|
||||
this.ownerView.delete(this);
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
@ -1946,7 +2345,7 @@ create({ constructor: Property, proto: Variable.prototype }, {
|
||||
*/
|
||||
_init: function P__init(aName, aDescriptor) {
|
||||
this._idString = generateId(this._nameString = aName);
|
||||
this._displayScope(aName, "property");
|
||||
this._displayScope(aName, "property variable-or-property");
|
||||
|
||||
// Don't allow displaying property information there's no name available.
|
||||
if (this._nameString) {
|
||||
|
@ -94,6 +94,11 @@
|
||||
<!ENTITY debuggerUI.searchVariable "Filter variables">
|
||||
<!ENTITY debuggerUI.searchVariable.key "V">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.focusVariables): This is the text that appears
|
||||
- in the source editor's context menu for the variables focus operation. -->
|
||||
<!ENTITY debuggerUI.focusVariables "Focus variables tree">
|
||||
<!ENTITY debuggerUI.focusVariables.key "V">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.condBreakPanelTitle): This is the text that
|
||||
- appears in the conditional breakpoint panel popup as a description. -->
|
||||
<!ENTITY debuggerUI.condBreakPanelTitle "This breakpoint will stop execution only if the following expression is true">
|
||||
|
@ -304,6 +304,11 @@
|
||||
* Scope element
|
||||
*/
|
||||
|
||||
.scope:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.scope > .title {
|
||||
text-shadow: 0 1px #222;
|
||||
color: #fff;
|
||||
@ -335,11 +340,20 @@
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
.variable:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.variable > .title > .name {
|
||||
color: #048;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.variable:not(:focus) > .title > .name {
|
||||
color: #048;
|
||||
}
|
||||
|
||||
.variable > .title > .value {
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
@ -363,7 +377,13 @@
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
.property > .title > .name {
|
||||
.property:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.property:not(:focus) > .title > .name {
|
||||
color: #881090;
|
||||
}
|
||||
|
||||
@ -377,7 +397,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Non enumerable, configurable and writable variables and properties.
|
||||
* Non enumerable, configurable and writable variables and properties
|
||||
*/
|
||||
|
||||
.variable[proto] > .title > .name,
|
||||
@ -415,8 +435,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.variable[exception] > .title > .name,
|
||||
.property[exception] > .title > .name {
|
||||
.variable[exception]:not(:focus) > .title > .name,
|
||||
.property[exception]:not(:focus) > .title > .name {
|
||||
color: #a00;
|
||||
text-shadow: 0 0 8px #fcc;
|
||||
}
|
||||
@ -469,28 +489,28 @@
|
||||
* Token value colors
|
||||
*/
|
||||
|
||||
.token-undefined {
|
||||
.variable-or-property:not(:focus) > .title > .token-undefined {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.token-null {
|
||||
.variable-or-property:not(:focus) > .title > .token-null {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token-boolean {
|
||||
.variable-or-property:not(:focus) > .title > .token-boolean {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.token-number {
|
||||
.variable-or-property:not(:focus) > .title > .token-number {
|
||||
color: #c40a16;
|
||||
}
|
||||
|
||||
.token-string {
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
max-width: 30em;
|
||||
color: #1c00cf;
|
||||
}
|
||||
|
||||
.token-other {
|
||||
.variable-or-property:not(:focus) > .title > .token-other {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
@ -311,6 +311,11 @@
|
||||
* Scope element
|
||||
*/
|
||||
|
||||
.scope:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.scope > .title {
|
||||
text-shadow: 0 1px #222;
|
||||
color: #fff;
|
||||
@ -342,11 +347,20 @@
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
.variable:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.variable > .title > .name {
|
||||
color: #048;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.variable:not(:focus) > .title > .name {
|
||||
color: #048;
|
||||
}
|
||||
|
||||
.variable > .title > .value {
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
@ -370,7 +384,13 @@
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
.property > .title > .name {
|
||||
.property:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.property:not(:focus) > .title > .name {
|
||||
color: #881090;
|
||||
}
|
||||
|
||||
@ -384,7 +404,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Non enumerable, configurable and writable variables and properties.
|
||||
* Non enumerable, configurable and writable variables and properties
|
||||
*/
|
||||
|
||||
.variable[proto] > .title > .name,
|
||||
@ -422,8 +442,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.variable[exception] > .title > .name,
|
||||
.property[exception] > .title > .name {
|
||||
.variable[exception]:not(:focus) > .title > .name,
|
||||
.property[exception]:not(:focus) > .title > .name {
|
||||
color: #a00;
|
||||
text-shadow: 0 0 8px #fcc;
|
||||
}
|
||||
@ -476,28 +496,28 @@
|
||||
* Token value colors
|
||||
*/
|
||||
|
||||
.token-undefined {
|
||||
.variable-or-property:not(:focus) > .title > .token-undefined {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.token-null {
|
||||
.variable-or-property:not(:focus) > .title > .token-null {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token-boolean {
|
||||
.variable-or-property:not(:focus) > .title > .token-boolean {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.token-number {
|
||||
.variable-or-property:not(:focus) > .title > .token-number {
|
||||
color: #c40a16;
|
||||
}
|
||||
|
||||
.token-string {
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
max-width: 30em;
|
||||
color: #1c00cf;
|
||||
}
|
||||
|
||||
.token-other {
|
||||
.variable-or-property:not(:focus) > .title > .token-other {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
@ -317,6 +317,11 @@
|
||||
* Scope element
|
||||
*/
|
||||
|
||||
.scope:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.scope > .title {
|
||||
text-shadow: 0 1px #222;
|
||||
color: #fff;
|
||||
@ -348,11 +353,20 @@
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
.variable:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.variable > .title > .name {
|
||||
color: #048;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.variable:not(:focus) > .title > .name {
|
||||
color: #048;
|
||||
}
|
||||
|
||||
.variable > .title > .value {
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
@ -376,7 +390,13 @@
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
.property > .title > .name {
|
||||
.property:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.property:not(:focus) > .title > .name {
|
||||
color: #881090;
|
||||
}
|
||||
|
||||
@ -390,7 +410,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Non enumerable, configurable and writable variables and properties.
|
||||
* Non enumerable, configurable and writable variables and properties
|
||||
*/
|
||||
|
||||
.variable[proto] > .title > .name,
|
||||
@ -428,8 +448,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.variable[exception] > .title > .name,
|
||||
.property[exception] > .title > .name {
|
||||
.variable[exception]:not(:focus) > .title > .name,
|
||||
.property[exception]:not(:focus) > .title > .name {
|
||||
color: #a00;
|
||||
text-shadow: 0 0 8px #fcc;
|
||||
}
|
||||
@ -482,28 +502,28 @@
|
||||
* Token value colors
|
||||
*/
|
||||
|
||||
.token-undefined {
|
||||
.variable-or-property:not(:focus) > .title > .token-undefined {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.token-null {
|
||||
.variable-or-property:not(:focus) > .title > .token-null {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token-boolean {
|
||||
.variable-or-property:not(:focus) > .title > .token-boolean {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.token-number {
|
||||
.variable-or-property:not(:focus) > .title > .token-number {
|
||||
color: #c40a16;
|
||||
}
|
||||
|
||||
.token-string {
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
max-width: 30em;
|
||||
color: #1c00cf;
|
||||
}
|
||||
|
||||
.token-other {
|
||||
.variable-or-property:not(:focus) > .title > .token-other {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user