Patch over slow debug log rendering

This commit is contained in:
Evan Hahn 2021-01-14 12:01:48 -06:00 committed by Scott Nonnenberg
parent d1355d5874
commit 7884f4033e
3 changed files with 135 additions and 29 deletions

View file

@ -30,7 +30,7 @@
<h1> {{ title }} </h1> <h1> {{ title }} </h1>
<p> {{ debugLogExplanation }}</p> <p> {{ debugLogExplanation }}</p>
</div> </div>
<textarea spellcheck='false' rows='5'></textarea> <textarea class='textarea' spellcheck='false' rows='5'></textarea>
<div class='buttons'> <div class='buttons'>
<button class='grey submit'>{{ submit }}</button> <button class='grey submit'>{{ submit }}</button>
</div> </div>

View file

@ -8,6 +8,22 @@
(function () { (function () {
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
// This enum-like object describes the load state of `DebugLogView`. It's designed to be
// unidirectional; `NotStarted` → `Started` → `LogsFetchedButNotInTextarea`, etc.
const LoadState = {
NotStarted: 0,
Started: 1,
LogsFetchedButNotInTextarea: 2,
PuttingLogsInTextarea: 3,
LogsInTextarea: 4,
};
Whisper.LoadingFullLogsToast = Whisper.ToastView.extend({
render_attributes() {
return { toastMessage: i18n('loading') };
},
});
Whisper.LinkedCopiedToast = Whisper.ToastView.extend({ Whisper.LinkedCopiedToast = Whisper.ToastView.extend({
render_attributes() { render_attributes() {
return { toastMessage: i18n('debugLogLinkCopied') }; return { toastMessage: i18n('debugLogLinkCopied') };
@ -36,19 +52,40 @@
Whisper.ToastView.show(Whisper.LinkedCopiedToast, document.body); Whisper.ToastView.show(Whisper.LinkedCopiedToast, document.body);
}, },
}); });
/**
* The bulk of the logic in this view involves grabbing the logs from disk and putting
* them in a `<textarea>`. The first part isn't instant but is reasonably fast; setting
* the textarea's `value` takes a long time.
*
* After loading the logs into memory, we only put a small number of lines into the
* textarea. If the user clicks or scrolls the textarea, we pull the full logs, which
* can cause the system to lock up for a bit.
*
* Ideally, we'd only show a sampling of the logs and allow the user to download and
* edit them in their own editor. This is mostly a stopgap solution.
*/
Whisper.DebugLogView = Whisper.View.extend({ Whisper.DebugLogView = Whisper.View.extend({
templateName: 'debug-log', templateName: 'debug-log',
className: 'debug-log modal', className: 'debug-log modal',
initialize() { initialize() {
this.render(); this.render();
this.$('textarea').val(i18n('loading'));
// eslint-disable-next-line more/no-then this.textarea = this.$('.textarea').get(0);
window.log.fetch().then(text => { if (!this.textarea) {
this.$('textarea').val(text); throw new Error('textarea not found');
}); }
this.textarea.setAttribute('readonly', '');
this.loadState = LoadState.NotStarted;
this.putFullLogsInTextareaPlease = false;
this.fetchLogs();
}, },
events: { events: {
'click .textarea': 'putFullLogsInTextarea',
'scroll .textarea': 'putFullLogsInTextarea',
'wheel .textarea': 'putFullLogsInTextarea',
'click .submit': 'submit', 'click .submit': 'submit',
'click .close': 'close', 'click .close': 'close',
}, },
@ -59,17 +96,86 @@
close: i18n('gotIt'), close: i18n('gotIt'),
debugLogExplanation: i18n('debugLogExplanation'), debugLogExplanation: i18n('debugLogExplanation'),
}, },
async fetchLogs() {
if (this.loadState !== LoadState.NotStarted) {
return;
}
this.loadState = LoadState.Started;
this.textarea.value = i18n('loading');
this.$('.submit').attr('disabled', 'disabled');
this.logText = await window.log.fetch();
this.loadState = LoadState.LogsFetchedButNotInTextarea;
// This number is somewhat arbitrary; we want to show enough that it's clear that
// we need to scroll, but not so many that things get slow.
const linesToShow = Math.ceil(Math.min(window.innerHeight, 2000) / 5);
this.textarea.value = this.logText
.split(/\n/g)
.slice(0, linesToShow)
.concat(['', i18n('loading')])
.join('\n');
this.$('.submit').removeAttr('disabled');
if (this.putFullLogsInTextareaPlease) {
this.putFullLogsInTextarea();
}
},
putFullLogsInTextarea() {
switch (this.loadState) {
case LoadState.NotStarted:
case LoadState.Started:
this.putFullLogsInTextareaPlease = true;
break;
case LoadState.LogsInTextarea:
case LoadState.PuttingLogsInTextarea:
break;
case LoadState.LogsFetchedButNotInTextarea:
if (!this.logText) {
throw new Error('Expected log text to be present');
}
this.loadState = LoadState.PuttingLogsInTextarea;
Whisper.ToastView.show(Whisper.LoadingFullLogsToast, document.body);
setTimeout(() => {
this.textarea.value = this.logText;
this.textarea.removeAttribute('readonly');
this.loadState = LoadState.LogsInTextarea;
}, 0);
break;
default:
// When we can, we should make this throw a `missingCaseError`.
break;
}
},
close() { close() {
window.closeDebugLog(); window.closeDebugLog();
}, },
async submit(e) { async submit(e) {
e.preventDefault(); e.preventDefault();
const text = this.$('textarea').val();
let text;
switch (this.loadState) {
case LoadState.NotStarted:
case LoadState.Started:
return;
case LoadState.LogsFetchedButNotInTextarea:
text = this.logText;
break;
case LoadState.LogsInTextarea:
text = this.textarea.value;
break;
default:
// When we can, we should make this throw a `missingCaseError`.
return;
}
if (text.length === 0) { if (text.length === 0) {
return; return;
} }
this.$('.buttons, textarea').remove(); this.$('.buttons, .textarea').remove();
this.$('.result').addClass('loading'); this.$('.result').addClass('loading');
try { try {

View file

@ -328,35 +328,35 @@
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('textarea').val(i18n('loading'));", "line": " this.textarea = this.$('.textarea').get(0);",
"lineNumber": 44, "lineNumber": 74,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-05-01T17:11:39.527Z", "updated": "2021-01-13T23:20:15.717Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Needed to get a reference to a textarea."
}, },
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('textarea').val(text);", "line": " this.$('.submit').attr('disabled', 'disabled');",
"lineNumber": 48, "lineNumber": 106,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-05-01T17:11:39.527Z", "updated": "2021-01-13T23:20:15.717Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Only changes attributes; doesn't write other data to the DOM. Is also trusted input."
}, },
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " const text = this.$('textarea').val();", "line": " this.$('.submit').removeAttr('disabled');",
"lineNumber": 67, "lineNumber": 120,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-05-01T17:11:39.527Z", "updated": "2021-01-13T23:20:15.717Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Only changes attributes; doesn't write other data to the DOM. Is also trusted input."
}, },
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('.buttons, textarea').remove();", "line": " this.$('.buttons, .textarea').remove();",
"lineNumber": 72, "lineNumber": 178,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-05-01T17:11:39.527Z", "updated": "2020-05-01T17:11:39.527Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -365,7 +365,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('.result').addClass('loading');", "line": " this.$('.result').addClass('loading');",
"lineNumber": 73, "lineNumber": 179,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-09-11T17:24:56.124Z", "updated": "2020-09-11T17:24:56.124Z",
"reasonDetail": "Static selector argument" "reasonDetail": "Static selector argument"
@ -374,7 +374,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " el: this.$('.result'),", "line": " el: this.$('.result'),",
"lineNumber": 82, "lineNumber": 188,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-05-01T17:11:39.527Z", "updated": "2020-05-01T17:11:39.527Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -383,7 +383,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('.loading').removeClass('loading');", "line": " this.$('.loading').removeClass('loading');",
"lineNumber": 84, "lineNumber": 190,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-05-01T17:11:39.527Z", "updated": "2020-05-01T17:11:39.527Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -392,7 +392,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('.link').focus().select();", "line": " this.$('.link').focus().select();",
"lineNumber": 86, "lineNumber": 192,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-11-18T03:39:29.033Z", "updated": "2020-11-18T03:39:29.033Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -401,7 +401,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('.loading').removeClass('loading');", "line": " this.$('.loading').removeClass('loading');",
"lineNumber": 92, "lineNumber": 198,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-11-18T03:39:29.033Z", "updated": "2020-11-18T03:39:29.033Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -410,7 +410,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/debug_log_view.js", "path": "js/views/debug_log_view.js",
"line": " this.$('.result').text(i18n('debugLogError'));", "line": " this.$('.result').text(i18n('debugLogError'));",
"lineNumber": 93, "lineNumber": 199,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-11-18T03:39:29.033Z", "updated": "2020-11-18T03:39:29.033Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"