Loading browser/devtools/webconsole/HUDService-content.js +5 −5 Original line number Diff line number Diff line Loading @@ -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 = []; Loading browser/devtools/webconsole/test/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -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) Loading browser/devtools/webconsole/test/browser_console_log_inspectable_object.js 0 → 100644 +57 −0 Original line number Diff line number Diff line /* 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, }); } browser/devtools/webconsole/webconsole.js +145 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. * Loading Loading @@ -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()); // Create the containing node and append all its elements to it. let node = this.document.createElementNS(XUL_NS, "richlistitem"); if (aBody instanceof Ci.nsIDOMNode) { bodyNode.appendChild(aBody); } if (typeof aBody == "string") { aBody = this.document.createTextNode(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"); Loading @@ -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"); Loading Loading @@ -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. Loading Loading @@ -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; Loading dom/base/ConsoleAPI.js +22 −5 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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": Loading @@ -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); }, /** Loading Loading
browser/devtools/webconsole/HUDService-content.js +5 −5 Original line number Diff line number Diff line Loading @@ -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 = []; Loading
browser/devtools/webconsole/test/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
browser/devtools/webconsole/test/browser_console_log_inspectable_object.js 0 → 100644 +57 −0 Original line number Diff line number Diff line /* 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, }); }
browser/devtools/webconsole/webconsole.js +145 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. * Loading Loading @@ -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()); // Create the containing node and append all its elements to it. let node = this.document.createElementNS(XUL_NS, "richlistitem"); if (aBody instanceof Ci.nsIDOMNode) { bodyNode.appendChild(aBody); } if (typeof aBody == "string") { aBody = this.document.createTextNode(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"); Loading @@ -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"); Loading Loading @@ -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. Loading Loading @@ -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; Loading
dom/base/ConsoleAPI.js +22 −5 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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": Loading @@ -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); }, /** Loading