Bug 859756 - [browserconsole] Show nsIConsoleMessages in the Browser Console; r=robcee

This commit is contained in:
Mihai Sucan 2013-05-17 13:36:56 +03:00
Родитель 2132f8743c
Коммит 504f602058
11 изменённых файлов: 378 добавлений и 84 удалений

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

@ -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;
}

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

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

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

@ -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,<title>bug859756</title>\n" +
"<p>hello world\n<p>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);
}

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

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

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

@ -174,6 +174,7 @@ const UnsolicitedNotifications = {
"consoleAPICall": "consoleAPICall",
"eventNotification": "eventNotification",
"fileActivity": "fileActivity",
"logMessage": "logMessage",
"networkEvent": "networkEvent",
"networkEventUpdate": "networkEventUpdate",
"newGlobal": "newGlobal",

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

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

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

@ -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);
},

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

@ -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^ \

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

@ -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" ||

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

@ -95,7 +95,7 @@ function doConsoleCalls()
<script class="testbody" type="text/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
let consoleAPIListener, pageErrorListener;
let consoleAPIListener, consoleServiceListener;
let consoleAPICalls = 0;
let pageErrors = 0;
@ -108,7 +108,7 @@ let handlers = {
}
},
onPageError: function onPageError()
onConsoleServiceMessage: function onConsoleServiceMessage()
{
pageErrors++;
if (pageErrors == expectedPageErrors.length) {
@ -153,16 +153,16 @@ function onCachedConsoleAPI(aState, aResponse)
});
closeDebugger(aState, function() {
pageErrorListener = new PageErrorListener(null, handlers);
pageErrorListener.init();
consoleServiceListener = new ConsoleServiceListener(null, handlers);
consoleServiceListener.init();
doPageErrors();
});
}
function testPageErrors()
{
pageErrorListener.destroy();
pageErrorListener = null;
consoleServiceListener.destroy();
consoleServiceListener = null;
attachConsole(["PageError"], onAttach2);
}

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

@ -0,0 +1,126 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf8">
<title>Test for nsIConsoleMessages</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript;version=1.8" src="common.js"></script>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
</head>
<body>
<p>Make sure that nsIConsoleMessages are logged. See bug 859756.</p>
<script class="testbody" type="text/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
let expectedMessages = [];
let testingTabActor = false;
function sendConsoleMessage(aMessage, aCategory, aWindowId)
{
let consoleMsg = Cc["@mozilla.org/consolemessage;1"]
.createInstance(Ci.nsIConsoleMessage);
consoleMsg.initMessage(aMessage, aCategory, aWindowId);
Services.console.logMessage(consoleMsg);
}
function sendMessages()
{
expectedMessages = [
{
message: "hello world! bug859756",
category: null,
timeStamp: /^\d+$/,
},
{
message: "foobarz",
category: null,
timeStamp: /^\d+$/,
},
{
message: "boom",
category: "cat7",
timeStamp: /^\d+$/,
},
{
message: "windowfun",
category: "cat9",
timeStamp: /^\d+$/,
},
];
Services.console.logStringMessage("hello world! bug859756");
sendConsoleMessage("foobarz");
sendConsoleMessage("boom", "cat7");
sendConsoleMessage("windowfun", "cat9", Date.now());
}
function sendMessagesForTab()
{
sendMessages();
expectedMessages = [
{
message: "windowfun3",
category: "cat10",
timeStamp: /^\d+$/,
},
];
let windowID = WebConsoleUtils.getInnerWindowId(top);
sendConsoleMessage("windowfun3", "cat10", windowID);
}
function startTest()
{
removeEventListener("load", startTest);
attachConsole(["PageError"], onAttach);
}
function onAttach(aState, aResponse)
{
window.onLogMessage_ = onLogMessage.bind(null, aState);
aState.dbgClient.addListener("logMessage", onLogMessage_);
if (!testingTabActor) {
sendMessages();
} else {
sendMessagesForTab();
}
info("waiting for messages");
}
let receivedMessages = [];
function onLogMessage(aState, aType, aPacket)
{
is(aPacket.from, aState.actor, "packet actor");
receivedMessages.push(aPacket);
if (receivedMessages.length != expectedMessages.length) {
return;
}
aState.dbgClient.removeListener("logMessage", onLogMessage_);
checkObject(receivedMessages, expectedMessages);
receivedMessages = expectedMessages = [];
window.onLogMessage_ = null;
closeDebugger(aState, () => {
if (!testingTabActor) {
testingTabActor = true;
info("start tab actor test");
attachConsole(["PageError"], onAttach, true);
} else {
SimpleTest.finish();
}
});
}
addEventListener("load", startTest);
</script>
</body>
</html>