diff --git a/packages/runtime-dom/src/modules/events.ts b/packages/runtime-dom/src/modules/events.ts index a81b6fdf3f0..7172e3efdd2 100644 --- a/packages/runtime-dom/src/modules/events.ts +++ b/packages/runtime-dom/src/modules/events.ts @@ -15,18 +15,23 @@ type EventValue = Function | Function[] // Async edge case fix requires storing an event listener's attach timestamp. let _getNow: () => number = Date.now -// Determine what event timestamp the browser is using. Annoyingly, the -// timestamp can either be hi-res (relative to page load) or low-res -// (relative to UNIX epoch), so in order to compare time we have to use the -// same timestamp type when saving the flush timestamp. -if ( - typeof document !== 'undefined' && - _getNow() > document.createEvent('Event').timeStamp -) { - // if the low-res timestamp which is bigger than the event timestamp - // (which is evaluated AFTER) it means the event is using a hi-res timestamp, - // and we need to use the hi-res version for event listeners as well. - _getNow = () => performance.now() +let skipTimestampCheck = false + +if (typeof window !== 'undefined') { + // Determine what event timestamp the browser is using. Annoyingly, the + // timestamp can either be hi-res (relative to page load) or low-res + // (relative to UNIX epoch), so in order to compare time we have to use the + // same timestamp type when saving the flush timestamp. + if (_getNow() > document.createEvent('Event').timeStamp) { + // if the low-res timestamp which is bigger than the event timestamp + // (which is evaluated AFTER) it means the event is using a hi-res timestamp, + // and we need to use the hi-res version for event listeners as well. + _getNow = () => performance.now() + } + // #3485: Firefox <= 53 has incorrect Event.timeStamp implementation + // and does not fire microtasks in between event propagation, so safe to exclude. + const ffMatch = navigator.userAgent.match(/firefox\/(\d+)/i) + skipTimestampCheck = !!(ffMatch && Number(ffMatch[1]) <= 53) } // To avoid the overhead of repeatedly calling performance.now(), we cache @@ -111,7 +116,8 @@ function createInvoker( // and the handler would only fire if the event passed to it was fired // AFTER it was attached. const timeStamp = e.timeStamp || _getNow() - if (timeStamp >= invoker.attached - 1) { + + if (skipTimestampCheck || timeStamp >= invoker.attached - 1) { callWithAsyncErrorHandling( patchStopImmediatePropagation(e, invoker.value), instance,