diff --git a/front_end/models/react_native/ReactDevToolsBindingsModel.ts b/front_end/models/react_native/ReactDevToolsBindingsModel.ts index e3cc309367e..59a57b18d7c 100644 --- a/front_end/models/react_native/ReactDevToolsBindingsModel.ts +++ b/front_end/models/react_native/ReactDevToolsBindingsModel.ts @@ -40,6 +40,18 @@ export class ReactDevToolsBindingsModel extends SDK.SDKModel.SDKModel { private fuseboxDispatcherIsInitialized = false; private readonly domainToMessageQueue: Map> = new Map(); + override dispose(): void { + this.domainToListeners.clear(); + this.domainToMessageQueue.clear(); + + const runtimeModel = this.target().model(SDK.RuntimeModel.RuntimeModel); + runtimeModel?.removeEventListener(SDK.RuntimeModel.Events.BindingCalled, this.bindingCalled, this); + runtimeModel?.removeEventListener( + SDK.RuntimeModel.Events.ExecutionContextCreated, this.onExecutionContextCreated, this); + runtimeModel?.removeEventListener( + SDK.RuntimeModel.Events.ExecutionContextDestroyed, this.onExecutionContextDestroyed, this); + } + private bindingCalled(event: BindingCalledEventTargetEvent): void { // If binding name is not initialized, then we failed to get its name if (this.messagingBindingName === null || event.data.name !== this.messagingBindingName) { diff --git a/front_end/panels/react_devtools/ReactDevToolsModel.ts b/front_end/panels/react_devtools/ReactDevToolsModel.ts index 772d2be45da..d964d931203 100644 --- a/front_end/panels/react_devtools/ReactDevToolsModel.ts +++ b/front_end/panels/react_devtools/ReactDevToolsModel.ts @@ -77,7 +77,34 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel { ); // Notify backend if Chrome DevTools was closed, marking frontend as disconnected - window.addEventListener('beforeunload', () => this.#bridge?.shutdown()); + window.addEventListener('beforeunload', this.#handleBeforeUnload); + } + + override dispose(): void { + this.#bridge?.removeListener('reloadAppForProfiling', this.#handleReloadAppForProfiling); + this.#bridge?.shutdown(); + + this.#bindingsModel.removeEventListener( + ReactNativeModels.ReactDevToolsBindingsModel.Events.BackendExecutionContextCreated, + this.#handleBackendExecutionContextCreated, + this, + ); + this.#bindingsModel.removeEventListener( + ReactNativeModels.ReactDevToolsBindingsModel.Events.BackendExecutionContextUnavailable, + this.#handleBackendExecutionContextUnavailable, + this, + ); + this.#bindingsModel.removeEventListener( + ReactNativeModels.ReactDevToolsBindingsModel.Events.BackendExecutionContextDestroyed, + this.#handleBackendExecutionContextDestroyed, + this, + ); + + window.removeEventListener('beforeunload', this.#handleBeforeUnload); + + this.#bridge = null; + this.#store = null; + this.#listeners.clear(); } override dispose(): void { @@ -151,6 +178,10 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel { return rdtBindingsModel.sendMessage(ReactDevToolsModel.FUSEBOX_BINDING_NAMESPACE, message); } + #handleBeforeUnload = (): void => { + this.#bridge?.shutdown(); + }; + #handleBackendExecutionContextCreated(): void { const rdtBindingsModel = this.#bindingsModel; if (!rdtBindingsModel) {