зеркало из https://github.com/mozilla/gecko-dev.git
Bug 676722 - The output of console.log(object) isn't expandable/inspectable in the Web Console; r=rcampbell
This commit is contained in:
Родитель
31ad3b872b
Коммит
2b290df88d
|
@ -1161,17 +1161,17 @@ let ConsoleAPIObserver = {
|
|||
WebConsoleUtils.cloneObject(aOriginalMessage.arguments, true);
|
||||
break;
|
||||
|
||||
case "log":
|
||||
case "info":
|
||||
case "warn":
|
||||
case "error":
|
||||
case "debug":
|
||||
case "groupEnd":
|
||||
aRemoteMessage.argumentsToString =
|
||||
Array.map(aOriginalMessage.arguments || [],
|
||||
this._formatObject.bind(this));
|
||||
break;
|
||||
|
||||
case "log":
|
||||
case "info":
|
||||
case "warn":
|
||||
case "error":
|
||||
case "debug":
|
||||
case "dir": {
|
||||
aRemoteMessage.objectsCacheId = Manager.sequenceId;
|
||||
aRemoteMessage.argumentsToString = [];
|
||||
|
|
|
@ -112,6 +112,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_result_format_as_string.js \
|
||||
browser_webconsole_bug_737873_mixedcontent.js \
|
||||
browser_output_breaks_after_console_dir_uninspectable.js \
|
||||
browser_console_log_inspectable_object.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that objects given to console.log() are inspectable.
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
addTab("data:text/html,test for bug 676722 - inspectable objects for window.console");
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, performTest);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function performTest(hud)
|
||||
{
|
||||
hud.jsterm.clearOutput(true);
|
||||
|
||||
hud.jsterm.execute("myObj = {abba: 'omgBug676722'}");
|
||||
hud.jsterm.execute("console.log('fooBug676722', myObj)");
|
||||
waitForSuccess({
|
||||
name: "eval results are shown",
|
||||
validatorFn: function()
|
||||
{
|
||||
return hud.outputNode.textContent.indexOf("fooBug676722") > -1 &&
|
||||
hud.outputNode.querySelector(".hud-clickable");
|
||||
},
|
||||
successFn: function()
|
||||
{
|
||||
isnot(hud.outputNode.textContent.indexOf("myObj = {"), -1,
|
||||
"myObj = ... is shown");
|
||||
|
||||
let clickable = hud.outputNode.querySelector(".hud-clickable");
|
||||
ok(clickable, "the console.log() object .hud-clickable was found");
|
||||
isnot(clickable.textContent.indexOf("omgBug676722"), -1,
|
||||
"clickable node content is correct");
|
||||
|
||||
document.addEventListener("popupshown", function _onPopupShown(aEvent) {
|
||||
document.removeEventListener("popupshown", _onPopupShown);
|
||||
|
||||
isnot(aEvent.target.label.indexOf("omgBug676722"), -1,
|
||||
"object inspector opened on click");
|
||||
|
||||
executeSoon(finishTest);
|
||||
});
|
||||
|
||||
executeSoon(function() {
|
||||
EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
|
||||
});
|
||||
},
|
||||
failureFn: finishTest,
|
||||
});
|
||||
}
|
|
@ -944,7 +944,12 @@ WebConsoleFrame.prototype = {
|
|||
case "warn":
|
||||
case "error":
|
||||
case "debug":
|
||||
body = argsToString.join(" ");
|
||||
body = {
|
||||
cacheId: aMessage.objectsCacheId,
|
||||
remoteObjects: args,
|
||||
argsToString: argsToString,
|
||||
};
|
||||
clipboardText = argsToString.join(" ");
|
||||
sourceURL = aMessage.apiMessage.filename;
|
||||
sourceLine = aMessage.apiMessage.lineNumber;
|
||||
break;
|
||||
|
@ -1068,6 +1073,57 @@ WebConsoleFrame.prototype = {
|
|||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* The click event handler for objects shown inline coming from the
|
||||
* window.console API.
|
||||
*
|
||||
* @private
|
||||
* @param nsIDOMNode aMessage
|
||||
* The message element this handler corresponds to.
|
||||
* @param nsIDOMNode aAnchor
|
||||
* The object inspector anchor element. This is the clickable element
|
||||
* in the console.log message we display.
|
||||
* @param array aRemoteObject
|
||||
* The remote object representation.
|
||||
*/
|
||||
_consoleLogClick:
|
||||
function WCF__consoleLogClick(aMessage, aAnchor, aRemoteObject)
|
||||
{
|
||||
if (aAnchor._panelOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
let options = {
|
||||
title: aAnchor.textContent,
|
||||
anchor: aAnchor,
|
||||
|
||||
// Data to inspect.
|
||||
data: {
|
||||
// This is where the resultObject children are cached.
|
||||
rootCacheId: aMessage._evalCacheId,
|
||||
remoteObject: aRemoteObject,
|
||||
// This is where all objects retrieved by the panel will be cached.
|
||||
panelCacheId: "HUDPanel-" + gSequenceId(),
|
||||
remoteObjectProvider: this.jsterm.remoteObjectProvider.bind(this.jsterm),
|
||||
},
|
||||
};
|
||||
|
||||
let propPanel = this.jsterm.openPropertyPanel(options);
|
||||
propPanel.panel.setAttribute("hudId", this.hudId);
|
||||
|
||||
let onPopupHide = function JST__evalInspectPopupHide() {
|
||||
propPanel.panel.removeEventListener("popuphiding", onPopupHide, false);
|
||||
|
||||
this.jsterm.clearObjectCache(options.data.panelCacheId);
|
||||
|
||||
if (!aMessage.parentNode && aMessage._evalCacheId) {
|
||||
this.jsterm.clearObjectCache(aMessage._evalCacheId);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
propPanel.panel.addEventListener("popuphiding", onPopupHide, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reports an error in the page source, either JavaScript or CSS.
|
||||
*
|
||||
|
@ -1828,19 +1884,31 @@ WebConsoleFrame.prototype = {
|
|||
aClipboardText = aClipboardText ||
|
||||
(aBody + (aSourceURL ? " @ " + aSourceURL : "") +
|
||||
(aSourceLine ? ":" + aSourceLine : ""));
|
||||
if (!(aBody instanceof Ci.nsIDOMNode)) {
|
||||
aBody = this.document.createTextNode(aLevel == "dir" ?
|
||||
aBody.resultString : aBody);
|
||||
}
|
||||
|
||||
if (!aBody.nodeType) {
|
||||
aBody = this.document.createTextNode(aBody.toString());
|
||||
}
|
||||
if (typeof aBody == "string") {
|
||||
aBody = this.document.createTextNode(aBody);
|
||||
}
|
||||
// Create the containing node and append all its elements to it.
|
||||
let node = this.document.createElementNS(XUL_NS, "richlistitem");
|
||||
|
||||
bodyNode.appendChild(aBody);
|
||||
if (aBody instanceof Ci.nsIDOMNode) {
|
||||
bodyNode.appendChild(aBody);
|
||||
}
|
||||
else {
|
||||
let str = undefined;
|
||||
if (aLevel == "dir") {
|
||||
str = aBody.resultString;
|
||||
}
|
||||
else if (["log", "info", "warn", "error", "debug"].indexOf(aLevel) > -1 &&
|
||||
typeof aBody == "object") {
|
||||
this._makeConsoleLogMessageBody(node, bodyNode, aBody);
|
||||
}
|
||||
else {
|
||||
str = aBody;
|
||||
}
|
||||
|
||||
if (str !== undefined) {
|
||||
aBody = this.document.createTextNode(str);
|
||||
bodyNode.appendChild(aBody);
|
||||
}
|
||||
}
|
||||
|
||||
let repeatContainer = this.document.createElementNS(XUL_NS, "hbox");
|
||||
repeatContainer.setAttribute("align", "start");
|
||||
|
@ -1863,8 +1931,6 @@ WebConsoleFrame.prototype = {
|
|||
locationNode = this.createLocationNode(aSourceURL, aSourceLine);
|
||||
}
|
||||
|
||||
// Create the containing node and append all its elements to it.
|
||||
let node = this.document.createElementNS(XUL_NS, "richlistitem");
|
||||
node.clipboardText = aClipboardText;
|
||||
node.classList.add("hud-msg-node");
|
||||
|
||||
|
@ -1924,6 +1990,58 @@ WebConsoleFrame.prototype = {
|
|||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Make the message body for console.log() calls.
|
||||
*
|
||||
* @private
|
||||
* @param nsIDOMElement aMessage
|
||||
* The message element that holds the output for the given call.
|
||||
* @param nsIDOMElement aContainer
|
||||
* The specific element that will hold each part of the console.log
|
||||
* output.
|
||||
* @param object aBody
|
||||
* The object given by this.logConsoleAPIMessage(). This object holds
|
||||
* the call information that we need to display.
|
||||
*/
|
||||
_makeConsoleLogMessageBody:
|
||||
function WCF__makeConsoleLogMessageBody(aMessage, aContainer, aBody)
|
||||
{
|
||||
aMessage._evalCacheId = aBody.cacheId;
|
||||
|
||||
Object.defineProperty(aMessage, "_panelOpen", {
|
||||
get: function() {
|
||||
let nodes = aContainer.querySelectorAll(".hud-clickable");
|
||||
return Array.prototype.some.call(nodes, function(aNode) {
|
||||
return aNode._panelOpen;
|
||||
});
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
aBody.remoteObjects.forEach(function(aItem, aIndex) {
|
||||
if (aContainer.firstChild) {
|
||||
aContainer.appendChild(this.document.createTextNode(" "));
|
||||
}
|
||||
|
||||
let text = aBody.argsToString[aIndex];
|
||||
if (!Array.isArray(aItem)) {
|
||||
aContainer.appendChild(this.document.createTextNode(text));
|
||||
return;
|
||||
}
|
||||
|
||||
let elem = this.document.createElement("description");
|
||||
elem.classList.add("hud-clickable");
|
||||
elem.setAttribute("aria-haspopup", "true");
|
||||
elem.appendChild(this.document.createTextNode(text));
|
||||
|
||||
this._addMessageLinkCallback(elem,
|
||||
this._consoleLogClick.bind(this, aMessage, elem, aItem));
|
||||
|
||||
aContainer.appendChild(elem);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the XUL label that displays the textual location of an incoming
|
||||
* message.
|
||||
|
@ -2031,6 +2149,20 @@ WebConsoleFrame.prototype = {
|
|||
|
||||
linkNode.setAttribute("aria-haspopup", "true");
|
||||
|
||||
this._addMessageLinkCallback(aNode, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the mouse event handlers needed to make a link.
|
||||
*
|
||||
* @private
|
||||
* @param nsIDOMNode aNode
|
||||
* The node for which you want to add the event handlers.
|
||||
* @param function aCallback
|
||||
* The function you want to invoke on click.
|
||||
*/
|
||||
_addMessageLinkCallback: function WCF__addMessageLinkCallback(aNode, aCallback)
|
||||
{
|
||||
aNode.addEventListener("mousedown", function(aEvent) {
|
||||
this._startX = aEvent.clientX;
|
||||
this._startY = aEvent.clientY;
|
||||
|
|
|
@ -335,9 +335,9 @@ ConsoleAPI.prototype = {
|
|||
/**
|
||||
* Process the console API call arguments in order to perform printf-like
|
||||
* string substitution.
|
||||
* TODO: object substitution should display an interactive property list (bug
|
||||
* 685815) and width and precision qualifiers should be taken into account
|
||||
* (bug 685813).
|
||||
*
|
||||
* TODO: object substitution should take into account width and precision
|
||||
* qualifiers (bug 685813).
|
||||
*
|
||||
* @param mixed aArguments
|
||||
* The arguments given to the console API call.
|
||||
|
@ -346,12 +346,18 @@ ConsoleAPI.prototype = {
|
|||
if (aArguments.length < 2 || typeof aArguments[0] != "string") {
|
||||
return aArguments;
|
||||
}
|
||||
|
||||
let args = Array.prototype.slice.call(aArguments);
|
||||
let format = args.shift();
|
||||
let splitter = "%" + format.length + Date.now() + "%";
|
||||
let objects = [];
|
||||
|
||||
// Format specification regular expression.
|
||||
let processed = format.replace(ARGUMENT_PATTERN, function CA_PA_substitute(match, submatch) {
|
||||
switch (submatch) {
|
||||
case "o":
|
||||
objects.push(args.shift());
|
||||
return splitter;
|
||||
case "s":
|
||||
return String(args.shift());
|
||||
case "d":
|
||||
|
@ -363,8 +369,19 @@ ConsoleAPI.prototype = {
|
|||
return submatch;
|
||||
};
|
||||
});
|
||||
args.unshift(processed);
|
||||
return args;
|
||||
|
||||
let result = [];
|
||||
let processedArray = processed.split(splitter);
|
||||
processedArray.forEach(function(aValue, aIndex) {
|
||||
if (aValue !== "") {
|
||||
result.push(aValue);
|
||||
}
|
||||
if (objects[aIndex]) {
|
||||
result.push(objects[aIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
return result.concat(args);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -207,7 +207,11 @@ function observeConsoleTest() {
|
|||
|
||||
expect("error", "arg");
|
||||
win.console.error("arg");
|
||||
yield;
|
||||
|
||||
let obj2 = { b: 2 };
|
||||
expect("log", "omg ", obj, " foo ", 4, obj2);
|
||||
win.console.log("omg %o foo %o", obj, 4, obj2);
|
||||
yield;
|
||||
|
||||
startTraceTest();
|
||||
|
|
Загрузка…
Ссылка в новой задаче