From 9f975534e122ddedab0f08e3f671692708d50a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Zugmeyer?= Date: Fri, 24 Apr 2020 11:09:05 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20[RUM]=20fix=20angular=20compatib?= =?UTF-8?q?ility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum/src/domMutationCollection.ts | 37 +++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/rum/src/domMutationCollection.ts b/packages/rum/src/domMutationCollection.ts index dc98449ed4..3b0f662ef7 100644 --- a/packages/rum/src/domMutationCollection.ts +++ b/packages/rum/src/domMutationCollection.ts @@ -1,13 +1,46 @@ import { monitor } from '@datadog/browser-core' import { LifeCycle, LifeCycleEventType } from './lifeCycle' +type MutationObserverConstructor = new (callback: MutationCallback) => MutationObserver + interface BrowserWindow extends Window { - MutationObserver?: MutationObserver + MutationObserver?: MutationObserverConstructor + Zone?: { + __symbol__(name: string): string + } +} + +function getMutationObserverConstructor(): MutationObserverConstructor | undefined { + let constructor: MutationObserverConstructor | undefined + const browserWindow: BrowserWindow = window + + // Angular uses Zone.js to provide a context persisting accross async tasks. Zone.js replaces the + // global MutationObserver constructor with a patched version to support the context propagation. + // There is an ongoing issue[1][2] with this setup when using a MutationObserver within a Angular + // component: on some occasions, the callback is being called in an infinite loop, causing the + // page to freeze (even if the callback is completely empty). + // + // To work around this issue, we are using the Zone __symbol__ API to get the original, unpatched + // MutationObserver constructor. + // + // [1] https://github.com/angular/angular/issues/26948 + // [2] https://github.com/angular/angular/issues/31712 + if (browserWindow.Zone) { + const symbol = browserWindow.Zone.__symbol__('MutationObserver') + constructor = browserWindow[symbol as any] as any + } + + if (!constructor) { + constructor = browserWindow.MutationObserver + } + + return constructor } export function startDOMMutationCollection(lifeCycle: LifeCycle) { let observer: MutationObserver | undefined - if ((window as BrowserWindow).MutationObserver) { + const MutationObserver = getMutationObserverConstructor() + if (MutationObserver) { observer = new MutationObserver( monitor(() => { lifeCycle.notify(LifeCycleEventType.DOM_MUTATED)