Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
isWindowIncluded,
const {
WebConsoleUtils,
// The page errors listener
/**
* 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 [window]
* Optional - the window object for which we are created. This is used
* for filtering out messages that belong to other windows.
* @param Function handler
* This function is invoked with one argument, the nsIConsoleMessage, whenever a
* relevant message is received.
* @param object filteringOptions
* Optional - The filteringOptions that this listener should listen to:
* - matchExactWindow: Set to true to match the messages on a specific window (when
* `window` is defined) and not on the whole window tree.
*/
class ConsoleServiceListener {
constructor(window, handler, { matchExactWindow } = {}) {
this.window = window;
this.handler = handler;
this.matchExactWindow = matchExactWindow;
}
QueryInterface = ChromeUtils.generateQI([Ci.nsIConsoleListener]);
/**
* The content window for which we listen to page errors.
* @type nsIDOMWindow
*/
window = null;
/**
* The function which is notified of messages from the console service.
* @type function
*/
handler = null;
/**
* Initialize the nsIConsoleService listener.
*/
init() {
Services.console.registerListener(this);
}
/**
* The nsIConsoleService observer. This method takes all the script error
* messages belonging to the current window and sends them to the remote Web
* Console instance.
*
* @param nsIConsoleMessage message
* The message object coming from the nsIConsoleService.
*/
observe(message) {
if (!this.handler) {
return;
}
if (this.window) {
if (
!(message instanceof Ci.nsIScriptError) ||
!message.outerWindowID ||
!this.isCategoryAllowed(message.category)
) {
return;
}
const errorWindow = Services.wm.getOuterWindowWithId(
message.outerWindowID
);
if (!errorWindow) {
return;
}
if (this.matchExactWindow && this.window !== errorWindow) {
return;
}
if (!isWindowIncluded(this.window, errorWindow)) {
return;
}
}
// Don't display messages triggered by eager evaluation.
if (message.sourceName === "debugger eager eval code") {
return;
}
this.handler(message);
}
/**
* 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 category
* The message category you want to check.
* @return boolean
* True if the category is allowed to be logged, false otherwise.
*/
isCategoryAllowed(category) {
if (!category) {
return false;
}
switch (category) {
case "XPConnect JavaScript":
case "component javascript":
case "chrome javascript":
case "chrome registration":
return false;
}
return true;
}
/**
* Get the cached page errors for the current inner window and its (i)frames.
*
* @param boolean [includePrivate=false]
* Tells if you want to also retrieve messages coming from private
* windows. Defaults to false.
* @return array
* The array of cached messages. Each element is an nsIScriptError or
* an nsIConsoleMessage
*/
getCachedMessages(includePrivate = false) {
const errors = Services.console.getMessageArray() || [];
// if !this.window, we're in a browser console. Still need to filter
// private messages.
if (!this.window) {
return errors.filter(error => {
if (error instanceof Ci.nsIScriptError) {
if (!includePrivate && error.isFromPrivateWindow) {
return false;
}
}
return true;
});
}
const ids = this.matchExactWindow
? [WebConsoleUtils.getInnerWindowId(this.window)]
: WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
return errors.filter(error => {
if (error instanceof Ci.nsIScriptError) {
if (!includePrivate && error.isFromPrivateWindow) {
return false;
}
if (
ids &&
(!ids.includes(error.innerWindowID) ||
!this.isCategoryAllowed(error.category))
) {
return false;
}
} else if (ids?.[0]) {
// If this is not an nsIScriptError and we need to do window-based
// filtering we skip this message.
return false;
}
return true;
});
}
/**
* Remove the nsIConsoleService listener.
*/
destroy() {
Services.console.unregisterListener(this);
this.handler = this.window = null;
}
}
exports.ConsoleServiceListener = ConsoleServiceListener;