Skip to content

Commit

Permalink
fix(replay): Disable mousemove sampling in rrweb for iOS browsers (#1…
Browse files Browse the repository at this point in the history
…4937)

This PR updates the rrweb sampling options depending on the userAgent as
this tends to block the main thread on iOS browsers.

closes #14534
  • Loading branch information
chargome committed Jan 8, 2025
1 parent e31e1c5 commit fa54a0f
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/core/src/utils-hoist/worldwide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type BackwardsCompatibleSentryCarrier = SentryCarrier & {

/** Internal global with common properties and Sentry extensions */
export type InternalGlobal = {
navigator?: { userAgent?: string };
navigator?: { userAgent?: string; maxTouchPoints?: number };
console: Console;
PerformanceObserver?: any;
Sentry?: any;
Expand Down
2 changes: 2 additions & 0 deletions packages/replay-internal/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { createBreadcrumb } from './util/createBreadcrumb';
import { createPerformanceEntries } from './util/createPerformanceEntries';
import { createPerformanceSpans } from './util/createPerformanceSpans';
import { debounce } from './util/debounce';
import { getRecordingSamplingOptions } from './util/getRecordingSamplingOptions';
import { getHandleRecordingEmit } from './util/handleRecordingEmit';
import { isExpired } from './util/isExpired';
import { isSessionExpired } from './util/isSessionExpired';
Expand Down Expand Up @@ -394,6 +395,7 @@ export class ReplayContainer implements ReplayContainerInterface {
checkoutEveryNms: Math.max(360_000, this._options._experiments.continuousCheckout),
}),
emit: getHandleRecordingEmit(this),
...getRecordingSamplingOptions(),
onMutation: this._onMutationHandler,
...(canvasOptions
? {
Expand Down
22 changes: 22 additions & 0 deletions packages/replay-internal/src/util/getRecordingSamplingOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GLOBAL_OBJ } from '@sentry/core';

const NAVIGATOR = GLOBAL_OBJ.navigator;

/**
* Disable sampling mousemove events on iOS browsers as this can cause blocking the main thread
* https://github.com/getsentry/sentry-javascript/issues/14534
*/
export function getRecordingSamplingOptions(): Partial<{ sampling: { mousemove: boolean } }> {
if (
/iPhone|iPad|iPod/i.test(NAVIGATOR?.userAgent ?? '') ||
(/Macintosh/i.test(NAVIGATOR?.userAgent ?? '') && NAVIGATOR?.maxTouchPoints && NAVIGATOR?.maxTouchPoints > 1)
) {
return {
sampling: {
mousemove: false,
},
};
}

return {};
}
54 changes: 54 additions & 0 deletions packages/replay-internal/test/integration/rrweb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,58 @@ describe('Integration | rrweb', () => {
}
`);
});

it('calls rrweb.record with updated sampling options on iOS', async () => {
// Mock iOS user agent
const originalNavigator = global.navigator;
Object.defineProperty(global, 'navigator', {
value: {
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
},
configurable: true,
});

const { mockRecord } = await resetSdkMock({
replayOptions: {},
sentryOptions: {
replaysOnErrorSampleRate: 1.0,
replaysSessionSampleRate: 1.0,
},
});

// Restore original navigator
Object.defineProperty(global, 'navigator', {
value: originalNavigator,
configurable: true,
});

expect(mockRecord.mock.calls[0]?.[0]).toMatchInlineSnapshot(`
{
"blockSelector": ".sentry-block,[data-sentry-block],base,iframe[srcdoc]:not([src]),img,image,svg,video,object,picture,embed,map,audio,link[rel="icon"],link[rel="apple-touch-icon"]",
"collectFonts": true,
"emit": [Function],
"errorHandler": [Function],
"ignoreSelector": ".sentry-ignore,[data-sentry-ignore],input[type="file"]",
"inlineImages": false,
"inlineStylesheet": true,
"maskAllInputs": true,
"maskAllText": true,
"maskAttributeFn": [Function],
"maskInputFn": undefined,
"maskInputOptions": {
"password": true,
},
"maskTextFn": undefined,
"maskTextSelector": ".sentry-mask,[data-sentry-mask]",
"onMutation": [Function],
"sampling": {
"mousemove": false,
},
"slimDOMOptions": "all",
"unblockSelector": "",
"unmaskTextSelector": "",
}
`);
});
});

0 comments on commit fa54a0f

Please sign in to comment.