1#![crate_name = "devtools_traits"]
16#![crate_type = "rlib"]
17#![deny(unsafe_code)]
18
19use core::fmt;
20use std::collections::HashMap;
21use std::net::TcpStream;
22use std::time::{Duration, SystemTime, UNIX_EPOCH};
23
24pub use embedder_traits::ConsoleLogLevel;
25use embedder_traits::Theme;
26use http::{HeaderMap, Method};
27use malloc_size_of_derive::MallocSizeOf;
28use net_traits::TlsSecurityInfo;
29use net_traits::http_status::HttpStatus;
30use net_traits::request::Destination;
31use profile_traits::mem::ReportsChan;
32use serde::de::{Error, Visitor};
33use serde::{Deserialize, Serialize};
34use servo_base::cross_process_instant::CrossProcessInstant;
35use servo_base::generic_channel::GenericSender;
36pub use servo_base::id::WorkerId;
37use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
38use servo_url::ServoUrl;
39use uuid::Uuid;
40
41#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
44pub struct DevtoolsPageInfo {
45 pub title: String,
46 pub url: ServoUrl,
47 pub is_top_level_global: bool,
48 pub is_service_worker: bool,
49}
50
51#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
52pub struct CSSError {
53 pub filename: String,
54 pub line: u32,
55 pub column: u32,
56 pub msg: String,
57}
58
59#[derive(Debug)]
62pub enum DevtoolsControlMsg {
63 FromChrome(ChromeToDevtoolsControlMsg),
65 FromScript(ScriptToDevtoolsControlMsg),
67 ClientExited,
69}
70
71#[expect(clippy::large_enum_variant)]
74#[derive(Debug)]
75pub enum ChromeToDevtoolsControlMsg {
76 AddClient(TcpStream),
78 ServerExitMsg,
80 NetworkEvent(Uuid, NetworkEvent),
83 CollectMemoryReport(ReportsChan),
85}
86
87#[derive(Debug, Deserialize, Serialize)]
89pub enum NavigationState {
90 Start(ServoUrl),
92 Stop(PipelineId, DevtoolsPageInfo),
94}
95
96#[derive(Debug, Deserialize, Serialize)]
97pub enum ScriptToDevtoolsControlMsg {
99 NewGlobal(
102 (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
103 GenericSender<DevtoolScriptControlMsg>,
104 DevtoolsPageInfo,
105 ),
106 Navigate(BrowsingContextId, NavigationState),
108 ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
110 ClearConsole(PipelineId, Option<WorkerId>),
112 FramerateTick(String, f64),
115
116 ReportCSSError(PipelineId, CSSError),
118
119 ReportPageError(PipelineId, PageError),
121
122 TitleChanged(PipelineId, String),
124
125 CreateSourceActor(
127 GenericSender<DevtoolScriptControlMsg>,
128 PipelineId,
129 SourceInfo,
130 ),
131
132 UpdateSourceContent(PipelineId, String),
133
134 DomMutation(PipelineId, DomMutation),
135
136 DebuggerPause(PipelineId, FrameOffset, PauseReason),
138
139 CreateFrameActor(GenericSender<String>, PipelineId, FrameInfo),
141
142 CreateObjectActor(GenericSender<String>, DebuggerValue),
144
145 CreateEnvironmentActor(
147 GenericSender<String>,
148 EnvironmentInfo,
149 Option<String>,
150 Option<String>,
151 ),
152}
153
154#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
155pub enum DomMutation {
156 AttributeModified {
157 node: String,
158 attribute_name: String,
159 new_value: Option<String>,
160 },
161}
162
163#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
164#[serde(rename_all = "camelCase")]
165pub struct ObjectPreview {
166 pub kind: String,
167 pub size: Option<u32>,
168 pub entries: Option<Vec<(DebuggerValue, DebuggerValue)>>,
169 pub own_properties: Option<Vec<PropertyDescriptor>>,
170 pub own_properties_length: Option<u32>,
171 pub function: Option<FunctionPreview>,
172 pub array_length: Option<u32>,
173 pub items: Option<Vec<DebuggerValue>>,
174}
175
176#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
177#[serde(rename_all = "camelCase")]
178pub struct FunctionPreview {
179 pub name: Option<String>,
180 pub display_name: Option<String>,
181 pub parameter_names: Vec<String>,
182 pub is_async: Option<bool>,
183 pub is_generator: Option<bool>,
184}
185
186struct DebuggerNumberVisitor;
187
188impl Visitor<'_> for DebuggerNumberVisitor {
189 type Value = f64;
190
191 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
192 formatter.write_str("a debugger value number")
193 }
194
195 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
196 Ok(value)
197 }
198
199 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
200 Ok(value as f64)
201 }
202
203 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
204 Ok(value as f64)
205 }
206
207 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
208 where
209 E: Error,
210 {
211 value.parse().map_err(E::custom)
212 }
213}
214
215fn deserialize_debugger_number<'de, D>(deserializer: D) -> Result<f64, D::Error>
216where
217 D: serde::Deserializer<'de>,
218{
219 if !deserializer.is_human_readable() {
221 return f64::deserialize(deserializer);
222 }
223
224 deserializer.deserialize_any(DebuggerNumberVisitor)
225}
226
227#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
228#[serde(rename_all_fields = "camelCase")]
229pub enum DebuggerValue {
230 VoidValue,
231 NullValue(bool),
232 BooleanValue(bool),
233 NumberValue(#[serde(deserialize_with = "deserialize_debugger_number")] f64),
234 StringValue(String),
235 ObjectValue {
236 actor: Option<String>,
237 class: String,
238 own_property_length: Option<u32>,
239 preview: Option<Box<ObjectPreview>>,
240 #[serde(default, skip_serializing_if = "Option::is_none")]
241 prototype: Option<Box<DebuggerValue>>,
242 },
243}
244
245#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
247#[serde(rename_all = "camelCase")]
248pub struct PropertyDescriptor {
249 pub name: String,
250 pub value: DebuggerValue,
251 pub configurable: bool,
252 pub enumerable: bool,
253 pub writable: bool,
254 pub is_accessor: bool,
255}
256
257#[derive(Debug, Deserialize, Serialize)]
258#[serde(rename_all = "camelCase")]
259pub struct EvaluateJSReply {
260 pub value: DebuggerValue,
261 pub exception_message: Option<String>,
262 pub has_exception: bool,
263}
264
265#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
266pub struct AttrInfo {
267 pub namespace: String,
268 pub name: String,
269 pub value: String,
270}
271
272#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
273#[serde(rename_all = "camelCase")]
274pub struct NodeInfo {
275 pub unique_id: String,
276 pub host: Option<String>,
277 #[serde(rename = "baseURI")]
278 pub base_uri: String,
279 pub parent: String,
280 pub node_type: u16,
281 pub node_name: String,
282 pub node_value: Option<String>,
283 pub num_children: usize,
284 pub attrs: Vec<AttrInfo>,
285 pub is_top_level_document: bool,
286 pub shadow_root_mode: Option<ShadowRootMode>,
287 pub is_shadow_host: bool,
288 pub display: Option<String>,
289 pub is_displayed: bool,
293
294 pub doctype_name: Option<String>,
296
297 pub doctype_public_identifier: Option<String>,
299
300 pub doctype_system_identifier: Option<String>,
302
303 pub has_event_listeners: bool,
304}
305
306pub struct StartedTimelineMarker {
307 name: String,
308 start_time: CrossProcessInstant,
309 start_stack: Option<Vec<()>>,
310}
311
312#[derive(Debug, Deserialize, Serialize)]
313pub struct TimelineMarker {
314 pub name: String,
315 pub start_time: CrossProcessInstant,
316 pub start_stack: Option<Vec<()>>,
317 pub end_time: CrossProcessInstant,
318 pub end_stack: Option<Vec<()>>,
319}
320
321#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
322pub enum TimelineMarkerType {
323 Reflow,
324 DOMEvent,
325}
326
327#[derive(Debug, Deserialize, Serialize)]
328#[serde(rename_all = "camelCase")]
329pub struct NodeStyle {
330 pub name: String,
331 pub value: String,
332 pub priority: String,
333}
334
335#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
336#[serde(tag = "type", rename_all = "camelCase")]
337pub enum AncestorData {
338 Layer {
339 actor_id: Option<String>,
340 value: Option<String>,
341 },
342}
343
344#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
345#[serde(rename_all = "camelCase")]
346pub struct MatchedRule {
347 pub selector: String,
348 pub stylesheet_index: usize,
349 pub block_id: usize,
350 pub ancestor_data: Vec<AncestorData>,
351}
352
353#[derive(Debug, Deserialize, Serialize)]
355#[serde(rename_all = "kebab-case")]
356pub struct ComputedNodeLayout {
357 pub display: String,
358 pub position: String,
359 pub z_index: String,
360 pub box_sizing: String,
361
362 pub margin_top: String,
363 pub margin_right: String,
364 pub margin_bottom: String,
365 pub margin_left: String,
366
367 pub border_top_width: String,
368 pub border_right_width: String,
369 pub border_bottom_width: String,
370 pub border_left_width: String,
371
372 pub padding_top: String,
373 pub padding_right: String,
374 pub padding_bottom: String,
375 pub padding_left: String,
376
377 pub width: f32,
378 pub height: f32,
379}
380
381#[derive(Debug, Default, Deserialize, Serialize)]
382pub struct AutoMargins {
383 pub top: bool,
384 pub right: bool,
385 pub bottom: bool,
386 pub left: bool,
387}
388
389#[derive(Debug, Deserialize, Serialize)]
390pub enum GetHTMLType {
391 OuterHTML,
392 InnerHTML,
393}
394
395#[derive(Debug, Deserialize, Serialize)]
398pub enum DevtoolScriptControlMsg {
399 GetRootNode(PipelineId, GenericSender<Option<NodeInfo>>),
401 GetDocumentElement(PipelineId, GenericSender<Option<NodeInfo>>),
403 GetChildren(PipelineId, String, GenericSender<Option<Vec<NodeInfo>>>),
405 GetAttributeStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
407 GetStylesheetStyle(
409 PipelineId,
410 String,
411 MatchedRule,
412 GenericSender<Option<Vec<NodeStyle>>>,
413 ),
414 GetStyleSheets(PipelineId, GenericSender<Vec<StyleSheetInfo>>),
416 GetStyleSheetText(PipelineId, i32, GenericSender<Option<String>>),
418 GetSelectors(PipelineId, String, GenericSender<Option<Vec<MatchedRule>>>),
421 GetComputedStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
423 GetEventListenerInfo(PipelineId, String, GenericSender<Vec<EventListenerInfo>>),
425 GetLayout(
427 PipelineId,
428 String,
429 GenericSender<Option<(ComputedNodeLayout, AutoMargins)>>,
430 ),
431 GetXPath(PipelineId, String, GenericSender<String>),
433 GetInnerOrOuterHTML(
435 PipelineId,
436 String,
437 GenericSender<Option<String>>,
438 GetHTMLType,
439 ),
440 ModifyAttribute(PipelineId, String, Vec<AttrModification>),
442 ModifyRule(PipelineId, String, Vec<RuleModification>),
444 WantsLiveNotifications(PipelineId, bool),
446 SetTimelineMarkers(
448 PipelineId,
449 Vec<TimelineMarkerType>,
450 GenericSender<Option<TimelineMarker>>,
451 ),
452 DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
454 RequestAnimationFrame(PipelineId, String),
457 NavigateTo(PipelineId, ServoUrl),
460 GoBack(PipelineId),
463 GoForward(PipelineId),
466 Reload(PipelineId),
468 GetCssDatabase(GenericSender<HashMap<String, CssDatabaseProperty>>),
470 SimulateColorScheme(PipelineId, Option<Theme>),
472 HighlightDomNode(PipelineId, Option<String>),
474
475 Eval(
476 String,
477 PipelineId,
478 Option<String>,
479 bool,
480 GenericSender<EvaluateJSReply>,
481 ),
482 GetPossibleBreakpoints(u32, GenericSender<Vec<RecommendedBreakpointLocation>>),
483 SetBreakpoint(u32, u32, u32),
484 ClearBreakpoint(u32, u32, u32),
485 Interrupt,
486 Resume(Option<String>, Option<String>),
487 ListFrames(PipelineId, u32, u32, GenericSender<Vec<String>>),
488 GetEnvironment(GetEnvironmentRequest, GenericSender<String>),
489 Blackbox(u32, BlackboxCoverage),
490 Unblackbox(u32, BlackboxCoverage),
491}
492
493#[derive(Debug, Deserialize, Serialize)]
494pub enum GetEnvironmentRequest {
495 Global(PipelineId),
496 Frame(String),
497}
498
499#[derive(Debug, Deserialize, Serialize)]
500pub enum BlackboxCoverage {
501 Full,
502 Partial((u32, u32), (u32, u32)),
503}
504
505#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
506#[serde(rename_all = "camelCase")]
507pub struct AttrModification {
508 pub attribute_name: String,
509 pub new_value: Option<String>,
510}
511
512#[derive(Clone, Debug, Deserialize, Serialize)]
513#[serde(rename_all = "camelCase")]
514pub struct RuleModification {
515 #[serde(rename = "type")]
516 pub type_: String,
517 pub index: u32,
518 pub name: String,
519 pub value: String,
520 pub priority: String,
521}
522
523#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
524#[serde(rename_all = "camelCase")]
525pub struct StackFrame {
526 pub filename: String,
527 pub function_name: String,
528 pub column_number: u32,
529 pub line_number: u32,
530 }
533
534pub fn get_time_stamp() -> u64 {
535 SystemTime::now()
536 .duration_since(UNIX_EPOCH)
537 .unwrap_or_default()
538 .as_millis() as u64
539}
540
541#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
542#[serde(rename_all = "camelCase")]
543pub struct ConsoleMessageFields {
544 pub level: ConsoleLogLevel,
545 pub filename: String,
546 pub line_number: u32,
547 pub column_number: u32,
548 pub time_stamp: u64,
549}
550
551#[derive(Clone, Debug, Deserialize, Serialize)]
552pub struct ConsoleMessage {
553 pub fields: ConsoleMessageFields,
554 pub arguments: Vec<DebuggerValue>,
555 pub stacktrace: Option<Vec<StackFrame>>,
556}
557
558#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
559#[serde(rename_all = "camelCase")]
560pub struct PageError {
561 pub error_message: String,
562 pub source_name: String,
563 pub line_number: u32,
564 pub column_number: u32,
565 pub time_stamp: u64,
566}
567
568#[derive(Debug, PartialEq, MallocSizeOf)]
569pub struct HttpRequest {
570 pub url: ServoUrl,
571 pub method: Method,
572 pub headers: HeaderMap,
573 pub body: Option<bytes::Bytes>,
574 pub pipeline_id: PipelineId,
575 pub started_date_time: SystemTime,
576 pub time_stamp: i64,
577 pub connect_time: Duration,
578 pub send_time: Duration,
579 pub destination: Destination,
580 pub is_xhr: bool,
581 pub browsing_context_id: BrowsingContextId,
582}
583
584#[derive(Debug, PartialEq, MallocSizeOf)]
585pub struct HttpResponse {
586 pub headers: Option<HeaderMap>,
587 pub status: HttpStatus,
588 pub body: Option<bytes::Bytes>,
589 pub from_cache: bool,
590 pub pipeline_id: PipelineId,
591 pub browsing_context_id: BrowsingContextId,
592}
593
594#[derive(Debug, PartialEq)]
595pub struct SecurityInfoUpdate {
596 pub browsing_context_id: BrowsingContextId,
597 pub security_info: Option<TlsSecurityInfo>,
598}
599
600#[derive(Debug)]
601pub enum NetworkEvent {
602 HttpRequest(HttpRequest),
603 HttpRequestUpdate(HttpRequest),
604 HttpResponse(HttpResponse),
605 SecurityInfo(SecurityInfoUpdate),
606}
607
608impl NetworkEvent {
609 pub fn forward_to_devtools(&self) -> bool {
610 match self {
611 NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
612 NetworkEvent::HttpRequestUpdate(_) => true,
613 NetworkEvent::HttpResponse(_) => true,
614 NetworkEvent::SecurityInfo(_) => true,
615 }
616 }
617}
618
619impl TimelineMarker {
620 pub fn start(name: String) -> StartedTimelineMarker {
621 StartedTimelineMarker {
622 name,
623 start_time: CrossProcessInstant::now(),
624 start_stack: None,
625 }
626 }
627}
628
629impl StartedTimelineMarker {
630 pub fn end(self) -> TimelineMarker {
631 TimelineMarker {
632 name: self.name,
633 start_time: self.start_time,
634 start_stack: self.start_stack,
635 end_time: CrossProcessInstant::now(),
636 end_stack: None,
637 }
638 }
639}
640#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
641#[serde(rename_all = "camelCase")]
642pub struct CssDatabaseProperty {
643 pub is_inherited: bool,
644 pub values: Vec<String>,
645 pub supports: Vec<String>,
646 pub subproperties: Vec<String>,
647}
648
649#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
650pub enum ShadowRootMode {
651 Open,
652 Closed,
653}
654
655impl fmt::Display for ShadowRootMode {
656 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
657 match self {
658 Self::Open => write!(f, "open"),
659 Self::Closed => write!(f, "close"),
660 }
661 }
662}
663
664#[derive(Debug, Deserialize, Serialize)]
665pub struct SourceInfo {
666 pub url: ServoUrl,
667 pub introduction_type: String,
668 pub inline: bool,
669 pub worker_id: Option<WorkerId>,
670 pub content: Option<String>,
671 pub content_type: Option<String>,
672 pub spidermonkey_id: u32,
673}
674
675#[derive(Clone, Debug, Deserialize, Serialize)]
676#[serde(rename_all = "camelCase")]
677pub struct RecommendedBreakpointLocation {
678 pub script_id: u32,
679 pub offset: u32,
680 pub line_number: u32,
681 pub column_number: u32,
682 pub is_step_start: bool,
683}
684
685#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
686pub struct FrameInfo {
687 pub display_name: Option<String>,
688 pub on_stack: bool,
689 pub oldest: bool,
690 pub this_value: DebuggerValue,
691 pub terminated: bool,
692 pub type_: String,
693 pub url: String,
694}
695
696#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
697pub struct EnvironmentInfo {
698 pub type_: Option<String>,
699 pub scope_kind: Option<String>,
700 pub function_display_name: Option<String>,
701 pub object: Option<DebuggerValue>,
702 pub binding_variables: Vec<PropertyDescriptor>,
703}
704
705#[derive(Clone, Debug, Deserialize, Serialize)]
706pub struct StyleSheetInfo {
707 pub href: Option<String>,
708 pub disabled: bool,
709 pub title: String,
710 pub style_sheet_index: i32,
711 pub system: bool,
712 pub rule_count: u32,
713}
714
715#[derive(Clone, Debug, Deserialize, Serialize)]
716pub struct EventListenerInfo {
717 pub event_type: String,
718 pub capturing: bool,
719}
720
721#[derive(Debug, Deserialize, Serialize)]
722#[serde(rename_all = "camelCase")]
723pub struct PauseReason {
724 #[serde(rename = "type")]
725 pub type_: String,
726 pub on_next: Option<bool>,
727}
728
729#[derive(Debug, Deserialize, Serialize)]
730pub struct FrameOffset {
731 pub actor: String,
732 pub column: u32,
733 pub line: u32,
734}