From db65956ab2311d7c93af8df59626382fba209d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Zugmeyer?= Date: Mon, 11 Dec 2023 18:37:16 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20[RUM-2280]=20fix=20duplicated=20?= =?UTF-8?q?mutations=20when=20using=20Shadow=20DOM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rum/src/domain/record/record.spec.ts | 16 ++++++++++++++++ .../src/domain/record/shadowRootsController.ts | 3 +++ 2 files changed, 19 insertions(+) diff --git a/packages/rum/src/domain/record/record.spec.ts b/packages/rum/src/domain/record/record.spec.ts index f94f007b1b..9a27282c12 100644 --- a/packages/rum/src/domain/record/record.spec.ts +++ b/packages/rum/src/domain/record/record.spec.ts @@ -311,6 +311,22 @@ describe('record', () => { expect(innerMutationData.isChecked).toBe(true) }) + it('should record the change event inside a shadow root only once, regardless if the DOM is serialized multiple times', () => { + const radio = appendElement('', createShadow()) as HTMLInputElement + startRecording() + + recordApi.takeSubsequentFullSnapshot() + + radio.checked = true + radio.dispatchEvent(createNewEvent('change', { target: radio, composed: false })) + + const inputRecords = getEmittedRecords().filter( + (record) => record.type === RecordType.IncrementalSnapshot && record.data.source === IncrementalSource.Input + ) + + expect(inputRecords.length).toBe(1) + }) + it('should clean the state once the shadow dom is removed to avoid memory leak', () => { const shadowRoot = createShadow() appendElement('
', shadowRoot) diff --git a/packages/rum/src/domain/record/shadowRootsController.ts b/packages/rum/src/domain/record/shadowRootsController.ts index db2a5c98c3..11bb2e346b 100644 --- a/packages/rum/src/domain/record/shadowRootsController.ts +++ b/packages/rum/src/domain/record/shadowRootsController.ts @@ -30,6 +30,9 @@ export const initShadowRootsController = ( const shadowRootsController: ShadowRootsController = { addShadowRoot: (shadowRoot: ShadowRoot) => { + if (controllerByShadowRoot.has(shadowRoot)) { + return + } const { stop: stopMutationObserver, flush } = initMutationObserver( mutationCb, configuration,