From 4964966b91368e734e2f792edb0779559f189176 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Fri, 12 Feb 2021 06:42:34 -0800 Subject: [PATCH] Fix Flipper crashing to an empty screen Summary: Changelog: Fixed issue where a Flipper crash would result in an entirely blank screen, rather than a useful error message. While debugging another issue, discovered that React errors that happen outside a Plugin aren't caught at all, resulting in the infamous gray screen of deaths. This was the case because no error boundary has been set up for our Chrome, and since React 16 the default error handling has becoming rendering blank, rather than freezing. See https://reactjs.org/docs/error-boundaries.html#new-behavior-for-uncaught-errors. Thanks to ant.design styling this decently was trivial :). But sadly involved a component class since error boundaries are not yet available as hook. With these changes the errors should also end up more readably in our monitoring. Reviewed By: nikoant Differential Revision: D26422666 fbshipit-source-id: 6c0f8611c80a4a5e0d7e61d58afcf5eabe410e57 --- desktop/app/src/init.tsx | 118 +++++++++++++++++++---- desktop/flipper-plugin/src/ui/Layout.tsx | 1 + 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index a9132fa7f..c67ec37e9 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -40,8 +40,15 @@ import { _setGlobalInteractionReporter, Logger, _LoggerContext, + Layout, + theme, } from 'flipper-plugin'; import isProduction from './utils/isProduction'; +import {Button, Input, Result, Typography} from 'antd'; +import constants from './fb-stubs/constants'; +import styled from '@emotion/styled'; +import {CopyOutlined} from '@ant-design/icons'; +import {clipboard} from 'electron/common'; if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') { // By default Node.JS has its internal certificate storage and doesn't use @@ -58,23 +65,93 @@ enableMapSet(); GK.init(); -const AppFrame = ({logger}: {logger: Logger}) => ( - <_LoggerContext.Provider value={logger}> - - - - - - <_NuxManagerContext.Provider value={_createNuxManager()}> - - - - - - - - -); +class AppFrame extends React.Component< + {logger: Logger}, + {error: any; errorInfo: any} +> { + state = {error: undefined as any, errorInfo: undefined as any}; + + getError() { + return this.state.error + ? `${this.state.error}\n\nComponent stack:\n${this.state.errorInfo?.componentStack}\n\nError stacktrace:\n${this.state.error?.stack}` + : ''; + } + + render() { + const {logger} = this.props; + return this.state.error ? ( + + + + A crash was detected in the Flipper chrome. Filing a{' '} + + bug report + {' '} + would be appreciated! Please include the details below. +

+ } + extra={[ + , + , + ]} + /> + +
+
+ ) : ( + <_LoggerContext.Provider value={logger}> + + + + + + <_NuxManagerContext.Provider value={_createNuxManager()}> + + + + + + + + + ); + } + + componentDidCatch(error: any, errorInfo: any) { + console.error( + `Flipper chrome crash: ${error}`, + error, + '\nComponents: ' + errorInfo?.componentStack, + ); + this.setState({ + error, + errorInfo, + }); + } +} function setProcessState(store: Store) { const settings = store.getState().settingsState; @@ -146,3 +223,10 @@ const persistor = persistStore(store, undefined, () => { }); setPersistor(persistor); + +const CodeBlock = styled(Input.TextArea)({ + fontFamily: + 'SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;', + fontSize: '0.8em', + color: theme.textColorSecondary, +}); diff --git a/desktop/flipper-plugin/src/ui/Layout.tsx b/desktop/flipper-plugin/src/ui/Layout.tsx index df20f0ff4..1b82e0982 100644 --- a/desktop/flipper-plugin/src/ui/Layout.tsx +++ b/desktop/flipper-plugin/src/ui/Layout.tsx @@ -139,6 +139,7 @@ type SplitLayoutProps = { */ center?: boolean; children: [React.ReactNode, React.ReactNode]; + style?: React.HTMLAttributes['style']; }; function renderSplitLayout(