1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: Resolves #5273: Prevent plugins from crashing the application

This commit is contained in:
Laurent Cozic 2021-08-06 13:08:32 +01:00
parent e9d590169b
commit f9cfefdb29
3 changed files with 37 additions and 15 deletions

View File

@ -23,13 +23,17 @@ interface State {
pluginInfos: PluginInfo[];
}
interface Props {}
interface Props {
message?: string;
}
export default class ErrorBoundary extends React.Component<Props, State> {
public state: State = { error: null, errorInfo: null, pluginInfos: [] };
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
componentDidCatch(error: any, errorInfo: ErrorInfo) {
if (typeof error === 'string') error = { message: error };
const pluginInfos: PluginInfo[] = [];
try {
const service = PluginService.instance();
@ -61,6 +65,11 @@ export default class ErrorBoundary extends React.Component<Props, State> {
ipcRenderer.on('appClose', onAppClose);
}
renderMessage() {
const message = this.props.message || 'Joplin encountered a fatal error and could not continue.';
return <p>{message}</p>;
}
render() {
if (this.state.error) {
const safeMode_click = async () => {
@ -118,8 +127,9 @@ export default class ErrorBoundary extends React.Component<Props, State> {
return (
<div style={{ overflow: 'auto', fontFamily: 'sans-serif', padding: '5px 20px' }}>
<h1>Error</h1>
<p>Joplin encountered a fatal error and could not continue. To report the error, please copy the *entire content* of this page and post it on Joplin forum or GitHub.</p>
<p>To continue you may close the app. Alternatively, if the error persists you may try to <a href="#" onClick={safeMode_click}>restart in safe mode</a>, which will temporarily disable all plugins.</p>
{this.renderMessage()}
<p>To report the error, please copy the *entire content* of this page and post it on Joplin forum or GitHub.</p>
<p>If the error persists you may try to <a href="#" onClick={safeMode_click}>restart in safe mode</a>, which will temporarily disable all plugins.</p>
{output}
</div>
);

View File

@ -32,6 +32,7 @@ const shared = require('@joplin/lib/components/shared/note-screen-shared.js');
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
import { reg } from '@joplin/lib/registry';
import ErrorBoundary from '../../../ErrorBoundary';
const menuUtils = new MenuUtils(CommandService.instance());
@ -599,7 +600,16 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
pluginAssets: renderedBody.pluginAssets,
downloadResources: Setting.value('sync.resourceDownloadMode'),
};
webviewRef.current.wrappedInstance.send('setHtml', renderedBody.html, options);
// It seems when there's an error immediately when the component is
// mounted, webviewReady might be true, but webviewRef.current will be
// undefined. Maybe due to the error boundary that unmount components.
// Since we can't do much about it we just print an error.
if (webviewRef.current && webviewRef.current.wrappedInstance) {
webviewRef.current.wrappedInstance.send('setHtml', renderedBody.html, options);
} else {
console.error('Trying to set HTML on an undefined webview ref');
}
}, [renderedBody, webviewReady]);
useEffect(() => {
@ -791,16 +801,18 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
}
return (
<div style={styles.root} ref={rootRef}>
<div style={styles.rowToolbar}>
<Toolbar themeId={props.themeId} />
{props.noteToolbar}
<ErrorBoundary message="The text editor encountered a fatal error and could not continue. The error might be due to a plugin, so please try to disable some of them and try again.">
<div style={styles.root} ref={rootRef}>
<div style={styles.rowToolbar}>
<Toolbar themeId={props.themeId} />
{props.noteToolbar}
</div>
<div style={styles.rowEditorViewer}>
{renderEditor()}
{renderViewer()}
</div>
</div>
<div style={styles.rowEditorViewer}>
{renderEditor()}
{renderViewer()}
</div>
</div>
</ErrorBoundary>
);
}

View File

@ -545,7 +545,7 @@ function NoteEditor(props: NoteEditorProps) {
onTitleChange={onTitleChange}
/>
{renderSearchInfo()}
<div style={{ display: 'flex', flex: 1, paddingLeft: theme.editorPaddingLeft }}>
<div style={{ display: 'flex', flex: 1, paddingLeft: theme.editorPaddingLeft, maxHeight: '100%' }}>
{editor}
</div>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>