zotero/chrome/content/zotero/xpcom/debug.js
Dan Stillman 730e86d661 fx-compat: Restore tests and test using actual Zotero executable
The test runner now uses the Zotero executable from
`zotero-standalone-build/staging` rather than the Firefox from
`zotero-standalone-build/xulrunner`. Along with testing the actual
program, this restores visible UI updates during tests, which should
make debugging various things easier. We can also now remove anything
related to Zotero being an extension.

Many tests are still broken, but this at least lets us start running
them.
2022-06-17 20:24:43 -04:00

327 lines
8.5 KiB
JavaScript

/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
Zotero.Debug = new function () {
var _console, _stackTrace, _store, _level, _lastTime, _output = [];
var _slowTime = false;
var _colorOutput = false;
var _consoleViewer = false;
var _consoleViewerQueue = [];
var _consoleViewerListener;
var _listeners = [];
/**
* Initialize debug logging
*
* Debug logging can be set in several different ways:
*
* - via the debug.log pref in the client or connector
* - by enabling debug output logging from the Help menu
* - by passing -ZoteroDebug or -ZoteroDebugText on the command line
*
* In the client, debug.log and -ZoteroDebugText enable logging via the terminal, while -ZoteroDebug
* enables logging via an in-app HTML-based window.
*
* @param {Integer} [forceDebugLog = 0] - Force output even if pref disabled
* 2: window (-ZoteroDebug)
* 1: text console (-ZoteroDebugText)
* 0: disabled
*/
this.init = function (forceDebugLog = 0) {
_console = Zotero.Prefs.get('debug.log') || forceDebugLog == 1;
_consoleViewer = forceDebugLog == 2;
// When logging to the text console from the client on Mac/Linux, colorize output
if (_console && Zotero.isFx && !Zotero.isBookmarklet) {
_colorOutput = true;
// Time threshold in ms above which intervals should be colored red in terminal output
_slowTime = Zotero.Prefs.get('debug.log.slowTime');
}
_store = Zotero.Prefs.get('debug.store');
if (_store) {
Zotero.Prefs.set('debug.store', false);
}
_level = Zotero.Prefs.get('debug.level');
_stackTrace = Zotero.Prefs.get('debug.stackTrace');
this.storing = _store;
this.updateEnabled();
if (Zotero.isStandalone) {
// Enable dump() from window (non-XPCOM) scopes when terminal or viewer logging is enabled.
// (These will always go to the terminal, even in viewer mode.)
Zotero.Prefs.set('browser.dom.window.dump.enabled', _console || _consoleViewer || Zotero.test, true);
if (_consoleViewer) {
setTimeout(function () {
Zotero.openInViewer("chrome://zotero/content/debugViewer.html");
}, 1000);
}
}
}
this.log = function (message, level, maxDepth, stack) {
if (!this.enabled) {
return;
}
if (typeof message != 'string') {
message = Zotero.Utilities.varDump(message, 0, maxDepth);
}
if (!level) {
level = 3;
}
// If level above debug.level value, don't display
if (level > _level) {
return;
}
var deltaStr = '';
var deltaStrStore = '';
var delta = 0;
var d = new Date();
if (_lastTime) {
delta = d - _lastTime;
}
_lastTime = d;
var slowPrefix = "";
var slowSuffix = "";
if (_slowTime && delta > _slowTime) {
slowPrefix = "\x1b[31;40m";
slowSuffix = "\x1b[0m";
}
delta = ("" + delta).padStart(7, "0")
deltaStr = "(" + slowPrefix + "+" + delta + slowSuffix + ")";
if (_store) {
deltaStrStore = "(+" + delta + ")";
}
if (stack === true) {
// Display stack starting from where this was called
stack = Components.stack.caller;
} else if (stack >= 0) {
let i = stack;
stack = Components.stack.caller;
while(stack && i--) {
stack = stack.caller;
}
} else if (_stackTrace) {
// Stack trace enabled globally
stack = Components.stack.caller;
} else {
stack = undefined;
}
if (stack) {
message += '\n' + this.stackToString(stack);
}
if (_console || _consoleViewer || _listeners.length) {
var output = '(' + level + ')' + deltaStr + ': ' + message;
if (Zotero.isFx && !Zotero.isBookmarklet) {
// Text console
if (_console) {
dump("zotero" + output + "\n\n");
}
// Remove ANSI color codes for the viewer and listeners. We could replace this with
// HTML for the viewer, but it's probably unnecessarily distracting/alarming to show
// the red. Devs who care about times should just use a terminal.
if (slowPrefix) {
output = output.replace(slowPrefix, '').replace(slowSuffix, '');
}
// Console window
if (_consoleViewer) {
// If there's a listener, pass line immediately
if (_consoleViewerListener) {
_consoleViewerListener(output);
}
// Otherwise add to queue
else {
_consoleViewerQueue.push(output);
}
}
// Other listeners
if (_listeners.length) {
for (let listener of _listeners) {
listener(output);
}
}
} else if(window.console) {
window.console.log(output);
}
}
if (_store) {
if (Math.random() < 1/1000) {
// Remove initial lines if over limit
var overage = this.count() - Zotero.Prefs.get('debug.store.limit');
if (overage > 0) {
_output.splice(0, Math.abs(overage));
}
}
_output.push('(' + level + ')' + deltaStrStore + ': ' + message);
}
}
this.get = Zotero.Promise.method(function(maxChars, maxLineLength) {
var output = _output;
var total = output.length;
if (total == 0) {
return "";
}
if (maxLineLength) {
for (var i=0, len=output.length; i<len; i++) {
if (output[i].length > maxLineLength) {
output[i] = Zotero.Utilities.ellipsize(output[i], maxLineLength, false, true);
}
}
}
output = output.join('\n\n');
if (maxChars) {
output = output.substr(maxChars * -1);
// Cut at two newlines
let matches = output.match(/^[\n]*\n\n/);
if (matches) {
output = output.substr(matches[0].length);
}
}
return Zotero.getSystemInfo().then(function(sysInfo) {
if (Zotero.isConnector) {
return Zotero.Errors.getErrors().then(function(errors) {
return errors.join('\n\n') +
"\n\n" + sysInfo + "\n\n" +
"=========================================================\n\n" +
output;
});
}
else {
return Zotero.getErrors(true).join('\n\n') +
"\n\n" + sysInfo + "\n\n" +
"=========================================================\n\n" +
output;
}
});
});
this.addListener = function (listener) {
this.enabled = true;
_listeners.push(listener);
};
this.removeListener = function (listener) {
var pos = _listeners.indexOf(listener);
if (pos != -1) {
_listeners.splice(pos, 1);
}
this.updateEnabled();
};
this.getConsoleViewerOutput = function () {
var queue = _output.concat(_consoleViewerQueue);
_consoleViewerQueue = [];
return queue;
}
this.addConsoleViewerListener = function (listener) {
this.enabled = _consoleViewer = true;
_consoleViewerListener = listener;
};
this.removeConsoleViewerListener = function () {
_consoleViewerListener = null;
// At least for now, stop logging once console viewer is closed
_consoleViewer = false;
this.updateEnabled();
};
this.setStore = function (enable) {
if (enable) {
this.clear();
}
_store = enable;
this.updateEnabled();
this.storing = _store;
}
this.updateEnabled = function () {
this.enabled = _console || _consoleViewer || _store || _listeners.length;
};
this.count = function () {
return _output.length;
}
this.clear = function () {
_output = [];
}
/**
* Format a stack trace for output in the same way that Error.stack does
* @param {Components.stack} stack
* @param {Integer} [lines=5] Number of lines to format
*/
this.stackToString = function (stack, lines) {
if (!lines) lines = 5;
var str = '';
while(stack && lines--) {
str += '\n ' + (stack.name || '') + '@' + stack.filename
+ ':' + stack.lineNumber;
stack = stack.caller;
}
return this.filterStack(str).substr(1);
};
/**
* Strip Bluebird lines from a stack trace
*
* @param {String} stack
*/
this.filterStack = function (stack) {
return stack.split(/\n/).filter(line => line.indexOf('zotero/bluebird') == -1).join('\n');
}
}