mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
Bug 719845 - [markup panel] The HTML Tree should have its own keybindings. r=dcamp
This commit is contained in:
parent
5b78a62d3d
commit
53febe327a
@ -214,12 +214,13 @@ InsideOutBox.prototype =
|
||||
this.selectObjectBox(objectBox, forceOpen);
|
||||
if (makeBoxVisible) {
|
||||
this.openObjectBox(objectBox);
|
||||
if (scrollIntoView) {
|
||||
// We want to center the label of the element, not the whole tag
|
||||
// (which includes all of its children, and is vertically huge).
|
||||
LayoutHelpers.scrollIntoViewIfNeeded(objectBox.firstElementChild);
|
||||
}
|
||||
}
|
||||
if (scrollIntoView) {
|
||||
// We want to center the label of the element, not the whole tag
|
||||
// (which includes all of its children, and is vertically huge).
|
||||
LayoutHelpers.scrollIntoViewIfNeeded(objectBox.firstElementChild);
|
||||
}
|
||||
|
||||
return objectBox;
|
||||
},
|
||||
|
||||
@ -340,6 +341,141 @@ InsideOutBox.prototype =
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the next object box in the tree for navigation purposes.
|
||||
*/
|
||||
nextObjectBox: function IOBox_nextObjectBox(aBoxObject)
|
||||
{
|
||||
let candidate;
|
||||
let boxObject = aBoxObject || this.selectedObjectBox;
|
||||
if (!boxObject)
|
||||
return this.rootObjectBox;
|
||||
|
||||
// If expanded, return the first child.
|
||||
let isOpen = this.view.hasClass(boxObject, "open");
|
||||
let childObjectBox = this.getChildObjectBox(boxObject);
|
||||
if (isOpen && childObjectBox && childObjectBox.firstChild) {
|
||||
candidate = childObjectBox.firstChild;
|
||||
} else {
|
||||
// Otherwise we get the next available sibling.
|
||||
while (boxObject) {
|
||||
if (boxObject.nextSibling) {
|
||||
boxObject = boxObject.nextSibling;
|
||||
break;
|
||||
}
|
||||
boxObject = this.getParentObjectBox(boxObject);
|
||||
}
|
||||
candidate = boxObject;
|
||||
}
|
||||
|
||||
// If the node is not an element (comments or text nodes), we
|
||||
// jump to the next line.
|
||||
if (candidate &&
|
||||
candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) {
|
||||
return this.nextObjectBox(candidate);
|
||||
}
|
||||
|
||||
return candidate;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the next object in the tree for navigation purposes.
|
||||
*/
|
||||
nextObject: function IOBox_nextObject()
|
||||
{
|
||||
let next = this.nextObjectBox();
|
||||
return next ? next.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the object that is below the selection.
|
||||
*
|
||||
* @param aDistance Number of lines to jump.
|
||||
*/
|
||||
farNextObject: function IOBox_farPreviousProject(aDistance)
|
||||
{
|
||||
let boxObject = this.selectedObjectBox;
|
||||
while (aDistance-- > 0) {
|
||||
let newBoxObject = this.nextObjectBox(boxObject);
|
||||
if (!newBoxObject) {
|
||||
break;
|
||||
}
|
||||
boxObject = newBoxObject;
|
||||
}
|
||||
return boxObject ? boxObject.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the last visible child box of an object box.
|
||||
*/
|
||||
lastVisible: function IOBox_lastVisibleChild(aNode)
|
||||
{
|
||||
if (!this.view.hasClass(aNode, "open"))
|
||||
return aNode;
|
||||
|
||||
let childBox = this.getChildObjectBox(aNode);
|
||||
if (!childBox || !childBox.lastChild)
|
||||
return aNode;
|
||||
|
||||
return this.lastVisible(childBox.lastChild);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the previous object box in the tree for navigation purposes.
|
||||
*/
|
||||
previousObjectBox: function IOBox_previousObjectBox(aBoxObject)
|
||||
{
|
||||
let boxObject = aBoxObject || this.selectedObjectBox;
|
||||
if (!boxObject)
|
||||
return this.rootObjectBox;
|
||||
|
||||
let candidate;
|
||||
let sibling = boxObject.previousSibling;
|
||||
if (sibling) {
|
||||
candidate = this.lastVisible(sibling);
|
||||
} else {
|
||||
candidate = this.getParentObjectBox(boxObject);
|
||||
}
|
||||
|
||||
// If the node is not an element (comments or text nodes), we
|
||||
// jump to the previous line.
|
||||
if (candidate &&
|
||||
candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) {
|
||||
return this.previousObjectBox(candidate);
|
||||
}
|
||||
|
||||
return candidate;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the previous object in the tree for navigation purposes.
|
||||
*/
|
||||
previousObject: function IOBox_previousObject()
|
||||
{
|
||||
let boxObject = this.previousObjectBox();
|
||||
return boxObject ? boxObject.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the object that is above the selection.
|
||||
*
|
||||
* @param aDistance Number of lines to jump.
|
||||
*/
|
||||
farPreviousObject: function IOBox_farPreviousProject(aDistance)
|
||||
{
|
||||
let boxObject = this.selectedObjectBox;
|
||||
while (aDistance-- > 0) {
|
||||
let newBoxObject = this.previousObjectBox(boxObject);
|
||||
if (!newBoxObject) {
|
||||
break;
|
||||
}
|
||||
boxObject = newBoxObject;
|
||||
if (boxObject === this.rootObjectBox)
|
||||
break;
|
||||
}
|
||||
return boxObject ? boxObject.repObject : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the ancestors of the given object box.
|
||||
* @param aObjectBox
|
||||
|
@ -5,11 +5,13 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource:///modules/domplate.jsm");
|
||||
Cu.import("resource:///modules/InsideOutBox.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/inspector.jsm");
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = ["TreePanel", "DOMHelpers"];
|
||||
|
||||
@ -80,6 +82,8 @@ TreePanel.prototype = {
|
||||
this.ioBox = new InsideOutBox(this, this.treePanelDiv);
|
||||
this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
|
||||
this.treeLoaded = true;
|
||||
this._boundTreeKeyPress = this.onTreeKeyPress.bind(this);
|
||||
this.treeIFrame.addEventListener("keypress", this._boundTreeKeyPress.bind(this), true);
|
||||
this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
|
||||
this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
|
||||
this.treeIFrame.focus();
|
||||
@ -182,6 +186,7 @@ TreePanel.prototype = {
|
||||
this.treePanelDiv.ownerPanel = null;
|
||||
let parent = this.treePanelDiv.parentNode;
|
||||
parent.removeChild(this.treePanelDiv);
|
||||
this.treeIFrame.removeEventListener("keypress", this._boundTreeKeyPress, true);
|
||||
delete this.treePanelDiv;
|
||||
delete this.treeBrowserDocument;
|
||||
}
|
||||
@ -272,8 +277,7 @@ TreePanel.prototype = {
|
||||
if (this.IUI.inspecting) {
|
||||
this.IUI.stopInspecting(true);
|
||||
} else {
|
||||
this.IUI.select(node, true, false);
|
||||
this.IUI.highlighter.highlight(node);
|
||||
this.navigate(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -316,6 +320,52 @@ TreePanel.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
navigate: function TP_navigate(node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
this.ioBox.select(node, false, false, true);
|
||||
|
||||
if (this.IUI.highlighter.isNodeHighlightable(node)) {
|
||||
this.IUI.select(node, true, false, "treepanel");
|
||||
this.IUI.highlighter.highlight(node);
|
||||
}
|
||||
},
|
||||
|
||||
onTreeKeyPress: function TP_onTreeKeyPress(aEvent)
|
||||
{
|
||||
let handled = true;
|
||||
switch(aEvent.keyCode) {
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
|
||||
this.ioBox.contractObjectBox(this.ioBox.selectedObjectBox);
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT:
|
||||
this.ioBox.expandObjectBox(this.ioBox.selectedObjectBox);
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_UP:
|
||||
this.navigate(this.ioBox.previousObject());
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
|
||||
this.navigate(this.ioBox.nextObject());
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP:
|
||||
this.navigate(this.ioBox.farPreviousObject(10));
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN:
|
||||
this.navigate(this.ioBox.farNextObject(10));
|
||||
break;
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
|
||||
this.navigate(this.ioBox.rootObject);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
}
|
||||
if (handled) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the editor for an attribute name or value.
|
||||
* @param aAttrObj
|
||||
@ -542,10 +592,10 @@ TreePanel.prototype = {
|
||||
* @param aNode the DOM node in the content document to select.
|
||||
* @param aScroll boolean scroll to the visible node?
|
||||
*/
|
||||
select: function TP_select(aNode, aScroll)
|
||||
select: function TP_select(aNode, aScroll, aFrom)
|
||||
{
|
||||
if (this.ioBox) {
|
||||
this.ioBox.select(aNode, true, true, aScroll);
|
||||
this.ioBox.select(aNode, true, aFrom != "treepanel", aScroll);
|
||||
} else {
|
||||
this.pendingSelection = { node: aNode, scroll: aScroll };
|
||||
}
|
||||
|
@ -440,7 +440,7 @@ InspectorUI.prototype = {
|
||||
/**
|
||||
* Toggle the TreePanel.
|
||||
*/
|
||||
toggleHTMLPanel: function TP_toggleHTMLPanel()
|
||||
toggleHTMLPanel: function IUI_toggleHTMLPanel()
|
||||
{
|
||||
if (this.treePanel.isOpen()) {
|
||||
this.treePanel.close();
|
||||
@ -849,7 +849,7 @@ InspectorUI.prototype = {
|
||||
|
||||
this.breadcrumbs.update();
|
||||
this.chromeWin.Tilt.update(aNode);
|
||||
this.treePanel.select(aNode, aScroll);
|
||||
this.treePanel.select(aNode, aScroll, aFrom);
|
||||
|
||||
this._notifySelected(aFrom);
|
||||
},
|
||||
|
@ -42,6 +42,8 @@ _BROWSER_FILES = \
|
||||
browser_inspector_pseudoClass_menu.js \
|
||||
browser_inspector_destroyselection.html \
|
||||
browser_inspector_destroyselection.js \
|
||||
browser_inspector_treePanel_navigation.html \
|
||||
browser_inspector_treePanel_navigation.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="html">
|
||||
<meta charset=utf-8 />
|
||||
|
||||
<body class="body">
|
||||
<div class="node0">
|
||||
<p class="node1">line1</p>
|
||||
<p class="node2">line2</p>
|
||||
<p class="node3">line3</p>
|
||||
<!-- A comment -->
|
||||
<p class="node4">line4
|
||||
<span class="node5">line5</span>
|
||||
<span class="node6">line6</span>
|
||||
<!-- A comment -->
|
||||
<a class="node7">line7<span class="node8">line8</span></a>
|
||||
<span class="node9">line9</span>
|
||||
<span class="node10">line10</span>
|
||||
<span class="node11">line11</span>
|
||||
<a class="node12">line12<span class="node13">line13</span></a>
|
||||
</p>
|
||||
<p class="node14">line14</p>
|
||||
<p class="node15">line15</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,103 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
function test() {
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
let doc;
|
||||
|
||||
let keySequence = "right down right ";
|
||||
keySequence += "down down down down right ";
|
||||
keySequence += "down down down right ";
|
||||
keySequence += "down down down down down right ";
|
||||
keySequence += "down down down down down ";
|
||||
keySequence += "up up up left down home ";
|
||||
keySequence += "pagedown left down down pageup pageup left down";
|
||||
|
||||
keySequence = keySequence.split(" ");
|
||||
|
||||
let keySequenceRes = "body node0 node0 ";
|
||||
keySequenceRes += "node1 node2 node3 node4 node4 ";
|
||||
keySequenceRes += "node5 node6 node7 node7 ";
|
||||
keySequenceRes += "node8 node9 node10 node11 node12 node12 ";
|
||||
keySequenceRes += "node13 node14 node15 node15 node15 ";
|
||||
keySequenceRes += "node14 node13 node12 node12 node14 html ";
|
||||
keySequenceRes += "node7 node7 node9 node10 body html html html";
|
||||
|
||||
keySequenceRes = keySequenceRes.split(" ");
|
||||
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
doc = content.document;
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "http://mochi.test:8888/browser/browser/devtools/highlighter/test/browser_inspector_treePanel_navigation.html";
|
||||
|
||||
function setupTest() {
|
||||
Services.obs.addObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
InspectorUI.toggleInspectorUI();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
Services.obs.removeObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
|
||||
Services.obs.addObserver(startNavigation, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
|
||||
InspectorUI.select(doc.body, true, true, true);
|
||||
InspectorUI.toggleHTMLPanel();
|
||||
}
|
||||
|
||||
function startNavigation() {
|
||||
Services.obs.removeObserver(startNavigation, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
|
||||
nextStep(0);
|
||||
}
|
||||
|
||||
function nextStep(cursor) {
|
||||
let key = keySequence[cursor];
|
||||
let className = keySequenceRes[cursor];
|
||||
switch(key) {
|
||||
case "right":
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {});
|
||||
break;
|
||||
case "down":
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
break;
|
||||
case "left":
|
||||
EventUtils.synthesizeKey("VK_LEFT", {});
|
||||
break;
|
||||
case "up":
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
break;
|
||||
case "pageup":
|
||||
EventUtils.synthesizeKey("VK_PAGE_UP", {});
|
||||
break;
|
||||
case "pagedown":
|
||||
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
|
||||
break;
|
||||
case "home":
|
||||
EventUtils.synthesizeKey("VK_HOME", {});
|
||||
break;
|
||||
}
|
||||
|
||||
executeSoon(function() {
|
||||
if (cursor >= keySequence.length) {
|
||||
Services.obs.addObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
} else {
|
||||
let node = InspectorUI.treePanel.ioBox.selectedObjectBox.repObject;
|
||||
is(node.className, className, "[" + cursor + "] right node selected: " + className);
|
||||
nextStep(cursor + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
|
||||
doc = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user