Bug 952277 - DOMNodes can be highlighted and selected from the debugger [Australis]; r=past, vporof
@ -1730,7 +1730,6 @@ function VariableBubbleView() {
|
||||
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
this._onMouseLeave = this._onMouseLeave.bind(this);
|
||||
this._onMouseScroll = this._onMouseScroll.bind(this);
|
||||
this._onPopupHiding = this._onPopupHiding.bind(this);
|
||||
}
|
||||
|
||||
@ -1741,16 +1740,23 @@ VariableBubbleView.prototype = {
|
||||
initialize: function() {
|
||||
dumpn("Initializing the VariableBubbleView");
|
||||
|
||||
this._tooltip = new Tooltip(document);
|
||||
this._editorContainer = document.getElementById("editor");
|
||||
|
||||
this._tooltip.defaultPosition = EDITOR_VARIABLE_POPUP_POSITION;
|
||||
this._tooltip.defaultShowDelay = EDITOR_VARIABLE_HOVER_DELAY;
|
||||
|
||||
this._tooltip.panel.addEventListener("popuphiding", this._onPopupHiding);
|
||||
this._editorContainer.addEventListener("mousemove", this._onMouseMove, false);
|
||||
this._editorContainer.addEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._editorContainer.addEventListener("scroll", this._onMouseScroll, true);
|
||||
|
||||
this._tooltip = new Tooltip(document, {
|
||||
closeOnEvents: [{
|
||||
emitter: DebuggerController._toolbox,
|
||||
event: "select"
|
||||
}, {
|
||||
emitter: this._editorContainer,
|
||||
event: "scroll",
|
||||
useCapture: true
|
||||
}]
|
||||
});
|
||||
this._tooltip.defaultPosition = EDITOR_VARIABLE_POPUP_POSITION;
|
||||
this._tooltip.defaultShowDelay = EDITOR_VARIABLE_HOVER_DELAY;
|
||||
this._tooltip.panel.addEventListener("popuphiding", this._onPopupHiding);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1762,7 +1768,6 @@ VariableBubbleView.prototype = {
|
||||
this._tooltip.panel.removeEventListener("popuphiding", this._onPopupHiding);
|
||||
this._editorContainer.removeEventListener("mousemove", this._onMouseMove, false);
|
||||
this._editorContainer.removeEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._editorContainer.removeEventListener("scroll", this._onMouseScroll, true);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1908,7 +1913,7 @@ VariableBubbleView.prototype = {
|
||||
DebuggerView.VariableBubble.hideContents();
|
||||
DebuggerView.WatchExpressions.addExpression(evalPrefix, true);
|
||||
}
|
||||
}]);
|
||||
}], DebuggerController._toolbox);
|
||||
}
|
||||
|
||||
this._tooltip.show(this._markedText.anchor);
|
||||
@ -1974,13 +1979,6 @@ VariableBubbleView.prototype = {
|
||||
clearNamedTimeout("editor-mouse-move");
|
||||
},
|
||||
|
||||
/**
|
||||
* The mousescroll listener for the source editor container node.
|
||||
*/
|
||||
_onMouseScroll: function() {
|
||||
this.hideContents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Listener handling the popup hiding event.
|
||||
*/
|
||||
|
@ -169,6 +169,10 @@ let DebuggerView = {
|
||||
lazyEmpty: true
|
||||
});
|
||||
|
||||
// Attach the current toolbox to the VView so it can link DOMNodes to
|
||||
// the inspector/highlighter
|
||||
this.Variables.toolbox = DebuggerController._toolbox;
|
||||
|
||||
// Attach a controller that handles interfacing with the debugger protocol.
|
||||
VariablesViewController.attach(this.Variables, {
|
||||
getEnvironmentClient: aObject => gThreadClient.environment(aObject),
|
||||
|
@ -20,6 +20,7 @@ function DebuggerPanel(iframeWindow, toolbox) {
|
||||
this._controller = this.panelWin.DebuggerController;
|
||||
this._view._hostType = this._toolbox.hostType;
|
||||
this._controller._target = this.target;
|
||||
this._controller._toolbox = this._toolbox;
|
||||
|
||||
this.handleHostChanged = this.handleHostChanged.bind(this);
|
||||
this.highlightWhenPaused = this.highlightWhenPaused.bind(this);
|
||||
|
@ -100,7 +100,7 @@ function testExpandVariables() {
|
||||
is(thisVar.get("document").target.querySelector(".value").getAttribute("value"),
|
||||
"HTMLDocument \u2192 doc_frame-parameters.html",
|
||||
"Should have the right property value for 'document'.");
|
||||
ok(thisVar.get("document").target.querySelector(".value").className.contains("token-other"),
|
||||
ok(thisVar.get("document").target.querySelector(".value").className.contains("token-domnode"),
|
||||
"Should have the right token class for 'document'.");
|
||||
|
||||
let argsProps = argsVar.target.querySelectorAll(".variables-view-property");
|
||||
|
@ -64,7 +64,7 @@ function performTest() {
|
||||
"Should have the right property name for 'button'.");
|
||||
is(buttonVar.target.querySelector(".value").getAttribute("value"), "<button>",
|
||||
"Should have the right property value for 'button'.");
|
||||
ok(buttonVar.target.querySelector(".value").className.contains("token-other"),
|
||||
ok(buttonVar.target.querySelector(".value").className.contains("token-domnode"),
|
||||
"Should have the right token class for 'button'.");
|
||||
|
||||
is(buttonAsProtoVar.target.querySelector(".name").getAttribute("value"), "buttonAsProto",
|
||||
@ -79,7 +79,7 @@ function performTest() {
|
||||
is(documentVar.target.querySelector(".value").getAttribute("value"),
|
||||
"HTMLDocument \u2192 doc_frame-parameters.html",
|
||||
"Should have the right property value for 'document'.");
|
||||
ok(documentVar.target.querySelector(".value").className.contains("token-other"),
|
||||
ok(documentVar.target.querySelector(".value").className.contains("token-domnode"),
|
||||
"Should have the right token class for 'document'.");
|
||||
|
||||
is(buttonVar.expanded, false,
|
||||
@ -147,7 +147,7 @@ function performTest() {
|
||||
"Should have the right property name for '__proto__'.");
|
||||
is(buttonAsProtoProtoVar.target.querySelector(".value").getAttribute("value"), "<button>",
|
||||
"Should have the right property value for '__proto__'.");
|
||||
ok(buttonAsProtoProtoVar.target.querySelector(".value").className.contains("token-other"),
|
||||
ok(buttonAsProtoProtoVar.target.querySelector(".value").className.contains("token-domnode"),
|
||||
"Should have the right token class for '__proto__'.");
|
||||
|
||||
is(documentProtoVar.target.querySelector(".name").getAttribute("value"), "__proto__",
|
||||
|
@ -64,22 +64,6 @@ function test()
|
||||
keysetMap.inspector.synthesizeKey();
|
||||
}
|
||||
|
||||
function moveMouseOver(aElement, aInspector, cb)
|
||||
{
|
||||
EventUtils.synthesizeMouse(aElement, 2, 2, {type: "mousemove"},
|
||||
aElement.ownerDocument.defaultView);
|
||||
aInspector.toolbox.once("picker-node-hovered", () => {
|
||||
executeSoon(cb);
|
||||
});
|
||||
}
|
||||
|
||||
function isHighlighting()
|
||||
{
|
||||
let outline = gBrowser.selectedBrowser.parentNode
|
||||
.querySelector(".highlighter-container .highlighter-outline");
|
||||
return outline && !outline.hasAttribute("hidden");
|
||||
}
|
||||
|
||||
function inspectorShouldBeOpenAndHighlighting(aInspector, aToolbox)
|
||||
{
|
||||
is (aToolbox.currentToolId, "inspector", "Correct tool has been loaded");
|
||||
|
@ -73,7 +73,7 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
|
||||
this._refreshHostTitle = this._refreshHostTitle.bind(this);
|
||||
this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this)
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this.stopPicker = this.stopPicker.bind(this);
|
||||
this.highlighterUtils = new ToolboxHighlighterUtils(this);
|
||||
|
||||
this._target.on("close", this.destroy);
|
||||
|
||||
@ -192,7 +192,7 @@ Toolbox.prototype = {
|
||||
* initialized first. Use `initInspector()` if needed.
|
||||
*/
|
||||
get highlighter() {
|
||||
if (this.isRemoteHighlightable) {
|
||||
if (this.highlighterUtils.isRemoteHighlightable) {
|
||||
return this._highlighter;
|
||||
} else {
|
||||
return null;
|
||||
@ -557,8 +557,8 @@ Toolbox.prototype = {
|
||||
let container = this.doc.querySelector("#toolbox-buttons");
|
||||
container.appendChild(this._pickerButton);
|
||||
|
||||
this.togglePicker = this.togglePicker.bind(this);
|
||||
this._pickerButton.addEventListener("command", this.togglePicker, false);
|
||||
this._togglePicker = this.highlighterUtils.togglePicker.bind(this.highlighterUtils);
|
||||
this._pickerButton.addEventListener("command", this._togglePicker, false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1036,7 +1036,7 @@ Toolbox.prototype = {
|
||||
this._inspector.getWalker().then(walker => {
|
||||
this._walker = walker;
|
||||
this._selection = new Selection(this._walker);
|
||||
if (this.isRemoteHighlightable) {
|
||||
if (this.highlighterUtils.isRemoteHighlightable) {
|
||||
this._inspector.getHighlighter().then(highlighter => {
|
||||
this._highlighter = highlighter;
|
||||
deferred.resolve();
|
||||
@ -1085,98 +1085,6 @@ Toolbox.prototype = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start/stop the element picker on the debuggee target.
|
||||
*/
|
||||
togglePicker: function() {
|
||||
if (this._isPicking) {
|
||||
return this.stopPicker();
|
||||
} else {
|
||||
return this.startPicker();
|
||||
}
|
||||
},
|
||||
|
||||
get isRemoteHighlightable() {
|
||||
return this._target.client.traits.highlightable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the element picker on the debuggee target.
|
||||
* This will request the inspector actor to start listening for mouse/touch
|
||||
* events on the target to highlight the hovered/picked element.
|
||||
* Depending on the server-side capabilities, this may fire events when nodes
|
||||
* are hovered.
|
||||
* @return A promise that resolves when the picker has started
|
||||
*/
|
||||
startPicker: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let done = () => {
|
||||
this.emit("picker-started");
|
||||
this.on("select", this.stopPicker);
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
promise.all([
|
||||
this.initInspector(),
|
||||
this.selectTool("inspector")
|
||||
]).then(() => {
|
||||
this._isPicking = true;
|
||||
this._pickerButton.setAttribute("checked", "true");
|
||||
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.highlighter.pick().then(done);
|
||||
|
||||
this._onPickerNodeHovered = res => {
|
||||
this.emit("picker-node-hovered", res.node);
|
||||
};
|
||||
this.walker.on("picker-node-hovered", this._onPickerNodeHovered);
|
||||
|
||||
this._onPickerNodePicked = res => {
|
||||
this.selection.setNodeFront(res.node, "picker-node-picked");
|
||||
this.stopPicker();
|
||||
};
|
||||
this.walker.on("picker-node-picked", this._onPickerNodePicked);
|
||||
} else {
|
||||
this.walker.pick().then(node => {
|
||||
this.selection.setNodeFront(node, "picker-node-picked");
|
||||
this.stopPicker();
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the element picker
|
||||
* @return A promise that resolves when the picker has stopped
|
||||
*/
|
||||
stopPicker: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let done = () => {
|
||||
this.emit("picker-stopped");
|
||||
this.off("select", this.stopPicker);
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
this.initInspector().then(() => {
|
||||
this._isPicking = false;
|
||||
this._pickerButton.removeAttribute("checked");
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.highlighter.cancelPick().then(done);
|
||||
this.walker.off("picker-node-hovered", this._onPickerNodeHovered);
|
||||
this.walker.off("picker-node-picked", this._onPickerNodePicked);
|
||||
} else {
|
||||
this.walker.cancelPick().then(done);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbox's notification box
|
||||
*
|
||||
@ -1228,7 +1136,7 @@ Toolbox.prototype = {
|
||||
outstanding.push(this.destroyInspector());
|
||||
|
||||
// Removing buttons
|
||||
this._pickerButton.removeEventListener("command", this.togglePicker, false);
|
||||
this._pickerButton.removeEventListener("command", this._togglePicker, false);
|
||||
this._pickerButton = null;
|
||||
let container = this.doc.getElementById("toolbox-buttons");
|
||||
while (container.firstChild) {
|
||||
@ -1260,3 +1168,184 @@ Toolbox.prototype = {
|
||||
}).then(null, console.error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The ToolboxHighlighterUtils is what you should use for anything related to
|
||||
* node highlighting and picking.
|
||||
* It encapsulates the logic to connecting to the HighlighterActor.
|
||||
*/
|
||||
function ToolboxHighlighterUtils(toolbox) {
|
||||
this.toolbox = toolbox;
|
||||
this._onPickerNodeHovered = this._onPickerNodeHovered.bind(this);
|
||||
this._onPickerNodePicked = this._onPickerNodePicked.bind(this);
|
||||
this.stopPicker = this.stopPicker.bind(this);
|
||||
}
|
||||
|
||||
ToolboxHighlighterUtils.prototype = {
|
||||
/**
|
||||
* Indicates whether the highlighter actor exists on the server.
|
||||
*/
|
||||
get isRemoteHighlightable() {
|
||||
return this.toolbox._target.client.traits.highlightable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start/stop the element picker on the debuggee target.
|
||||
*/
|
||||
togglePicker: function() {
|
||||
if (this._isPicking) {
|
||||
return this.stopPicker();
|
||||
} else {
|
||||
return this.startPicker();
|
||||
}
|
||||
},
|
||||
|
||||
_onPickerNodeHovered: function(res) {
|
||||
this.toolbox.emit("picker-node-hovered", res.node);
|
||||
},
|
||||
|
||||
_onPickerNodePicked: function(res) {
|
||||
this.toolbox.selection.setNodeFront(res.node, "picker-node-picked");
|
||||
this.stopPicker();
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the element picker on the debuggee target.
|
||||
* This will request the inspector actor to start listening for mouse/touch
|
||||
* events on the target to highlight the hovered/picked element.
|
||||
* Depending on the server-side capabilities, this may fire events when nodes
|
||||
* are hovered.
|
||||
* @return A promise that resolves when the picker has started
|
||||
*/
|
||||
startPicker: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let done = () => {
|
||||
this.toolbox.emit("picker-started");
|
||||
this.toolbox.on("select", this.stopPicker);
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
promise.all([
|
||||
this.toolbox.initInspector(),
|
||||
this.toolbox.selectTool("inspector")
|
||||
]).then(() => {
|
||||
this._isPicking = true;
|
||||
this.toolbox._pickerButton.setAttribute("checked", "true");
|
||||
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.toolbox.highlighter.pick().then(done);
|
||||
|
||||
this.toolbox.walker.on("picker-node-hovered", this._onPickerNodeHovered);
|
||||
this.toolbox.walker.on("picker-node-picked", this._onPickerNodePicked);
|
||||
} else {
|
||||
this.toolbox.walker.pick().then(node => {
|
||||
this.toolbox.selection.setNodeFront(node, "picker-node-picked");
|
||||
this.stopPicker();
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the element picker
|
||||
* @return A promise that resolves when the picker has stopped
|
||||
*/
|
||||
stopPicker: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let done = () => {
|
||||
this.toolbox.emit("picker-stopped");
|
||||
this.toolbox.off("select", this.stopPicker);
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
this.toolbox.initInspector().then(() => {
|
||||
this._isPicking = false;
|
||||
this.toolbox._pickerButton.removeAttribute("checked");
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.toolbox.highlighter.cancelPick().then(done);
|
||||
this.toolbox.walker.off("picker-node-hovered", this._onPickerNodeHovered);
|
||||
this.toolbox.walker.off("picker-node-picked", this._onPickerNodePicked);
|
||||
} else {
|
||||
this.toolbox.walker.cancelPick().then(done);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the box model highlighter on a node, given its NodeFront (this type
|
||||
* of front is normally returned by the WalkerActor).
|
||||
* @return a promise that resolves to the nodeFront when the node has been
|
||||
* highlit
|
||||
*/
|
||||
highlightNodeFront: function(nodeFront, options={}) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
// If the remote highlighter exists on the target, use it
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.toolbox.initInspector().then(() => {
|
||||
this.toolbox.highlighter.showBoxModel(nodeFront, options).then(() => {
|
||||
this.toolbox.emit("node-highlight", nodeFront);
|
||||
deferred.resolve(nodeFront);
|
||||
});
|
||||
});
|
||||
}
|
||||
// Else, revert to the "older" version of the highlighter in the walker
|
||||
// actor
|
||||
else {
|
||||
this.toolbox.walker.highlight(nodeFront).then(() => {
|
||||
this.toolbox.emit("node-highlight", nodeFront);
|
||||
deferred.resolve(nodeFront);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This is a convenience method in case you don't have a nodeFront but a
|
||||
* valueGrip. This is often the case with VariablesView properties.
|
||||
* This method will simply translate the grip into a nodeFront and call
|
||||
* highlightNodeFront
|
||||
* @return a promise that resolves to the nodeFront when the node has been
|
||||
* highlit
|
||||
*/
|
||||
highlightDomValueGrip: function(valueGrip, options={}) {
|
||||
return this._translateGripToNodeFront(valueGrip).then(nodeFront => {
|
||||
if (nodeFront) {
|
||||
return this.highlightNodeFront(nodeFront, options);
|
||||
} else {
|
||||
return promise.reject();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_translateGripToNodeFront: function(grip) {
|
||||
return this.toolbox.initInspector().then(() => {
|
||||
return this.toolbox.walker.getNodeActorFromObjectActor(grip.actor);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the highlighter.
|
||||
* @return a promise that resolves when the highlighter is hidden
|
||||
*/
|
||||
unhighlight: function() {
|
||||
if (this.isRemoteHighlightable) {
|
||||
// If the remote highlighter exists on the target, use it
|
||||
return this.toolbox.initInspector().then(() => {
|
||||
return this.toolbox.highlighter.hideBoxModel();
|
||||
});
|
||||
} else {
|
||||
// If not, no need to unhighlight as the older highlight method uses a
|
||||
// setTimeout to hide itself
|
||||
return promise.resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -47,7 +47,7 @@ function test() {
|
||||
let container = getContainerForRawNode(inspector.markup, doc.querySelector("h1"));
|
||||
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
|
||||
inspector.markup.doc.defaultView);
|
||||
inspector.markup.once("node-highlight", deferred.resolve);
|
||||
inspector.toolbox.once("node-highlight", deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ function test()
|
||||
|
||||
function runTests()
|
||||
{
|
||||
inspector.toolbox.startPicker().then(() => {
|
||||
inspector.toolbox.highlighterUtils.startPicker().then(() => {
|
||||
moveMouseOver(iframeNode, 1, 1, isTheIframeHighlighted);
|
||||
});
|
||||
}
|
||||
@ -86,7 +86,7 @@ function test()
|
||||
let outlineRect = getHighlighterOutlineRect();
|
||||
is(outlineRect.height, 184, "highlighter height");
|
||||
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
inspector.toolbox.highlighterUtils.stopPicker().then(() => {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
finishUp();
|
||||
|
@ -15,7 +15,7 @@ function test() {
|
||||
}
|
||||
|
||||
function showHighlighter(cb) {
|
||||
inspector.toolbox.startPicker().then(() => {
|
||||
inspector.toolbox.highlighterUtils.startPicker().then(() => {
|
||||
EventUtils.synthesizeMouse(content.document.body, 1, 1,
|
||||
{type: "mousemove"}, content);
|
||||
inspector.toolbox.once("picker-node-hovered", () => {
|
||||
@ -63,7 +63,7 @@ function test() {
|
||||
is(iframeLoads, 2, "iframe loads");
|
||||
ok(checksAfterLoads, "the Inspector tests got the chance to run after iframe reloads");
|
||||
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
inspector.toolbox.highlighterUtils.stopPicker().then(() => {
|
||||
iframe = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
executeSoon(finish);
|
||||
|
@ -24,7 +24,7 @@ function startTests() {
|
||||
Task.spawn(function() {
|
||||
yield openToolbox();
|
||||
yield startPickerAndAssertSwitchToInspector();
|
||||
yield toolbox.stopPicker();
|
||||
yield toolbox.highlighterUtils.stopPicker();
|
||||
|
||||
finishTests();
|
||||
}).then(null, Cu.reportError);
|
||||
|
@ -87,7 +87,7 @@ function hoverContainer(container) {
|
||||
let deferred = promise.defer();
|
||||
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
|
||||
markupView.doc.defaultView);
|
||||
inspector.markup.once("node-highlight", deferred.resolve);
|
||||
inspector.toolbox.once("node-highlight", deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ function test() {
|
||||
});
|
||||
|
||||
Task.spawn(function() {
|
||||
yield toolbox.startPicker();
|
||||
yield toolbox.highlighterUtils.startPicker();
|
||||
yield toolbox.selectNextTool();
|
||||
}).then(null, Cu.reportError);
|
||||
});
|
||||
|
@ -52,7 +52,7 @@ function createDocument() {
|
||||
inspector.selection.setNode(div, null);
|
||||
inspector.once("inspector-updated", () => {
|
||||
getHighlighterOutline().setAttribute("disable-transitions", "true");
|
||||
inspector.toolbox.startPicker().then(testMouseOverH1Highlights);
|
||||
inspector.toolbox.highlighterUtils.startPicker().then(testMouseOverH1Highlights);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -109,7 +109,7 @@ function testOutlineDimensions() {
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
inspector.toolbox.highlighterUtils.stopPicker().then(() => {
|
||||
doc = h1 = inspector = null;
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
|
@ -34,7 +34,7 @@ function createDocument() {
|
||||
// Open the inspector, start the picker mode, and start the tests
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
inspector.toolbox.startPicker().then(runTests);
|
||||
inspector.toolbox.highlighterUtils.startPicker().then(runTests);
|
||||
});
|
||||
}, false);
|
||||
|
||||
@ -75,14 +75,14 @@ function testDiv2Highlighter() {
|
||||
|
||||
function selectRoot() {
|
||||
// Select the root document element to clear the breadcrumbs.
|
||||
inspector.selection.setNode(doc.documentElement);
|
||||
inspector.selection.setNode(doc.documentElement, null);
|
||||
inspector.once("inspector-updated", selectIframe);
|
||||
}
|
||||
|
||||
function selectIframe() {
|
||||
// Directly select an element in an iframe (without navigating to it
|
||||
// with mousemoves).
|
||||
inspector.selection.setNode(div2);
|
||||
inspector.selection.setNode(div2, null);
|
||||
inspector.once("inspector-updated", () => {
|
||||
let breadcrumbs = inspector.breadcrumbs;
|
||||
is(breadcrumbs.nodeHierarchy.length, 9, "Should have 9 items");
|
||||
@ -91,7 +91,7 @@ function selectIframe() {
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
inspector.toolbox.highlighterUtils.stopPicker().then(() => {
|
||||
doc = div1 = div2 = iframe1 = iframe2 = inspector = null;
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
|
@ -109,7 +109,7 @@ Tools.inspector = {
|
||||
|
||||
preventClosingOnKey: true,
|
||||
onkey: function(panel) {
|
||||
panel.toolbox.togglePicker();
|
||||
panel.toolbox.highlighterUtils.togglePicker();
|
||||
},
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
|
@ -54,10 +54,6 @@ loader.lazyGetter(this, "AutocompletePopup", () => {
|
||||
* The inspector we're watching.
|
||||
* @param iframe aFrame
|
||||
* An iframe in which the caller has kindly loaded markup-view.xhtml.
|
||||
*
|
||||
* Fires the following events:
|
||||
* - node-highlight: When a node in the markup-view is hovered and the
|
||||
* corresponding node in the content gets highlighted
|
||||
*/
|
||||
function MarkupView(aInspector, aFrame, aControllerWindow) {
|
||||
this._inspector = aInspector;
|
||||
@ -174,41 +170,11 @@ MarkupView.prototype = {
|
||||
},
|
||||
|
||||
_showBoxModel: function(nodeFront, options={}) {
|
||||
let toolbox = this._inspector.toolbox;
|
||||
|
||||
// If the remote highlighter exists on the target, use it
|
||||
if (toolbox.isRemoteHighlightable) {
|
||||
toolbox.initInspector().then(() => {
|
||||
toolbox.highlighter.showBoxModel(nodeFront, options).then(() => {
|
||||
this.emit("node-highlight", nodeFront);
|
||||
});
|
||||
});
|
||||
}
|
||||
// Else, revert to the "older" version of the highlighter in the walker
|
||||
// actor
|
||||
else {
|
||||
this.walker.highlight(nodeFront).then(() => {
|
||||
this.emit("node-highlight", nodeFront);
|
||||
});
|
||||
}
|
||||
this._inspector.toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
|
||||
},
|
||||
|
||||
_hideBoxModel: function() {
|
||||
let deferred = promise.defer();
|
||||
let toolbox = this._inspector.toolbox;
|
||||
|
||||
// If the remote highlighter exists on the target, use it
|
||||
if (toolbox.isRemoteHighlightable) {
|
||||
toolbox.initInspector().then(() => {
|
||||
toolbox.highlighter.hideBoxModel().then(deferred.resolve);
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
// If not, no need to unhighlight as the older highlight method uses a
|
||||
// setTimeout to hide itself
|
||||
|
||||
return deferred.promise;
|
||||
this._inspector.toolbox.highlighterUtils.unhighlight();
|
||||
},
|
||||
|
||||
_briefBoxModelTimer: null,
|
||||
|
@ -34,6 +34,7 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const SPECTRUM_FRAME = "chrome://browser/content/devtools/spectrum-frame.xhtml";
|
||||
const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
|
||||
const ENTER_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
|
||||
const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"];
|
||||
|
||||
/**
|
||||
* Tooltip widget.
|
||||
@ -136,14 +137,27 @@ let PanelFactory = {
|
||||
* @param {XULDocument} doc
|
||||
* The XUL document hosting this tooltip
|
||||
* @param {Object} options
|
||||
* Optional options that give options to consumers
|
||||
* Optional options that give options to consumers:
|
||||
* - consumeOutsideClick {Boolean} Wether the first click outside of the
|
||||
* tooltip should close the tooltip and be consumed or not.
|
||||
* Defaults to false
|
||||
* Defaults to false.
|
||||
* - closeOnKeys {Array} An array of key codes that should close the
|
||||
* tooltip. Defaults to [27] (escape key)
|
||||
* tooltip. Defaults to [27] (escape key).
|
||||
* - closeOnEvents [{emitter: {Object}, event: {String}, useCapture: {Boolean}}]
|
||||
* Provide an optional list of emitter objects and event names here to
|
||||
* trigger the closing of the tooltip when these events are fired by the
|
||||
* emitters. The emitter objects should either implement on/off(event, cb)
|
||||
* or addEventListener/removeEventListener(event, cb). Defaults to [].
|
||||
* For instance, the following would close the tooltip whenever the
|
||||
* toolbox selects a new tool and when a DOM node gets scrolled:
|
||||
* new Tooltip(doc, {
|
||||
* closeOnEvents: [
|
||||
* {emitter: toolbox, event: "select"},
|
||||
* {emitter: myContainer, event: "scroll", useCapture: true}
|
||||
* ]
|
||||
* });
|
||||
* - noAutoFocus {Boolean} Should the focus automatically go to the panel
|
||||
* when it opens. Defaults to true
|
||||
* when it opens. Defaults to true.
|
||||
*
|
||||
* Fires these events:
|
||||
* - showing : just before the tooltip shows
|
||||
@ -159,7 +173,8 @@ function Tooltip(doc, options) {
|
||||
this.options = new OptionsStore({
|
||||
consumeOutsideClick: false,
|
||||
closeOnKeys: [ESCAPE_KEYCODE],
|
||||
noAutoFocus: true
|
||||
noAutoFocus: true,
|
||||
closeOnEvents: []
|
||||
}, options);
|
||||
this.panel = PanelFactory.get(doc, this.options);
|
||||
|
||||
@ -167,7 +182,7 @@ function Tooltip(doc, options) {
|
||||
this.uid = "tooltip-" + Date.now();
|
||||
|
||||
// Emit show/hide events
|
||||
for (let event of ["shown", "hidden", "showing", "hiding"]) {
|
||||
for (let event of POPUP_EVENTS) {
|
||||
this["_onPopup" + event] = ((e) => {
|
||||
return () => this.emit(e);
|
||||
})(event);
|
||||
@ -187,6 +202,18 @@ function Tooltip(doc, options) {
|
||||
}
|
||||
};
|
||||
win.addEventListener("keypress", this._onKeyPress, false);
|
||||
|
||||
// Listen to custom emitters' events to close the tooltip
|
||||
this.hide = this.hide.bind(this);
|
||||
let closeOnEvents = this.options.get("closeOnEvents");
|
||||
for (let {emitter, event, useCapture} of closeOnEvents) {
|
||||
for (let add of ["addEventListener", "on"]) {
|
||||
if (add in emitter) {
|
||||
emitter[add](event, this.hide, useCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.Tooltip = Tooltip;
|
||||
@ -264,7 +291,7 @@ Tooltip.prototype = {
|
||||
destroy: function () {
|
||||
this.hide();
|
||||
|
||||
for (let event of ["shown", "hidden", "showing", "hiding"]) {
|
||||
for (let event of POPUP_EVENTS) {
|
||||
this.panel.removeEventListener("popup" + event,
|
||||
this["_onPopup" + event], false);
|
||||
}
|
||||
@ -272,6 +299,16 @@ Tooltip.prototype = {
|
||||
let win = this.doc.querySelector("window");
|
||||
win.removeEventListener("keypress", this._onKeyPress, false);
|
||||
|
||||
let closeOnEvents = this.options.get("closeOnEvents");
|
||||
for (let {emitter, event, useCapture} of closeOnEvents) {
|
||||
for (let remove of ["removeEventListener", "off"]) {
|
||||
if (remove in emitter) {
|
||||
emitter[remove](event, this.hide, useCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.content = null;
|
||||
|
||||
if (this._basedNode) {
|
||||
@ -476,13 +513,17 @@ Tooltip.prototype = {
|
||||
* Pass false to instantiate a brand new widget for this variable.
|
||||
* Otherwise, if a variable was previously inspected, its widget
|
||||
* will be reused.
|
||||
* @param {Toolbox} toolbox [optional]
|
||||
* Pass the instance of the current toolbox if you want the variables
|
||||
* view widget to allow highlighting and selection of DOM nodes
|
||||
*/
|
||||
setVariableContent: function(
|
||||
objectActor,
|
||||
viewOptions = {},
|
||||
controllerOptions = {},
|
||||
relayEvents = {},
|
||||
extraButtons = []) {
|
||||
extraButtons = [],
|
||||
toolbox = null) {
|
||||
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.className = "devtools-tooltip-variables-view-box";
|
||||
@ -503,6 +544,11 @@ Tooltip.prototype = {
|
||||
|
||||
let widget = new VariablesView(innerbox, viewOptions);
|
||||
|
||||
// If a toolbox was provided, link it to the vview
|
||||
if (toolbox) {
|
||||
widget.toolbox = toolbox;
|
||||
}
|
||||
|
||||
// Analyzing state history isn't useful with transient object inspectors.
|
||||
widget.commitHierarchy = () => {};
|
||||
|
||||
|
@ -23,6 +23,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
|
||||
Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
||||
"resource://gre/modules/devtools/Loader.jsm");
|
||||
@ -210,6 +212,12 @@ VariablesView.prototype = {
|
||||
}, aTimeout);
|
||||
},
|
||||
|
||||
/**
|
||||
* Optional DevTools toolbox containing this VariablesView. Used to
|
||||
* communicate with the inspector and highlighter.
|
||||
*/
|
||||
toolbox: null,
|
||||
|
||||
/**
|
||||
* The controller for this VariablesView, if it has one.
|
||||
*/
|
||||
@ -324,6 +332,15 @@ VariablesView.prototype = {
|
||||
*/
|
||||
editButtonTooltip: STR.GetStringFromName("variablesEditButtonTooltip"),
|
||||
|
||||
/**
|
||||
* The tooltip text shown on a variable or property's value if that value is
|
||||
* a DOMNode that can be highlighted and selected in the inspector.
|
||||
*
|
||||
* This flag is applied recursively onto each scope in this view and
|
||||
* affects only the child nodes when they're created.
|
||||
*/
|
||||
domNodeValueTooltip: STR.GetStringFromName("variablesDomNodeValueTooltip"),
|
||||
|
||||
/**
|
||||
* The tooltip text shown on a variable or property's delete button if a
|
||||
* |delete| function is provided, in order to delete the variable or property.
|
||||
@ -1209,6 +1226,7 @@ function Scope(aView, aName, aFlags = {}) {
|
||||
this.editableValueTooltip = aView.editableValueTooltip;
|
||||
this.editButtonTooltip = aView.editButtonTooltip;
|
||||
this.deleteButtonTooltip = aView.deleteButtonTooltip;
|
||||
this.domNodeValueTooltip = aView.domNodeValueTooltip;
|
||||
this.contextMenuId = aView.contextMenuId;
|
||||
this.separatorStr = aView.separatorStr;
|
||||
|
||||
@ -2069,6 +2087,7 @@ Scope.prototype = {
|
||||
editableValueTooltip: "",
|
||||
editButtonTooltip: "",
|
||||
deleteButtonTooltip: "",
|
||||
domNodeValueTooltip: "",
|
||||
contextMenuId: "",
|
||||
separatorStr: "",
|
||||
|
||||
@ -2119,6 +2138,9 @@ function Variable(aScope, aName, aDescriptor) {
|
||||
this._setTooltips = this._setTooltips.bind(this);
|
||||
this._activateNameInput = this._activateNameInput.bind(this);
|
||||
this._activateValueInput = this._activateValueInput.bind(this);
|
||||
this.openNodeInInspector = this.openNodeInInspector.bind(this);
|
||||
this.highlightDomNode = this.highlightDomNode.bind(this);
|
||||
this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
|
||||
|
||||
// Treat safe getter descriptors as descriptors with a value.
|
||||
if ("getterValue" in aDescriptor) {
|
||||
@ -2171,6 +2193,13 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
* Remove this Variable from its parent and remove all children recursively.
|
||||
*/
|
||||
remove: function() {
|
||||
if (this._linkedToInspector) {
|
||||
this.unhighlightDomNode();
|
||||
this._valueLabel.removeEventListener("mouseover", this.highlightDomNode, false);
|
||||
this._valueLabel.removeEventListener("mouseout", this.unhighlightDomNode, false);
|
||||
this._openInspectorNode.removeEventListener("mousedown", this.openNodeInInspector, false);
|
||||
}
|
||||
|
||||
this.ownerView._store.delete(this._nameString);
|
||||
this._variablesView._itemsByElement.delete(this._target);
|
||||
this._variablesView._currHierarchy.delete(this._absoluteName);
|
||||
@ -2385,6 +2414,11 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this._valueLabel.classList.add(this._valueClassName);
|
||||
this._valueLabel.setAttribute("value", this._valueString);
|
||||
this._separatorLabel.hidden = false;
|
||||
|
||||
// DOMNodes get special treatment since they can be linked to the inspector
|
||||
if (this._valueGrip.preview && this._valueGrip.preview.kind === "DOMNode") {
|
||||
this._linkToInspector();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2555,7 +2589,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
if (!descriptor.writable && !ownerView.getter && !ownerView.setter) {
|
||||
let nonWritableIcon = this.document.createElement("hbox");
|
||||
nonWritableIcon.className = "variable-or-property-non-writable-icon";
|
||||
nonWritableIcon.className = "plain variable-or-property-non-writable-icon";
|
||||
nonWritableIcon.setAttribute("optional-visibility", "");
|
||||
this._title.appendChild(nonWritableIcon);
|
||||
}
|
||||
@ -2623,6 +2657,9 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
if (this._editNode && ownerView.eval) {
|
||||
this._editNode.setAttribute("tooltiptext", ownerView.editButtonTooltip);
|
||||
}
|
||||
if (this._openInspectorNode && this._linkedToInspector) {
|
||||
this._openInspectorNode.setAttribute("tooltiptext", this.ownerView.domNodeValueTooltip);
|
||||
}
|
||||
if (this._valueLabel && ownerView.eval) {
|
||||
this._valueLabel.setAttribute("tooltiptext", ownerView.editableValueTooltip);
|
||||
}
|
||||
@ -2634,6 +2671,110 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the parent variablesview toolbox, if any.
|
||||
*/
|
||||
get toolbox() {
|
||||
return this._variablesView.toolbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if this variable is a DOMNode and is part of a variablesview that
|
||||
* has been linked to the toolbox, so that highlighting and jumping to the
|
||||
* inspector can be done.
|
||||
*/
|
||||
_isLinkableToInspector: function() {
|
||||
let isDomNode = this._valueGrip && this._valueGrip.preview.kind === "DOMNode";
|
||||
let hasBeenLinked = this._linkedToInspector;
|
||||
let hasToolbox = !!this.toolbox;
|
||||
|
||||
return isDomNode && !hasBeenLinked && hasToolbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* If the variable is a DOMNode, and if a toolbox is set, then link it to the
|
||||
* inspector (highlight on hover, and jump to markup-view on click)
|
||||
*/
|
||||
_linkToInspector: function() {
|
||||
if (!this._isLinkableToInspector()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen to value mouseover/click events to highlight and jump
|
||||
this._valueLabel.addEventListener("mouseover", this.highlightDomNode, false);
|
||||
this._valueLabel.addEventListener("mouseout", this.unhighlightDomNode, false);
|
||||
|
||||
// Add a button to open the node in the inspector
|
||||
this._openInspectorNode = this.document.createElement("toolbarbutton");
|
||||
this._openInspectorNode.className = "plain variables-view-open-inspector";
|
||||
this._openInspectorNode.addEventListener("mousedown", this.openNodeInInspector, false);
|
||||
this._title.insertBefore(this._openInspectorNode, this._title.querySelector("toolbarbutton"));
|
||||
|
||||
this._linkedToInspector = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* In case this variable is a DOMNode and part of a variablesview that has been
|
||||
* linked to the toolbox's inspector, then select the corresponding node in
|
||||
* the inspector, and switch the inspector tool in the toolbox
|
||||
* @return a promise that resolves when the node is selected and the inspector
|
||||
* has been switched to and is ready
|
||||
*/
|
||||
openNodeInInspector: function(event) {
|
||||
if (!this.toolbox) {
|
||||
return promise.reject(new Error("Toolbox not available"));
|
||||
}
|
||||
|
||||
event && event.stopPropagation();
|
||||
|
||||
return Task.spawn(function*() {
|
||||
yield this.toolbox.initInspector();
|
||||
|
||||
let nodeFront = this._nodeFront;
|
||||
if (!nodeFront) {
|
||||
nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this._valueGrip.actor);
|
||||
}
|
||||
|
||||
if (nodeFront) {
|
||||
yield this.toolbox.selectTool("inspector");
|
||||
|
||||
let inspectorReady = promise.defer();
|
||||
this.toolbox.getPanel("inspector").once("inspector-updated", inspectorReady.resolve);
|
||||
yield this.toolbox.selection.setNodeFront(nodeFront, "variables-view");
|
||||
yield inspectorReady.promise;
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* In case this variable is a DOMNode and part of a variablesview that has been
|
||||
* linked to the toolbox's inspector, then highlight the corresponding node
|
||||
*/
|
||||
highlightDomNode: function() {
|
||||
if (this.toolbox) {
|
||||
if (this._nodeFront) {
|
||||
// If the nodeFront has been retrieved before, no need to ask the server
|
||||
// again for it
|
||||
this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
|
||||
return;
|
||||
}
|
||||
|
||||
this.toolbox.highlighterUtils.highlightDomValueGrip(this._valueGrip).then(front => {
|
||||
this._nodeFront = front;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Unhighlight a previously highlit node
|
||||
* @see highlightDomNode
|
||||
*/
|
||||
unhighlightDomNode: function() {
|
||||
if (this.toolbox) {
|
||||
this.toolbox.highlighterUtils.unhighlight();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a variable's configurable, enumerable and writable attributes,
|
||||
* and specifies if it's a 'this', '<exception>', '<return>' or '__proto__'
|
||||
@ -2740,6 +2881,9 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
_activateValueInput: function(e) {
|
||||
EditableValue.create(this, {
|
||||
onSave: aString => {
|
||||
if (this._linkedToInspector) {
|
||||
this.unhighlightDomNode();
|
||||
}
|
||||
if (!this._variablesView.preventDisableOnChange) {
|
||||
this._disable();
|
||||
}
|
||||
@ -3573,6 +3717,13 @@ VariablesView.stringifiers._getNMoreString = function(aNumber) {
|
||||
*/
|
||||
VariablesView.getClass = function(aGrip) {
|
||||
if (aGrip && typeof aGrip == "object") {
|
||||
if (aGrip.preview) {
|
||||
switch (aGrip.preview.kind) {
|
||||
case "DOMNode":
|
||||
return "token-domnode";
|
||||
}
|
||||
}
|
||||
|
||||
switch (aGrip.type) {
|
||||
case "undefined":
|
||||
return "token-undefined";
|
||||
|
@ -79,6 +79,7 @@
|
||||
|
||||
.variable-or-property[pseudo-item] > tooltip,
|
||||
.variable-or-property[pseudo-item] > .title > .variables-view-edit,
|
||||
.variable-or-property[pseudo-item] > .title > .variables-view-open-inspector,
|
||||
.variable-or-property[pseudo-item] > .title > .variables-view-delete,
|
||||
.variable-or-property[pseudo-item] > .title > .variables-view-add-property,
|
||||
.variable-or-property[pseudo-item] > .title > .variable-or-property-frozen-label,
|
||||
@ -89,11 +90,13 @@
|
||||
}
|
||||
|
||||
*:not(:hover) .variables-view-delete,
|
||||
*:not(:hover) .variables-view-open-inspector,
|
||||
*:not(:hover) .variables-view-add-property {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.variables-view-delete > .toolbarbutton-text,
|
||||
.variables-view-open-inspector > .toolbarbutton-text,
|
||||
.variables-view-add-property > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
@ -104,6 +104,7 @@ support-files =
|
||||
test-bug_923281_test1.js
|
||||
test-bug_923281_test2.js
|
||||
test-bug_939783_console_trace_duplicates.html
|
||||
test-bug-952277-highlight-nodes-in-vview.html
|
||||
|
||||
[browser_bug664688_sandbox_update_after_navigation.js]
|
||||
[browser_bug_638949_copy_link_location.js]
|
||||
@ -258,4 +259,5 @@ run-if = os == "mac"
|
||||
[browser_webconsole_output_03.js]
|
||||
[browser_webconsole_output_04.js]
|
||||
[browser_webconsole_output_events.js]
|
||||
[browser_console_variables_view_highlighter.js]
|
||||
[browser_webconsole_console_trace_duplicates.js]
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Check that variables view is linked to the inspector for highlighting and
|
||||
// selecting DOM nodes
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html";
|
||||
|
||||
let gWebConsole, gJSTerm, gVariablesView, gToolbox;
|
||||
|
||||
function test()
|
||||
{
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, consoleOpened);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function consoleOpened(hud)
|
||||
{
|
||||
gWebConsole = hud;
|
||||
gJSTerm = hud.jsterm;
|
||||
gToolbox = gDevTools.getToolbox(hud.target);
|
||||
gJSTerm.execute("document.querySelectorAll('p')", onQSAexecuted);
|
||||
}
|
||||
|
||||
function onQSAexecuted(msg)
|
||||
{
|
||||
ok(msg, "output message found");
|
||||
let anchor = msg.querySelector("a");
|
||||
ok(anchor, "object link found");
|
||||
|
||||
gJSTerm.once("variablesview-fetched", onNodeListVviewFetched);
|
||||
|
||||
executeSoon(() =>
|
||||
EventUtils.synthesizeMouse(anchor, 2, 2, {}, gWebConsole.iframeWindow)
|
||||
);
|
||||
}
|
||||
|
||||
function onNodeListVviewFetched(aEvent, aVar)
|
||||
{
|
||||
gVariablesView = aVar._variablesView;
|
||||
ok(gVariablesView, "variables view object");
|
||||
|
||||
// Transform the vview into an array we can filter properties from
|
||||
let props = [[id, prop] for([id, prop] of aVar)];
|
||||
// These properties are the DOM nodes ones
|
||||
props = props.filter(v => v[0].match(/[0-9]+/));
|
||||
|
||||
function hoverOverDomNodeVariableAndAssertHighlighter(index) {
|
||||
if (props[index]) {
|
||||
let prop = props[index][1];
|
||||
let valueEl = prop._valueLabel;
|
||||
|
||||
gToolbox.once("node-highlight", () => {
|
||||
ok(true, "The highlighter was shown on hover of the DOMNode");
|
||||
gToolbox.highlighterUtils.unhighlight().then(() => {
|
||||
clickOnDomNodeVariableAndAssertInspectorSelected(index);
|
||||
});
|
||||
});
|
||||
|
||||
// Rather than trying to emulate a mouseenter event, let's call the
|
||||
// variable's highlightDomNode and see if it has the desired effect
|
||||
prop.highlightDomNode();
|
||||
} else {
|
||||
finishTest();
|
||||
}
|
||||
}
|
||||
|
||||
function clickOnDomNodeVariableAndAssertInspectorSelected(index) {
|
||||
let prop = props[index][1];
|
||||
|
||||
// Make sure the inspector is initialized so we can listen to its events
|
||||
gToolbox.initInspector().then(() => {
|
||||
// Rather than trying to click on the value here, let's just call the
|
||||
// variable's openNodeInInspector function and see if it has the
|
||||
// desired effect
|
||||
prop.openNodeInInspector().then(() => {
|
||||
is(gToolbox.currentToolId, "inspector", "The toolbox switched over the inspector on DOMNode click");
|
||||
gToolbox.selectTool("webconsole").then(() => {
|
||||
hoverOverDomNodeVariableAndAssertHighlighter(index + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
hoverOverDomNodeVariableAndAssertHighlighter(0);
|
||||
}
|
@ -49,7 +49,7 @@ function setupHighlighterTests() {
|
||||
function runSelectionTests(aInspector) {
|
||||
inspector = aInspector;
|
||||
|
||||
inspector.toolbox.startPicker();
|
||||
inspector.toolbox.highlighterUtils.startPicker();
|
||||
inspector.toolbox.once("picker-started", () => {
|
||||
info("Picker mode started, now clicking on H1 to select that node");
|
||||
executeSoon(() => {
|
||||
|
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p>
|
||||
<p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p>
|
||||
<p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3420,6 +3420,7 @@ JSTerm.prototype = {
|
||||
_createVariablesView: function JST__createVariablesView(aOptions)
|
||||
{
|
||||
let view = new VariablesView(aOptions.container);
|
||||
view.toolbox = gDevTools.getToolbox(this.hud.owner.target);
|
||||
view.searchPlaceholder = l10n.getStr("propertiesFilterPlaceholder");
|
||||
view.emptyText = l10n.getStr("emptyPropertiesList");
|
||||
view.searchEnabled = !aOptions.hideFilterInput;
|
||||
|
@ -266,6 +266,11 @@ variablesCloseButtonTooltip=Click to remove
|
||||
# in the variables list on a getter or setter which can be edited.
|
||||
variablesEditButtonTooltip=Click to set value
|
||||
|
||||
# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed
|
||||
# in a tooltip on the "open in inspector" button in the the variables list for a
|
||||
# DOMNode item.
|
||||
variablesDomNodeValueTooltip=Click to select the node in the inspector
|
||||
|
||||
# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed
|
||||
# in the variables list on certain variables or properties as tooltips.
|
||||
# Expanations of what these represent can be found at the following links:
|
||||
|
Before Width: | Height: | Size: 833 B |
Before Width: | Height: | Size: 238 B |
@ -224,8 +224,8 @@ browser.jar:
|
||||
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
|
||||
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
|
||||
skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
|
||||
skin/classic/browser/devtools/inspector.css (devtools/inspector.css)
|
||||
skin/classic/browser/devtools/profiler-stopwatch.png (../shared/devtools/images/profiler-stopwatch.png)
|
||||
skin/classic/browser/devtools/inspector.css (devtools/inspector.css)
|
||||
skin/classic/browser/devtools/profiler-stopwatch.png (../shared/devtools/images/profiler-stopwatch.png)
|
||||
skin/classic/browser/devtools/tool-options.svg (../shared/devtools/images/tool-options.svg)
|
||||
skin/classic/browser/devtools/tool-webconsole.svg (../shared/devtools/images/tool-webconsole.svg)
|
||||
skin/classic/browser/devtools/tool-debugger.svg (../shared/devtools/images/tool-debugger.svg)
|
||||
@ -236,16 +236,18 @@ browser.jar:
|
||||
skin/classic/browser/devtools/tool-profiler.svg (../shared/devtools/images/tool-profiler.svg)
|
||||
skin/classic/browser/devtools/tool-network.svg (../shared/devtools/images/tool-network.svg)
|
||||
skin/classic/browser/devtools/tool-scratchpad.svg (../shared/devtools/images/tool-scratchpad.svg)
|
||||
skin/classic/browser/devtools/close.png (../shared/devtools/images/close.png)
|
||||
skin/classic/browser/devtools/close@2x.png (../shared/devtools/images/close@2x.png)
|
||||
skin/classic/browser/devtools/vview-delete.png (devtools/vview-delete.png)
|
||||
skin/classic/browser/devtools/vview-edit.png (devtools/vview-edit.png)
|
||||
skin/classic/browser/devtools/undock@2x.png (../shared/devtools/images/undock@2x.png)
|
||||
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
|
||||
skin/classic/browser/devtools/computedview.css (devtools/computedview.css)
|
||||
skin/classic/browser/devtools/arrow-e.png (devtools/arrow-e.png)
|
||||
skin/classic/browser/devtools/responsiveui-rotate.png (../shared/devtools/responsiveui-rotate.png)
|
||||
skin/classic/browser/devtools/responsiveui-touch.png (../shared/devtools/responsiveui-touch.png)
|
||||
skin/classic/browser/devtools/close.png (../shared/devtools/images/close.png)
|
||||
skin/classic/browser/devtools/close@2x.png (../shared/devtools/images/close@2x.png)
|
||||
skin/classic/browser/devtools/vview-delete.png (../shared/devtools/images/vview-delete.png)
|
||||
skin/classic/browser/devtools/vview-lock.png (../shared/devtools/images/vview-lock.png)
|
||||
skin/classic/browser/devtools/vview-edit.png (../shared/devtools/images/vview-edit.png)
|
||||
skin/classic/browser/devtools/vview-open-inspector.png (../shared/devtools/images/vview-open-inspector.png)
|
||||
skin/classic/browser/devtools/undock@2x.png (../shared/devtools/images/undock@2x.png)
|
||||
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
|
||||
skin/classic/browser/devtools/computedview.css (devtools/computedview.css)
|
||||
skin/classic/browser/devtools/arrow-e.png (devtools/arrow-e.png)
|
||||
skin/classic/browser/devtools/responsiveui-rotate.png (../shared/devtools/responsiveui-rotate.png)
|
||||
skin/classic/browser/devtools/responsiveui-touch.png (../shared/devtools/responsiveui-touch.png)
|
||||
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
|
||||
skin/classic/browser/devtools/app-manager/connection-footer.css (../shared/devtools/app-manager/connection-footer.css)
|
||||
skin/classic/browser/devtools/app-manager/index.css (../shared/devtools/app-manager/index.css)
|
||||
|
Before Width: | Height: | Size: 833 B |
Before Width: | Height: | Size: 238 B |
@ -345,8 +345,10 @@ browser.jar:
|
||||
skin/classic/browser/devtools/tool-scratchpad.svg (../shared/devtools/images/tool-scratchpad.svg)
|
||||
skin/classic/browser/devtools/close.png (../shared/devtools/images/close.png)
|
||||
skin/classic/browser/devtools/close@2x.png (../shared/devtools/images/close@2x.png)
|
||||
skin/classic/browser/devtools/vview-delete.png (devtools/vview-delete.png)
|
||||
skin/classic/browser/devtools/vview-edit.png (devtools/vview-edit.png)
|
||||
skin/classic/browser/devtools/vview-delete.png (../shared/devtools/images/vview-delete.png)
|
||||
skin/classic/browser/devtools/vview-lock.png (../shared/devtools/images/vview-lock.png)
|
||||
skin/classic/browser/devtools/vview-edit.png (../shared/devtools/images/vview-edit.png)
|
||||
skin/classic/browser/devtools/vview-open-inspector.png (../shared/devtools/images/vview-open-inspector.png)
|
||||
skin/classic/browser/devtools/undock@2x.png (../shared/devtools/images/undock@2x.png)
|
||||
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
|
||||
skin/classic/browser/devtools/computedview.css (devtools/computedview.css)
|
||||
|
@ -148,10 +148,15 @@
|
||||
.cm-s-mozilla .cm-quote,
|
||||
.cm-s-mozilla .cm-error,
|
||||
.variable-or-property .token-boolean,
|
||||
.variable-or-property .token-domnode,
|
||||
.variable-or-property[exception] > .title > .name { /* Red */
|
||||
color: #bf5656;
|
||||
}
|
||||
|
||||
.variable-or-property .token-domnode {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.theme-toolbar,
|
||||
.devtools-toolbar,
|
||||
.devtools-sidebar-tabs > tabs { /* General toolbar styling */
|
||||
|
BIN
browser/themes/shared/devtools/images/vview-delete.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
browser/themes/shared/devtools/images/vview-delete@2x.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
browser/themes/shared/devtools/images/vview-edit.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
browser/themes/shared/devtools/images/vview-edit@2x.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
browser/themes/shared/devtools/images/vview-lock.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
browser/themes/shared/devtools/images/vview-lock@2x.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
browser/themes/shared/devtools/images/vview-open-inspector.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
@ -147,10 +147,15 @@
|
||||
.cm-s-mozilla .cm-quote,
|
||||
.cm-s-mozilla .cm-error,
|
||||
.variable-or-property .token-boolean,
|
||||
.variable-or-property .token-domnode,
|
||||
.variable-or-property[exception] > .title > .name { /* Red */
|
||||
color: #bf5656;
|
||||
}
|
||||
|
||||
.variable-or-property .token-domnode {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.theme-fg-contrast { /* To be used for text on theme-bg-contrast */
|
||||
color: black;
|
||||
}
|
||||
|
@ -643,19 +643,18 @@
|
||||
}
|
||||
|
||||
.variable-or-property-non-writable-icon {
|
||||
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
|
||||
background: url("chrome://browser/skin/devtools/vview-lock.png") no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
/*@media (min-resolution: 2dppx) {
|
||||
.variable-or-property-non-writable-icon {
|
||||
background-image: url("chrome://browser/skin/identity-icons-https@2x.png");
|
||||
background-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
.variable-or-property-frozen-label,
|
||||
.variable-or-property-sealed-label,
|
||||
.variable-or-property-non-extensible-label {
|
||||
@ -686,6 +685,7 @@
|
||||
/* Actions first */
|
||||
|
||||
.variables-view-container[actions-first] .variables-view-delete,
|
||||
.variables-view-container[actions-first] .variables-view-open-inspector,
|
||||
.variables-view-container[actions-first] .variables-view-add-property {
|
||||
-moz-box-ordinal-group: 0;
|
||||
}
|
||||
@ -727,18 +727,52 @@
|
||||
}
|
||||
|
||||
.variables-view-delete:hover {
|
||||
-moz-image-region: rect(0,32px,16px,16px);
|
||||
}
|
||||
|
||||
.variables-view-delete:active {
|
||||
-moz-image-region: rect(0,48px,16px,32px);
|
||||
}
|
||||
|
||||
.variables-view-delete:active {
|
||||
-moz-image-region: rect(0,32px,16px,16px);
|
||||
}
|
||||
|
||||
.variable-or-property:focus .variables-view-delete {
|
||||
-moz-image-region: rect(0,16px,16px,0);
|
||||
}
|
||||
|
||||
.variables-view-edit {
|
||||
background: url("chrome://browser/skin/devtools/vview-edit.png") center no-repeat;
|
||||
width: 20px;
|
||||
height: 16px;
|
||||
list-style-image: url("chrome://browser/skin/devtools/vview-edit.png");
|
||||
-moz-image-region: rect(0,16px,16px,0);
|
||||
cursor: pointer;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.variables-view-edit:hover {
|
||||
-moz-image-region: rect(0,48px,16px,32px);
|
||||
}
|
||||
|
||||
.variables-view-edit:active {
|
||||
-moz-image-region: rect(0,32px,16px,16px);
|
||||
}
|
||||
|
||||
.variable-or-property:focus .variables-view-edit {
|
||||
-moz-image-region: rect(0,16px,16px,0);
|
||||
}
|
||||
|
||||
.variables-view-open-inspector {
|
||||
list-style-image: url("chrome://browser/skin/devtools/vview-open-inspector.png");
|
||||
-moz-image-region: rect(0,16px,16px,0);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.variables-view-open-inspector:hover {
|
||||
-moz-image-region: rect(0,48px,16px,32px);
|
||||
}
|
||||
|
||||
.variables-view-open-inspector:active {
|
||||
-moz-image-region: rect(0,32px,16px,16px);
|
||||
}
|
||||
|
||||
.variable-or-property:focus .variables-view-open-inspector {
|
||||
-moz-image-region: rect(0,16px,16px,0);
|
||||
}
|
||||
|
||||
.variables-view-throbber {
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 238 B |
@ -266,8 +266,10 @@ browser.jar:
|
||||
skin/classic/browser/devtools/tool-scratchpad.svg (../shared/devtools/images/tool-scratchpad.svg)
|
||||
skin/classic/browser/devtools/close.png (../shared/devtools/images/close.png)
|
||||
skin/classic/browser/devtools/close@2x.png (../shared/devtools/images/close@2x.png)
|
||||
skin/classic/browser/devtools/vview-delete.png (devtools/vview-delete.png)
|
||||
skin/classic/browser/devtools/vview-edit.png (devtools/vview-edit.png)
|
||||
skin/classic/browser/devtools/vview-delete.png (../shared/devtools/images/vview-delete.png)
|
||||
skin/classic/browser/devtools/vview-lock.png (../shared/devtools/images/vview-lock.png)
|
||||
skin/classic/browser/devtools/vview-edit.png (../shared/devtools/images/vview-edit.png)
|
||||
skin/classic/browser/devtools/vview-open-inspector.png (../shared/devtools/images/vview-open-inspector.png)
|
||||
skin/classic/browser/devtools/undock@2x.png (../shared/devtools/images/undock@2x.png)
|
||||
skin/classic/browser/devtools/font-inspector.css (devtools/font-inspector.css)
|
||||
skin/classic/browser/devtools/computedview.css (devtools/computedview.css)
|
||||
@ -572,8 +574,10 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/tool-scratchpad.svg (../shared/devtools/images/tool-scratchpad.svg)
|
||||
skin/classic/aero/browser/devtools/close.png (../shared/devtools/images/close.png)
|
||||
skin/classic/aero/browser/devtools/close@2x.png (../shared/devtools/images/close@2x.png)
|
||||
skin/classic/aero/browser/devtools/vview-delete.png (devtools/vview-delete.png)
|
||||
skin/classic/aero/browser/devtools/vview-edit.png (devtools/vview-edit.png)
|
||||
skin/classic/aero/browser/devtools/vview-delete.png (../shared/devtools/images/vview-delete.png)
|
||||
skin/classic/aero/browser/devtools/vview-lock.png (../shared/devtools/images/vview-lock.png)
|
||||
skin/classic/aero/browser/devtools/vview-edit.png (../shared/devtools/images/vview-edit.png)
|
||||
skin/classic/aero/browser/devtools/vview-open-inspector.png (../shared/devtools/images/vview-open-inspector.png)
|
||||
skin/classic/aero/browser/devtools/undock@2x.png (../shared/devtools/images/undock@2x.png)
|
||||
skin/classic/aero/browser/devtools/font-inspector.css (devtools/font-inspector.css)
|
||||
skin/classic/aero/browser/devtools/computedview.css (devtools/computedview.css)
|
||||
|
@ -2086,7 +2086,31 @@ var WalkerActor = protocol.ActorClass({
|
||||
// Need to force a release of this node, because those nodes can't
|
||||
// be accessed anymore.
|
||||
this.releaseNode(documentActor, { force: true });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Given an ObjectActor (identified by its ID), commonly used in the debugger,
|
||||
* webconsole and variablesView, return the corresponding inspector's NodeActor
|
||||
*/
|
||||
getNodeActorFromObjectActor: method(function(objectActorID) {
|
||||
let debuggerObject = this.conn.poolFor(objectActorID).get(objectActorID).obj;
|
||||
let rawNode = debuggerObject.unsafeDereference();
|
||||
|
||||
// This is a special case for the document object whereby it is considered
|
||||
// as document.documentElement (the <html> node)
|
||||
if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
|
||||
rawNode = rawNode.documentElement;
|
||||
}
|
||||
|
||||
return this.attachElement(rawNode);
|
||||
}, {
|
||||
request: {
|
||||
objectActorID: Arg(0, "string")
|
||||
},
|
||||
response: {
|
||||
nodeFront: RetVal("disconnectedNode")
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
@ -2220,6 +2244,14 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
||||
impl: "_querySelector"
|
||||
}),
|
||||
|
||||
getNodeActorFromObjectActor: protocol.custom(function(objectActorID) {
|
||||
return this._getNodeActorFromObjectActor(objectActorID).then(response => {
|
||||
return response.node;
|
||||
});
|
||||
}, {
|
||||
impl: "_getNodeActorFromObjectActor"
|
||||
}),
|
||||
|
||||
_releaseFront: function(node, force) {
|
||||
if (node.retained && !force) {
|
||||
node.reparent(null);
|
||||
|