Skip to content

Commit cbfd87d

Browse files
committed
Cleanup
1 parent f5da6bc commit cbfd87d

13 files changed

Lines changed: 589 additions & 151 deletions

File tree

front_end/core/host/InspectorFrontendHost.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,6 @@ export class InspectorFrontendHostStub implements InspectorFrontendHostAPI {
141141
bringToFront(): void {
142142
}
143143

144-
sendToDevmate(prompt: string): void {
145-
}
146-
147144
closeWindow(): void {
148145
}
149146

front_end/core/host/InspectorFrontendHostAPI.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,6 @@ export interface InspectorFrontendHostAPI {
297297

298298
bringToFront(): void;
299299

300-
sendToDevmate(prompt: string): void;
301-
302300
closeWindow(): void;
303301

304302
copyText(text: string|null|undefined): void;
@@ -455,12 +453,6 @@ export interface SyncInformation {
455453
isSyncPaused?: boolean;
456454
}
457455

458-
export interface FileWriteResult {
459-
success: boolean;
460-
path: string|null;
461-
error?: string;
462-
}
463-
464456
/**
465457
* Enum for recordPerformanceHistogram
466458
* Warning: There is another definition of this enum in the DevTools code

front_end/core/rn_experiments/experimentsImpl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,5 +196,5 @@ Instance.register({
196196
name: RNExperimentName.ENABLE_LIVEMATE_PANEL,
197197
title: 'Enable Livemate Panel',
198198
unstable: true,
199-
enabledByDefault: false,
199+
enabledByDefault: () => globalThis.enableLivematePanel ?? false,
200200
});

front_end/entrypoints/rn_fusebox/rn_fusebox.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import '../../panels/network/network-meta.js';
1414
import '../../panels/react_devtools/react_devtools_components-meta.js';
1515
import '../../panels/react_devtools/react_devtools_profiler-meta.js';
1616
import '../../panels/rn_welcome/rn_welcome-meta.js';
17-
import '../../panels/livemate/livemate-meta.js';
1817
import '../../panels/timeline/timeline-meta.js';
1918

2019
import * as Host from '../../core/host/host.js';
@@ -79,7 +78,6 @@ RNExperiments.RNExperimentsImpl.setIsReactNativeEntryPoint(true);
7978
RNExperiments.RNExperimentsImpl.Instance.enableExperimentsByDefault([
8079
Root.Runtime.ExperimentName.JS_HEAP_PROFILER_ENABLE,
8180
Root.Runtime.ExperimentName.REACT_NATIVE_SPECIFIC_UI,
82-
Root.Runtime.ExperimentName.ENABLE_LIVEMATE_PANEL,
8381
]);
8482

8583
document.addEventListener('visibilitychange', () => {

front_end/global_typings/react_native.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ declare global {
1818
// eslint-disable-next-line no-var
1919
var enableTimelineFrames: boolean|undefined;
2020
// eslint-disable-next-line no-var
21+
var enableLivematePanel: boolean|undefined;
22+
// eslint-disable-next-line no-var
2123
var reactNativeOpenInEditorButtonImage: string|undefined;
2224
// eslint-disable-next-line no-var,@typescript-eslint/naming-convention
2325
var FB_ONLY__reactNativeFeedbackLink: string|undefined;

front_end/panels/livemate/BUILD.gn

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,19 @@ generate_css("css_files") {
1313
}
1414

1515
devtools_module("livemate") {
16-
sources = [ "LivematePanel.ts" ]
16+
sources = [
17+
"LivemateModel.ts",
18+
"LivematePanel.ts",
19+
"LivemateSpec.ts",
20+
]
1721

1822
deps = [
23+
"../../core/common:bundle",
24+
"../../core/i18n:bundle",
25+
"../../core/platform:bundle",
26+
"../../core/sdk:bundle",
27+
"../../generated:protocol",
1928
"../../ui/legacy:bundle",
20-
"../react_devtools:bundle",
2129
]
2230
}
2331

@@ -26,7 +34,6 @@ devtools_entrypoint("bundle") {
2634

2735
deps = [
2836
":css_files",
29-
"../react_devtools:bundle",
3037
":livemate",
3138
]
3239

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
// Copyright 2025 The Chromium Authors. All rights reserved.
3+
// Use of this source code is governed by a BSD-style license that can be
4+
// found in the LICENSE file.
5+
6+
import * as Common from '../../core/common/common.js';
7+
import * as SDK from '../../core/sdk/sdk.js';
8+
import * as ProtocolClient from '../../core/protocol_client/protocol_client.js';
9+
10+
import {
11+
LivemateEventType,
12+
parseLivemateEvent,
13+
type ElementData,
14+
} from './LivemateSpec.js';
15+
16+
export type {ElementData} from './LivemateSpec.js';
17+
18+
let livemateModelInstance: LivemateModel | undefined;
19+
20+
/**
21+
* CDP domain constants for Livemate.
22+
* These match the domain implemented on the C++ side.
23+
*/
24+
const LivemateDomain = {
25+
ENABLE: 'Livemate.enable',
26+
DISABLE: 'Livemate.disable',
27+
ENABLE_INSPECTION: 'Livemate.enableInspection',
28+
DISABLE_INSPECTION: 'Livemate.disableInspection',
29+
INSPECTION_DATA_RECEIVED: 'Livemate.inspectionDataReceived',
30+
} as const;
31+
32+
/**
33+
* LivemateModel handles CDP communication between React Native DevTools
34+
* and the React Native runtime for the Livemate panel.
35+
*
36+
* Communication uses the Livemate CDP domain:
37+
* - Commands: enable, disable, enableInspection, disableInspection
38+
* - Events: inspectionDataReceived
39+
*/
40+
export class LivemateModel extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements SDK.TargetManager.Observer {
41+
#enabled = false;
42+
#target?: SDK.Target.Target;
43+
#inspectionEnabled = false;
44+
#selectedElement?: ElementData;
45+
#originalOnMessageReceived: ((message: object, target: ProtocolClient.InspectorBackend.TargetBase | null) => void) | null = null;
46+
47+
private constructor() {
48+
super();
49+
SDK.TargetManager.TargetManager.instance().observeTargets(this);
50+
}
51+
52+
static instance(opts: {forceNew?: boolean} = {forceNew: false}): LivemateModel {
53+
const {forceNew} = opts;
54+
if (!livemateModelInstance || forceNew) {
55+
livemateModelInstance = new LivemateModel();
56+
}
57+
return livemateModelInstance;
58+
}
59+
60+
get isEnabled(): boolean {
61+
return this.#enabled;
62+
}
63+
64+
get isInspectionEnabled(): boolean {
65+
return this.#inspectionEnabled;
66+
}
67+
68+
get selectedElement(): ElementData | undefined {
69+
return this.#selectedElement;
70+
}
71+
72+
async targetAdded(target: SDK.Target.Target): Promise<void> {
73+
if (target !== SDK.TargetManager.TargetManager.instance().primaryPageTarget()) {
74+
return;
75+
}
76+
this.#target = target;
77+
}
78+
79+
async targetRemoved(target: SDK.Target.Target): Promise<void> {
80+
if (target !== this.#target) {
81+
return;
82+
}
83+
await this.disable();
84+
this.#target = undefined;
85+
86+
const primaryPageTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
87+
if (primaryPageTarget) {
88+
this.#target = primaryPageTarget;
89+
}
90+
}
91+
92+
/**
93+
* Enables Livemate by sending Livemate.enable CDP command.
94+
*/
95+
async enable(): Promise<void> {
96+
if (!this.#target || this.#enabled) {
97+
return;
98+
}
99+
100+
this.#registerEventListener();
101+
102+
try {
103+
await this.#sendCdpCommand(LivemateDomain.ENABLE);
104+
this.#enabled = true;
105+
this.dispatchEventToListeners(Events.STATUS_CHANGED, {enabled: true});
106+
} catch (e) {
107+
console.warn('[Livemate] Failed to enable:', e);
108+
this.#unregisterEventListener();
109+
}
110+
}
111+
112+
/**
113+
* Disables Livemate by sending Livemate.disable CDP command.
114+
*/
115+
async disable(): Promise<void> {
116+
if (!this.#target || !this.#enabled) {
117+
return;
118+
}
119+
120+
try {
121+
await this.#sendCdpCommand(LivemateDomain.DISABLE);
122+
} catch (e) {
123+
console.warn('[Livemate] Failed to disable:', e);
124+
}
125+
126+
this.#unregisterEventListener();
127+
this.#enabled = false;
128+
this.#inspectionEnabled = false;
129+
this.#selectedElement = undefined;
130+
this.dispatchEventToListeners(Events.STATUS_CHANGED, {enabled: false});
131+
}
132+
133+
/**
134+
* Enables inspection mode by sending Livemate.enableInspection CDP command.
135+
*/
136+
async enableInspection(): Promise<void> {
137+
if (!this.#target || !this.#enabled) {
138+
return;
139+
}
140+
141+
try {
142+
await this.#sendCdpCommand(LivemateDomain.ENABLE_INSPECTION);
143+
this.#inspectionEnabled = true;
144+
this.dispatchEventToListeners(Events.INSPECTION_STATE_CHANGED, {inspecting: true});
145+
} catch (e) {
146+
console.warn('[Livemate] Failed to enable inspection:', e);
147+
}
148+
}
149+
150+
/**
151+
* Disables inspection mode by sending Livemate.disableInspection CDP command.
152+
*/
153+
async disableInspection(): Promise<void> {
154+
if (!this.#target || !this.#enabled) {
155+
return;
156+
}
157+
158+
try {
159+
await this.#sendCdpCommand(LivemateDomain.DISABLE_INSPECTION);
160+
this.#inspectionEnabled = false;
161+
this.dispatchEventToListeners(Events.INSPECTION_STATE_CHANGED, {inspecting: false});
162+
} catch (e) {
163+
console.warn('[Livemate] Failed to disable inspection:', e);
164+
}
165+
}
166+
167+
async toggleInspection(): Promise<void> {
168+
if (this.#inspectionEnabled) {
169+
await this.disableInspection();
170+
} else {
171+
await this.enableInspection();
172+
}
173+
}
174+
175+
/**
176+
* Sends a raw CDP command.
177+
*/
178+
#sendCdpCommand(method: string, params?: object): Promise<unknown> {
179+
return new Promise((resolve, reject) => {
180+
if (!ProtocolClient.InspectorBackend.test.sendRawMessage) {
181+
reject(new Error('sendRawMessage not available'));
182+
return;
183+
}
184+
185+
ProtocolClient.InspectorBackend.test.sendRawMessage(
186+
method as ProtocolClient.InspectorBackend.QualifiedName,
187+
params ?? null,
188+
(result: unknown) => {
189+
resolve(result);
190+
}
191+
);
192+
});
193+
}
194+
195+
/**
196+
* Registers a listener for Livemate CDP events via the message hook.
197+
*/
198+
#registerEventListener(): void {
199+
this.#originalOnMessageReceived = ProtocolClient.InspectorBackend.test.onMessageReceived;
200+
ProtocolClient.InspectorBackend.test.onMessageReceived = (message: object, target) => {
201+
this.#handleCdpMessage(message);
202+
if (this.#originalOnMessageReceived) {
203+
this.#originalOnMessageReceived(message, target);
204+
}
205+
};
206+
}
207+
208+
#unregisterEventListener(): void {
209+
ProtocolClient.InspectorBackend.test.onMessageReceived = this.#originalOnMessageReceived;
210+
this.#originalOnMessageReceived = null;
211+
}
212+
213+
/**
214+
* Handles incoming CDP messages, filtering for Livemate.inspectionDataReceived events.
215+
*/
216+
#handleCdpMessage(message: object): void {
217+
const msg = message as {method?: string; params?: {payload?: string}};
218+
219+
if (msg.method !== LivemateDomain.INSPECTION_DATA_RECEIVED) {
220+
return;
221+
}
222+
223+
const payload = msg.params?.payload;
224+
if (!payload) {
225+
console.warn('[Livemate] Received inspectionDataReceived without payload');
226+
return;
227+
}
228+
229+
const livemateEvent = parseLivemateEvent(payload);
230+
if (!livemateEvent) {
231+
console.warn('[Livemate] Failed to parse event payload:', payload);
232+
return;
233+
}
234+
235+
switch (livemateEvent.type) {
236+
case LivemateEventType.ELEMENT_SELECTED:
237+
if (livemateEvent.data) {
238+
this.#selectedElement = livemateEvent.data;
239+
this.dispatchEventToListeners(Events.ELEMENT_SELECTED, livemateEvent.data);
240+
}
241+
break;
242+
243+
case LivemateEventType.INSPECTION_STARTED:
244+
this.#inspectionEnabled = true;
245+
this.dispatchEventToListeners(Events.INSPECTION_STATE_CHANGED, {inspecting: true});
246+
break;
247+
248+
case LivemateEventType.INSPECTION_STOPPED:
249+
this.#inspectionEnabled = false;
250+
this.dispatchEventToListeners(Events.INSPECTION_STATE_CHANGED, {inspecting: false});
251+
break;
252+
}
253+
}
254+
}
255+
256+
export const enum Events {
257+
STATUS_CHANGED = 'StatusChanged',
258+
ELEMENT_SELECTED = 'ElementSelected',
259+
INSPECTION_STATE_CHANGED = 'InspectionStateChanged',
260+
}
261+
262+
export interface StatusChangedEvent {
263+
enabled: boolean;
264+
}
265+
266+
export interface InspectionStateChangedEvent {
267+
inspecting: boolean;
268+
}
269+
270+
export interface EventTypes {
271+
[Events.STATUS_CHANGED]: StatusChangedEvent;
272+
[Events.ELEMENT_SELECTED]: ElementData;
273+
[Events.INSPECTION_STATE_CHANGED]: InspectionStateChangedEvent;
274+
}

0 commit comments

Comments
 (0)