11// DO NOT IMPORT window.config HERE!
2- // to make sure the error handler always works, we should never import `window.config`, because some user's custom template breaks it.
2+ // to make sure the error handler always works, we should never import `window.config`, because
3+ // some user's custom template breaks it.
34
45// This sets up the URL prefix used in webpack's chunk loading.
56// This file must be imported before any lazy-loading is being attempted.
@@ -26,29 +27,42 @@ export function showGlobalErrorMessage(msg) {
2627}
2728
2829/**
29- * @param {ErrorEvent } e
30+ * @param {ErrorEvent|PromiseRejectionEvent } event - Event
31+ * @param {string } event.message - Only present on ErrorEvent
32+ * @param {string } event.error - Only present on ErrorEvent
33+ * @param {string } event.type - Only present on ErrorEvent
34+ * @param {string } event.filename - Only present on ErrorEvent
35+ * @param {number } event.lineno - Only present on ErrorEvent
36+ * @param {number } event.colno - Only present on ErrorEvent
37+ * @param {string } event.reason - Only present on PromiseRejectionEvent
38+ * @param {number } event.promise - Only present on PromiseRejectionEvent
3039 */
31- function processWindowErrorEvent ( e ) {
32- const err = e . error ?? e . reason ;
40+ function processWindowErrorEvent ( { error , reason , message , type , filename , lineno , colno } ) {
41+ const err = error ?? reason ;
3342 const assetBaseUrl = String ( new URL ( __webpack_public_path__ , window . location . origin ) ) ;
43+ const { runModeIsProd} = window . config ?? { } ;
3444
35- // error is likely from browser extension or inline script. Do not show these in production builds.
36- if ( ! err . stack ?. includes ( assetBaseUrl ) && window . config ?. runModeIsProd ) return ;
37-
38- let message ;
39- if ( e . type === 'unhandledrejection' ) {
40- message = `JavaScript promise rejection: ${ err . message } .` ;
41- } else {
42- message = `JavaScript error: ${ e . message } ( ${ e . filename } @ ${ e . lineno } : ${ e . colno } ).` ;
45+ // ` error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likly a
46+ // non-critical event from the browser. We log them but don't show them to users. Examples:
47+ // - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors
48+ // - https://github.com/mozilla-mobile/firefox-ios/issues/10817
49+ // - https://github.com/go-gitea/gitea/issues/20240
50+ if ( ! err ) {
51+ if ( message ) console . error ( new Error ( message ) ) ;
52+ if ( runModeIsProd ) return ;
4353 }
4454
45- if ( ! e . error && e . lineno === 0 && e . colno === 0 && e . filename === '' && window . navigator . userAgent . includes ( 'FxiOS/' ) ) {
46- // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240
47- // If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0.
48- return ; // ignore such nonsense error event
55+ // If the error stack trace does not include the base URL of our script assets, it likely came
56+ // from a browser extension or inline script. Do not show such errors in production.
57+ if ( err instanceof Error && ! err . stack ?. includes ( assetBaseUrl ) && runModeIsProd ) {
58+ return ;
4959 }
5060
51- showGlobalErrorMessage ( `${ message } Open browser console to see more details.` ) ;
61+ let msg = err ?. message ?? message ;
62+ if ( lineno ) msg += ` (${ filename } @ ${ lineno } :${ colno } )` ;
63+ const dot = msg . endsWith ( '.' ) ? '' : '.' ;
64+ const renderedType = type === 'unhandledrejection' ? 'promise rejection' : type ;
65+ showGlobalErrorMessage ( `JavaScript ${ renderedType } : ${ msg } ${ dot } Open browser console to see more details.` ) ;
5266}
5367
5468function initGlobalErrorHandler ( ) {
@@ -59,13 +73,14 @@ function initGlobalErrorHandler() {
5973 if ( ! window . config ) {
6074 showGlobalErrorMessage ( `Gitea JavaScript code couldn't run correctly, please check your custom templates` ) ;
6175 }
62- // we added an event handler for window error at the very beginning of <script> of page head
63- // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init
64- // then in this init, we can collect all error events and show them
76+ // we added an event handler for window error at the very beginning of <script> of page head the
77+ // handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before
78+ // this init then in this init, we can collect all error events and show them.
6579 for ( const e of window . _globalHandlerErrors || [ ] ) {
6680 processWindowErrorEvent ( e ) ;
6781 }
68- // then, change _globalHandlerErrors to an object with push method, to process further error events directly
82+ // then, change _globalHandlerErrors to an object with push method, to process further error
83+ // events directly
6984 window . _globalHandlerErrors = { _inited : true , push : ( e ) => processWindowErrorEvent ( e ) } ;
7085}
7186
0 commit comments