diff --git a/browser/devtools/shared/DeveloperToolbar.jsm b/browser/devtools/shared/DeveloperToolbar.jsm index 901f28a12efe..b0784dbfcff4 100644 --- a/browser/devtools/shared/DeveloperToolbar.jsm +++ b/browser/devtools/shared/DeveloperToolbar.jsm @@ -9,6 +9,8 @@ this.EXPORTED_SYMBOLS = [ "DeveloperToolbar", "CommandUtils" ]; const NS_XHTML = "http://www.w3.org/1999/xhtml"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource:///modules/devtools/Commands.jsm"); @@ -24,7 +26,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "gcli", XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands", "resource:///modules/devtools/BuiltinCommands.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PageErrorListener", +XPCOMUtils.defineLazyModuleGetter(this, "ConsoleServiceListener", "resource://gre/modules/devtools/WebConsoleUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", @@ -435,8 +437,8 @@ DeveloperToolbar.prototype._initErrorsCount = function DT__initErrorsCount(aTab) } let window = aTab.linkedBrowser.contentWindow; - let listener = new PageErrorListener(window, { - onPageError: this._onPageError.bind(this, tabId), + let listener = new ConsoleServiceListener(window, { + onConsoleServiceMessage: this._onPageError.bind(this, tabId), }); listener.init(); @@ -593,7 +595,8 @@ DeveloperToolbar.prototype.handleEvent = function DT_handleEvent(aEvent) DeveloperToolbar.prototype._onPageError = function DT__onPageError(aTabId, aPageError) { - if (aPageError.category == "CSS Parser" || + if (!(aPageError instanceof Ci.nsIScriptError) || + aPageError.category == "CSS Parser" || aPageError.category == "CSS Loader") { return; } diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/webconsole/test/Makefile.in index 565388573055..a223a7b4acc9 100644 --- a/browser/devtools/webconsole/test/Makefile.in +++ b/browser/devtools/webconsole/test/Makefile.in @@ -130,6 +130,7 @@ MOCHITEST_BROWSER_FILES = \ browser_bug_862916_console_dir_and_filter_off.js \ browser_console_native_getters.js \ browser_bug_871156_ctrlw_close_tab.js \ + browser_console_nsiconsolemessage.js \ head.js \ $(NULL) diff --git a/browser/devtools/webconsole/test/browser_console_nsiconsolemessage.js b/browser/devtools/webconsole/test/browser_console_nsiconsolemessage.js new file mode 100644 index 000000000000..6a8af77bad4e --- /dev/null +++ b/browser/devtools/webconsole/test/browser_console_nsiconsolemessage.js @@ -0,0 +1,93 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Check that nsIConsoleMessages show in the Browser Console and in the Web +// Console. See bug 859756. + +const TEST_URI = "data:text/html;charset=utf8,
hello world\n
nsIConsoleMessages ftw!"; + +let gWebConsole, gJSTerm, gVariablesView; + +function test() +{ + addTab(TEST_URI); + browser.addEventListener("load", function onLoad() { + browser.removeEventListener("load", onLoad, true); + + // Test for cached nsIConsoleMessages. + sendMessage("test1 for bug859756", "cat2012"); + + openConsole(null, consoleOpened); + }, true); +} + +function sendMessage(aMessage, aCategory) +{ + let windowID = WebConsoleUtils.getInnerWindowId(content); + let consoleMsg = Cc["@mozilla.org/consolemessage;1"] + .createInstance(Ci.nsIConsoleMessage); + consoleMsg.initMessage(aMessage, aCategory, windowID); + Services.console.logMessage(consoleMsg); +} + +function consoleOpened(hud) +{ + gWebConsole = hud; + gJSTerm = hud.jsterm; + + // Send a message with no window ID. + Services.console.logStringMessage("do-not-show-me"); + + sendMessage("test2 for bug859756", "cat2013"); + + waitForMessages({ + webconsole: hud, + messages: [ + { + text: "test1 for bug859756", + category: CATEGORY_JS, + }, + { + text: "test2 for bug859756", + category: CATEGORY_JS, + }, + ], + }).then(onLogMessages); +} + +function onLogMessages() +{ + let text = gWebConsole.outputNode.textContent; + is(text.indexOf("do-not-show-me"), -1, + "message without window ID is not displayed"); + closeConsole(null, onWebConsoleClose); +} + +function onWebConsoleClose() +{ + HUDConsoleUI.toggleBrowserConsole().then(onBrowserConsoleOpen); +} + +function onBrowserConsoleOpen(hud) +{ + waitForMessages({ + webconsole: hud, + messages: [ + { + text: "test1 for bug859756", + category: CATEGORY_JS, + }, + { + text: "test2 for bug859756", + category: CATEGORY_JS, + }, + { + text: "do-not-show-me", + category: CATEGORY_JS, + }, + ], + }).then(finishTest); +} diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 8a757cccc23d..789ffc6b8f6d 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -956,11 +956,14 @@ WebConsoleFrame.prototype = { aRemoteMessages.forEach(function(aMessage) { switch (aMessage._type) { case "PageError": { - let category = Utils.categoryForScriptError(aMessage); + let category = Utils.categoryForConsoleMessage(aMessage); this.outputMessage(category, this.reportPageError, [category, aMessage]); break; } + case "LogMessage": + this.handleLogMessage(aMessage); + break; case "ConsoleAPI": this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage, [aMessage]); @@ -1188,10 +1191,26 @@ WebConsoleFrame.prototype = { */ handlePageError: function WCF_handlePageError(aPageError) { - let category = Utils.categoryForScriptError(aPageError); + let category = Utils.categoryForConsoleMessage(aPageError); this.outputMessage(category, this.reportPageError, [category, aPageError]); }, + /** + * Handle log messages received from the server. This method outputs the given + * message. + * + * @param object aPacket + * The message packet received from the server. + */ + handleLogMessage: function WCF_handleLogMessage(aPacket) + { + let category = Utils.categoryForConsoleMessage(aPacket.category); + this.outputMessage(category, () => { + return this.createMessageNode(category, SEVERITY_LOG, aPacket.message, + null, null, null, null, aPacket.timeStamp); + }); + }, + /** * Log network event. * @@ -4337,17 +4356,17 @@ var Utils = { }, /** - * Determine the category of a given nsIScriptError. + * Determine the category of a given nsIConsoleMessage. * - * @param nsIScriptError aScriptError - * The script error you want to determine the category for. + * @param nsIConsoleMessage aMessage + * The message you want to determine the category for. * @return CATEGORY_JS|CATEGORY_CSS|CATEGORY_SECURITY - * Depending on the script error CATEGORY_JS, CATEGORY_CSS, or + * Depending on the message kind CATEGORY_JS, CATEGORY_CSS, or * CATEGORY_SECURITY can be returned. */ - categoryForScriptError: function Utils_categoryForScriptError(aScriptError) + categoryForConsoleMessage: function Utils_categoryForConsoleMessage(aMessage) { - switch (aScriptError.category) { + switch (aMessage.category) { case "CSS Parser": case "CSS Loader": return CATEGORY_CSS; @@ -4508,6 +4527,7 @@ function WebConsoleConnectionProxy(aWebConsole, aTarget) this.target = aTarget; this._onPageError = this._onPageError.bind(this); + this._onLogMessage = this._onLogMessage.bind(this); this._onConsoleAPICall = this._onConsoleAPICall.bind(this); this._onNetworkEvent = this._onNetworkEvent.bind(this); this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this); @@ -4611,6 +4631,7 @@ WebConsoleConnectionProxy.prototype = { let client = this.client = this.target.client; + client.addListener("logMessage", this._onLogMessage); client.addListener("pageError", this._onPageError); client.addListener("consoleAPICall", this._onConsoleAPICall); client.addListener("networkEvent", this._onNetworkEvent); @@ -4731,6 +4752,23 @@ WebConsoleConnectionProxy.prototype = { } }, + /** + * The "logMessage" message type handler. We redirect any message to the UI + * for displaying. + * + * @private + * @param string aType + * Message type. + * @param object aPacket + * The message received from the server. + */ + _onLogMessage: function WCCP__onLogMessage(aType, aPacket) + { + if (this.owner && aPacket.from == this._consoleActor) { + this.owner.handleLogMessage(aPacket); + } + }, + /** * The "consoleAPICall" message type handler. We redirect any message to * the UI for displaying. @@ -4858,6 +4896,7 @@ WebConsoleConnectionProxy.prototype = { } this.client.removeListener("pageError", this._onPageError); + this.client.removeListener("logMessage", this._onLogMessage); this.client.removeListener("consoleAPICall", this._onConsoleAPICall); this.client.removeListener("networkEvent", this._onNetworkEvent); this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate); diff --git a/toolkit/devtools/debugger/dbg-client.jsm b/toolkit/devtools/debugger/dbg-client.jsm index c15c6e18b6d8..5f6b9115e04c 100644 --- a/toolkit/devtools/debugger/dbg-client.jsm +++ b/toolkit/devtools/debugger/dbg-client.jsm @@ -174,6 +174,7 @@ const UnsolicitedNotifications = { "consoleAPICall": "consoleAPICall", "eventNotification": "eventNotification", "fileActivity": "fileActivity", + "logMessage": "logMessage", "networkEvent": "networkEvent", "networkEventUpdate": "networkEventUpdate", "newGlobal": "newGlobal", diff --git a/toolkit/devtools/webconsole/WebConsoleUtils.jsm b/toolkit/devtools/webconsole/WebConsoleUtils.jsm index f45a0414530a..c11f2198bffe 100644 --- a/toolkit/devtools/webconsole/WebConsoleUtils.jsm +++ b/toolkit/devtools/webconsole/WebConsoleUtils.jsm @@ -39,7 +39,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm"); this.EXPORTED_SYMBOLS = ["WebConsoleUtils", "JSPropertyProvider", "JSTermHelpers", - "PageErrorListener", "ConsoleAPIListener", + "ConsoleServiceListener", "ConsoleAPIListener", "NetworkResponseListener", "NetworkMonitor", "ConsoleProgressListener"]; @@ -873,25 +873,25 @@ return JSPropertyProvider; /////////////////////////////////////////////////////////////////////////////// /** - * The nsIConsoleService listener. This is used to send all the page errors - * (JavaScript, CSS and more) to the remote Web Console instance. + * The nsIConsoleService listener. This is used to send all of the console + * messages (JavaScript, CSS and more) to the remote Web Console instance. * * @constructor * @param nsIDOMWindow [aWindow] * Optional - the window object for which we are created. This is used * for filtering out messages that belong to other windows. * @param object aListener - * The listener object must have a method: onPageError. This method is - * invoked with one argument, the nsIScriptError, whenever a relevant - * page error is received. + * The listener object must have one method: + * - onConsoleServiceMessage(). This method is invoked with one argument, the + * nsIConsoleMessage, whenever a relevant message is received. */ -this.PageErrorListener = function PageErrorListener(aWindow, aListener) +this.ConsoleServiceListener = function ConsoleServiceListener(aWindow, aListener) { this.window = aWindow; this.listener = aListener; } -PageErrorListener.prototype = +ConsoleServiceListener.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]), @@ -902,8 +902,7 @@ PageErrorListener.prototype = window: null, /** - * The listener object which is notified of page errors. It must have - * a onPageError method which is invoked with one argument: the nsIScriptError. + * The listener object which is notified of messages from the console service. * @type object */ listener: null, @@ -911,7 +910,7 @@ PageErrorListener.prototype = /** * Initialize the nsIConsoleService listener. */ - init: function PEL_init() + init: function CSL_init() { Services.console.registerListener(this); }, @@ -921,43 +920,47 @@ PageErrorListener.prototype = * messages belonging to the current window and sends them to the remote Web * Console instance. * - * @param nsIScriptError aScriptError - * The script error object coming from the nsIConsoleService. + * @param nsIConsoleMessage aMessage + * The message object coming from the nsIConsoleService. */ - observe: function PEL_observe(aScriptError) + observe: function CSL_observe(aMessage) { - if (!this.listener || - !(aScriptError instanceof Ci.nsIScriptError)) { + if (!this.listener) { return; } if (this.window) { - if (!aScriptError.outerWindowID || - !this.isCategoryAllowed(aScriptError.category)) { + if (!aMessage.outerWindowID || + !this.isCategoryAllowed(aMessage.category)) { return; } - let errorWindow = - Services.wm.getOuterWindowWithId(aScriptError.outerWindowID); + let errorWindow = Services.wm.getOuterWindowWithId(aMessage.outerWindowID); if (!errorWindow || errorWindow.top != this.window) { return; } } - this.listener.onPageError(aScriptError); + if (aMessage.message) { + this.listener.onConsoleServiceMessage(aMessage); + } }, /** - * Check if the given script error category is allowed to be tracked or not. + * Check if the given message category is allowed to be tracked or not. * We ignore chrome-originating errors as we only care about content. * * @param string aCategory - * The nsIScriptError category you want to check. + * The message category you want to check. * @return boolean * True if the category is allowed to be logged, false otherwise. */ - isCategoryAllowed: function PEL_isCategoryAllowed(aCategory) + isCategoryAllowed: function CSL_isCategoryAllowed(aCategory) { + if (!aCategory) { + return false; + } + switch (aCategory) { case "XPConnect JavaScript": case "component javascript": @@ -977,28 +980,26 @@ PageErrorListener.prototype = * Get the cached page errors for the current inner window. * * @return array - * The array of cached messages. Each element is an nsIScriptError - * with an added _type property so the remote Web Console instance can - * tell the difference between various types of cached messages. + * The array of cached messages. Each element is an nsIScriptError or + * an nsIConsoleMessage. */ - getCachedMessages: function PEL_getCachedMessages() + getCachedMessages: function CSL_getCachedMessages() { let innerWindowId = this.window ? WebConsoleUtils.getInnerWindowId(this.window) : null; let errors = Services.console.getMessageArray() || []; - return errors.filter(function(aError) { - return aError instanceof Ci.nsIScriptError && - (!innerWindowId || + return errors.filter((aError) => { + return !innerWindowId || (aError.innerWindowID == innerWindowId && - this.isCategoryAllowed(aError.category))); - }, this); + this.isCategoryAllowed(aError.category)); + }); }, /** * Remove the nsIConsoleService listener. */ - destroy: function PEL_destroy() + destroy: function CSL_destroy() { Services.console.unregisterListener(this); this.listener = this.window = null; @@ -1091,8 +1092,7 @@ ConsoleAPIListener.prototype = * Get the cached messages for the current inner window. * * @return array - * The array of cached messages. Each element is a Console API - * prepared to be sent to the remote Web Console instance. + * The array of cached messages. */ getCachedMessages: function CAL_getCachedMessages() { diff --git a/toolkit/devtools/webconsole/dbg-webconsole-actors.js b/toolkit/devtools/webconsole/dbg-webconsole-actors.js index 0c7db6fdcb61..05d6f5c4ff30 100644 --- a/toolkit/devtools/webconsole/dbg-webconsole-actors.js +++ b/toolkit/devtools/webconsole/dbg-webconsole-actors.js @@ -18,7 +18,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services", XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils", "resource://gre/modules/devtools/WebConsoleUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PageErrorListener", +XPCOMUtils.defineLazyModuleGetter(this, "ConsoleServiceListener", "resource://gre/modules/devtools/WebConsoleUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPIListener", @@ -165,10 +165,10 @@ WebConsoleActor.prototype = _window: null, /** - * The PageErrorListener instance. + * The ConsoleServiceListener instance. * @type object */ - pageErrorListener: null, + consoleServiceListener: null, /** * The ConsoleAPIListener instance. @@ -211,9 +211,9 @@ WebConsoleActor.prototype = */ disconnect: function WCA_disconnect() { - if (this.pageErrorListener) { - this.pageErrorListener.destroy(); - this.pageErrorListener = null; + if (this.consoleServiceListener) { + this.consoleServiceListener.destroy(); + this.consoleServiceListener = null; } if (this.consoleAPIListener) { this.consoleAPIListener.destroy(); @@ -363,10 +363,10 @@ WebConsoleActor.prototype = let listener = aRequest.listeners.shift(); switch (listener) { case "PageError": - if (!this.pageErrorListener) { - this.pageErrorListener = - new PageErrorListener(window, this); - this.pageErrorListener.init(); + if (!this.consoleServiceListener) { + this.consoleServiceListener = + new ConsoleServiceListener(window, this); + this.consoleServiceListener.init(); } startedListeners.push(listener); break; @@ -426,9 +426,9 @@ WebConsoleActor.prototype = let listener = toDetach.shift(); switch (listener) { case "PageError": - if (this.pageErrorListener) { - this.pageErrorListener.destroy(); - this.pageErrorListener = null; + if (this.consoleServiceListener) { + this.consoleServiceListener.destroy(); + this.consoleServiceListener = null; } stoppedListeners.push(listener); break; @@ -495,13 +495,24 @@ WebConsoleActor.prototype = } break; case "PageError": - if (this.pageErrorListener) { - let cache = this.pageErrorListener.getCachedMessages(); - cache.forEach(function(aMessage) { - let message = this.preparePageErrorForRemote(aMessage); - message._type = type; + if (this.consoleServiceListener) { + let cache = this.consoleServiceListener.getCachedMessages(); + cache.forEach((aMessage) => { + let message = null; + if (aMessage instanceof Ci.nsIScriptError) { + message = this.preparePageErrorForRemote(aMessage); + message._type = type; + } + else { + message = { + _type: "LogMessage", + message: aMessage.message, + timeStamp: aMessage.timeStamp, + category: aMessage.category, + }; + } messages.push(message); - }, this); + }); } break; } @@ -595,6 +606,10 @@ WebConsoleActor.prototype = let windowId = !this._isGlobalActor ? WebConsoleUtils.getInnerWindowId(this.window) : null; ConsoleAPIStorage.clearEvents(windowId); + if (this._isGlobalActor) { + Services.console.logStringMessage(null); // for the Error Console + Services.console.reset(); + } return {}; }, @@ -852,19 +867,31 @@ WebConsoleActor.prototype = ////////////////// /** - * Handler for page errors received from the PageErrorListener. This method - * sends the nsIScriptError to the remote Web Console client. + * Handler for messages received from the ConsoleServiceListener. This method + * sends the nsIConsoleMessage to the remote Web Console client. * - * @param nsIScriptError aPageError - * The page error we need to send to the client. + * @param nsIConsoleMessage aMessage + * The message we need to send to the client. */ - onPageError: function WCA_onPageError(aPageError) + onConsoleServiceMessage: function WCA_onConsoleServiceMessage(aMessage) { - let packet = { - from: this.actorID, - type: "pageError", - pageError: this.preparePageErrorForRemote(aPageError), - }; + let packet; + if (aMessage instanceof Ci.nsIScriptError) { + packet = { + from: this.actorID, + type: "pageError", + pageError: this.preparePageErrorForRemote(aMessage), + }; + } + else { + packet = { + from: this.actorID, + type: "logMessage", + message: aMessage.message, + timeStamp: aMessage.timeStamp, + category: aMessage.category, + }; + } this.conn.send(packet); }, diff --git a/toolkit/devtools/webconsole/test/Makefile.in b/toolkit/devtools/webconsole/test/Makefile.in index ad9802f3bda6..64b5348446da 100644 --- a/toolkit/devtools/webconsole/test/Makefile.in +++ b/toolkit/devtools/webconsole/test/Makefile.in @@ -24,6 +24,7 @@ MOCHITEST_CHROME_FILES = \ test_bug819670_getter_throws.html \ test_object_actor_native_getters.html \ test_object_actor_native_getters_lenient_this.html \ + test_nsiconsolemessage.html \ network_requests_iframe.html \ data.json \ data.json^headers^ \ diff --git a/toolkit/devtools/webconsole/test/common.js b/toolkit/devtools/webconsole/test/common.js index 12d537725386..8904300365c3 100644 --- a/toolkit/devtools/webconsole/test/common.js +++ b/toolkit/devtools/webconsole/test/common.js @@ -106,7 +106,10 @@ function checkObject(aObject, aExpected) function checkValue(aName, aValue, aExpected) { - if (aValue === undefined) { + if (aExpected === null) { + ok(!aValue, "'" + aName + "' is null"); + } + else if (aValue === undefined) { ok(false, "'" + aName + "' is undefined"); } else if (typeof aExpected == "string" || typeof aExpected == "number" || diff --git a/toolkit/devtools/webconsole/test/test_cached_messages.html b/toolkit/devtools/webconsole/test/test_cached_messages.html index ef3930db716e..366675af0d99 100644 --- a/toolkit/devtools/webconsole/test/test_cached_messages.html +++ b/toolkit/devtools/webconsole/test/test_cached_messages.html @@ -95,7 +95,7 @@ function doConsoleCalls() + + + +
+Make sure that nsIConsoleMessages are logged. See bug 859756.
+ + + +