Bug 879520 - Remote the inspector breadcrumbs. r=paul

This commit is contained in:
Dave Camp 2013-06-07 14:33:39 -07:00
parent af6dfe54f4
commit 2142a26f97
8 changed files with 326 additions and 144 deletions

View File

@ -12,6 +12,9 @@ const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let promise = require("sdk/core/promise");
const LOW_PRIORITY_ELEMENTS = {
"HEAD": true,
@ -25,6 +28,18 @@ const LOW_PRIORITY_ELEMENTS = {
"TITLE": true,
};
function resolveNextTick(value) {
let deferred = promise.defer();
Services.tm.mainThread.dispatch(() => {
try {
deferred.resolve(value);
} catch(ex) {
console.error(ex);
}
}, Ci.nsIThread.DISPATCH_NORMAL);
return deferred.promise;
}
///////////////////////////////////////////////////////////////////////////
//// HTML Breadcrumbs
@ -53,6 +68,8 @@ function HTMLBreadcrumbs(aInspector)
exports.HTMLBreadcrumbs = HTMLBreadcrumbs;
HTMLBreadcrumbs.prototype = {
get walker() this.inspector.walker,
_init: function BC__init()
{
this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
@ -83,12 +100,37 @@ HTMLBreadcrumbs.prototype = {
this.update = this.update.bind(this);
this.updateSelectors = this.updateSelectors.bind(this);
this.selection.on("new-node", this.update);
this.selection.on("new-node-front", this.update);
this.selection.on("pseudoclass", this.updateSelectors);
this.selection.on("attribute-changed", this.updateSelectors);
this.update();
},
/**
* Include in a promise's then() chain to reject the chain
* when the breadcrumbs' selection has changed while the promise
* was outstanding.
*/
selectionGuard: function() {
let selection = this.selection.nodeFront;
return (result) => {
if (selection != this.selection.nodeFront) {
return promise.reject("selection-changed");
}
return result;
}
},
/**
* Print any errors (except selection guard errors).
*/
selectionGuardEnd: function(err) {
if (err != "selection-changed") {
console.error(err);
}
promise.reject(err);
},
/**
* Build a string that represents the node: tagName#id.class1.class2.
*
@ -101,12 +143,19 @@ HTMLBreadcrumbs.prototype = {
if (aNode.id) {
text += "#" + aNode.id;
}
for (let i = 0; i < aNode.classList.length; i++) {
text += "." + aNode.classList[i];
if (aNode.className) {
let classList = aNode.className.split(/\s+/);
for (let i = 0; i < classList.length; i++) {
text += "." + classList[i];
}
}
// XXX: needs updating when pseudoclass-lock is remotable
let rawNode = aNode.rawNode();
for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
let pseudo = PSEUDO_CLASSES[i];
if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) {
if (DOMUtils.hasPseudoClassLock(rawNode, pseudo)) {
text += pseudo;
}
}
@ -143,14 +192,20 @@ HTMLBreadcrumbs.prototype = {
tagLabel.textContent = aNode.tagName.toLowerCase();
idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
let classesText = "";
for (let i = 0; i < aNode.classList.length; i++) {
classesText += "." + aNode.classList[i];
if (aNode.className) {
let classesText = "";
let classList = aNode.className.split(/\s+/);
for (let i = 0; i < classList.length; i++) {
classesText += "." + classList[i];
}
classesLabel.textContent = classesText;
}
classesLabel.textContent = classesText;
// XXX: Until we have pseudoclass lock in the node.
let rawNode = aNode.rawNode();
let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
return DOMUtils.hasPseudoClassLock(aNode, pseudo);
return DOMUtils.hasPseudoClassLock(rawNode, pseudo);
}, this);
pseudosLabel.textContent = pseudos.join("");
@ -173,7 +228,7 @@ HTMLBreadcrumbs.prototype = {
// We make sure that the targeted node is selected
// because we want to use the nodemenu that only works
// for inspector.selection
this.selection.setNode(aNode, "breadcrumbs");
this.selection.setNodeFront(aNode, "breadcrumbs");
let title = this.chromeDoc.createElement("menuitem");
title.setAttribute("label", this.inspector.strings.GetStringFromName("breadcrumbs.siblings"));
@ -183,9 +238,11 @@ HTMLBreadcrumbs.prototype = {
let items = [title, separator];
let nodes = aNode.parentNode.childNodes;
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].nodeType == aNode.ELEMENT_NODE) {
this.walker.siblings(aNode, {
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
}).then(siblings => {
let nodes = siblings.nodes;
for (let i = 0; i < nodes.length; i++) {
let item = this.chromeDoc.createElement("menuitem");
if (nodes[i] === aNode) {
item.setAttribute("disabled", "true");
@ -198,14 +255,14 @@ HTMLBreadcrumbs.prototype = {
let selection = this.selection;
item.onmouseup = (function(aNode) {
return function() {
selection.setNode(aNode, "breadcrumbs");
selection.setNodeFront(aNode, "breadcrumbs");
}
})(nodes[i]);
items.push(item);
this.inspector.showNodeMenu(aButton, "before_start", items);
}
}
this.inspector.showNodeMenu(aButton, "before_start", items);
});
},
/**
@ -252,33 +309,40 @@ HTMLBreadcrumbs.prototype = {
if (event.type == "keypress" && this.selection.isElementNode()) {
let node = null;
switch (event.keyCode) {
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
if (this.currentIndex != 0) {
node = this.nodeHierarchy[this.currentIndex - 1].node;
this._keyPromise = this._keyPromise || promise.resolve(null);
this._keyPromise = (this._keyPromise || promise.resolve(null)).then(() => {
switch (event.keyCode) {
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
if (this.currentIndex != 0) {
node = promise.resolve(this.nodeHierarchy[this.currentIndex - 1].node);
}
break;
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
if (this.currentIndex < this.nodeHierarchy.length - 1) {
node = promise.resolve(this.nodeHierarchy[this.currentIndex + 1].node);
}
break;
case this.chromeWin.KeyEvent.DOM_VK_UP:
node = this.walker.previousSibling(this.selection.nodeFront, {
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
});
break;
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
node = this.walker.nextSibling(this.selection.nodeFront, {
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
});
break;
}
return node.then((node) => {
if (node) {
this.selection.setNodeFront(node, "breadcrumbs");
}
break;
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
if (this.currentIndex < this.nodeHierarchy.length - 1) {
node = this.nodeHierarchy[this.currentIndex + 1].node;
}
break;
case this.chromeWin.KeyEvent.DOM_VK_UP:
node = this.selection.node.previousSibling;
while (node && (node.nodeType != node.ELEMENT_NODE)) {
node = node.previousSibling;
}
break;
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
node = this.selection.node.nextSibling;
while (node && (node.nodeType != node.ELEMENT_NODE)) {
node = node.nextSibling;
}
break;
}
if (node) {
this.selection.setNode(node, "breadcrumbs");
}
});
});
event.preventDefault();
event.stopPropagation();
}
@ -290,12 +354,18 @@ HTMLBreadcrumbs.prototype = {
destroy: function BC_destroy()
{
this.nodeHierarchy.forEach(function(crumb) {
if (LayoutHelpers.isNodeConnected(crumb.node)) {
DOMUtils.clearPseudoClassLocks(crumb.node);
// This node might have already been destroyed during
// shutdown. Will clean this up when pseudo-class lock
// is ported to the walker.
if (crumb.node.actorID) {
let rawNode = crumb.node.rawNode();
if (LayoutHelpers.isNodeConnected(rawNode)) {
DOMUtils.clearPseudoClassLocks(rawNode);
}
}
});
this.selection.off("new-node", this.update);
this.selection.off("new-node-front", this.update);
this.selection.off("pseudoclass", this.updateSelectors);
this.selection.off("attribute-changed", this.updateSelectors);
@ -401,7 +471,7 @@ HTMLBreadcrumbs.prototype = {
}
button.onBreadcrumbsClick = function onBreadcrumbsClick() {
this.selection.setNode(aNode, "breadcrumbs");
this.selection.setNodeFront(aNode, "breadcrumbs");
}.bind(this);
button.onclick = (function _onBreadcrumbsRightClick(event) {
@ -437,7 +507,7 @@ HTMLBreadcrumbs.prototype = {
fragment.insertBefore(button, lastButtonInserted);
lastButtonInserted = button;
this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
toAppend = this.DOMHelpers.getParentObject(toAppend);
toAppend = toAppend.parentNode();
}
this.container.appendChild(fragment, this.container.firstChild);
},
@ -451,24 +521,37 @@ HTMLBreadcrumbs.prototype = {
*/
getInterestingFirstNode: function BC_getInterestingFirstNode(aNode)
{
let nextChild = this.DOMHelpers.getChildObject(aNode, 0);
let fallback = null;
let deferred = promise.defer();
while (nextChild) {
if (nextChild.nodeType == aNode.ELEMENT_NODE) {
if (!(nextChild.tagName in LOW_PRIORITY_ELEMENTS)) {
return nextChild;
var fallback = null;
var moreChildren = () => {
this.walker.children(aNode, {
start: fallback,
maxNodes: 10,
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
}).then(this.selectionGuard()).then(response => {
for (let node of response.nodes) {
if (!(node.tagName in LOW_PRIORITY_ELEMENTS)) {
deferred.resolve(node);
return;
}
if (!fallback) {
fallback = node;
}
}
if (!fallback) {
fallback = nextChild;
if (response.hasLast) {
deferred.resolve(fallback);
return;
} else {
moreChildren();
}
}
nextChild = this.DOMHelpers.getNextSibling(nextChild);
}).then(null, this.selectionGuardEnd);
}
return fallback;
moreChildren();
return deferred.promise;
},
/**
* Find the "youngest" ancestor of a node which is already in the breadcrumbs.
*
@ -483,7 +566,7 @@ HTMLBreadcrumbs.prototype = {
if (idx > -1) {
return idx;
} else {
node = this.DOMHelpers.getParentObject(node);
node = node.parentNode();
}
}
return -1;
@ -498,13 +581,16 @@ HTMLBreadcrumbs.prototype = {
// If the last displayed node is the selected node
if (this.currentIndex == this.nodeHierarchy.length - 1) {
let node = this.nodeHierarchy[this.currentIndex].node;
let child = this.getInterestingFirstNode(node);
// If the node has a child
if (child) {
// Show this child
this.expand(child);
}
return this.getInterestingFirstNode(node).then(child => {
// If the node has a child
if (child) {
// Show this child
this.expand(child);
}
});
}
return resolveNextTick(true);
},
/**
@ -560,7 +646,7 @@ HTMLBreadcrumbs.prototype = {
return;
}
let idx = this.indexOf(this.selection.node);
let idx = this.indexOf(this.selection.nodeFront);
// Is the node already displayed in the breadcrumbs?
if (idx > -1) {
@ -571,24 +657,32 @@ HTMLBreadcrumbs.prototype = {
if (this.nodeHierarchy.length > 0) {
// No. We drop all the element that are not direct ancestors
// of the selection
let parent = this.DOMHelpers.getParentObject(this.selection.node);
let parent = this.selection.nodeFront.parentNode();
let idx = this.getCommonAncestor(parent);
this.cutAfter(idx);
}
// we append the missing button between the end of the breadcrumbs display
// and the current node.
this.expand(this.selection.node);
this.expand(this.selection.nodeFront);
// we select the current node button
idx = this.indexOf(this.selection.node);
idx = this.indexOf(this.selection.nodeFront);
this.setCursor(idx);
}
// Add the first child of the very last node of the breadcrumbs if possible.
this.ensureFirstChild();
this.updateSelectors();
// Make sure the selected node and its neighbours are visible.
this.scroll();
let doneUpdating = this.inspector.updating("breadcrumbs");
// Add the first child of the very last node of the breadcrumbs if possible.
this.ensureFirstChild().then(this.selectionGuard()).then(() => {
this.updateSelectors();
// Make sure the selected node and its neighbours are visible.
this.scroll();
this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
doneUpdating();
}).then(null, err => {
doneUpdating(this.selection.nodeFront);
this.selectionGuardEnd(err);
});
},
}

View File

@ -75,7 +75,7 @@ InspectorPanel.prototype = {
this.onNewSelection = this.onNewSelection.bind(this);
this.selection.on("new-node-front", this.onNewSelection);
this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
this.selection.on("before-new-node", this.onBeforeNewSelection);
this.selection.on("before-new-node-front", this.onBeforeNewSelection);
this.onDetached = this.onDetached.bind(this);
this.selection.on("detached-front", this.onDetached);
@ -309,6 +309,69 @@ InspectorPanel.prototype = {
*/
onNewSelection: function InspectorPanel_onNewSelection() {
this.cancelLayoutChange();
// Wait for all the known tools to finish updating and then let the
// client know.
let selection = this.selection.nodeFront;
let selfUpdate = this.updating("inspector-panel");
Services.tm.mainThread.dispatch(() => {
try {
selfUpdate(selection);
} catch(ex) {
console.error(ex);
}
}, Ci.nsIThread.DISPATCH_NORMAL);
},
/**
* Delay the "inspector-updated" notification while a tool
* is updating itself. Returns a function that must be
* invoked when the tool is done updating with the node
* that the tool is viewing.
*/
updating: function(name) {
if (this._updateProgress && this._updateProgress.node != this.selection.nodeFront) {
this.cancelUpdate();
}
if (!this._updateProgress) {
// Start an update in progress.
var self = this;
this._updateProgress = {
node: this.selection.nodeFront,
outstanding: new Set(),
checkDone: function() {
if (this !== self._updateProgress) {
return;
}
if (this.node !== self.selection.nodeFront) {
self.cancelUpdate();
return;
}
if (this.outstanding.size !== 0) {
return;
}
self._updateProgress = null;
self.emit("inspector-updated");
},
}
}
let progress = this._updateProgress;
let done = function() {
progress.outstanding.delete(done);
progress.checkDone();
};
progress.outstanding.add(done);
return done;
},
/**
* Cancel notification of inspector updates.
*/
cancelUpdate: function() {
this._updateProgress = null;
},
/**
@ -345,6 +408,7 @@ InspectorPanel.prototype = {
this._destroyPromise = Promise.resolve(null);
}
this.cancelUpdate();
this.cancelLayoutChange();
if (this.browser) {
@ -376,6 +440,7 @@ InspectorPanel.prototype = {
this.searchSuggestions.destroy();
this.selection.off("new-node-front", this.onNewSelection);
this.selection.off("before-new-node", this.onBeforeNewSelection);
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
this.selection.off("detached-front", this.onDetached);
this._destroyMarkup();
this._selection.destroy();
@ -517,7 +582,7 @@ InspectorPanel.prototype = {
if (this.selection.isElementNode()) {
if (DOMUtils.hasPseudoClassLock(this.selection.node, aPseudo)) {
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
DOMUtils.removePseudoClassLock(crumb.node, aPseudo);
DOMUtils.removePseudoClassLock(crumb.node.rawNode(), aPseudo);
});
} else {
let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
@ -538,7 +603,7 @@ InspectorPanel.prototype = {
clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
try {
DOMUtils.clearPseudoClassLocks(crumb.node);
DOMUtils.clearPseudoClassLocks(crumb.node.rawNode());
} catch(e) {
// Ignore dead nodes after navigation.
}

View File

@ -44,7 +44,7 @@ function test()
{
inspector = aInspector;
cursor = 0;
inspector.selection.on("new-node", nodeSelected);
inspector.on("breadcrumbs-updated", nodeSelected);
executeSoon(function() {
inspector.selection.setNode(nodes[0].node);
});
@ -52,18 +52,16 @@ function test()
function nodeSelected()
{
executeSoon(function() {
performTest();
cursor++;
performTest();
cursor++;
if (cursor >= nodes.length) {
inspector.selection.off("new-node", nodeSelected);
finishUp();
} else {
let node = nodes[cursor].node;
inspector.selection.setNode(node);
}
});
if (cursor >= nodes.length) {
inspector.off("breadcrumbs-updated", nodeSelected);
finishUp();
} else {
let node = nodes[cursor].node;
inspector.selection.setNode(node);
}
}
function performTest()

View File

@ -34,11 +34,12 @@ function test()
{
inspector = aInspector;
executeSoon(function() {
inspector.selection.once("new-node", highlightHeaderNode);
// Test that navigating around without a selected node gets us to the
// head element.
node = doc.querySelector("h1");
// Make sure the body element is selected initially.
node = doc.querySelector("body");
inspector.once("inspector-updated", () => {
is(inspector.selection.node, node, "Body should be selected initially.");
node = doc.querySelector("h1")
inspector.once("inspector-updated", highlightHeaderNode);
let bc = inspector.breadcrumbs;
bc.nodeHierarchy[bc.currentIndex].button.focus();
EventUtils.synthesizeKey("VK_RIGHT", { });
@ -50,7 +51,7 @@ function test()
is(inspector.selection.node, node, "selected h1 element");
executeSoon(function() {
inspector.selection.once("new-node", highlightParagraphNode);
inspector.once("inspector-updated", highlightParagraphNode);
// Test that moving to the next sibling works.
node = doc.querySelector("p");
EventUtils.synthesizeKey("VK_DOWN", { });
@ -62,7 +63,7 @@ function test()
is(inspector.selection.node, node, "selected p element");
executeSoon(function() {
inspector.selection.once("new-node", highlightHeaderNodeAgain);
inspector.once("inspector-updated", highlightHeaderNodeAgain);
// Test that moving to the previous sibling works.
node = doc.querySelector("h1");
EventUtils.synthesizeKey("VK_UP", { });
@ -74,7 +75,7 @@ function test()
is(inspector.selection.node, node, "selected h1 element");
executeSoon(function() {
inspector.selection.once("new-node", highlightParentNode);
inspector.once("inspector-updated", highlightParentNode);
// Test that moving to the parent works.
node = doc.querySelector("body");
EventUtils.synthesizeKey("VK_LEFT", { });

View File

@ -40,27 +40,29 @@ function startInspectorTests(toolbox)
let p = doc.querySelector("p");
inspector.selection.setNode(p);
inspector.once("inspector-updated", () => {
testHighlighter(p);
testMarkupView(p);
testBreadcrumbs(p);
testHighlighter(p);
testMarkupView(p);
testBreadcrumbs(p);
let span = doc.querySelector("span");
span.scrollIntoView();
let span = doc.querySelector("span");
span.scrollIntoView();
inspector.selection.setNode(span);
inspector.once("inspector-updated", () => {
testHighlighter(span);
testMarkupView(span);
testBreadcrumbs(span);
inspector.selection.setNode(span);
testHighlighter(span);
testMarkupView(span);
testBreadcrumbs(span);
toolbox.once("destroyed", function() {
ok("true", "'destroyed' notification received.");
let target = TargetFactory.forTab(gBrowser.selectedTab);
ok(!gDevTools.getToolbox(target), "Toolbox destroyed.");
executeSoon(runContextMenuTest);
toolbox.once("destroyed", function() {
ok("true", "'destroyed' notification received.");
let target = TargetFactory.forTab(gBrowser.selectedTab);
ok(!gDevTools.getToolbox(target), "Toolbox destroyed.");
executeSoon(runContextMenuTest);
});
toolbox.destroy();
});
});
toolbox.destroy();
}
@ -79,7 +81,7 @@ function testMarkupView(node)
function testBreadcrumbs(node)
{
let b = getActiveInspector().breadcrumbs;
let expectedText = b.prettyPrintNodeAsText(node);
let expectedText = b.prettyPrintNodeAsText(getNodeFront(node));
let button = b.container.querySelector("button[checked=true]");
ok(button, "A crumbs is checked=true");
is(button.getAttribute("tooltiptext"), expectedText, "Crumb refers to the right node");

View File

@ -31,21 +31,23 @@ function test() {
function checkDocTypeMenuItems() {
info("Checking context menu entries for doctype node");
inspector.selection.setNode(doc.doctype);
let docTypeNode = getMarkupTagNodeContaining("<!DOCTYPE html>");
inspector.once("inspector-updated", () => {
let docTypeNode = getMarkupTagNodeContaining("<!DOCTYPE html>");
// Right-click doctype tag
contextMenuClick(docTypeNode);
// Right-click doctype tag
contextMenuClick(docTypeNode);
checkDisabled("node-menu-copyinner");
checkDisabled("node-menu-copyouter");
checkDisabled("node-menu-copyuniqueselector");
checkDisabled("node-menu-delete");
checkDisabled("node-menu-copyinner");
checkDisabled("node-menu-copyouter");
checkDisabled("node-menu-copyuniqueselector");
checkDisabled("node-menu-delete");
for (let name of ["hover", "active", "focus"]) {
checkDisabled("node-menu-pseudo-" + name);
}
for (let name of ["hover", "active", "focus"]) {
checkDisabled("node-menu-pseudo-" + name);
}
checkElementMenuItems();
checkElementMenuItems();
});
}
function checkElementMenuItems() {

View File

@ -51,11 +51,11 @@ function createDocument()
function selectNode(aInspector)
{
inspector = aInspector;
inspector.selection.setNode(div);
inspector.sidebar.once("ruleview-ready", function() {
ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
inspector.sidebar.select("ruleview");
performTests();
inspector.selection.setNode(div);
inspector.once("inspector-updated", performTests);
});
}
@ -75,29 +75,37 @@ function performTests()
// toggle it back on
inspector.togglePseudoClass(pseudo);
testNavigate();
// close the inspector
finishUp();
testNavigate(() => {
// close the inspector
finishUp();
});
}
function testNavigate()
function testNavigate(callback)
{
inspector.selection.setNode(parentDiv);
inspector.once("inspector-updated", () => {
// make sure it's still on after naving to parent
is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
"pseudo-class lock is still applied after inspecting ancestor");
// make sure it's still on after naving to parent
is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
"pseudo-class lock is still applied after inspecting ancestor");
inspector.selection.setNode(div2);
inspector.selection.setNode(div2);
// make sure it's removed after naving to a non-hierarchy node
is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
"pseudo-class lock is removed after inspecting sibling node");
inspector.once("inspector-updated", () => {
// toggle it back on
inspector.selection.setNode(div);
inspector.togglePseudoClass(pseudo);
// make sure it's removed after naving to a non-hierarchy node
is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
"pseudo-class lock is removed after inspecting sibling node");
// toggle it back on
inspector.selection.setNode(div);
inspector.once("inspector-updated", () => {
inspector.togglePseudoClass(pseudo);
callback();
});
});
});
}
function testAdded()

View File

@ -26,6 +26,12 @@ let console = tempScope.console;
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
SimpleTest.registerCleanupFunction(() => {
console.error("Here we are\n")
let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
console.error("DebuggerServer open connections: " + Object.getOwnPropertyNames(DebuggerServer._connections).length);
});
function openInspector(callback)
{
let target = TargetFactory.forTab(gBrowser.selectedTab);
@ -40,6 +46,12 @@ function getActiveInspector()
return gDevTools.getToolbox(target).getPanel("inspector");
}
function getNodeFront(node)
{
let inspector = getActiveInspector();
return inspector.walker.frontForRawNode(node);
}
function isHighlighting()
{
let outline = getActiveInspector().highlighter.outline;