Skip to content

Commit

Permalink
Add HTML-based console viewer for easier real-time debug output
Browse files Browse the repository at this point in the history
Since 1) debug output logging via the prefs isn't necessarily possible
for startup errors in Standalone, 2) real-time output is prohibitively
slow and has a miniscule scrollback buffer on Windows unless you use a
Cygwin or Git terminal, and 3) copying/pasting/emailing was annoying
anyway, make -ZoteroDebug open a popup window that shows errors and
debug output and allows submitting straight to the server with a Debug
ID.

This should replace the existing debug output viewer as well, but that's
less of a priority.

-ZoteroDebugText or the debug.log pref can still be used to dump to the
terminal.
  • Loading branch information
dstillman committed Jan 14, 2017
1 parent 9c0befc commit f44264c
Show file tree
Hide file tree
Showing 4 changed files with 414 additions and 29 deletions.
139 changes: 139 additions & 0 deletions chrome/content/zotero/debugViewer.html
@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Debug Output</title>
<script src="include.js"></script>
<script src="debugViewer.js"></script>

<style>
body {
margin: 0;
}

header {
position: fixed;
top: 0;
background: lightgrey;
display: flex;
align-items: center;
width: calc(100% - 20px);
height: 18px;
padding: 10px;
margin-bottom: 10px;
font-family: sans-serif;
font-size: 11pt;
}

header > * {
margin-right: 10px;
}

progress {
width: 125px;
}

#debug-id {
font-weight: bold;
}

#submit-result {
line-height: 1.25em;
}

#submit-result-copy-id {
cursor: pointer;
padding-left: 2px;
}

#submit-error {
font-weight: bold;
color: red;
}

#content {
margin-top: 38px;
padding: 10px 9px;
font-family: Monaco, Consolas, Inconsolata, monospace;
font-size: 9pt;
}

#errors {
padding-bottom: 12px;
border-bottom: 1px lightgray solid;
white-space: pre-wrap;
}



/*
CSS tooltip, adapted from http://stackoverflow.com/a/25836471
*/
[data-tooltip] {
display: inline-block;
position: relative;
cursor: pointer;
padding: 2px;
}
[data-tooltip]:before {
content: attr(data-tooltip);
display: none;
position: absolute;
background: #000;
color: #fff;
padding: 4px 8px;
font-size: 12px;
font-family: sans-serif;
line-height: 1.4;
text-align: center;
border-radius: 4px;

left: 50%;
transform: translateX(-50%);

top: 100%;
margin-top: 6px;

white-space: nowrap;
}
[data-tooltip]:after {
content: '';
display: none;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;

left: 50%;
margin-left: -6px;

top: 100%;
border-width: 0 6px 6px;
border-bottom-color: #000;
}
/* Show the tooltip when hovering */
[data-tooltip]:hover:before,
[data-tooltip]:hover:after {
display: block;
z-index: 50;
}
</style>
</head>
<body>
<header>
<button id="submit-button" onclick="submit(this)" disabled>Submit…</button>
<button id="clear-button" onclick="clearOutput(this)" disabled>Clear</button>
<progress id="submit-progress" hidden></progress>
<p id="submit-result" hidden>
Submitted with Debug ID <span id="debug-id"></span>
<span id="submit-result-copy-id" onclick="copyIDToClipboard(this)">&#128203;</span>
</p>
<p id="submit-error" hidden></p>
</header>
<div id="content">
<div id="errors"></div>
<div id="output"></div>
</div>
</body>
</html>
189 changes: 189 additions & 0 deletions chrome/content/zotero/debugViewer.js
@@ -0,0 +1,189 @@
"use strict";

var interval = 1000;
var intervalID;
var stopping = false;

function start() {
updateErrors().then(function () {
if (stopping) return;

addInitialOutput();
Zotero.Debug.addConsoleViewerListener(addLine)
intervalID = setInterval(() => updateErrors(), interval);
});
}

function stop() {
stopping = true;
if (intervalID) {
clearInterval(intervalID);
intervalID = null;
}
Zotero.Debug.removeConsoleViewerListener()
}

function updateErrors() {
return Zotero.getSystemInfo()
.then(function (sysInfo) {
if (stopping) return;

var errors = Zotero.getErrors(true);
var errorStr = errors.length ? errors.join('\n\n') + '\n\n' : '';

var scroll = atPageBottom();

document.getElementById('errors').textContent = errorStr + sysInfo;

// TODO: This doesn't seem to work for some reason -- when errors are logged, it doesn't stay
// at the bottom
if (scroll) {
scrollToPageBottom();
}
});
}

function addInitialOutput() {
Zotero.Debug.getConsoleViewerOutput().forEach(function (line) {
addLine(line);
});
}

function addLine(line) {
var scroll = atPageBottom()

var p = document.createElement('p');
p.textContent = line;
var output = document.getElementById('output');
output.appendChild(p);

// If scrolled to the bottom of the page, stay there
if (scroll) {
scrollToPageBottom();
}

document.getElementById('submit-button').removeAttribute('disabled');
document.getElementById('clear-button').removeAttribute('disabled');
}

function atPageBottom() {
return (window.innerHeight + window.scrollY) >= document.body.offsetHeight - 100;
}

function scrollToPageBottom() {
window.scrollTo(0, document.body.scrollHeight);
}

function submit(button) {
button.setAttribute('disabled', '');
clearSubmitStatus();

Components.utils.import("resource://zotero/config.js");
var url = ZOTERO_CONFIG.REPOSITORY_URL + "report?debug=1";
var output = document.getElementById('errors').textContent
+ "\n\n" + "=========================================================\n\n"
+ Array.from(document.getElementById('output').childNodes).map(p => p.textContent).join("\n\n");
var pm = document.getElementById('submit-progress');
pm.removeAttribute('hidden');

Zotero.HTTP.request(
"POST",
url,
{
compressBody: true,
body: output,
logBodyLength: 30,
timeout: 30000,
// Update progress meter
requestObserver: function (req) {
req.channel.notificationCallbacks = {
onProgress: function (request, context, progress, progressMax) {
if (!pm.value || progress > pm.value) {
pm.value = progress;
}
if (!pm.max || progressMax > pm.max) {
pm.max = progressMax;
}
},

// nsIInterfaceRequestor
getInterface: function (iid) {
try {
return this.QueryInterface(iid);
}
catch (e) {
throw Components.results.NS_NOINTERFACE;
}
},

QueryInterface: function(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
iid.equals(Components.interfaces.nsIProgressEventSink)) {
return this;
}
throw Components.results.NS_NOINTERFACE;
},

}
}
}
)
.then(function (xmlhttp) {
var reported = xmlhttp.responseXML.getElementsByTagName('reported');
if (reported.length != 1) {
showSubmitError(e);
return false;
}

showSubmitResult(reported[0].getAttribute('reportID'));
})
.catch(function (e) {
showSubmitError(e);
return false;
})
.finally(function () {
pm.setAttribute('hidden', '');
button.removeAttribute('disabled');
});
}

function showSubmitResult(id) {
var elem = document.getElementById('submit-result');
elem.removeAttribute('hidden');
document.getElementById('debug-id').textContent = "D" + id;
var copyID = document.getElementById('submit-result-copy-id');
copyID.style.visibility = 'visible';
copyID.setAttribute('data-tooltip', 'Copy ID to Clipboard');
}

function copyIDToClipboard(elem) {
var id = document.getElementById('debug-id').textContent;
Components.classes["@mozilla.org/widget/clipboardhelper;1"]
.getService(Components.interfaces.nsIClipboardHelper)
.copyString(id);
elem.setAttribute('data-tooltip', 'Copied');
setTimeout(() => elem.style.visibility = 'hidden', 750);
}

function showSubmitError(e) {
var elem = document.getElementById('submit-error');
elem.removeAttribute('hidden');
elem.textContent = "Error submitting output";
Components.utils.reportError(e);
Zotero.debug(e, 1);
}

function clearSubmitStatus() {
document.getElementById('submit-result').setAttribute('hidden', '');
document.getElementById('submit-error').setAttribute('hidden', '');
}

function clearOutput(button) {
button.setAttribute('disabled', '');
document.getElementById('output').textContent = '';
clearSubmitStatus();
}

window.addEventListener('load', start);
window.addEventListener("unload", stop);

0 comments on commit f44264c

Please sign in to comment.