summaryrefslogtreecommitdiffstats
path: root/comm/chat/modules/imXPCOMUtils.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'comm/chat/modules/imXPCOMUtils.sys.mjs')
-rw-r--r--comm/chat/modules/imXPCOMUtils.sys.mjs249
1 files changed, 249 insertions, 0 deletions
diff --git a/comm/chat/modules/imXPCOMUtils.sys.mjs b/comm/chat/modules/imXPCOMUtils.sys.mjs
new file mode 100644
index 0000000000..4a48f2116d
--- /dev/null
+++ b/comm/chat/modules/imXPCOMUtils.sys.mjs
@@ -0,0 +1,249 @@
+/* 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+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.
+ */
+export 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 lazy.gLogLevels) {
+ logLevel = lazy.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);
+ }
+}
+
+export 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;
+}
+
+const lazy = {};
+XPCOMUtils.defineLazyGetter(lazy, "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) {
+ lazy.gLogLevels[module] = Services.prefs.getIntPref(aData);
+ } else {
+ delete lazy.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.
+ Services.env
+ .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;
+});
+
+export 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. */
+export 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,
+};
+
+export 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) {
+ console.error(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
+ */
+export 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();
+ },
+};
+
+export var EmptyEnumerator = {
+ hasMoreElements: () => false,
+ getNext() {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]),
+ *[Symbol.iterator]() {},
+};