diff --git a/libraries/browser-tracker-core/src/helpers/browser_props.ts b/libraries/browser-tracker-core/src/helpers/browser_props.ts index 4a247550d..41f63b612 100644 --- a/libraries/browser-tracker-core/src/helpers/browser_props.ts +++ b/libraries/browser-tracker-core/src/helpers/browser_props.ts @@ -18,26 +18,44 @@ function useResizeObserver(): boolean { } let resizeObserverInitialized = false; +let readBrowserPropertiesTask: number | null = null; function initializeResizeObserver() { if (resizeObserverInitialized) { return; } - if(!document || !document.body || !document.documentElement) { + if (!document || !document.body || !document.documentElement) { return; } resizeObserverInitialized = true; const resizeObserver = new ResizeObserver((entries) => { - for (let entry of entries) { - if (entry.target === document.body || entry.target === document.documentElement) { - cachedProperties = readBrowserProperties(); - } + if (readBrowserPropertiesTask === null) { + // The browser property lookup causes a forced synchronous layout when offsets/sizes are + // queried. It's possible that the forced synchronous layout causes the ResizeObserver + // to be fired again, leading to an infinite loop and the "ResizeObserver loop completed + // with undelivered notifications" error. + readBrowserPropertiesTask = scheduleBrowserPropertiesCallback(() => { + readBrowserPropertiesTask = null; + for (let entry of entries) { + if (entry.target === document.body || entry.target === document.documentElement) { + cachedProperties = readBrowserProperties(); + } + } + }); } }); resizeObserver.observe(document.body); resizeObserver.observe(document.documentElement); } +function scheduleBrowserPropertiesCallback(func: () => void) { + if (requestAnimationFrame) { + return requestAnimationFrame(func); + } else { + return window.setTimeout(func, 0); + } +} + let cachedProperties: BrowserProperties; /* Separator used for dimension values e.g. widthxheight */