1use serde::{Deserialize, Serialize};
9
10pub const MIN_SUPPORTED_PROTOCOL_VERSION: u32 = 1;
14
15pub const PROTOCOL_VERSION: u32 = 4;
21
22pub const DEVTOOLS_VERSION: &str = env!("CARGO_PKG_VERSION");
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum PeerKind {
28 Target,
29 Devtools,
30}
31
32#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
36pub struct Hello {
37 pub protocol_version: u32,
38 pub devtools_version: String,
39 pub kind: PeerKind,
40 pub auth_token: String,
41 #[serde(default)]
44 pub take_over: bool,
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "snake_case")]
49pub enum ErrorCode {
50 ProtocolVersionMismatch,
51 Unauthorized,
52 UnknownId,
53 StaleId,
54 Unsupported,
55 InvalidRequest,
56 InternalError,
57 SessionBusy,
59}
60
61macro_rules! opaque_id {
68 ($name:ident) => {
69 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
70 pub struct $name {
71 index: u64,
72 generation: u64,
73 }
74 impl $name {
75 pub const fn new(index: u64, generation: u64) -> Self {
76 Self { index, generation }
77 }
78 pub const fn index(&self) -> u64 {
79 self.index
80 }
81 pub const fn generation(&self) -> u64 {
82 self.generation
83 }
84 }
85 impl std::fmt::Display for $name {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 write!(
88 f,
89 "{}:{}:{}",
90 stringify!($name),
91 self.index,
92 self.generation
93 )
94 }
95 }
96 };
97}
98
99opaque_id!(DevWindowId);
100opaque_id!(DevWidgetId);
101opaque_id!(DevRenderId);
102opaque_id!(DevLayerId);
103opaque_id!(DevSemanticsId);
104opaque_id!(DevSignalId);
105
106#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
111#[serde(rename_all = "snake_case")]
112pub enum DebugValue {
113 Bool(bool),
114 Int(i64),
115 Uint(u64),
116 Float(f64),
117 Str(String),
119 Color(u8, u8, u8, u8),
120 Enum(String),
121 Size([f32; 2]),
122 Offset([f32; 2]),
123 Rect([f32; 4]),
124 Insets([f32; 4]),
125 Constraints {
126 min_width: f32,
127 #[serde(with = "unbounded_maximum")]
130 max_width: f32,
131 min_height: f32,
132 #[serde(with = "unbounded_maximum")]
133 max_height: f32,
134 },
135 Optional(Option<Box<DebugValue>>),
136 List(Vec<DebugValue>),
137 Redacted,
138}
139
140mod animation_speed_payload {
141 use serde::{Deserialize, Deserializer, Serialize, Serializer};
142
143 #[derive(Serialize, Deserialize)]
144 struct Payload {
145 scale: f32,
146 }
147
148 pub(super) fn serialize<S: Serializer>(scale: &f32, serializer: S) -> Result<S::Ok, S::Error> {
149 Payload { scale: *scale }.serialize(serializer)
150 }
151
152 pub(super) fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<f32, D::Error> {
153 Payload::deserialize(deserializer).map(|payload| payload.scale)
154 }
155}
156
157mod profiler_mode_payload {
158 use super::DevtoolsProfilerMode;
159 use serde::{Deserialize, Deserializer, Serialize, Serializer};
160
161 #[derive(Serialize, Deserialize)]
162 struct Payload {
163 mode: DevtoolsProfilerMode,
164 }
165
166 pub(super) fn serialize<S: Serializer>(
167 mode: &DevtoolsProfilerMode,
168 serializer: S,
169 ) -> Result<S::Ok, S::Error> {
170 Payload { mode: *mode }.serialize(serializer)
171 }
172
173 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
174 deserializer: D,
175 ) -> Result<DevtoolsProfilerMode, D::Error> {
176 Payload::deserialize(deserializer).map(|payload| payload.mode)
177 }
178}
179
180mod unbounded_maximum {
181 use serde::{Deserialize, Deserializer, Serializer, ser::Error};
182
183 pub fn serialize<S: Serializer>(value: &f32, serializer: S) -> Result<S::Ok, S::Error> {
184 if value.is_finite() {
185 serializer.serialize_f32(*value)
186 } else if *value == f32::INFINITY {
187 serializer.serialize_none()
188 } else {
189 Err(S::Error::custom(
190 "constraint maximum must be finite or positive infinity",
191 ))
192 }
193 }
194
195 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<f32, D::Error> {
196 Option::<f32>::deserialize(deserializer).map(|maximum| maximum.unwrap_or(f32::INFINITY))
197 }
198}
199
200#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
201pub struct DebugProperty {
202 pub name: String,
203 pub value: DebugValue,
204 #[serde(default)]
206 pub overridden: bool,
207 #[serde(default)]
208 pub editable: bool,
209 #[serde(default, skip_serializing_if = "Vec::is_empty")]
211 pub options: Vec<String>,
212 #[serde(default, skip_serializing_if = "Option::is_none")]
214 pub min: Option<f64>,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub max: Option<f64>,
218 #[serde(default, skip_serializing_if = "Option::is_none")]
220 pub step: Option<f64>,
221}
222
223#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
224pub struct PropertyChange {
225 pub name: String,
226 pub old: Option<DebugValue>,
227 pub new: Option<DebugValue>,
228}
229
230#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
231#[serde(rename_all = "snake_case", tag = "kind")]
232pub enum InvalidationReason {
233 SignalWrite {
234 signal: DevSignalId,
235 name: String,
236 old: Option<String>,
237 new: Option<String>,
238 },
239 ParentReconciliation {
240 changed: Vec<PropertyChange>,
241 },
242 WidgetConfigurationChanged,
243 EnvironmentChanged {
244 field: String,
245 old: Option<String>,
246 new: Option<String>,
247 },
248 LocaleChanged,
249 WindowMetricsChanged,
250 ConstraintsChanged,
251 Animation,
252 TaskCompletion,
253 Navigation,
254 Restoration,
255 ManualInvalidation,
256 Mounted,
257}
258
259#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
260pub struct SourceLocation {
261 pub file: String,
262 pub line: u32,
263 pub column: u32,
264}
265
266#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
271pub struct WidgetNode {
272 pub id: DevWidgetId,
273 pub parent: Option<DevWidgetId>,
274 pub type_name: String,
275 pub key: Option<String>,
276 pub label: Option<String>,
278 pub child_ids: Vec<DevWidgetId>,
279 pub revision: u64,
282}
283
284#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
285#[serde(rename_all = "snake_case", tag = "kind")]
286pub enum TreeDelta {
287 Snapshot {
288 window: DevWindowId,
289 root: Box<WidgetNode>,
290 nodes: Vec<WidgetNode>,
291 truncated: bool,
292 },
293 Insert {
294 node: Box<WidgetNode>,
295 },
296 Remove {
297 id: DevWidgetId,
298 },
299 Move {
300 id: DevWidgetId,
301 parent: DevWidgetId,
302 position: usize,
303 },
304 Update {
305 node: Box<WidgetNode>,
306 },
307}
308
309#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
314pub struct ElementState {
315 pub mount_generation: u64,
316 pub builds: u64,
317 pub layouts: u64,
318 pub paints: u64,
319 pub composites: u64,
320 pub semantic_updates: u64,
321 pub constraints: Option<DebugValue>,
322 pub size: Option<[f32; 2]>,
323 pub offset: Option<[f32; 2]>,
324 pub world_bounds: Option<[f32; 4]>,
325 #[serde(default)]
327 pub baseline: Option<f32>,
328 #[serde(default)]
331 pub clip: Option<[f32; 4]>,
332}
333
334#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
338pub struct LayoutInspection {
339 pub node: DevWidgetId,
340 pub parent: Option<DevWidgetId>,
341 pub incoming_constraints: Option<DebugValue>,
342 pub resolved_size: [f32; 2],
343 pub local_offset: [f32; 2],
344 pub local_transform: [f32; 6],
346 pub world_transform: [f32; 6],
348 pub world_bounds: [f32; 4],
349 pub content_bounds: [f32; 4],
350 pub padding: Option<[f32; 4]>,
351 pub clip: Option<[f32; 4]>,
352 pub baseline: Option<f32>,
353 pub details: LayoutDetails,
354}
355
356#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
357pub struct LayoutHistoryEntry {
358 pub sequence: u64,
359 pub old_constraints: Option<DebugValue>,
360 pub new_constraints: DebugValue,
361 pub old_size: [f32; 2],
362 pub new_size: [f32; 2],
363 pub cause: Option<String>,
364}
365
366#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
367pub struct WorkReasons {
368 pub layout: Option<String>,
369 pub paint: Option<String>,
370 pub composite: Option<String>,
371}
372
373#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
377#[serde(rename_all = "snake_case", tag = "kind")]
378pub enum LayoutDetails {
379 Box,
380 Flex {
381 axis: String,
382 available_main: Option<f32>,
383 non_flex_extent: f32,
384 flexible_extent: f32,
385 used_extent: f32,
386 remaining_extent: f32,
387 overflow: f32,
388 children: Vec<FlexChildInspection>,
389 },
390 Stack {
391 alignment: [f32; 2],
392 indexed_active: Option<usize>,
393 children: Vec<StackChildInspection>,
394 },
395 Positioned {
396 left: Option<f32>,
397 right: Option<f32>,
398 top: Option<f32>,
399 bottom: Option<f32>,
400 width: Option<f32>,
401 height: Option<f32>,
402 },
403 Transform {
404 matrix: [f32; 6],
405 determinant: f32,
406 invertible: bool,
407 },
408 Fitted {
409 fit: String,
410 alignment: [f32; 2],
411 source_size: Option<[f32; 2]>,
412 destination_size: [f32; 2],
413 matrix: [f32; 6],
414 determinant: f32,
415 invertible: bool,
416 },
417 Scroll {
418 axis: String,
419 viewport_extent: f32,
420 content_extent: f32,
421 offset: f32,
422 min_scroll: f32,
423 max_scroll: f32,
424 },
425 LazyViewport {
426 item_count: usize,
427 materialized_start: usize,
428 materialized_end: usize,
429 materialized_items: usize,
430 viewport_extent: f32,
431 cache_extent: f32,
432 scroll_offset: f32,
433 },
434 Text {
435 text_length: usize,
436 max_lines: Option<usize>,
437 overflow: String,
438 line_count: Option<usize>,
439 },
440 Custom {
441 layout_kind: String,
442 },
443}
444
445#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
446pub struct FlexChildInspection {
447 pub node: DevWidgetId,
448 pub index: usize,
449 pub type_name: String,
450 pub size: [f32; 2],
451 pub offset: [f32; 2],
452 pub flex: Option<u32>,
453 pub fit: Option<String>,
454 pub allocated_main_extent: Option<f32>,
456 pub actual_main_extent: f32,
457 pub cross_extent: f32,
458}
459
460#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
461pub struct StackChildInspection {
462 pub node: DevWidgetId,
463 pub index: usize,
464 pub type_name: String,
465 pub bounds: [f32; 4],
466 pub painted: bool,
467 pub left: Option<f32>,
468 pub right: Option<f32>,
469 pub top: Option<f32>,
470 pub bottom: Option<f32>,
471 pub width: Option<f32>,
472 pub height: Option<f32>,
473}
474
475#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
476pub struct NodeDetails {
477 pub id: DevWidgetId,
478 pub window: DevWindowId,
479 pub type_name: String,
480 pub key: Option<String>,
481 pub state: ElementState,
482 #[serde(default)]
485 pub layout: Option<LayoutInspection>,
486 #[serde(default)]
489 pub layout_history: Vec<LayoutHistoryEntry>,
490 #[serde(default)]
491 pub work_reasons: WorkReasons,
492 pub properties: Vec<DebugProperty>,
493 #[serde(default)]
496 pub property_changes: Vec<PropertyChange>,
497 #[serde(default)]
500 pub invalidation_causes: Vec<InvalidationReason>,
501 #[serde(default)]
504 pub consumed_signals: Vec<DevSignalId>,
505 pub invalidation: Option<InvalidationReason>,
506 pub render: Option<DevRenderId>,
507 pub semantics: Option<DevSemanticsId>,
508 pub source: Option<SourceLocation>,
509}
510
511#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
512pub struct FrameTimingsWire {
513 pub event_processing: u32,
514 pub runtime_messages: u32,
515 pub build: u32,
516 pub layout: u32,
517 pub composite: u32,
518 pub semantics: u32,
519 pub paint: u32,
520 pub cpu_total: u32,
521 pub prepare: u32,
522 pub encode: u32,
523 pub submit: u32,
524 pub gpu_us: Option<f64>,
526}
527
528#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
529pub struct FrameRecordEvent {
530 pub window: DevWindowId,
531 pub frame: u64,
532 pub timings: FrameTimingsWire,
533 #[serde(default)]
536 pub budget_us: Option<u32>,
537 pub over_budget: bool,
538 pub draw_calls: u32,
539 pub instances: u32,
540 pub upload_bytes: u64,
541 pub pipelines_created: u32,
542}
543
544#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
545#[serde(rename_all = "snake_case")]
546pub enum DevtoolsProfilerMode {
547 #[default]
548 Basic,
549 Performance,
550 Deep,
551}
552
553#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
554#[serde(rename_all = "snake_case")]
555pub enum TracePhase {
556 Build,
557 Layout,
558 Paint,
559 Semantics,
560 Composite,
561}
562
563#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
565pub struct TraceEvent {
566 pub node: DevWidgetId,
567 pub phase: TracePhase,
568 pub parent: Option<u32>,
569 pub start_us: u32,
570 pub duration_us: u32,
571}
572
573#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
576pub struct DeepFrameTrace {
577 pub window: DevWindowId,
578 pub frame: u64,
579 pub events: Vec<TraceEvent>,
580 pub truncated: bool,
581 pub dropped_events: u32,
582}
583
584#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
585pub struct ResourceCounts {
586 pub elements: usize,
587 pub render_objects: usize,
588 pub layers: usize,
589 pub semantics_nodes: usize,
590 pub signals: usize,
591 pub tasks_active: usize,
592 pub glyph_atlas_pages: usize,
593 pub image_resources: usize,
594 pub gradient_resources: usize,
595 pub path_meshes: usize,
596 pub offscreen_bytes: usize,
597 pub effect_cached_bytes: usize,
598 pub rss_mb: usize,
599}
600
601#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
602pub struct MemorySnapshot {
603 pub label: String,
604 pub counts: ResourceCounts,
605}
606
607#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
608pub struct SignalSummary {
609 pub id: DevSignalId,
610 pub name: Option<String>,
611 pub type_name: String,
612 #[serde(default)]
614 pub generation: u64,
615 pub write_count: u64,
616 pub subscriber_count: usize,
617 pub last_write_summary: Option<String>,
618 pub editable: bool,
619}
620
621#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
622pub struct SignalSubscriber {
623 pub signal: DevSignalId,
624 pub element: DevWidgetId,
625 pub path: String,
626}
627
628#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
629#[serde(rename_all = "snake_case")]
630pub enum DebugOption {
631 LayoutBounds,
632 LayoutBoundsSubtree,
633 LayoutBoundsWholeWindow,
634 PaddingContent,
635 Baselines,
636 Clips,
637 HitTestRegions,
638 SemanticsBounds,
639 ScrollViewports,
640 LayerBoundaries,
641 RepaintRainbow,
642 FlashRebuilds,
643 HighlightBuild,
644 HighlightLayout,
645 HighlightPaint,
646 HighlightSemantics,
647 HighlightComposite,
648 HighlightOversizedImages,
649 PerformanceOverlay,
651}
652
653#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
654#[serde(rename_all = "snake_case")]
655pub enum CacheKind {
656 TextLayouts,
657 GlyphAtlas,
658 Images,
659 Paths,
660 Gradients,
661 Offscreen,
662}
663
664#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
665#[serde(rename_all = "snake_case")]
666pub enum EditableValue {
667 Bool(bool),
668 Int(i64),
669 Uint(u64),
670 Float(f64),
671 Str(String),
672 Color(u8, u8, u8, u8),
673}
674
675#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
676pub struct CacheReport {
677 pub which: CacheKind,
678 pub entries: usize,
679 pub hits: u64,
680 pub misses: u64,
681}
682
683#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
690#[serde(rename_all = "snake_case", tag = "method")]
691pub enum RequestMethod {
692 GetTargetInfo,
693 GetWidgetTree {
694 window: DevWindowId,
695 },
696 GetNodeDetails {
697 id: DevWidgetId,
698 },
699 EditProperty {
700 id: DevWidgetId,
701 name: String,
702 value: DebugValue,
703 },
704 StartInspectMode {
705 window: DevWindowId,
706 },
707 StopInspectMode {
708 window: DevWindowId,
709 },
710 HighlightNode {
711 window: DevWindowId,
712 id: Option<DevWidgetId>,
713 },
714 SetDebugOption {
715 name: DebugOption,
716 enabled: bool,
717 },
718 SetProfilerMode {
719 mode: DevtoolsProfilerMode,
720 },
721 StartRecording,
722 StopRecording,
723 TakeMemorySnapshot {
724 label: String,
725 },
726 ListSignals,
727 GetSignalSubscribers {
728 id: DevSignalId,
729 },
730 EditSignal {
731 id: DevSignalId,
732 value: EditableValue,
733 },
734 ResetOverrides,
735 ClearCache {
736 which: CacheKind,
737 },
738 SetAnimationSpeed {
739 scale: f32,
740 },
741 GetRuntimeDiagnostics {
743 window: DevWindowId,
744 },
745 GetSemanticsTree {
747 window: DevWindowId,
748 },
749 GetLayerTree {
751 window: DevWindowId,
752 },
753 GetWorkCounts {
755 window: DevWindowId,
756 limit: u32,
757 },
758 HighlightRect {
761 window: DevWindowId,
762 rect: Option<[f32; 4]>,
763 },
764}
765
766#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
768#[serde(default)]
769pub struct WidgetPipelineCounters {
770 pub mounts: u64,
771 pub unmounts: u64,
772 pub rebuilds: u64,
773 pub layouts: u64,
774 pub paints: u64,
775 pub composites: u64,
776 pub animation_ticks: u64,
777 pub scroll_offset_updates: u64,
778 pub reconciliation_fast_paths: u64,
779 pub layout_cache_hits: u64,
780 pub display_lists_reused: u64,
781 pub compositor_only_updates: u64,
782 pub lazy_layouts: u64,
783 pub items_built: u64,
784 pub items_reused: u64,
785 pub child_list_scans: u64,
786 pub identical_child_bailouts: u64,
787 pub elements_created: u64,
788 pub elements_removed: u64,
789 pub elements_moved: u64,
790 pub dirty_requests: u64,
791 pub dirty_queue_deduplicated: u64,
792 pub elements_total: u64,
793 pub render_objects_total: u64,
794 pub layers_total: u64,
795}
796
797#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
799#[serde(default)]
800pub struct TextCacheCounters {
801 pub layouts_requested: u64,
802 pub cache_hits: u64,
803 pub cache_misses: u64,
804 pub font_resolution_requests: u64,
805 pub font_resolution_cache_hits: u64,
806 pub font_resolution_cache_misses: u64,
807 pub coverage_queries: u64,
808 pub fallback_runs: u64,
809 pub fallback_fonts_used: u64,
810 pub missing_clusters: u64,
811 pub shaping_runs: u64,
812 pub paragraphs_considered: u64,
813 pub paragraphs_reshaped: u64,
814 pub parley_layouts_reused: u64,
815 pub documents_composed: u64,
816 pub documents_reused: u64,
817 pub width_reuses: u64,
818 pub cache_growths: u64,
819 pub cache_shrinks: u64,
820 pub paragraphs_carried: u64,
821 pub shapes_reused: u64,
822}
823
824#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
826#[serde(default)]
827pub struct SemanticsCounters {
828 pub nodes: u64,
829 pub nodes_created: u64,
830 pub nodes_removed: u64,
831 pub nodes_updated: u64,
832 pub property_updates: u64,
833 pub geometry_updates: u64,
834 pub native_updates: u64,
835 pub actions_received: u64,
836 pub revisions: u64,
837}
838
839#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
841#[serde(default)]
842pub struct CompositorCounters {
843 pub layers: u64,
844 pub picture_layers_repainted: u64,
845 pub transform_updates: u64,
846 pub clip_updates: u64,
847 pub layers_culled: u64,
848 pub opacity_updates: u64,
849 pub leader_publish_passes: u64,
850 pub leaders_published: u64,
851}
852
853#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
854#[serde(default)]
855pub struct RuntimeDiagnosticsWire {
856 pub widgets: WidgetPipelineCounters,
857 pub text: TextCacheCounters,
858 pub semantics: SemanticsCounters,
859 pub compositor: CompositorCounters,
860}
861
862#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
863#[serde(default)]
864pub struct SemanticsNodeWire {
865 pub id: u64,
866 pub parent: Option<u64>,
867 pub children: Vec<u64>,
868 pub role: String,
869 pub label: Option<String>,
870 pub value: Option<String>,
871 pub description: Option<String>,
872 pub bounds: [f32; 4],
873 pub flags: Vec<String>,
875 pub actions: Vec<String>,
876}
877
878#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
879#[serde(default)]
880pub struct LayerNodeWire {
881 pub id: u64,
882 pub parent: Option<u64>,
883 pub children: Vec<u64>,
884 pub kind: String,
885 pub summary: String,
886 pub bounds: Option<[f32; 4]>,
887}
888
889#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
890pub struct WorkCountEntry {
891 pub id: DevWidgetId,
892 pub type_name: String,
893 #[serde(default)]
894 pub builds: u64,
895 #[serde(default)]
896 pub layouts: u64,
897 #[serde(default)]
898 pub paints: u64,
899 #[serde(default)]
900 pub semantics: u64,
901 #[serde(default)]
902 pub composites: u64,
903}
904
905#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
906#[serde(rename_all = "snake_case", tag = "result")]
907pub enum ResponsePayload {
908 Ok,
909 TargetInfo(Box<TargetInfo>),
910 WidgetTree {
911 deltas: Vec<TreeDelta>,
912 tree_revision: u64,
913 },
914 NodeDetails(Box<NodeDetails>),
915 RecordingStarted,
916 RecordingStopped {
917 frames: u64,
918 },
919 MemorySnapshot(MemorySnapshot),
920 Signals {
921 signals: Vec<SignalSummary>,
922 },
923 SignalSubscribers {
924 subscribers: Vec<SignalSubscriber>,
925 },
926 Edited,
927 OverridesReset,
928 CacheCleared(CacheReport),
929 AnimationSpeedSet(#[serde(with = "animation_speed_payload")] f32),
932 ProfilerModeSet(#[serde(with = "profiler_mode_payload")] DevtoolsProfilerMode),
934 RuntimeDiagnostics(Box<RuntimeDiagnosticsWire>),
935 SemanticsTree {
936 window: DevWindowId,
937 nodes: Vec<SemanticsNodeWire>,
938 truncated: bool,
939 },
940 LayerTree {
941 window: DevWindowId,
942 layers: Vec<LayerNodeWire>,
943 truncated: bool,
944 },
945 WorkCounts {
946 window: DevWindowId,
947 entries: Vec<WorkCountEntry>,
948 },
949 Error {
950 code: ErrorCode,
951 message: String,
952 },
953}
954
955#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
957#[serde(rename_all = "snake_case", tag = "event")]
958pub enum TargetEvent {
959 WidgetTreeDeltas {
963 window: DevWindowId,
964 deltas: Vec<TreeDelta>,
965 tree_revision: u64,
966 },
967 FrameRecord(FrameRecordEvent),
968 DeepTrace(DeepFrameTrace),
969 WidgetSelectedByUser {
970 window: DevWindowId,
971 id: DevWidgetId,
972 },
973 Log {
974 level: String,
975 target: String,
976 message: String,
977 },
978 WindowsChanged,
979 DroppedTelemetry {
980 count: u64,
981 },
982 InspectModeEnded {
983 window: DevWindowId,
984 },
985}
986
987#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
989#[serde(rename_all = "snake_case", tag = "type")]
990pub enum Message {
991 Request {
992 request_id: u64,
993 body: RequestMethod,
994 },
995 Response {
996 request_id: u64,
997 payload: Result<ResponsePayload, ErrorCode>,
998 },
999 Event(TargetEvent),
1000 Rejection {
1002 code: ErrorCode,
1003 message: String,
1004 },
1005}
1006
1007#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1008pub struct TargetInfo {
1009 pub protocol_version: u32,
1010 pub framework_version: String,
1011 pub pid: u32,
1012 pub executable: String,
1013 pub platform: String,
1014 pub windows: Vec<WindowSummary>,
1015}
1016
1017#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1018pub struct WindowSummary {
1019 pub id: DevWindowId,
1020 pub title: String,
1021 pub logical_size: [f32; 2],
1022 pub scale_factor: f64,
1023}
1024
1025#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1028pub struct DiscoveryRecord {
1029 pub pid: u32,
1030 pub app_name: String,
1031 pub port: u16,
1032 pub auth_token: String,
1035 pub started_unix_ms: u64,
1036 pub protocol_version: u32,
1037}
1038
1039pub fn check_hello(
1042 hello: &Hello,
1043 expected_kind: PeerKind,
1044 expected_token: Option<&str>,
1045) -> Result<(), ErrorCode> {
1046 if !(MIN_SUPPORTED_PROTOCOL_VERSION..=PROTOCOL_VERSION).contains(&hello.protocol_version) {
1047 return Err(ErrorCode::ProtocolVersionMismatch);
1048 }
1049 if let Some(token) = expected_token {
1050 if !token_eq(&hello.auth_token, token) {
1053 return Err(ErrorCode::Unauthorized);
1054 }
1055 }
1056 if hello.kind != expected_kind {
1057 return Err(ErrorCode::InvalidRequest);
1058 }
1059 Ok(())
1060}
1061
1062fn token_eq(a: &str, b: &str) -> bool {
1063 let (a, b) = (a.as_bytes(), b.as_bytes());
1064 if a.len() != b.len() {
1065 return false;
1066 }
1067 let mut diff = 0u8;
1068 for (x, y) in a.iter().zip(b.iter()) {
1069 diff |= x ^ y;
1070 }
1071 diff == 0
1072}