diff options
author | Giteabot <teabot@gitea.io> | 2024-02-29 08:22:53 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-29 01:22:53 +0100 |
commit | 9abba8c11a2eddfa8da25946fdd43be8d0c53689 (patch) | |
tree | a07819d34bdb2a646f0bb8454dffbdbe0407f4f7 /web_src | |
parent | 5477728282de19b1638691b88449b1933ed5a4d8 (diff) | |
download | gitea-9abba8c11a2eddfa8da25946fdd43be8d0c53689.tar.gz gitea-9abba8c11a2eddfa8da25946fdd43be8d0c53689.zip |
Fix/Improve `processWindowErrorEvent` (#29407) (#29480)
Backport #29407 by @silverwind
- `e.error` can be undefined in some cases which would raise an error
inside this error handler, fixed that.
- The displayed message mentions looking into the console, but in my
case of error from `ResizeObserver` there was nothing there, so add this
logging. I think this logging was once there but got lost during
refactoring.
Co-authored-by: silverwind <me@silverwind.io>
Diffstat (limited to 'web_src')
-rw-r--r-- | web_src/js/bootstrap.js | 57 |
1 files changed, 36 insertions, 21 deletions
diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index e46c91e5e6..c0047b0ac2 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -1,5 +1,6 @@ // DO NOT IMPORT window.config HERE! -// to make sure the error handler always works, we should never import `window.config`, because some user's custom template breaks it. +// to make sure the error handler always works, we should never import `window.config`, because +// some user's custom template breaks it. // This sets up the URL prefix used in webpack's chunk loading. // This file must be imported before any lazy-loading is being attempted. @@ -26,29 +27,42 @@ export function showGlobalErrorMessage(msg) { } /** - * @param {ErrorEvent} e + * @param {ErrorEvent|PromiseRejectionEvent} event - Event + * @param {string} event.message - Only present on ErrorEvent + * @param {string} event.error - Only present on ErrorEvent + * @param {string} event.type - Only present on ErrorEvent + * @param {string} event.filename - Only present on ErrorEvent + * @param {number} event.lineno - Only present on ErrorEvent + * @param {number} event.colno - Only present on ErrorEvent + * @param {string} event.reason - Only present on PromiseRejectionEvent + * @param {number} event.promise - Only present on PromiseRejectionEvent */ -function processWindowErrorEvent(e) { - const err = e.error ?? e.reason; +function processWindowErrorEvent({error, reason, message, type, filename, lineno, colno}) { + const err = error ?? reason; const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin)); + const {runModeIsProd} = window.config ?? {}; - // error is likely from browser extension or inline script. Do not show these in production builds. - if (!err.stack?.includes(assetBaseUrl) && window.config?.runModeIsProd) return; - - let message; - if (e.type === 'unhandledrejection') { - message = `JavaScript promise rejection: ${err.message}.`; - } else { - message = `JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}).`; + // `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likly a + // non-critical event from the browser. We log them but don't show them to users. Examples: + // - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors + // - https://github.com/mozilla-mobile/firefox-ios/issues/10817 + // - https://github.com/go-gitea/gitea/issues/20240 + if (!err) { + if (message) console.error(new Error(message)); + if (runModeIsProd) return; } - if (!e.error && e.lineno === 0 && e.colno === 0 && e.filename === '' && window.navigator.userAgent.includes('FxiOS/')) { - // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240 - // 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. - return; // ignore such nonsense error event + // If the error stack trace does not include the base URL of our script assets, it likely came + // from a browser extension or inline script. Do not show such errors in production. + if (err instanceof Error && !err.stack?.includes(assetBaseUrl) && runModeIsProd) { + return; } - showGlobalErrorMessage(`${message} Open browser console to see more details.`); + let msg = err?.message ?? message; + if (lineno) msg += ` (${filename} @ ${lineno}:${colno})`; + const dot = msg.endsWith('.') ? '' : '.'; + const renderedType = type === 'unhandledrejection' ? 'promise rejection' : type; + showGlobalErrorMessage(`JavaScript ${renderedType}: ${msg}${dot} Open browser console to see more details.`); } function initGlobalErrorHandler() { @@ -59,13 +73,14 @@ function initGlobalErrorHandler() { if (!window.config) { showGlobalErrorMessage(`Gitea JavaScript code couldn't run correctly, please check your custom templates`); } - // we added an event handler for window error at the very beginning of <script> of page head - // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init - // then in this init, we can collect all error events and show them + // we added an event handler for window error at the very beginning of <script> of page head the + // handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before + // this init then in this init, we can collect all error events and show them. for (const e of window._globalHandlerErrors || []) { processWindowErrorEvent(e); } - // then, change _globalHandlerErrors to an object with push method, to process further error events directly + // then, change _globalHandlerErrors to an object with push method, to process further error + // events directly window._globalHandlerErrors = {_inited: true, push: (e) => processWindowErrorEvent(e)}; } |