Context isolation for the debug log window
This commit is contained in:
parent
0f9608d9a3
commit
fa66ddde0f
28 changed files with 477 additions and 686 deletions
|
@ -470,25 +470,33 @@
|
||||||
"description": "Shown in conversation banner when more than one group member's safety number has changed, but they were previously verified."
|
"description": "Shown in conversation banner when more than one group member's safety number has changed, but they were previously verified."
|
||||||
},
|
},
|
||||||
"debugLogExplanation": {
|
"debugLogExplanation": {
|
||||||
"message": "This log will be posted publicly online for contributors to view. You may examine and edit it before submitting."
|
"message": "This log will be posted publicly online for contributors to view. You may download the full log before submitting."
|
||||||
},
|
},
|
||||||
"debugLogError": {
|
"debugLogError": {
|
||||||
"message": "Something went wrong with the upload! Please consider manually adding your log to the bug you file."
|
"message": "Something went wrong with the upload! Please email support@signal.org and attach your log as a text file."
|
||||||
|
},
|
||||||
|
"debugLogSuccess": {
|
||||||
|
"message": "Debug log submitted",
|
||||||
|
"description": "Title of the success page for submitting a debug log"
|
||||||
|
},
|
||||||
|
"debugLogSuccessNextSteps": {
|
||||||
|
"message": "Debug log uploaded. When you contact support, copy the link below and attach it along with a description of the problem you saw and steps to reproduce it.",
|
||||||
|
"description": "Explanation of next steps to take when submitting debug log"
|
||||||
},
|
},
|
||||||
"debugLogCopy": {
|
"debugLogCopy": {
|
||||||
"message": "Copy",
|
"message": "Copy Link",
|
||||||
"description": "Shown as the text for the copy button on the debug log screen"
|
"description": "Shown as the text for the copy button on the debug log screen"
|
||||||
},
|
},
|
||||||
"debugLogCopyAlt": {
|
"debugLogSave": {
|
||||||
"message": "Copy link to your clipboard",
|
"message": "Download",
|
||||||
"description": "Shown as the alt text for the copy button on the debug log screen"
|
"description": "Shown as the text for the download button on the debug log screen"
|
||||||
},
|
},
|
||||||
"debugLogLinkCopied": {
|
"debugLogLinkCopied": {
|
||||||
"message": "Link Copied to Your Clipboard",
|
"message": "Link Copied to Your Clipboard",
|
||||||
"description": "Shown in a toast to let the user know that the link to the debug log has been copied to their clipboard"
|
"description": "Shown in a toast to let the user know that the link to the debug log has been copied to their clipboard"
|
||||||
},
|
},
|
||||||
"reportIssue": {
|
"reportIssue": {
|
||||||
"message": "Report an issue",
|
"message": "Contact Support",
|
||||||
"description": "Link to open the issue tracker"
|
"description": "Link to open the issue tracker"
|
||||||
},
|
},
|
||||||
"gotIt": {
|
"gotIt": {
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'none';
|
content="default-src 'none';
|
||||||
|
frame-src 'none';
|
||||||
|
form-action 'none';
|
||||||
font-src 'self';
|
font-src 'self';
|
||||||
img-src 'self' blob: data:;
|
img-src 'self' blob: data:;
|
||||||
media-src 'self' blob:;
|
media-src 'self' blob:;
|
||||||
|
|
21
app/main.ts
21
app/main.ts
|
@ -4,7 +4,7 @@
|
||||||
import { join, normalize } from 'path';
|
import { join, normalize } from 'path';
|
||||||
import { pathToFileURL } from 'url';
|
import { pathToFileURL } from 'url';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { chmod, realpath } from 'fs-extra';
|
import { chmod, realpath, writeFile } from 'fs-extra';
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
|
|
||||||
import pify from 'pify';
|
import pify from 'pify';
|
||||||
|
@ -1001,7 +1001,6 @@ function showSettingsWindow() {
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
backgroundColor: '#3a76f0',
|
backgroundColor: '#3a76f0',
|
||||||
show: false,
|
show: false,
|
||||||
modal: false,
|
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
|
@ -1135,13 +1134,12 @@ async function showDebugLogWindow() {
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
backgroundColor: '#3a76f0',
|
backgroundColor: '#3a76f0',
|
||||||
show: false,
|
show: false,
|
||||||
modal: true,
|
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
contextIsolation: false,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../debug_log_preload.js'),
|
preload: join(__dirname, '../ts/windows/debuglog/preload.js'),
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
},
|
},
|
||||||
parent: mainWindow,
|
parent: mainWindow,
|
||||||
|
@ -1156,13 +1154,11 @@ async function showDebugLogWindow() {
|
||||||
);
|
);
|
||||||
|
|
||||||
debugLogWindow.on('closed', () => {
|
debugLogWindow.on('closed', () => {
|
||||||
removeDarkOverlay();
|
|
||||||
debugLogWindow = undefined;
|
debugLogWindow = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
debugLogWindow.once('ready-to-show', () => {
|
debugLogWindow.once('ready-to-show', () => {
|
||||||
if (debugLogWindow) {
|
if (debugLogWindow) {
|
||||||
addDarkOverlay();
|
|
||||||
debugLogWindow.show();
|
debugLogWindow.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1834,6 +1830,17 @@ ipc.on('close-debug-log', () => {
|
||||||
debugLogWindow.close();
|
debugLogWindow.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ipc.on(
|
||||||
|
'show-debug-log-save-dialog',
|
||||||
|
async (_event: Electron.Event, logText: string) => {
|
||||||
|
const { filePath } = await dialog.showSaveDialog({
|
||||||
|
defaultPath: 'debuglog.txt',
|
||||||
|
});
|
||||||
|
if (filePath) {
|
||||||
|
await writeFile(filePath, logText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Permissions Popup-related IPC calls
|
// Permissions Popup-related IPC calls
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,9 @@
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'none';
|
content="default-src 'none';
|
||||||
child-src 'self';
|
|
||||||
connect-src 'self' https: wss:;
|
|
||||||
font-src 'self';
|
|
||||||
form-action 'self';
|
|
||||||
frame-src 'none';
|
frame-src 'none';
|
||||||
|
form-action 'none';
|
||||||
|
font-src 'self';
|
||||||
img-src 'self' blob: data:;
|
img-src 'self' blob: data:;
|
||||||
media-src 'self' blob:;
|
media-src 'self' blob:;
|
||||||
object-src 'none';
|
object-src 'none';
|
||||||
|
@ -23,40 +21,13 @@
|
||||||
type="text/css"
|
type="text/css"
|
||||||
/>
|
/>
|
||||||
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||||
<style></style>
|
|
||||||
</head>
|
</head>
|
||||||
<body class="debug-log-window"></body>
|
<body>
|
||||||
<script type="text/x-tmpl-mustache" id="debug-log">
|
<div id="app"></div>
|
||||||
<div class='content'>
|
<script
|
||||||
<div>
|
type="application/javascript"
|
||||||
<a class='x close' alt='close debug log' href='#'></a>
|
src="ts/windows/applyTheme.js"
|
||||||
<h1> {{ title }} </h1>
|
></script>
|
||||||
<p> {{ debugLogExplanation }}</p>
|
<script type="application/javascript" src="ts/windows/init.js"></script>
|
||||||
</div>
|
</body>
|
||||||
<textarea class='textarea' spellcheck='false' rows='5'></textarea>
|
|
||||||
<div class='buttons'>
|
|
||||||
<button class='grey submit'>{{ submit }}</button>
|
|
||||||
</div>
|
|
||||||
<div class='result'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
<script type="text/x-tmpl-mustache" id="debug-log-link">
|
|
||||||
<div class='input-group clearfix'>
|
|
||||||
<input type='text' class='link' readonly value='{{ url }}' />
|
|
||||||
<a class='copy' alt='{{ debugLogCopyAlt }}' target='_blank' href='{{ url }}'>{{ debugLogCopy }}</a>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
<a class='report-link' target='_blank'
|
|
||||||
href='https://support.signal.org/hc/requests/new'>
|
|
||||||
{{ reportIssue }}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</script>
|
|
||||||
<script type="text/x-tmpl-mustache" id="toast">
|
|
||||||
{{ toastMessage }}
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="js/components.js"></script>
|
|
||||||
<script type="text/javascript" src="ts/backboneJquery.js"></script>
|
|
||||||
<script type="text/javascript" src="js/debug_log_start.js"></script>
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
// Copyright 2018-2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global window */
|
|
||||||
|
|
||||||
const { ipcRenderer } = require('electron');
|
|
||||||
const url = require('url');
|
|
||||||
|
|
||||||
// It is important to call this as early as possible
|
|
||||||
require('./ts/windows/context');
|
|
||||||
|
|
||||||
const { setupI18n } = require('./ts/util/setupI18n');
|
|
||||||
const { createSetting } = require('./ts/util/preload');
|
|
||||||
const {
|
|
||||||
getEnvironment,
|
|
||||||
setEnvironment,
|
|
||||||
parseEnvironment,
|
|
||||||
} = require('./ts/environment');
|
|
||||||
|
|
||||||
const config = url.parse(window.location.toString(), true).query;
|
|
||||||
const { locale } = config;
|
|
||||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
|
||||||
setEnvironment(parseEnvironment(config.environment));
|
|
||||||
|
|
||||||
window.getVersion = () => config.version;
|
|
||||||
window.themeSetting = createSetting('themeSetting', { setter: false });
|
|
||||||
window.i18n = setupI18n(locale, localeMessages);
|
|
||||||
|
|
||||||
// got.js appears to need this to successfully submit debug logs to the cloud
|
|
||||||
window.nodeSetImmediate = setImmediate;
|
|
||||||
|
|
||||||
window.getNodeVersion = () => config.node_version;
|
|
||||||
window.getEnvironment = getEnvironment;
|
|
||||||
|
|
||||||
window.Backbone = require('backbone');
|
|
||||||
require('./ts/backbone/views/whisper_view');
|
|
||||||
require('./ts/logging/set_up_renderer_logging').initialize();
|
|
||||||
require('./ts/views/debug_log_view');
|
|
||||||
|
|
||||||
window.closeDebugLog = () => ipcRenderer.send('close-debug-log');
|
|
||||||
window.Backbone = require('backbone');
|
|
33
index.html
33
index.html
|
@ -1,33 +0,0 @@
|
||||||
<!-- Copyright 2014-2021 Signal Messenger, LLC -->
|
|
||||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html class="no-js" lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta
|
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"
|
|
||||||
name="viewport"
|
|
||||||
/>
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<title>Signal</title>
|
|
||||||
<meta name="description" content="" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link href="/stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
|
||||||
<script type="text/javascript" src="js/chromium.js"></script>
|
|
||||||
</head>
|
|
||||||
<body id="signal-container" class="signal index">
|
|
||||||
<div class="app-loading-screen">
|
|
||||||
<div class="content">
|
|
||||||
<div class="module-splash-screen__logo module-img--150"></div>
|
|
||||||
<div class="container">
|
|
||||||
<span class="dot"></span>
|
|
||||||
<span class="dot"></span>
|
|
||||||
<span class="dot"></span>
|
|
||||||
</div>
|
|
||||||
<div class="message"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript" src="js/index.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,37 +0,0 @@
|
||||||
// Copyright 2018-2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global $: false */
|
|
||||||
|
|
||||||
$(document).on('keydown', e => {
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
window.closeDebugLog();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const $body = $(document.body);
|
|
||||||
|
|
||||||
async function applyTheme() {
|
|
||||||
const theme = await window.themeSetting.getValue();
|
|
||||||
document.body.classList.remove('light-theme');
|
|
||||||
document.body.classList.remove('dark-theme');
|
|
||||||
document.body.classList.add(
|
|
||||||
`${
|
|
||||||
theme === 'system'
|
|
||||||
? window.SignalContext.nativeThemeListener.getSystemTheme()
|
|
||||||
: theme
|
|
||||||
}-theme`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
applyTheme();
|
|
||||||
|
|
||||||
window.SignalContext.nativeThemeListener.subscribe(() => {
|
|
||||||
applyTheme();
|
|
||||||
});
|
|
||||||
|
|
||||||
// got.js appears to need this to successfully submit debug logs to the cloud
|
|
||||||
window.setImmediate = window.nodeSetImmediate;
|
|
||||||
|
|
||||||
window.view = new window.Whisper.DebugLogView();
|
|
||||||
window.view.$el.appendTo($body);
|
|
12
loading.html
12
loading.html
|
@ -4,6 +4,18 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'none';
|
||||||
|
frame-src 'none';
|
||||||
|
form-action 'none';
|
||||||
|
font-src 'self';
|
||||||
|
img-src 'self' blob: data:;
|
||||||
|
media-src 'self' blob:;
|
||||||
|
object-src 'none';
|
||||||
|
script-src 'self';
|
||||||
|
style-src 'self' 'unsafe-inline';"
|
||||||
|
/>
|
||||||
<link
|
<link
|
||||||
href="node_modules/sanitize.css/sanitize.css"
|
href="node_modules/sanitize.css/sanitize.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
|
|
@ -419,8 +419,6 @@
|
||||||
"app/*",
|
"app/*",
|
||||||
"preload.bundle.js",
|
"preload.bundle.js",
|
||||||
"preload_utils.js",
|
"preload_utils.js",
|
||||||
"permissions_popup_preload.js",
|
|
||||||
"debug_log_preload.js",
|
|
||||||
"main.js",
|
"main.js",
|
||||||
"images/**",
|
"images/**",
|
||||||
"fonts/**",
|
"fonts/**",
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'none';
|
content="default-src 'none';
|
||||||
|
frame-src 'none';
|
||||||
|
form-action 'none';
|
||||||
font-src 'self';
|
font-src 'self';
|
||||||
img-src 'self' blob: data:;
|
img-src 'self' blob: data:;
|
||||||
media-src 'self' blob:;
|
media-src 'self' blob:;
|
||||||
|
|
|
@ -76,7 +76,6 @@ try {
|
||||||
}
|
}
|
||||||
return localBuildExpiration;
|
return localBuildExpiration;
|
||||||
};
|
};
|
||||||
window.getNodeVersion = () => config.node_version;
|
|
||||||
window.getHostName = () => config.hostname;
|
window.getHostName = () => config.hostname;
|
||||||
window.getServerTrustRoot = () => config.serverTrustRoot;
|
window.getServerTrustRoot = () => config.serverTrustRoot;
|
||||||
window.getServerPublicParams = () => config.serverPublicParams;
|
window.getServerPublicParams = () => config.serverPublicParams;
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'none';
|
content="default-src 'none';
|
||||||
|
frame-src 'none';
|
||||||
|
form-action 'none';
|
||||||
font-src 'self';
|
font-src 'self';
|
||||||
img-src 'self' blob: data:;
|
img-src 'self' blob: data:;
|
||||||
media-src 'self' blob:;
|
media-src 'self' blob:;
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'none';
|
content="default-src 'none';
|
||||||
|
frame-src 'none';
|
||||||
|
form-action 'none';
|
||||||
font-src 'self';
|
font-src 'self';
|
||||||
img-src 'self' blob: data:;
|
img-src 'self' blob: data:;
|
||||||
media-src 'self' blob:;
|
media-src 'self' blob:;
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
// Copyright 2016-2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
.debug-log {
|
|
||||||
&.modal {
|
|
||||||
padding: 50px;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
margin: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
|
||||||
resize: none;
|
|
||||||
min-height: 100px;
|
|
||||||
|
|
||||||
font-family: Monaco, Consolas, 'Courier New', Courier, monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
@include dark-theme {
|
|
||||||
background-color: $color-gray-90;
|
|
||||||
border: 1px solid $color-gray-45;
|
|
||||||
color: $color-gray-02;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
$open-height: 36px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy {
|
|
||||||
height: $open-height;
|
|
||||||
text-align: center;
|
|
||||||
line-height: $open-height;
|
|
||||||
padding: 0 30px;
|
|
||||||
color: unset;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
border-radius: 0 5px 5px 0;
|
|
||||||
|
|
||||||
@include light-theme {
|
|
||||||
border: solid 1px $color-gray-25;
|
|
||||||
background: $color-gray-02;
|
|
||||||
&:active {
|
|
||||||
background: $color-gray-25;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@include dark-theme {
|
|
||||||
border: solid 1px $color-gray-45;
|
|
||||||
background-color: $color-gray-90;
|
|
||||||
color: $color-gray-02;
|
|
||||||
&:active {
|
|
||||||
background: $color-gray-25;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
border-radius: 5px 0 0 5px;
|
|
||||||
flex-grow: 1;
|
|
||||||
min-width: 400px;
|
|
||||||
height: $open-height;
|
|
||||||
padding: 0 10px;
|
|
||||||
outline-offset: -4px;
|
|
||||||
|
|
||||||
@include light-theme {
|
|
||||||
border: solid 1px $color-gray-25;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
@include dark-theme {
|
|
||||||
color: $color-gray-02;
|
|
||||||
border: solid 1px $color-gray-45;
|
|
||||||
border-right: none;
|
|
||||||
background-color: $color-gray-90;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
74
stylesheets/components/DebugLogWindow.scss
Normal file
74
stylesheets/components/DebugLogWindow.scss
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2016-2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
.DebugLogWindow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__textarea {
|
||||||
|
font-family: Monaco, Consolas, 'Courier New', Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 100%;
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@include dark-theme {
|
||||||
|
background-color: $color-gray-90;
|
||||||
|
border: 1px solid $color-gray-45;
|
||||||
|
color: $color-gray-02;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
@include font-title-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
@include font-body-2;
|
||||||
|
@include light-theme {
|
||||||
|
color: $color-gray-60;
|
||||||
|
}
|
||||||
|
@include dark-theme {
|
||||||
|
color: $color-gray-25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.module-Button {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 10px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@include light-theme {
|
||||||
|
border: solid 1px $color-gray-25;
|
||||||
|
}
|
||||||
|
@include dark-theme {
|
||||||
|
background-color: $color-gray-90;
|
||||||
|
border: solid 1px $color-gray-45;
|
||||||
|
color: $color-gray-02;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
&__buttons {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
// Old style: components
|
// Old style: components
|
||||||
@import 'progress';
|
@import 'progress';
|
||||||
@import 'modal';
|
@import 'modal';
|
||||||
@import 'debugLog';
|
|
||||||
@import 'recorder';
|
@import 'recorder';
|
||||||
@import 'emoji';
|
@import 'emoji';
|
||||||
@import 'settings';
|
@import 'settings';
|
||||||
|
@ -56,6 +55,7 @@
|
||||||
@import './components/ConversationView.scss';
|
@import './components/ConversationView.scss';
|
||||||
@import './components/CustomColorEditor.scss';
|
@import './components/CustomColorEditor.scss';
|
||||||
@import './components/CustomizingPreferredReactionsModal.scss';
|
@import './components/CustomizingPreferredReactionsModal.scss';
|
||||||
|
@import './components/DebugLogWindow.scss';
|
||||||
@import './components/DisappearingTimeDialog.scss';
|
@import './components/DisappearingTimeDialog.scss';
|
||||||
@import './components/DisappearingTimerSelect.scss';
|
@import './components/DisappearingTimerSelect.scss';
|
||||||
@import './components/EditConversationAttributesModal.scss';
|
@import './components/EditConversationAttributesModal.scss';
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('i18n', () => {
|
||||||
assert.strictEqual(i18n('random'), '');
|
assert.strictEqual(i18n('random'), '');
|
||||||
});
|
});
|
||||||
it('returns message for given string', () => {
|
it('returns message for given string', () => {
|
||||||
assert.equal(i18n('reportIssue'), ['Report an issue']);
|
assert.equal(i18n('reportIssue'), ['Contact Support']);
|
||||||
});
|
});
|
||||||
it('returns message with single substitution', () => {
|
it('returns message with single substitution', () => {
|
||||||
const actual = i18n('cannotUpdateDetail', [
|
const actual = i18n('cannotUpdateDetail', [
|
||||||
|
|
32
ts/components/DebugLogWindow.stories.tsx
Normal file
32
ts/components/DebugLogWindow.stories.tsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
import { DebugLogWindow, PropsType } from './DebugLogWindow';
|
||||||
|
import { setupI18n } from '../util/setupI18n';
|
||||||
|
import { sleep } from '../util/sleep';
|
||||||
|
|
||||||
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
const createProps = (): PropsType => ({
|
||||||
|
closeWindow: action('closeWindow'),
|
||||||
|
downloadLog: action('downloadLog'),
|
||||||
|
i18n,
|
||||||
|
fetchLogs: () => {
|
||||||
|
action('fetchLogs')();
|
||||||
|
return Promise.resolve('Sample logs');
|
||||||
|
},
|
||||||
|
uploadLogs: async (logs: string) => {
|
||||||
|
action('uploadLogs')(logs);
|
||||||
|
await sleep(5000);
|
||||||
|
return 'https://picsum.photos/1800/900';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const story = storiesOf('Components/DebugLogWindow', module);
|
||||||
|
|
||||||
|
story.add('DebugLogWindow', () => <DebugLogWindow {...createProps()} />);
|
207
ts/components/DebugLogWindow.tsx
Normal file
207
ts/components/DebugLogWindow.tsx
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
// Copyright 2015-2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React, { MouseEvent, useEffect, useState } from 'react';
|
||||||
|
import copyText from 'copy-text-to-clipboard';
|
||||||
|
import * as log from '../logging/log';
|
||||||
|
import { Button, ButtonVariant } from './Button';
|
||||||
|
import { LocalizerType } from '../types/Util';
|
||||||
|
import { Spinner } from './Spinner';
|
||||||
|
import { ToastDebugLogError } from './ToastDebugLogError';
|
||||||
|
import { ToastLinkCopied } from './ToastLinkCopied';
|
||||||
|
import { ToastLoadingFullLogs } from './ToastLoadingFullLogs';
|
||||||
|
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
|
||||||
|
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
||||||
|
|
||||||
|
enum LoadState {
|
||||||
|
NotStarted,
|
||||||
|
Started,
|
||||||
|
Loaded,
|
||||||
|
Submitting,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PropsType = {
|
||||||
|
closeWindow: () => unknown;
|
||||||
|
downloadLog: (text: string) => unknown;
|
||||||
|
i18n: LocalizerType;
|
||||||
|
fetchLogs: () => Promise<string>;
|
||||||
|
uploadLogs: (logs: string) => Promise<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ToastType {
|
||||||
|
Copied,
|
||||||
|
Error,
|
||||||
|
Loading,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DebugLogWindow = ({
|
||||||
|
closeWindow,
|
||||||
|
downloadLog,
|
||||||
|
i18n,
|
||||||
|
fetchLogs,
|
||||||
|
uploadLogs,
|
||||||
|
}: PropsType): JSX.Element => {
|
||||||
|
const [loadState, setLoadState] = useState<LoadState>(LoadState.NotStarted);
|
||||||
|
const [logText, setLogText] = useState<string | undefined>();
|
||||||
|
const [publicLogURL, setPublicLogURL] = useState<string | undefined>();
|
||||||
|
const [textAreaValue, setTextAreaValue] = useState<string>(i18n('loading'));
|
||||||
|
const [toastType, setToastType] = useState<ToastType | undefined>();
|
||||||
|
|
||||||
|
useEscapeHandling(closeWindow);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoadState(LoadState.Started);
|
||||||
|
|
||||||
|
let shouldCancel = false;
|
||||||
|
|
||||||
|
async function doFetchLogs() {
|
||||||
|
const fetchedLogText = await fetchLogs();
|
||||||
|
|
||||||
|
if (shouldCancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setToastType(ToastType.Loading);
|
||||||
|
setLogText(fetchedLogText);
|
||||||
|
setLoadState(LoadState.Loaded);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
const value = fetchedLogText.split(/\n/g, linesToShow).join('\n');
|
||||||
|
|
||||||
|
setTextAreaValue(value);
|
||||||
|
setToastType(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
doFetchLogs();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
shouldCancel = true;
|
||||||
|
};
|
||||||
|
}, [fetchLogs]);
|
||||||
|
|
||||||
|
const handleSubmit = async (ev: MouseEvent) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
const text = logText;
|
||||||
|
|
||||||
|
if (!text || text.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadState(LoadState.Submitting);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const publishedLogURL = await uploadLogs(text);
|
||||||
|
setPublicLogURL(publishedLogURL);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(
|
||||||
|
'DebugLogWindow error:',
|
||||||
|
error && error.stack ? error.stack : error
|
||||||
|
);
|
||||||
|
setLoadState(LoadState.Loaded);
|
||||||
|
setToastType(ToastType.Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function closeToast() {
|
||||||
|
setToastType(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
let toastElement: JSX.Element | undefined;
|
||||||
|
if (toastType === ToastType.Loading) {
|
||||||
|
toastElement = <ToastLoadingFullLogs i18n={i18n} onClose={closeToast} />;
|
||||||
|
} else if (toastType === ToastType.Copied) {
|
||||||
|
toastElement = <ToastLinkCopied i18n={i18n} onClose={closeToast} />;
|
||||||
|
} else if (toastType === ToastType.Error) {
|
||||||
|
toastElement = <ToastDebugLogError i18n={i18n} onClose={closeToast} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicLogURL) {
|
||||||
|
const copyLog = (ev: MouseEvent) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
copyText(publicLogURL);
|
||||||
|
setToastType(ToastType.Copied);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="DebugLogWindow">
|
||||||
|
<div>
|
||||||
|
<div className="DebugLogWindow__title">{i18n('debugLogSuccess')}</div>
|
||||||
|
<p className="DebugLogWindow__subtitle">
|
||||||
|
{i18n('debugLogSuccessNextSteps')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="DebugLogWindow__container">
|
||||||
|
<input
|
||||||
|
className="DebugLogWindow__link"
|
||||||
|
readOnly
|
||||||
|
type="text"
|
||||||
|
value={publicLogURL}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="DebugLogWindow__footer">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
openLinkInWebBrowser(
|
||||||
|
'https://support.signal.org/hc/requests/new'
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
variant={ButtonVariant.Secondary}
|
||||||
|
>
|
||||||
|
{i18n('reportIssue')}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={copyLog}>{i18n('debugLogCopy')}</Button>
|
||||||
|
</div>
|
||||||
|
{toastElement}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const canSubmit = Boolean(logText) && loadState !== LoadState.Submitting;
|
||||||
|
const canSave = Boolean(logText);
|
||||||
|
const isLoading =
|
||||||
|
loadState === LoadState.Started || loadState === LoadState.Submitting;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="DebugLogWindow">
|
||||||
|
<div>
|
||||||
|
<div className="DebugLogWindow__title">{i18n('submitDebugLog')}</div>
|
||||||
|
<p className="DebugLogWindow__subtitle">
|
||||||
|
{i18n('debugLogExplanation')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="DebugLogWindow__container">
|
||||||
|
{isLoading ? (
|
||||||
|
<Spinner svgSize="normal" />
|
||||||
|
) : (
|
||||||
|
<textarea
|
||||||
|
className="DebugLogWindow__textarea"
|
||||||
|
readOnly
|
||||||
|
rows={5}
|
||||||
|
spellCheck={false}
|
||||||
|
value={textAreaValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="DebugLogWindow__footer">
|
||||||
|
<Button
|
||||||
|
disabled={!canSave}
|
||||||
|
onClick={() => {
|
||||||
|
if (logText) {
|
||||||
|
downloadLog(logText);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
variant={ButtonVariant.Secondary}
|
||||||
|
>
|
||||||
|
{i18n('debugLogSave')}
|
||||||
|
</Button>
|
||||||
|
<Button disabled={!canSubmit} onClick={handleSubmit}>
|
||||||
|
{i18n('submit')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{toastElement}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
21
ts/components/ToastDebugLogError.stories.tsx
Normal file
21
ts/components/ToastDebugLogError.stories.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { ToastDebugLogError } from './ToastDebugLogError';
|
||||||
|
|
||||||
|
import { setupI18n } from '../util/setupI18n';
|
||||||
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
|
|
||||||
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
i18n,
|
||||||
|
onClose: action('onClose'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const story = storiesOf('Components/ToastDebugLogError', module);
|
||||||
|
|
||||||
|
story.add('ToastDebugLogError', () => <ToastDebugLogError {...defaultProps} />);
|
18
ts/components/ToastDebugLogError.tsx
Normal file
18
ts/components/ToastDebugLogError.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { LocalizerType } from '../types/Util';
|
||||||
|
import { Toast } from './Toast';
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
i18n: LocalizerType;
|
||||||
|
onClose: () => unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToastDebugLogError = ({
|
||||||
|
i18n,
|
||||||
|
onClose,
|
||||||
|
}: PropsType): JSX.Element => {
|
||||||
|
return <Toast onClose={onClose}>{i18n('debugLogError')}</Toast>;
|
||||||
|
};
|
|
@ -114,19 +114,23 @@ const headerSection = (
|
||||||
].join('\n');
|
].join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHeader = ({
|
const getHeader = (
|
||||||
capabilities,
|
{
|
||||||
remoteConfig,
|
capabilities,
|
||||||
statistics,
|
remoteConfig,
|
||||||
user,
|
statistics,
|
||||||
}: Omit<FetchLogIpcData, 'logEntries'>): string =>
|
user,
|
||||||
|
}: Omit<FetchLogIpcData, 'logEntries'>,
|
||||||
|
nodeVersion: string,
|
||||||
|
appVersion: string
|
||||||
|
): string =>
|
||||||
[
|
[
|
||||||
headerSection('System info', {
|
headerSection('System info', {
|
||||||
Time: Date.now(),
|
Time: Date.now(),
|
||||||
'User agent': window.navigator.userAgent,
|
'User agent': window.navigator.userAgent,
|
||||||
'Node version': window.getNodeVersion(),
|
'Node version': nodeVersion,
|
||||||
Environment: getEnvironment(),
|
Environment: getEnvironment(),
|
||||||
'App version': window.getVersion(),
|
'App version': appVersion,
|
||||||
}),
|
}),
|
||||||
headerSection('User info', user),
|
headerSection('User info', user),
|
||||||
headerSection('Capabilities', capabilities),
|
headerSection('Capabilities', capabilities),
|
||||||
|
@ -154,7 +158,10 @@ function formatLine(mightBeEntry: unknown): string {
|
||||||
return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`;
|
return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetch(): Promise<string> {
|
export function fetch(
|
||||||
|
nodeVersion: string,
|
||||||
|
appVersion: string
|
||||||
|
): Promise<string> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
ipc.send('fetch-log');
|
ipc.send('fetch-log');
|
||||||
|
|
||||||
|
@ -163,7 +170,7 @@ export function fetch(): Promise<string> {
|
||||||
let body: string;
|
let body: string;
|
||||||
if (isFetchLogIpcData(data)) {
|
if (isFetchLogIpcData(data)) {
|
||||||
const { logEntries } = data;
|
const { logEntries } = data;
|
||||||
header = getHeader(data);
|
header = getHeader(data, nodeVersion, appVersion);
|
||||||
body = logEntries.map(formatLine).join('\n');
|
body = logEntries.map(formatLine).join('\n');
|
||||||
} else {
|
} else {
|
||||||
header = headerSectionTitle('Partial logs');
|
header = headerSectionTitle('Partial logs');
|
||||||
|
|
|
@ -106,30 +106,6 @@
|
||||||
"updated": "2018-09-18T19:19:27.699Z",
|
"updated": "2018-09-18T19:19:27.699Z",
|
||||||
"reasonDetail": "Very limited in what HTML can be injected - dark/light options specify colors for the light/dark parts of QRCode"
|
"reasonDetail": "Very limited in what HTML can be injected - dark/light options specify colors for the light/dark parts of QRCode"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "js/debug_log_start.js",
|
|
||||||
"line": "$(document).on('keydown', e => {",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2018-09-19T21:59:32.770Z",
|
|
||||||
"reasonDetail": "Protected from arbitrary input"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "js/debug_log_start.js",
|
|
||||||
"line": "const $body = $(document.body);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2018-09-19T21:59:32.770Z",
|
|
||||||
"reasonDetail": "Protected from arbitrary input"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-appendTo(",
|
|
||||||
"path": "js/debug_log_start.js",
|
|
||||||
"line": "window.view.$el.appendTo($body);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2018-09-19T18:13:29.628Z",
|
|
||||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"rule": "jQuery-$(",
|
"rule": "jQuery-$(",
|
||||||
"path": "js/views/key_verification_view.js",
|
"path": "js/views/key_verification_view.js",
|
||||||
|
@ -13306,202 +13282,6 @@
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2021-09-17T21:51:57.475Z"
|
"updated": "2021-09-17T21:51:57.475Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " template: () => $('#debug-log-link').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " template: () => $('#debug-log').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.textarea = this.$('.textarea').get(0);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.submit').attr('disabled', 'disabled');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.submit').removeAttr('disabled');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.buttons, .textarea').remove();",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.result').addClass('loading');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " el: this.$('.result'),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.loading').removeClass('loading');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.link').focus().select();",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.loading').removeClass('loading');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " this.$('.result').text(window.i18n('debugLogError'));",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-html(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " template: () => $('#debug-log-link').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-html(",
|
|
||||||
"path": "ts/views/debug_log_view.js",
|
|
||||||
"line": " template: () => $('#debug-log').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " template: () => $('#debug-log-link').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " template: () => $('#debug-log').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.textarea = this.$('.textarea').get(0);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.submit').attr('disabled', 'disabled');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.submit').removeAttr('disabled');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.buttons, .textarea').remove();",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.result').addClass('loading');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " el: this.$('.result'),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.loading').removeClass('loading');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.link').focus().select();",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.loading').removeClass('loading');",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-$(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " this.$('.result').text(window.i18n('debugLogError'));",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-html(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " template: () => $('#debug-log-link').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule": "jQuery-html(",
|
|
||||||
"path": "ts/views/debug_log_view.ts",
|
|
||||||
"line": " template: () => $('#debug-log').html(),",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2021-09-15T21:07:50.995Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"rule": "jQuery-$(",
|
"rule": "jQuery-$(",
|
||||||
"path": "ts/views/inbox_view.js",
|
"path": "ts/views/inbox_view.js",
|
||||||
|
|
|
@ -1,192 +0,0 @@
|
||||||
// Copyright 2015-2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import copyText from 'copy-text-to-clipboard';
|
|
||||||
import * as log from '../logging/log';
|
|
||||||
import * as debugLog from '../logging/debuglogs';
|
|
||||||
import { ToastLoadingFullLogs } from '../components/ToastLoadingFullLogs';
|
|
||||||
import { ToastLinkCopied } from '../components/ToastLinkCopied';
|
|
||||||
import { showToast } from '../util/showToast';
|
|
||||||
|
|
||||||
window.Whisper = window.Whisper || {};
|
|
||||||
const { Whisper } = window;
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DebugLogLinkView = Whisper.View.extend({
|
|
||||||
template: () => $('#debug-log-link').html(),
|
|
||||||
initialize(options: { url: string }) {
|
|
||||||
this.url = options.url;
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
'click .copy': 'copy',
|
|
||||||
},
|
|
||||||
render_attributes() {
|
|
||||||
return {
|
|
||||||
url: this.url,
|
|
||||||
reportIssue: window.i18n('reportIssue'),
|
|
||||||
debugLogCopy: window.i18n('debugLogCopy'),
|
|
||||||
debugLogCopyAlt: window.i18n('debugLogCopyAlt'),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
copy(e: MouseEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
const target = e.currentTarget as HTMLAnchorElement;
|
|
||||||
copyText(target.href);
|
|
||||||
showToast(ToastLinkCopied);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
export const DebugLogView = Whisper.View.extend({
|
|
||||||
template: () => $('#debug-log').html(),
|
|
||||||
className: 'debug-log modal',
|
|
||||||
initialize() {
|
|
||||||
this.render();
|
|
||||||
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
render_attributes: {
|
|
||||||
title: window.i18n('submitDebugLog'),
|
|
||||||
cancel: window.i18n('cancel'),
|
|
||||||
submit: window.i18n('submit'),
|
|
||||||
close: window.i18n('gotIt'),
|
|
||||||
debugLogExplanation: window.i18n('debugLogExplanation'),
|
|
||||||
},
|
|
||||||
async fetchLogs() {
|
|
||||||
if (this.loadState !== LoadState.NotStarted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadState = LoadState.Started;
|
|
||||||
this.textarea.value = window.i18n('loading');
|
|
||||||
this.$('.submit').attr('disabled', 'disabled');
|
|
||||||
|
|
||||||
this.logText = await debugLog.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, linesToShow)
|
|
||||||
.concat(['', window.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;
|
|
||||||
showToast(ToastLoadingFullLogs);
|
|
||||||
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: SubmitEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
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.$('.result').addClass('loading');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const publishedLogURL = await debugLog.upload(text, window.getVersion());
|
|
||||||
const view = new DebugLogLinkView({
|
|
||||||
url: publishedLogURL,
|
|
||||||
el: this.$('.result'),
|
|
||||||
});
|
|
||||||
this.$('.loading').removeClass('loading');
|
|
||||||
view.render();
|
|
||||||
this.$('.link').focus().select();
|
|
||||||
} catch (error) {
|
|
||||||
log.error(
|
|
||||||
'DebugLogView error:',
|
|
||||||
error && error.stack ? error.stack : error
|
|
||||||
);
|
|
||||||
this.$('.loading').removeClass('loading');
|
|
||||||
this.$('.result').text(window.i18n('debugLogError'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
window.Whisper.DebugLogView = DebugLogView;
|
|
5
ts/window.d.ts
vendored
5
ts/window.d.ts
vendored
|
@ -111,7 +111,6 @@ import { QualifiedAddress } from './types/QualifiedAddress';
|
||||||
import { CI } from './CI';
|
import { CI } from './CI';
|
||||||
import { IPCEventsType, IPCEventsValuesType } from './util/createIPCEvents';
|
import { IPCEventsType, IPCEventsValuesType } from './util/createIPCEvents';
|
||||||
import { ConversationView } from './views/conversation_view';
|
import { ConversationView } from './views/conversation_view';
|
||||||
import { DebugLogView } from './views/debug_log_view';
|
|
||||||
import { LoggerType } from './types/Logging';
|
import { LoggerType } from './types/Logging';
|
||||||
import { SettingType } from './util/preload';
|
import { SettingType } from './util/preload';
|
||||||
|
|
||||||
|
@ -162,7 +161,6 @@ declare global {
|
||||||
startApp: () => void;
|
startApp: () => void;
|
||||||
|
|
||||||
QRCode: any;
|
QRCode: any;
|
||||||
closeDebugLog: () => unknown;
|
|
||||||
removeSetupMenuItems: () => unknown;
|
removeSetupMenuItems: () => unknown;
|
||||||
showPermissionsPopup: () => unknown;
|
showPermissionsPopup: () => unknown;
|
||||||
|
|
||||||
|
@ -209,7 +207,6 @@ declare global {
|
||||||
getLocale: () => ElectronLocaleType;
|
getLocale: () => ElectronLocaleType;
|
||||||
getMediaCameraPermissions: () => Promise<boolean>;
|
getMediaCameraPermissions: () => Promise<boolean>;
|
||||||
getMediaPermissions: () => Promise<boolean>;
|
getMediaPermissions: () => Promise<boolean>;
|
||||||
getNodeVersion: () => string;
|
|
||||||
getServerPublicParams: () => string;
|
getServerPublicParams: () => string;
|
||||||
getSfuUrl: () => string;
|
getSfuUrl: () => string;
|
||||||
getSocketStatus: () => SocketStatus;
|
getSocketStatus: () => SocketStatus;
|
||||||
|
@ -499,6 +496,7 @@ declare global {
|
||||||
context: SignalContext;
|
context: SignalContext;
|
||||||
getAppInstance: () => string | undefined;
|
getAppInstance: () => string | undefined;
|
||||||
getEnvironment: () => string;
|
getEnvironment: () => string;
|
||||||
|
getNodeVersion: () => string;
|
||||||
getVersion: () => string;
|
getVersion: () => string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
log: LoggerType;
|
log: LoggerType;
|
||||||
|
@ -567,7 +565,6 @@ export class BasicReactWrapperViewClass extends AnyViewClass {
|
||||||
export type WhisperType = {
|
export type WhisperType = {
|
||||||
Conversation: typeof ConversationModel;
|
Conversation: typeof ConversationModel;
|
||||||
ConversationCollection: typeof ConversationModelCollectionType;
|
ConversationCollection: typeof ConversationModelCollectionType;
|
||||||
DebugLogView: typeof DebugLogView;
|
|
||||||
Message: typeof MessageModel;
|
Message: typeof MessageModel;
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ export const SignalWindow = {
|
||||||
getAppInstance: (): string | undefined =>
|
getAppInstance: (): string | undefined =>
|
||||||
config.appInstance ? String(config.appInstance) : undefined,
|
config.appInstance ? String(config.appInstance) : undefined,
|
||||||
getEnvironment,
|
getEnvironment,
|
||||||
|
getNodeVersion: (): string => String(config.node_version),
|
||||||
getVersion: (): string => String(config.version),
|
getVersion: (): string => String(config.version),
|
||||||
i18n: setupI18n(locale, localeMessages),
|
i18n: setupI18n(locale, localeMessages),
|
||||||
log: window.SignalWindow.log,
|
log: window.SignalWindow.log,
|
||||||
|
|
44
ts/windows/debuglog/preload.ts
Normal file
44
ts/windows/debuglog/preload.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
// It is important to call this as early as possible
|
||||||
|
import '../context';
|
||||||
|
|
||||||
|
import { SignalWindow } from '../configure';
|
||||||
|
import { DebugLogWindow } from '../../components/DebugLogWindow';
|
||||||
|
import * as debugLog from '../../logging/debuglogs';
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('SignalWindow', {
|
||||||
|
...SignalWindow,
|
||||||
|
renderWindow: () => {
|
||||||
|
const environmentText: Array<string> = [SignalWindow.getEnvironment()];
|
||||||
|
|
||||||
|
const appInstance = SignalWindow.getAppInstance();
|
||||||
|
if (appInstance) {
|
||||||
|
environmentText.push(appInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
React.createElement(DebugLogWindow, {
|
||||||
|
closeWindow: () => ipcRenderer.send('close-debug-log'),
|
||||||
|
downloadLog: (logText: string) =>
|
||||||
|
ipcRenderer.send('show-debug-log-save-dialog', logText),
|
||||||
|
i18n: SignalWindow.i18n,
|
||||||
|
fetchLogs() {
|
||||||
|
return debugLog.fetch(
|
||||||
|
SignalWindow.getNodeVersion(),
|
||||||
|
SignalWindow.getVersion()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
uploadLogs(logs: string) {
|
||||||
|
return debugLog.upload(logs, SignalWindow.getVersion());
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
document.getElementById('app')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue