diff --git a/config/gni/devtools_grd_files.gni b/config/gni/devtools_grd_files.gni index 55953d67917..fb9a9681cae 100644 --- a/config/gni/devtools_grd_files.gni +++ b/config/gni/devtools_grd_files.gni @@ -439,6 +439,8 @@ grd_files_release_sources = [ "front_end/models/persistence/persistence-legacy.js", "front_end/models/persistence/persistence-meta.js", "front_end/models/persistence/persistence.js", + "front_end/models/react_native/ReactDevToolsBindingsModel.js", + "front_end/models/react_native/react_native.js", "front_end/models/source_map_scopes/source_map_scopes.js", "front_end/models/text_utils/text_utils-legacy.js", "front_end/models/text_utils/text_utils.js", @@ -1394,6 +1396,7 @@ grd_files_debug_sources = [ "front_end/panels/protocol_monitor/components/JSONEditor.css.js", "front_end/panels/protocol_monitor/components/JSONEditor.js", "front_end/panels/protocol_monitor/protocolMonitor.css.js", + "front_end/panels/react_devtools/ReactDevToolsModel.js", "front_end/panels/react_devtools/reactDevToolsPlaceholder.css.js", "front_end/panels/react_devtools/ReactDevToolsPlaceholder.js", "front_end/panels/react_devtools/ReactDevToolsView.js", diff --git a/front_end/models/react_native/BUILD.gn b/front_end/models/react_native/BUILD.gn index 06084e402a8..b66d06a691f 100644 --- a/front_end/models/react_native/BUILD.gn +++ b/front_end/models/react_native/BUILD.gn @@ -27,6 +27,7 @@ devtools_entrypoint("bundle") { visibility = [ ":*", + "../../panels/react_devtools/*" ] visibility += devtools_models_visibility diff --git a/front_end/models/react_native/ReactDevToolsBindingsModel.ts b/front_end/models/react_native/ReactDevToolsBindingsModel.ts index 6579d918944..650db71ebd7 100644 --- a/front_end/models/react_native/ReactDevToolsBindingsModel.ts +++ b/front_end/models/react_native/ReactDevToolsBindingsModel.ts @@ -13,6 +13,14 @@ type BindingCalledEventTargetEvent = Common.EventTarget.EventTargetEvent> = new Map(); @@ -29,7 +37,11 @@ export class ReactDevToolsBindingsModel extends SDK.SDKModel.SDKModel { } runtimeModel.addEventListener(SDK.RuntimeModel.Events.BindingCalled, this.bindingCalled, this); - void this.enable(target); + void this.enable(target).then(() => this.onInitialization()); + } + + private onInitialization(): void { + this.dispatchEventToListeners(Events.Initialized); } private bindingCalled(event: BindingCalledEventTargetEvent): void { diff --git a/front_end/panels/react_devtools/BUILD.gn b/front_end/panels/react_devtools/BUILD.gn index 30885f373dc..f145e95de83 100644 --- a/front_end/panels/react_devtools/BUILD.gn +++ b/front_end/panels/react_devtools/BUILD.gn @@ -24,9 +24,10 @@ devtools_module("react_devtools_placeholder") { } devtools_module("react_devtools") { - sources = [ "ReactDevToolsView.ts" ] + sources = [ "ReactDevToolsView.ts", "ReactDevToolsModel.ts" ] deps = [ + "../../models/react_native:bundle", "../../ui/legacy:bundle", ] } diff --git a/front_end/panels/react_devtools/ReactDevToolsModel.ts b/front_end/panels/react_devtools/ReactDevToolsModel.ts new file mode 100644 index 00000000000..a278a4e6586 --- /dev/null +++ b/front_end/panels/react_devtools/ReactDevToolsModel.ts @@ -0,0 +1,68 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// Copyright 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import * as SDK from '../../core/sdk/sdk.js'; +import * as ReactNativeModels from '../../models/react_native/react_native.js'; + +type JSONValue = null | string | number | boolean | {[key: string]: JSONValue} | JSONValue[]; + +export const enum Events { + Initialized = 'Initialized', + MessageReceived = 'MessageReceived', +} + +export type EventTypes = { + [Events.Initialized]: void, + [Events.MessageReceived]: JSONValue, +}; + +export class ReactDevToolsModel extends SDK.SDKModel.SDKModel { + private static readonly FUSEBOX_BINDING_NAMESPACE = 'react-devtools'; + private readonly rdtModel: ReactNativeModels.ReactDevToolsBindingsModel.ReactDevToolsBindingsModel | null; + + constructor(target: SDK.Target.Target) { + super(target); + + const model = target.model(ReactNativeModels.ReactDevToolsBindingsModel.ReactDevToolsBindingsModel); + if (!model) { + throw new Error('Failed to construct ReactDevToolsModel: ReactDevToolsBindingsModel was null'); + } + + this.rdtModel = model; + model.addEventListener(ReactNativeModels.ReactDevToolsBindingsModel.Events.Initialized, this.initialize, this); + } + + private initialize(): void { + const rdtModel = this.rdtModel; + if (!rdtModel) { + throw new Error('Failed to initialize ReactDevToolsModel: ReactDevToolsBindingsModel was null'); + } + + rdtModel.subscribeToDomainMessages( + ReactDevToolsModel.FUSEBOX_BINDING_NAMESPACE, + message => this.onMessage(message), + ); + void rdtModel.initializeDomain(ReactDevToolsModel.FUSEBOX_BINDING_NAMESPACE).then(() => this.onInitialization()); + } + + private onInitialization(): void { + this.dispatchEventToListeners(Events.Initialized); + } + + async sendMessage(message: JSONValue): Promise { + const rdtModel = this.rdtModel; + if (!rdtModel) { + throw new Error('Failed to send message from ReactDevToolsModel: ReactDevToolsBindingsModel was null'); + } + + await rdtModel.sendMessage(ReactDevToolsModel.FUSEBOX_BINDING_NAMESPACE, message); + } + + onMessage(message: JSONValue): void { + this.dispatchEventToListeners(Events.MessageReceived, message); + } +} + +SDK.SDKModel.SDKModel.register(ReactDevToolsModel, {capabilities: SDK.Target.Capability.JS, autostart: false}); diff --git a/front_end/panels/react_devtools/react_devtools.ts b/front_end/panels/react_devtools/react_devtools.ts index 4d7c2bfb3f0..cf7a4bb3979 100644 --- a/front_end/panels/react_devtools/react_devtools.ts +++ b/front_end/panels/react_devtools/react_devtools.ts @@ -5,5 +5,6 @@ import * as ReactDevToolsView from './ReactDevToolsView.js'; import * as ReactDevToolsPlaceholder from './ReactDevToolsPlaceholder.js'; +import * as ReactDevToolsModel from './ReactDevToolsModel.js'; -export {ReactDevToolsView, ReactDevToolsPlaceholder}; +export {ReactDevToolsView, ReactDevToolsPlaceholder, ReactDevToolsModel};