Revision control

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/. */
const EXPORTED_SYMBOLS = [
"XPCOMUtils",
"executeSoon",
"nsSimpleEnumerator",
"EmptyEnumerator",
"ClassInfo",
"l10nHelper",
"initLogModule",
"scriptError",
];
const { XPCOMUtils } = ChromeUtils.import(
);
const { Services } = ChromeUtils.import("resource:///modules/imServices.jsm");
var kLogLevelPref = "purple.debug.loglevel";
/**
* Creates an nsIScriptError instance and logs it.
*
* @param aModule
* string identifying the module within which the error occurred.
* @param aLevel
* the error level as defined in imIDebugMessage.
* @param aMessage
* the error message string.
* @param aOriginalError
* (optional) JS Error object containing the location where the
* actual error occurred. Its error message is appended to aMessage.
*/
function scriptError(aModule, aLevel, aMessage, aOriginalError) {
// Figure out the log level, based on the module and the prefs set.
// The module name is split on periods, and if no pref is set the pref with
// the last section removed is attempted (until no sections are left, using
// the global default log level).
let logLevel = -1;
let logKeys = ["level"].concat(aModule.split("."));
for (; logKeys.length > 0; logKeys.pop()) {
let logKey = logKeys.join(".");
if (logKey in gLogLevels) {
logLevel = gLogLevels[logKey];
break;
}
}
// Only continue if we will log this message.
if (logLevel > aLevel && !("imAccount" in this)) {
return;
}
let flag = Ci.nsIScriptError.warningFlag;
if (aLevel >= Ci.imIDebugMessage.LEVEL_ERROR) {
flag = Ci.nsIScriptError.errorFlag;
}
let scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(
Ci.nsIScriptError
);
let caller = Components.stack.caller;
let sourceLine = aModule || caller.sourceLine;
if (caller.name) {
if (sourceLine) {
sourceLine += ": ";
}
sourceLine += caller.name;
}
let fileName = caller.filename;
let lineNumber = caller.lineNumber;
if (aOriginalError) {
aMessage += "\n" + (aOriginalError.message || aOriginalError);
if (aOriginalError.fileName) {
fileName = aOriginalError.fileName;
}
if (aOriginalError.lineNumber) {
lineNumber = aOriginalError.lineNumber;
}
}
scriptError.init(
aMessage,
fileName,
sourceLine,
lineNumber,
null,
flag,
"component javascript"
);
if (logLevel <= aLevel) {
dump(aModule + ": " + aMessage + "\n");
if (aLevel == Ci.imIDebugMessage.LEVEL_LOG && logLevel == aLevel) {
Services.console.logStringMessage(aMessage);
} else {
Services.console.logMessage(scriptError);
}
}
if ("imAccount" in this) {
this.imAccount.logDebugMessage(scriptError, aLevel);
}
}
function initLogModule(aModule, aObj = {}) {
aObj.DEBUG = scriptError.bind(aObj, aModule, Ci.imIDebugMessage.LEVEL_DEBUG);
aObj.LOG = scriptError.bind(aObj, aModule, Ci.imIDebugMessage.LEVEL_LOG);
aObj.WARN = scriptError.bind(aObj, aModule, Ci.imIDebugMessage.LEVEL_WARNING);
aObj.ERROR = scriptError.bind(aObj, aModule, Ci.imIDebugMessage.LEVEL_ERROR);
return aObj;
}
XPCOMUtils.defineLazyGetter(this, "gLogLevels", function() {
// This object functions both as an obsever as well as a dict keeping the
// log levels with prefs; the log levels all start with "level" (i.e. "level"
// for the global level, "level.irc" for the IRC module). The dual-purpose
// is necessary to make sure the observe is left alive while being a weak ref
// to avoid cycles with the pref service.
let logLevels = {
observe(aSubject, aTopic, aData) {
let module = "level" + aData.substr(kLogLevelPref.length);
if (Services.prefs.getPrefType(aData) == Services.prefs.PREF_INT) {
gLogLevels[module] = Services.prefs.getIntPref(aData);
} else {
delete gLogLevels[module];
}
},
QueryInterface: ChromeUtils.generateQI([
"nsIObserver",
"nsISupportsWeakReference",
]),
};
// Add weak pref observer to see log level pref changes.
Services.prefs.addObserver(kLogLevelPref, logLevels, true /* weak */);
// Initialize with existing log level prefs.
for (let pref of Services.prefs.getChildList(kLogLevelPref)) {
if (Services.prefs.getPrefType(pref) == Services.prefs.PREF_INT) {
logLevels[
"level" + pref.substr(kLogLevelPref.length)
] = Services.prefs.getIntPref(pref);
}
}
// Let environment variables override prefs.
Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment)
.get("PRPL_LOG")
.split(/[;,]/)
.filter(n => n != "")
.forEach(function(env) {
let [, module, level] = env.match(/(?:(.*?)[:=])?(\d+)/);
logLevels["level" + (module ? "." + module : "")] = parseInt(level, 10);
});
return logLevels;
});
function executeSoon(aFunction) {
Services.tm.mainThread.dispatch(aFunction, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
/* Common nsIClassInfo and QueryInterface implementation
* shared by all generic objects implemented in this file. */
function ClassInfo(aInterfaces, aDescription = "JS Proto Object") {
if (!(this instanceof ClassInfo)) {
return new ClassInfo(aInterfaces, aDescription);
}
if (!Array.isArray(aInterfaces)) {
aInterfaces = [aInterfaces];
}
for (let i of aInterfaces) {
if (typeof i == "string" && !(i in Ci)) {
Services.console.logStringMessage("ClassInfo: unknown interface " + i);
}
}
this._interfaces = aInterfaces.map(i => (typeof i == "string" ? Ci[i] : i));
this.classDescription = aDescription;
}
ClassInfo.prototype = {
// eslint-disable-next-line mozilla/use-chromeutils-generateqi
QueryInterface(iid) {
if (
iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIClassInfo) ||
this._interfaces.some(i => i.equals(iid))
) {
return this;
}
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
},
get interfaces() {
return [Ci.nsIClassInfo, Ci.nsISupports].concat(this._interfaces);
},
getScriptableHelper: () => null,
contractID: null,
classID: null,
flags: 0,
};
function l10nHelper(aChromeURL) {
let bundle = Services.strings.createBundle(aChromeURL);
return function(aStringId) {
try {
if (arguments.length == 1) {
return bundle.GetStringFromName(aStringId);
}
return bundle.formatStringFromName(
aStringId,
Array.prototype.slice.call(arguments, 1)
);
} catch (e) {
Cu.reportError(e);
dump("Failed to get " + aStringId + "\n");
return aStringId;
}
};
}
/**
* Constructs an nsISimpleEnumerator for the given array of items.
* Copied from netwerk/test/httpserver/httpd.js
*
* @param items : Array
* the items, which must all implement nsISupports
*/
function nsSimpleEnumerator(items) {
this._items = items;
this._nextIndex = 0;
}
nsSimpleEnumerator.prototype = {
hasMoreElements() {
return this._nextIndex < this._items.length;
},
getNext() {
if (!this.hasMoreElements()) {
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
}
return this._items[this._nextIndex++];
},
QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]),
[Symbol.iterator]() {
return this._items.values();
},
};
var EmptyEnumerator = {
hasMoreElements: () => false,
getNext() {
throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
},
QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]),
*[Symbol.iterator]() {},
};