Patch over slow debug log rendering
This commit is contained in:
parent
d1355d5874
commit
7884f4033e
3 changed files with 135 additions and 29 deletions
|
@ -30,7 +30,7 @@
|
|||
<h1> {{ title }} </h1>
|
||||
<p> {{ debugLogExplanation }}</p>
|
||||
</div>
|
||||
<textarea spellcheck='false' rows='5'></textarea>
|
||||
<textarea class='textarea' spellcheck='false' rows='5'></textarea>
|
||||
<div class='buttons'>
|
||||
<button class='grey submit'>{{ submit }}</button>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,22 @@
|
|||
(function () {
|
||||
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({
|
||||
render_attributes() {
|
||||
return { toastMessage: i18n('debugLogLinkCopied') };
|
||||
|
@ -36,19 +52,40 @@
|
|||
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({
|
||||
templateName: 'debug-log',
|
||||
className: 'debug-log modal',
|
||||
initialize() {
|
||||
this.render();
|
||||
this.$('textarea').val(i18n('loading'));
|
||||
|
||||
// eslint-disable-next-line more/no-then
|
||||
window.log.fetch().then(text => {
|
||||
this.$('textarea').val(text);
|
||||
});
|
||||
this.textarea = this.$('.textarea').get(0);
|
||||
if (!this.textarea) {
|
||||
throw new Error('textarea not found');
|
||||
}
|
||||
this.textarea.setAttribute('readonly', '');
|
||||
|
||||
this.loadState = LoadState.NotStarted;
|
||||
this.putFullLogsInTextareaPlease = false;
|
||||
|
||||
this.fetchLogs();
|
||||
},
|
||||
events: {
|
||||
'click .textarea': 'putFullLogsInTextarea',
|
||||
'scroll .textarea': 'putFullLogsInTextarea',
|
||||
'wheel .textarea': 'putFullLogsInTextarea',
|
||||
'click .submit': 'submit',
|
||||
'click .close': 'close',
|
||||
},
|
||||
|
@ -59,17 +96,86 @@
|
|||
close: i18n('gotIt'),
|
||||
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() {
|
||||
window.closeDebugLog();
|
||||
},
|
||||
async submit(e) {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$('.buttons, textarea').remove();
|
||||
this.$('.buttons, .textarea').remove();
|
||||
this.$('.result').addClass('loading');
|
||||
|
||||
try {
|
||||
|
|
|
@ -328,35 +328,35 @@
|
|||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('textarea').val(i18n('loading'));",
|
||||
"lineNumber": 44,
|
||||
"line": " this.textarea = this.$('.textarea').get(0);",
|
||||
"lineNumber": 74,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-01T17:11:39.527Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
"updated": "2021-01-13T23:20:15.717Z",
|
||||
"reasonDetail": "Needed to get a reference to a textarea."
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('textarea').val(text);",
|
||||
"lineNumber": 48,
|
||||
"line": " this.$('.submit').attr('disabled', 'disabled');",
|
||||
"lineNumber": 106,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-01T17:11:39.527Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
"updated": "2021-01-13T23:20:15.717Z",
|
||||
"reasonDetail": "Only changes attributes; doesn't write other data to the DOM. Is also trusted input."
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " const text = this.$('textarea').val();",
|
||||
"lineNumber": 67,
|
||||
"line": " this.$('.submit').removeAttr('disabled');",
|
||||
"lineNumber": 120,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-01T17:11:39.527Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
"updated": "2021-01-13T23:20:15.717Z",
|
||||
"reasonDetail": "Only changes attributes; doesn't write other data to the DOM. Is also trusted input."
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('.buttons, textarea').remove();",
|
||||
"lineNumber": 72,
|
||||
"line": " this.$('.buttons, .textarea').remove();",
|
||||
"lineNumber": 178,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-01T17:11:39.527Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
|
@ -365,7 +365,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('.result').addClass('loading');",
|
||||
"lineNumber": 73,
|
||||
"lineNumber": 179,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-11T17:24:56.124Z",
|
||||
"reasonDetail": "Static selector argument"
|
||||
|
@ -374,7 +374,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " el: this.$('.result'),",
|
||||
"lineNumber": 82,
|
||||
"lineNumber": 188,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-01T17:11:39.527Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
|
@ -383,7 +383,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('.loading').removeClass('loading');",
|
||||
"lineNumber": 84,
|
||||
"lineNumber": 190,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-01T17:11:39.527Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
|
@ -392,7 +392,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('.link').focus().select();",
|
||||
"lineNumber": 86,
|
||||
"lineNumber": 192,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-11-18T03:39:29.033Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
|
@ -401,7 +401,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('.loading').removeClass('loading');",
|
||||
"lineNumber": 92,
|
||||
"lineNumber": 198,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-11-18T03:39:29.033Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
|
@ -410,7 +410,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/debug_log_view.js",
|
||||
"line": " this.$('.result').text(i18n('debugLogError'));",
|
||||
"lineNumber": 93,
|
||||
"lineNumber": 199,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-11-18T03:39:29.033Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
|
@ -15223,4 +15223,4 @@
|
|||
"updated": "2021-01-08T15:46:32.143Z",
|
||||
"reasonDetail": "Doesn't manipulate the DOM. This is just a function."
|
||||
}
|
||||
]
|
||||
]
|
Loading…
Reference in a new issue