ref(browser)!: Drop Safari 14 support - remove navigationStart fallback and pagehide listeners#21293
Conversation
logaretm
left a comment
There was a problem hiding this comment.
The Safari 14/pagehide removal is fine for v11, but the PR needs to clean up the stale tests.
At any case, this PR won't be merged at the moment until v11 is being prepared for release given its breaking changes nature.
| const navigationStart = performance.timing?.navigationStart; | ||
| if (typeof navigationStart === 'number') { | ||
| const navigationStartDelta = Math.abs(navigationStart + performanceNow - dateNow); | ||
| if (navigationStartDelta < threshold) { | ||
| return navigationStart; | ||
| } | ||
| } |
There was a problem hiding this comment.
Some tests still assert the navigationStart. Those tests will fail.
|
Thanks for the review @logaretm! You're right — the stale tests would have failed. I've pushed a follow-up commit that cleans everything up:
Understood on the v11 merge timing — happy for this to wait until the release prep begins. |
|
Thanks for applying the changes, I will mark it as a draft to prevent merging it by mistake. |
28e6ed1 to
c3f0e2a
Compare
navigationStart fallback and pagehide listeners
…rt fallback and pagehide listeners Safari 15+ has full performance.timeOrigin support and reliable visibilitychange events. This removes: - performance.timing?.navigationStart fallback in getBrowserTimeOrigin() - pagehide event listeners in web-vitals vendored code (onHidden, getVisibilityWatcher, whenIdleOrHidden) - isPageHidden() helper that checked for pagehide event type - Updated integration tests to use visibilitychange instead of pagehide This saves ~100 bytes of unnecessary browser compatibility code. Closes getsentry#18707
Addresses review feedback from @logaretm: - Remove test that asserted navigationStart fallback (code path removed) - Remove navigationStart stubs from remaining tests (no longer relevant) - Update test descriptions to reflect current behavior - Update replay fixture timeOrigin.mode from 'navigationStart' to 'timeOrigin' - Fix missing trailing newlines in all modified files
The Safari 14 support removal dropped the `pagehide` listener from the vendored web-vitals code, so the React/react-router SPA E2E apps that flushed the INP span by dispatching a bare `pagehide` event no longer triggered a flush and timed out. Switch them to the same `visibilitychange` pattern already used in the browser integration tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ff256f6 to
284e5ea
Compare
| }; | ||
|
|
||
| addPageListener('visibilitychange', onHiddenOrPageHide, { capture: true, once: true }); | ||
| // Some browsers have buggy implementations of visibilitychange, | ||
| // so we use pagehide in addition, just to be safe. | ||
| addPageListener('pagehide', onHiddenOrPageHide, { capture: true, once: true }); | ||
| addPageListener('visibilitychange', onHiddenCallback, { capture: true }); | ||
| }; |
There was a problem hiding this comment.
Bug: The onHidden function adds a visibilitychange event listener without the { once: true } option and never removes it, causing a memory leak on every page load.
Severity: MEDIUM
Suggested Fix
Restore the { once: true } option to the addPageListener call within the onHidden function. This will ensure the event listener is automatically removed after it has been invoked once, preventing the memory leak and restoring the previous, correct behavior.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: packages/browser-utils/src/metrics/web-vitals/lib/onHidden.ts#L39-L42
Potential issue: The `onHidden` function registers a `visibilitychange` event listener
using `addPageListener` but omits the `{ once: true }` option that was present in the
previous implementation. Since there is no corresponding call to `removePageListener`,
the listener is never garbage collected and persists for the entire page lifetime. This
function is called during the setup of `webVitalsIntegration` via `trackLcpAsSpan` and
`trackClsAsSpan`. Consequently, new, persistent listeners are created on every page
load, leading to a memory leak. While an internal guard prevents the callback from
executing more than once, the listener itself remains attached to the DOM.
There was a problem hiding this comment.
a page load cleans up all event listeners and the integration is only set up once, so this doesn't cause a memory leak. also matches web-vitals@4.2.4
Summary
Closes #18707
Drops Safari 14 compatibility code by removing the
performance.timing.navigationStartfallback andpagehideevent listeners from the vendored web-vitals code.Changes
Core timing (
packages/core/src/utils/time.ts):performance.timing?.navigationStartfallback ingetBrowserTimeOrigin()— Safari 15+ supportsperformance.timeOriginnativelyWeb-vitals vendored code (
packages/browser-utils/src/metrics/web-vitals/lib/):onHidden.ts: Removedpagehideevent listener, now only listens tovisibilitychangegetVisibilityWatcher.ts: Removedpagehideevent listener, removedisPageHidden()helper, simplifiedonVisibilityUpdateto only checkdocument.visibilityStatewhenIdleOrHidden.ts: Removedpagehideadd/remove listener pairsIntegration tests:
hidePage()helpers to dispatchvisibilitychange(withdocument.visibilityState = 'hidden') instead ofpagehidepagehidedispatches to use the same patternsentry.report_event: 'pagehide'data labels unchanged (backend-facing API contract)Bundle size impact
Saves ~100 bytes by removing unnecessary fallback code paths and event listeners.