Skip to content

ref: Drop Safari 14 support - remove navigationStart fallback and pagehide listeners#21293

Open
eddie333016 wants to merge 2 commits into
getsentry:developfrom
eddie333016:eddie333016/drop-safari-14-support
Open

ref: Drop Safari 14 support - remove navigationStart fallback and pagehide listeners#21293
eddie333016 wants to merge 2 commits into
getsentry:developfrom
eddie333016:eddie333016/drop-safari-14-support

Conversation

@eddie333016
Copy link
Copy Markdown

Summary

Closes #18707

Drops Safari 14 compatibility code by removing the performance.timing.navigationStart fallback and pagehide event listeners from the vendored web-vitals code.

Changes

Core timing (packages/core/src/utils/time.ts):

  • Removed performance.timing?.navigationStart fallback in getBrowserTimeOrigin() — Safari 15+ supports performance.timeOrigin natively
  • Updated JSDoc to reflect the simplified fallback chain

Web-vitals vendored code (packages/browser-utils/src/metrics/web-vitals/lib/):

  • onHidden.ts: Removed pagehide event listener, now only listens to visibilitychange
  • getVisibilityWatcher.ts: Removed pagehide event listener, removed isPageHidden() helper, simplified onVisibilityUpdate to only check document.visibilityState
  • whenIdleOrHidden.ts: Removed pagehide add/remove listener pairs

Integration tests:

  • Updated hidePage() helpers to dispatch visibilitychange (with document.visibilityState = 'hidden') instead of pagehide
  • Updated inline pagehide dispatches to use the same pattern
  • Kept sentry.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.

…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
@eddie333016 eddie333016 requested a review from a team as a code owner June 2, 2026 13:04
@eddie333016 eddie333016 requested review from logaretm and mydea and removed request for a team June 2, 2026 13:04
Copy link
Copy Markdown
Member

@logaretm logaretm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines -117 to -123
const navigationStart = performance.timing?.navigationStart;
if (typeof navigationStart === 'number') {
const navigationStartDelta = Math.abs(navigationStart + performanceNow - dateNow);
if (navigationStartDelta < threshold) {
return navigationStart;
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some tests still assert the navigationStart. Those tests will fail.

@logaretm logaretm requested a review from Lms24 June 2, 2026 18:20
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

👋 @mydea, @Lms24 — Please review this PR when you get a chance!

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
@eddie333016
Copy link
Copy Markdown
Author

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:

  1. Removed the navigationStart fallback test — the code path no longer exists, so this test would have incorrectly expected navigationStart to be returned when it now falls through to Date.now() - performance.now()
  2. Removed timing.navigationStart stubs from the two remaining tests — they're no longer relevant since the production code never reads performance.timing
  3. Updated test descriptions — no longer reference navigationStart
  4. Updated replay test fixturestimeOrigin.mode changed from 'navigationStart' to 'timeOrigin' to match the new code path
  5. Fixed missing trailing newlines in all modified files

Understood on the v11 merge timing — happy for this to wait until the release prep begins.

Comment on lines 39 to 42
};

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, once: true });
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The visibilitychange listener uses { once: true }, causing it to be prematurely removed if a page loads in the background, preventing web vital metrics from being reported.
Severity: HIGH

Suggested Fix

Remove the { once: true } option from the addPageListener call for the visibilitychange event. Instead, the listener should be manually removed from within the callback function only after the document.visibilityState === 'hidden' condition is met and the provided callback cb has been executed. This ensures the listener persists across visibility state changes until it has successfully performed its action.

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 `visibilitychange` event listener in `onHidden.ts` is configured
with `{ once: true }`, which causes it to be removed after its first invocation. If a
page loads in a background tab and is later brought to the foreground, a
`visibilitychange` event fires. At this moment, `document.visibilityState` is
`'visible'`, so the callback's condition check fails. However, the listener is still
removed. When the user eventually navigates away, making the page hidden, no listener
exists to handle the event. This prevents the `onHidden` callback from ever executing,
leading to a complete loss of web vital metrics (like LCP and CLS) for any page loaded
in a background tab.

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Drop Support for Safari 14

2 participants