Bug 676722 - The output of console.log(object) isn't expandable/inspectable in the Web Console; r=rcampbell

This commit is contained in:
Mihai Sucan 2012-08-03 23:14:01 +03:00
Родитель 31ad3b872b
Коммит 2b290df88d
6 изменённых файлов: 235 добавлений и 24 удалений

Просмотреть файл

@ -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();