Skip to content

Commit 592a808

Browse files
mydeabillyvg
authored andcommitted
feat: Ensure to use unwrapped versions of setTimeout / clearTimeout (#176)
Let's see if that helps with Angular performance some more...! Closes getsentry/sentry-javascript#11661 (hopefully...)
1 parent dc20d46 commit 592a808

File tree

5 files changed

+42
-18
lines changed

5 files changed

+42
-18
lines changed

packages/rrweb/src/record/observer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
patch,
2222
StyleSheetMirror,
2323
nowTimestamp,
24+
setTimeout,
2425
} from '../utils';
2526
import type { observerParam, MutationBufferParam } from '../types';
2627
import {

packages/rrweb/src/record/observers/canvas/2d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
IWindow,
66
listenerHandler,
77
} from '@sentry-internal/rrweb-types';
8-
import { hookSetter, isBlocked, patch } from '../../../utils';
8+
import { hookSetter, isBlocked, patch, setTimeout } from '../../../utils';
99
import { serializeArgs } from './serialize-args';
1010

1111
export default function initCanvas2DMutationObserver(

packages/rrweb/src/record/shadow-dom-manager.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
initScrollObserver,
1010
initAdoptedStyleSheetObserver,
1111
} from './observer';
12-
import { patch, inDom } from '../utils';
12+
import { patch, inDom, setTimeout } from '../utils';
1313
import type { Mirror } from '@sentry-internal/rrweb-snapshot';
1414
import { isNativeShadowDom } from '@sentry-internal/rrweb-snapshot';
1515

packages/rrweb/src/replay/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ import {
7676
getPositionsAndIndex,
7777
uniqueTextMutations,
7878
StyleSheetMirror,
79+
clearTimeout,
80+
setTimeout,
7981
} from '../utils';
8082
import getInjectStyleRules from './styles/inject-style';
8183
import './styles/style.css';

packages/rrweb/src/utils.ts

+37-16
Original file line numberDiff line numberDiff line change
@@ -607,45 +607,66 @@ export function inDom(n: Node): boolean {
607607
return doc.contains(n) || shadowHostInDom(n);
608608
}
609609

610-
let cachedRequestAnimationFrameImplementation:
611-
| undefined
612-
| typeof requestAnimationFrame;
613-
614610
/**
615-
* We generally want to use window.requestAnimationFrame.
611+
* We generally want to use window.requestAnimationFrame / window.setTimeout / window.clearTimeout.
616612
* However, in some cases this may be wrapped (e.g. by Zone.js for Angular),
617613
* so we try to get an unpatched version of this from a sandboxed iframe.
618614
*/
619-
function getRequestAnimationFrameImplementation(): typeof requestAnimationFrame {
620-
if (cachedRequestAnimationFrameImplementation) {
621-
return cachedRequestAnimationFrameImplementation;
615+
616+
interface CacheableImplementations {
617+
requestAnimationFrame: typeof requestAnimationFrame;
618+
setTimeout: typeof setTimeout;
619+
clearTimeout: typeof clearTimeout;
620+
}
621+
622+
const cachedImplementations: Partial<CacheableImplementations> = {};
623+
624+
function getImplementation<T extends keyof CacheableImplementations>(
625+
name: T,
626+
): CacheableImplementations[T] {
627+
const cached = cachedImplementations[name];
628+
if (cached) {
629+
return cached;
622630
}
623631

624632
const document = window.document;
625-
let requestAnimationFrameImplementation = window.requestAnimationFrame;
633+
let impl = window[name] as CacheableImplementations[T];
626634
if (document && typeof document.createElement === 'function') {
627635
try {
628636
const sandbox = document.createElement('iframe');
629637
sandbox.hidden = true;
630638
document.head.appendChild(sandbox);
631639
const contentWindow = sandbox.contentWindow;
632-
if (contentWindow && contentWindow.requestAnimationFrame) {
633-
requestAnimationFrameImplementation =
640+
if (contentWindow && contentWindow[name]) {
641+
impl =
634642
// eslint-disable-next-line @typescript-eslint/unbound-method
635-
contentWindow.requestAnimationFrame;
643+
contentWindow[name] as CacheableImplementations[T];
636644
}
637645
document.head.removeChild(sandbox);
638646
} catch (e) {
639-
// Could not create sandbox iframe, just use window.requestAnimationFrame
647+
// Could not create sandbox iframe, just use window.xxx
640648
}
641649
}
642650

643-
return (cachedRequestAnimationFrameImplementation =
644-
requestAnimationFrameImplementation.bind(window));
651+
return (cachedImplementations[name] = impl.bind(
652+
window,
653+
) as CacheableImplementations[T]);
645654
}
646655

647656
export function onRequestAnimationFrame(
648657
...rest: Parameters<typeof requestAnimationFrame>
649658
): ReturnType<typeof requestAnimationFrame> {
650-
return getRequestAnimationFrameImplementation()(...rest);
659+
return getImplementation('requestAnimationFrame')(...rest);
660+
}
661+
662+
export function setTimeout(
663+
...rest: Parameters<typeof window.setTimeout>
664+
): ReturnType<typeof window.setTimeout> {
665+
return getImplementation('setTimeout')(...rest);
666+
}
667+
668+
export function clearTimeout(
669+
...rest: Parameters<typeof window.clearTimeout>
670+
): ReturnType<typeof window.clearTimeout> {
671+
return getImplementation('clearTimeout')(...rest);
651672
}

0 commit comments

Comments
 (0)