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 34fa3a062e7..13607e8774e 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(); } ensureInitialized(): void { @@ -147,6 +174,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) { @@ -163,10 +194,17 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel { #finishInitializationAndNotify(): void { this.#bridge = ReactDevTools.createBridge(this.#wall); - this.#store = ReactDevTools.createStore(this.#bridge); + this.#store = ReactDevTools.createStore(this.#bridge, { + supportsReloadAndProfile: true, + }); + this.#bridge.addListener('reloadAppForProfiling', this.#handleReloadAppForProfiling); this.dispatchEventToListeners(Events.InitializationCompleted); } + #handleReloadAppForProfiling(): void { + SDK.ResourceTreeModel.ResourceTreeModel.reloadAllPages(false); + } + #handleBackendExecutionContextUnavailable({data: errorMessage}: ReactDevToolsBindingsBackendExecutionContextUnavailableEvent): void { this.dispatchEventToListeners(Events.InitializationFailed, errorMessage); } diff --git a/front_end/third_party/react-devtools/package/frontend.d.ts b/front_end/third_party/react-devtools/package/frontend.d.ts index 74a88c36a85..52536739cca 100644 --- a/front_end/third_party/react-devtools/package/frontend.d.ts +++ b/front_end/third_party/react-devtools/package/frontend.d.ts @@ -15,7 +15,9 @@ export type Wall = { }; export type Bridge = { - shutdown: () => void, + addListener(event: string, listener: (params: unknown) => any): void; + removeListener(event: string, listener: Function): void; + shutdown: () => void; }; export type Store = Object; export type BrowserTheme = 'dark' | 'light';