Dactyloidae/mailnews/base/util/errUtils.js

309 lines
9.1 KiB
JavaScript

/* 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/. */
/**
* This file contains helper methods for debugging -- things like logging
* exception objects, dumping DOM nodes, Events, and generic object dumps.
*/
this.EXPORTED_SYMBOLS = ["logObject", "logException", "logElement", "logEvent",
"errorWithDebug"];
/**
* Report on an object to stdout.
* @param aObj the object to be dumped
* @param aName the name of the object, for informational purposes
*/
function logObject(aObj, aName) {
dump("Dumping Object: " + aName + "\n");
stringifier.dumpObj(aObj, aName);
}
/**
* Log an exception to stdout. This function should not be called in
* expected circumstances.
* @param aException the exception to log
* @param [aRethrow] set to true to rethrow the exception after logging
* @param [aMsg] optional message to log
*/
function logException(aException, aRethrow, aMsg) {
stringifier.dumpException(aException, aMsg);
if (aMsg)
Components.utils.reportError(aMsg);
Components.utils.reportError(aException);
if (aRethrow)
throw aException;
}
/**
* Log an DOM element to stdout.
* @param aElement the DOM element to dump
*/
function logElement(aElement) {
stringifier.dumpDOM(aElement);
}
/**
* Log an DOM event to stdout.
* @param aEvent the DOM event object to dump
*/
function logEvent(aEvent) {
stringifier.dumpEvent(aEvent);
}
/**
* Dump the current stack and return an Error suitable for throwing. We return
* the new Error so that your code can use a "throw" statement which makes it
* obvious to syntactic analysis that there is an exit occuring at that point.
*
* Example:
* throw errorWithDebug("I did not expect this!");
*
* @param aString The message payload for the exception.
*/
function errorWithDebug(aString) {
dump("PROBLEM: " + aString + "\n");
dump("CURRENT STACK (and throwing):\n");
// skip this frame.
dump(stringifier.getStack(1));
return new Error(aString);
}
function Stringifier() {};
Stringifier.prototype = {
dumpObj: function (o, name) {
this._reset();
this._append(this.objectTreeAsString(o, true, true, 0));
dump(this._asString());
},
dumpDOM: function(node, level, recursive) {
this._reset();
let s = this.DOMNodeAsString(node, level, recursive);
dump(s);
},
dumpEvent: function(event) {
dump(this.eventAsString(event));
},
dumpException: function(exc, message) {
dump(exc + "\n");
this._reset();
if (message)
this._append("Exception (" + message + ")\n");
this._append("-- Exception object --\n");
this._append(this.objectTreeAsString(exc));
if (exc.stack) {
this._append("-- Stack Trace --\n");
this._append(exc.stack); // skip dumpException and logException
}
dump(this._asString());
},
_reset: function() {
this._buffer = [];
},
_append: function(string) {
this._buffer.push(string);
},
_asString: function() {
let str = this._buffer.join('');
this._reset();
return str;
},
getStack: function(skipCount) {
if (!((typeof Components == "object") &&
(typeof Components.classes == "object")))
return "No stack trace available.";
if (typeof(skipCount) === undefined)
skipCount = 0;
let frame = Components.stack.caller;
let str = "<top>";
while (frame) {
if (skipCount > 0) {
// Skip this frame.
skipCount -= 1;
}
else {
// Include the data from this frame.
let name = frame.name ? frame.name : "[anonymous]";
str += "\n" + name + "@" + frame.filename + ':' + frame.lineNumber;
}
frame = frame.caller;
}
return str + "\n";
},
objectTreeAsString: function(o, recurse, compress, level) {
let s = "";
if (recurse === undefined)
recurse = 0;
if (level === undefined)
level = 0;
if (compress === undefined)
compress = true;
let pfx = "";
for (var junk = 0; junk < level; junk++)
pfx += (compress) ? "| " : "| ";
let tee = (compress) ? "+ " : "+- ";
if (typeof(o) != "object") {
s += pfx + tee + " (" + typeof(o) + ") " + o + "\n";
}
else {
for (let i in o) {
try {
let t = typeof o[i];
switch (t) {
case "function":
let sfunc = String(o[i]).split("\n");
if (sfunc[2] == " [native code]")
sfunc = "[native code]";
else
sfunc = sfunc.length + " lines";
s += pfx + tee + i + " (function) " + sfunc + "\n";
break;
case "object":
s += pfx + tee + i + " (object) " + o[i] + "\n";
if (!compress)
s += pfx + "|\n";
if ((i != "parent") && (recurse))
s += this.objectTreeAsString(o[i], recurse - 1,
compress, level + 1);
break;
case "string":
if (o[i].length > 200)
s += pfx + tee + i + " (" + t + ") " + o[i].length + " chars\n";
else
s += pfx + tee + i + " (" + t + ") '" + o[i] + "'\n";
break;
default:
s += pfx + tee + i + " (" + t + ") " + o[i] + "\n";
}
} catch (ex) {
s += pfx + tee + " (exception) " + ex + "\n";
}
if (!compress)
s += pfx + "|\n";
}
}
s += pfx + "*\n";
return s;
},
_repeatStr: function (str, aCount) {
let res = "";
while (--aCount >= 0)
res += str;
return res;
},
DOMNodeAsString: function(node, level, recursive) {
if (level === undefined)
level = 0
if (recursive === undefined)
recursive = true;
this._append(this._repeatStr(" ", 2*level) + "<" + node.nodeName + "\n");
if (node.nodeType == 3) {
this._append(this._repeatStr(" ", (2*level) + 4) + node.nodeValue + "'\n");
}
else {
if (node.attributes) {
for (let i = 0; i < node.attributes.length; i++) {
this._append(this._repeatStr(
" ", (2*level) + 4) + node.attributes[i].nodeName +
"='" + node.attributes[i].nodeValue + "'\n");
}
}
if (node.childNodes.length == 0) {
this._append(this._repeatStr(" ", (2*level)) + "/>\n");
}
else if (recursive) {
this._append(this._repeatStr(" ", (2*level)) + ">\n");
for (let i = 0; i < node.childNodes.length; i++) {
this._append(this.DOMNodeAsString(node.childNodes[i], level + 1));
}
this._append(this._repeatStr(" ", 2*level) + "</" + node.nodeName + ">\n");
}
}
return this._asString();
},
eventAsString: function (event) {
this._reset();
this._append("-EVENT --------------------------\n");
this._append("type: " + event.type + "\n");
this._append("eventPhase: " + event.eventPhase + "\n");
if ("charCode" in event) {
this._append("charCode: " + event.charCode + "\n");
if ("name" in event)
this._append("str(charCode): '" + String.fromCharCode(event.charCode) + "'\n");
}
if (("target" in event) && event.target) {
this._append("target: " + event.target + "\n");
if ("nodeName" in event.target)
this._append("target.nodeName: " + event.target.nodeName + "\n");
if ("getAttribute" in event.target)
this._append("target.id: " + event.target.getAttribute("id") + "\n");
}
if (("currentTarget" in event) && event.currentTarget) {
this._append("currentTarget: " + event.currentTarget + "\n");
if ("nodeName" in event.currentTarget)
this._append("currentTarget.nodeName: "+ event.currentTarget.nodeName + "\n");
if ("getAttribute" in event.currentTarget)
this._append("currentTarget.id: "+ event.currentTarget.getAttribute("id") + "\n");
}
if (("originalTarget" in event) && event.originalTarget) {
this._append("originalTarget: " + event.originalTarget + "\n");
if ("nodeName" in event.originalTarget)
this._append("originalTarget.nodeName: "+ event.originalTarget.nodeName + "\n");
if ("getAttribute" in event.originalTarget)
this._append("originalTarget.id: "+ event.originalTarget.getAttribute("id") + "\n");
}
let names = [
"bubbles",
"cancelable",
"detail",
"button",
"keyCode",
"isChar",
"shiftKey",
"altKey",
"ctrlKey",
"metaKey",
"clientX",
"clientY",
"screenX",
"screenY",
"layerX",
"layerY",
"isTrusted",
"timeStamp",
"currentTargetXPath",
"targetXPath",
"originalTargetXPath"
];
for (let i in names) {
if (names[i] in event)
this._append(names[i] + ": " + event[names[i]] + "\n");
}
this._append("-------------------------------------\n");
return this._asString();
}
};
var stringifier = new Stringifier();