1#![crate_name = "devtools_traits"]
16#![crate_type = "rlib"]
17#![deny(unsafe_code)]
18
19use core::fmt;
20use std::collections::HashMap;
21use std::fmt::Display;
22use std::net::TcpStream;
23use std::str::FromStr;
24use std::time::{Duration, SystemTime, UNIX_EPOCH};
25
26pub use embedder_traits::ConsoleLogLevel;
27use embedder_traits::Theme;
28use http::{HeaderMap, Method};
29use malloc_size_of_derive::MallocSizeOf;
30use net_traits::http_status::HttpStatus;
31use net_traits::request::Destination;
32use net_traits::{DebugVec, TlsSecurityInfo};
33use profile_traits::mem::ReportsChan;
34use serde::de::{Error, Visitor};
35use serde::{Deserialize, Serialize};
36use servo_base::cross_process_instant::CrossProcessInstant;
37use servo_base::generic_channel::GenericSender;
38use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
39use servo_url::ServoUrl;
40use uuid::Uuid;
41
42#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
45pub struct DevtoolsPageInfo {
46 pub title: String,
47 pub url: ServoUrl,
48 pub is_top_level_global: bool,
49 pub is_service_worker: bool,
50}
51
52#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
53pub struct CSSError {
54 pub filename: String,
55 pub line: u32,
56 pub column: u32,
57 pub msg: String,
58}
59
60#[derive(Debug)]
63pub enum DevtoolsControlMsg {
64 FromChrome(ChromeToDevtoolsControlMsg),
66 FromScript(ScriptToDevtoolsControlMsg),
68 ClientExited,
70}
71
72#[expect(clippy::large_enum_variant)]
75#[derive(Debug)]
76pub enum ChromeToDevtoolsControlMsg {
77 AddClient(TcpStream),
79 ServerExitMsg,
81 NetworkEvent(String, NetworkEvent),
84 CollectMemoryReport(ReportsChan),
86}
87
88#[derive(Debug, Deserialize, Serialize)]
90pub enum NavigationState {
91 Start(ServoUrl),
93 Stop(PipelineId, DevtoolsPageInfo),
95}
96
97#[derive(Debug, Deserialize, Serialize)]
98pub enum ScriptToDevtoolsControlMsg {
100 NewGlobal(
103 (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
104 GenericSender<DevtoolScriptControlMsg>,
105 DevtoolsPageInfo,
106 ),
107 Navigate(BrowsingContextId, NavigationState),
109 ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
111 ClearConsole(PipelineId, Option<WorkerId>),
113 FramerateTick(String, f64),
116
117 ReportCSSError(PipelineId, CSSError),
119
120 ReportPageError(PipelineId, PageError),
122
123 TitleChanged(PipelineId, String),
125
126 CreateSourceActor(
128 GenericSender<DevtoolScriptControlMsg>,
129 PipelineId,
130 SourceInfo,
131 ),
132
133 UpdateSourceContent(PipelineId, String),
134
135 DomMutation(PipelineId, DomMutation),
136
137 DebuggerPause(PipelineId, FrameOffset, PauseReason),
139
140 CreateFrameActor(GenericSender<String>, PipelineId, FrameInfo),
142
143 CreateEnvironmentActor(
145 GenericSender<String>,
146 EnvironmentInfo,
147 Option<String>,
148 Option<String>,
149 ),
150}
151
152#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
153pub enum DomMutation {
154 AttributeModified {
155 node: String,
156 attribute_name: String,
157 new_value: Option<String>,
158 },
159}
160
161#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
162#[serde(rename_all = "camelCase")]
163pub struct ObjectPreview {
164 pub kind: String,
165 pub own_properties: Option<Vec<PropertyDescriptor>>,
166 pub own_properties_length: Option<u32>,
167 pub function: Option<FunctionPreview>,
168 pub array_length: Option<u32>,
169 pub items: Option<Vec<DebuggerValue>>,
170}
171
172#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
173#[serde(rename_all = "camelCase")]
174pub struct FunctionPreview {
175 pub name: Option<String>,
176 pub display_name: Option<String>,
177 pub parameter_names: Vec<String>,
178 pub is_async: Option<bool>,
179 pub is_generator: Option<bool>,
180}
181
182fn debugger_value_uuid() -> String {
183 Uuid::new_v4().to_string()
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,
232 BooleanValue(bool),
233 NumberValue(#[serde(deserialize_with = "deserialize_debugger_number")] f64),
234 StringValue(String),
235 ObjectValue {
236 #[serde(default = "debugger_value_uuid")]
237 uuid: String,
238 class: String,
239 own_property_length: Option<u32>,
240 preview: Option<ObjectPreview>,
241 },
242}
243
244#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
246#[serde(rename_all = "camelCase")]
247pub struct PropertyDescriptor {
248 pub name: String,
249 pub value: DebuggerValue,
250 pub configurable: bool,
251 pub enumerable: bool,
252 pub writable: bool,
253 pub is_accessor: bool,
254}
255
256#[derive(Debug, Deserialize, Serialize)]
257#[serde(rename_all = "camelCase")]
258pub struct EvaluateJSReply {
259 pub value: DebuggerValue,
260 pub has_exception: bool,
261}
262
263#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
264pub struct AttrInfo {
265 pub namespace: String,
266 pub name: String,
267 pub value: String,
268}
269
270#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
271#[serde(rename_all = "camelCase")]
272pub struct NodeInfo {
273 pub unique_id: String,
274 pub host: Option<String>,
275 #[serde(rename = "baseURI")]
276 pub base_uri: String,
277 pub parent: String,
278 pub node_type: u16,
279 pub node_name: String,
280 pub node_value: Option<String>,
281 pub num_children: usize,
282 pub attrs: Vec<AttrInfo>,
283 pub is_top_level_document: bool,
284 pub shadow_root_mode: Option<ShadowRootMode>,
285 pub is_shadow_host: bool,
286 pub display: Option<String>,
287 pub is_displayed: bool,
291
292 pub doctype_name: Option<String>,
294
295 pub doctype_public_identifier: Option<String>,
297
298 pub doctype_system_identifier: Option<String>,
300
301 pub has_event_listeners: bool,
302}
303
304pub struct StartedTimelineMarker {
305 name: String,
306 start_time: CrossProcessInstant,
307 start_stack: Option<Vec<()>>,
308}
309
310#[derive(Debug, Deserialize, Serialize)]
311pub struct TimelineMarker {
312 pub name: String,
313 pub start_time: CrossProcessInstant,
314 pub start_stack: Option<Vec<()>>,
315 pub end_time: CrossProcessInstant,
316 pub end_stack: Option<Vec<()>>,
317}
318
319#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
320pub enum TimelineMarkerType {
321 Reflow,
322 DOMEvent,
323}
324
325#[derive(Debug, Deserialize, Serialize)]
326#[serde(rename_all = "camelCase")]
327pub struct NodeStyle {
328 pub name: String,
329 pub value: String,
330 pub priority: String,
331}
332
333#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
334#[serde(tag = "type", rename_all = "camelCase")]
335pub enum AncestorData {
336 Layer {
337 actor_id: Option<String>,
338 value: Option<String>,
339 },
340}
341
342#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
343#[serde(rename_all = "camelCase")]
344pub struct MatchedRule {
345 pub selector: String,
346 pub stylesheet_index: usize,
347 pub block_id: usize,
348 pub ancestor_data: Vec<AncestorData>,
349}
350
351#[derive(Debug, Deserialize, Serialize)]
353#[serde(rename_all = "kebab-case")]
354pub struct ComputedNodeLayout {
355 pub display: String,
356 pub position: String,
357 pub z_index: String,
358 pub box_sizing: String,
359
360 pub margin_top: String,
361 pub margin_right: String,
362 pub margin_bottom: String,
363 pub margin_left: String,
364
365 pub border_top_width: String,
366 pub border_right_width: String,
367 pub border_bottom_width: String,
368 pub border_left_width: String,
369
370 pub padding_top: String,
371 pub padding_right: String,
372 pub padding_bottom: String,
373 pub padding_left: String,
374
375 pub width: f32,
376 pub height: f32,
377}
378
379#[derive(Debug, Default, Deserialize, Serialize)]
380pub struct AutoMargins {
381 pub top: bool,
382 pub right: bool,
383 pub bottom: bool,
384 pub left: bool,
385}
386
387#[derive(Debug, Deserialize, Serialize)]
390pub enum DevtoolScriptControlMsg {
391 GetRootNode(PipelineId, GenericSender<Option<NodeInfo>>),
393 GetDocumentElement(PipelineId, GenericSender<Option<NodeInfo>>),
395 GetChildren(PipelineId, String, GenericSender<Option<Vec<NodeInfo>>>),
397 GetAttributeStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
399 GetStylesheetStyle(
401 PipelineId,
402 String,
403 MatchedRule,
404 GenericSender<Option<Vec<NodeStyle>>>,
405 ),
406 GetStyleSheets(PipelineId, GenericSender<Vec<StyleSheetInfo>>),
408 GetStyleSheetText(PipelineId, i32, GenericSender<Option<String>>),
410 GetSelectors(PipelineId, String, GenericSender<Option<Vec<MatchedRule>>>),
413 GetComputedStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
415 GetEventListenerInfo(PipelineId, String, GenericSender<Vec<EventListenerInfo>>),
417 GetLayout(
419 PipelineId,
420 String,
421 GenericSender<Option<(ComputedNodeLayout, AutoMargins)>>,
422 ),
423 GetXPath(PipelineId, String, GenericSender<String>),
425 ModifyAttribute(PipelineId, String, Vec<AttrModification>),
427 ModifyRule(PipelineId, String, Vec<RuleModification>),
429 WantsLiveNotifications(PipelineId, bool),
431 SetTimelineMarkers(
433 PipelineId,
434 Vec<TimelineMarkerType>,
435 GenericSender<Option<TimelineMarker>>,
436 ),
437 DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
439 RequestAnimationFrame(PipelineId, String),
442 NavigateTo(PipelineId, ServoUrl),
445 GoBack(PipelineId),
448 GoForward(PipelineId),
451 Reload(PipelineId),
453 GetCssDatabase(GenericSender<HashMap<String, CssDatabaseProperty>>),
455 SimulateColorScheme(PipelineId, Theme),
457 HighlightDomNode(PipelineId, Option<String>),
459
460 Eval(
461 String,
462 PipelineId,
463 Option<String>,
464 GenericSender<EvaluateJSReply>,
465 ),
466 GetPossibleBreakpoints(u32, GenericSender<Vec<RecommendedBreakpointLocation>>),
467 SetBreakpoint(u32, u32, u32),
468 ClearBreakpoint(u32, u32, u32),
469 Interrupt,
470 Resume(Option<String>, Option<String>),
471 ListFrames(PipelineId, u32, u32, GenericSender<Vec<String>>),
472 GetEnvironment(String, GenericSender<String>),
473 Blackbox(u32, BlackboxCoverage),
474 Unblackbox(u32, BlackboxCoverage),
475}
476
477#[derive(Debug, Deserialize, Serialize)]
478pub enum BlackboxCoverage {
479 Full,
480 Partial((u32, u32), (u32, u32)),
481}
482
483#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
484#[serde(rename_all = "camelCase")]
485pub struct AttrModification {
486 pub attribute_name: String,
487 pub new_value: Option<String>,
488}
489
490#[derive(Clone, Debug, Deserialize, Serialize)]
491#[serde(rename_all = "camelCase")]
492pub struct RuleModification {
493 #[serde(rename = "type")]
494 pub type_: String,
495 pub index: u32,
496 pub name: String,
497 pub value: String,
498 pub priority: String,
499}
500
501#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
502#[serde(rename_all = "camelCase")]
503pub struct StackFrame {
504 pub filename: String,
505 pub function_name: String,
506 pub column_number: u32,
507 pub line_number: u32,
508 }
511
512pub fn get_time_stamp() -> u64 {
513 SystemTime::now()
514 .duration_since(UNIX_EPOCH)
515 .unwrap_or_default()
516 .as_millis() as u64
517}
518
519#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
520#[serde(rename_all = "camelCase")]
521pub struct ConsoleMessageFields {
522 pub level: ConsoleLogLevel,
523 pub filename: String,
524 pub line_number: u32,
525 pub column_number: u32,
526 pub time_stamp: u64,
527}
528
529#[derive(Clone, Debug, Deserialize, Serialize)]
530pub struct ConsoleMessage {
531 pub fields: ConsoleMessageFields,
532 pub arguments: Vec<DebuggerValue>,
533 pub stacktrace: Option<Vec<StackFrame>>,
534}
535
536#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
537#[serde(rename_all = "camelCase")]
538pub struct PageError {
539 pub error_message: String,
540 pub source_name: String,
541 pub line_number: u32,
542 pub column_number: u32,
543 pub time_stamp: u64,
544}
545
546#[derive(Debug, PartialEq, MallocSizeOf)]
547pub struct HttpRequest {
548 pub url: ServoUrl,
549 pub method: Method,
550 pub headers: HeaderMap,
551 pub body: Option<DebugVec>,
552 pub pipeline_id: PipelineId,
553 pub started_date_time: SystemTime,
554 pub time_stamp: i64,
555 pub connect_time: Duration,
556 pub send_time: Duration,
557 pub destination: Destination,
558 pub is_xhr: bool,
559 pub browsing_context_id: BrowsingContextId,
560}
561
562#[derive(Debug, PartialEq, MallocSizeOf)]
563pub struct HttpResponse {
564 #[ignore_malloc_size_of = "Http type"]
565 pub headers: Option<HeaderMap>,
566 pub status: HttpStatus,
567 pub body: Option<DebugVec>,
568 pub from_cache: bool,
569 pub pipeline_id: PipelineId,
570 pub browsing_context_id: BrowsingContextId,
571}
572
573#[derive(Debug, PartialEq)]
574pub struct SecurityInfoUpdate {
575 pub browsing_context_id: BrowsingContextId,
576 pub security_info: Option<TlsSecurityInfo>,
577}
578
579#[derive(Debug)]
580pub enum NetworkEvent {
581 HttpRequest(HttpRequest),
582 HttpRequestUpdate(HttpRequest),
583 HttpResponse(HttpResponse),
584 SecurityInfo(SecurityInfoUpdate),
585}
586
587impl NetworkEvent {
588 pub fn forward_to_devtools(&self) -> bool {
589 match self {
590 NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
591 NetworkEvent::HttpRequestUpdate(_) => true,
592 NetworkEvent::HttpResponse(_) => true,
593 NetworkEvent::SecurityInfo(_) => true,
594 }
595 }
596}
597
598impl TimelineMarker {
599 pub fn start(name: String) -> StartedTimelineMarker {
600 StartedTimelineMarker {
601 name,
602 start_time: CrossProcessInstant::now(),
603 start_stack: None,
604 }
605 }
606}
607
608impl StartedTimelineMarker {
609 pub fn end(self) -> TimelineMarker {
610 TimelineMarker {
611 name: self.name,
612 start_time: self.start_time,
613 start_stack: self.start_stack,
614 end_time: CrossProcessInstant::now(),
615 end_stack: None,
616 }
617 }
618}
619#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
620pub struct WorkerId(pub Uuid);
621impl Display for WorkerId {
622 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
623 write!(f, "{}", self.0)
624 }
625}
626impl FromStr for WorkerId {
627 type Err = uuid::Error;
628
629 fn from_str(s: &str) -> Result<Self, Self::Err> {
630 Ok(Self(s.parse()?))
631 }
632}
633
634#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
635#[serde(rename_all = "camelCase")]
636pub struct CssDatabaseProperty {
637 pub is_inherited: bool,
638 pub values: Vec<String>,
639 pub supports: Vec<String>,
640 pub subproperties: Vec<String>,
641}
642
643#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
644pub enum ShadowRootMode {
645 Open,
646 Closed,
647}
648
649impl fmt::Display for ShadowRootMode {
650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
651 match self {
652 Self::Open => write!(f, "open"),
653 Self::Closed => write!(f, "close"),
654 }
655 }
656}
657
658#[derive(Debug, Deserialize, Serialize)]
659pub struct SourceInfo {
660 pub url: ServoUrl,
661 pub introduction_type: String,
662 pub inline: bool,
663 pub worker_id: Option<WorkerId>,
664 pub content: Option<String>,
665 pub content_type: Option<String>,
666 pub spidermonkey_id: u32,
667}
668
669#[derive(Clone, Debug, Deserialize, Serialize)]
670#[serde(rename_all = "camelCase")]
671pub struct RecommendedBreakpointLocation {
672 pub script_id: u32,
673 pub offset: u32,
674 pub line_number: u32,
675 pub column_number: u32,
676 pub is_step_start: bool,
677}
678
679#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
680pub struct FrameInfo {
681 pub display_name: String,
682 pub on_stack: bool,
683 pub oldest: bool,
684 pub terminated: bool,
685 pub type_: String,
686 pub url: String,
687}
688
689#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
690pub struct EnvironmentInfo {
691 pub type_: Option<String>,
692 pub scope_kind: Option<String>,
693 pub function_display_name: Option<String>,
694 pub binding_variables: Vec<PropertyDescriptor>,
695}
696
697#[derive(Clone, Debug, Deserialize, Serialize)]
698pub struct StyleSheetInfo {
699 pub href: Option<String>,
700 pub disabled: bool,
701 pub title: String,
702 pub style_sheet_index: i32,
703 pub system: bool,
704 pub rule_count: u32,
705}
706
707#[derive(Clone, Debug, Deserialize, Serialize)]
708pub struct EventListenerInfo {
709 pub event_type: String,
710 pub capturing: bool,
711}
712
713#[derive(Debug, Deserialize, Serialize)]
714#[serde(rename_all = "camelCase")]
715pub struct PauseReason {
716 #[serde(rename = "type")]
717 pub type_: String,
718 pub on_next: Option<bool>,
719}
720
721#[derive(Debug, Deserialize, Serialize)]
722pub struct FrameOffset {
723 pub actor: String,
724 pub column: u32,
725 pub line: u32,
726}