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 CreateObjectActor(GenericSender<String>, DebuggerValue),
145
146 CreateEnvironmentActor(
148 GenericSender<String>,
149 EnvironmentInfo,
150 Option<String>,
151 Option<String>,
152 ),
153}
154
155#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
156pub enum DomMutation {
157 AttributeModified {
158 node: String,
159 attribute_name: String,
160 new_value: Option<String>,
161 },
162}
163
164#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
165#[serde(rename_all = "camelCase")]
166pub struct ObjectPreview {
167 pub kind: String,
168 pub size: Option<u32>,
169 pub entries: Option<Vec<(DebuggerValue, DebuggerValue)>>,
170 pub own_properties: Option<Vec<PropertyDescriptor>>,
171 pub own_properties_length: Option<u32>,
172 pub function: Option<FunctionPreview>,
173 pub array_length: Option<u32>,
174 pub items: Option<Vec<DebuggerValue>>,
175}
176
177#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
178#[serde(rename_all = "camelCase")]
179pub struct FunctionPreview {
180 pub name: Option<String>,
181 pub display_name: Option<String>,
182 pub parameter_names: Vec<String>,
183 pub is_async: Option<bool>,
184 pub is_generator: Option<bool>,
185}
186
187struct DebuggerNumberVisitor;
188
189impl Visitor<'_> for DebuggerNumberVisitor {
190 type Value = f64;
191
192 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
193 formatter.write_str("a debugger value number")
194 }
195
196 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
197 Ok(value)
198 }
199
200 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
201 Ok(value as f64)
202 }
203
204 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
205 Ok(value as f64)
206 }
207
208 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
209 where
210 E: Error,
211 {
212 value.parse().map_err(E::custom)
213 }
214}
215
216fn deserialize_debugger_number<'de, D>(deserializer: D) -> Result<f64, D::Error>
217where
218 D: serde::Deserializer<'de>,
219{
220 if !deserializer.is_human_readable() {
222 return f64::deserialize(deserializer);
223 }
224
225 deserializer.deserialize_any(DebuggerNumberVisitor)
226}
227
228#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
229#[serde(rename_all_fields = "camelCase")]
230pub enum DebuggerValue {
231 VoidValue,
232 NullValue(bool),
233 BooleanValue(bool),
234 NumberValue(#[serde(deserialize_with = "deserialize_debugger_number")] f64),
235 StringValue(String),
236 ObjectValue {
237 actor: Option<String>,
238 class: String,
239 own_property_length: Option<u32>,
240 preview: Option<Box<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 exception_message: Option<String>,
261 pub has_exception: bool,
262}
263
264#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
265pub struct AttrInfo {
266 pub namespace: String,
267 pub name: String,
268 pub value: String,
269}
270
271#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
272#[serde(rename_all = "camelCase")]
273pub struct NodeInfo {
274 pub unique_id: String,
275 pub host: Option<String>,
276 #[serde(rename = "baseURI")]
277 pub base_uri: String,
278 pub parent: String,
279 pub node_type: u16,
280 pub node_name: String,
281 pub node_value: Option<String>,
282 pub num_children: usize,
283 pub attrs: Vec<AttrInfo>,
284 pub is_top_level_document: bool,
285 pub shadow_root_mode: Option<ShadowRootMode>,
286 pub is_shadow_host: bool,
287 pub display: Option<String>,
288 pub is_displayed: bool,
292
293 pub doctype_name: Option<String>,
295
296 pub doctype_public_identifier: Option<String>,
298
299 pub doctype_system_identifier: Option<String>,
301
302 pub has_event_listeners: bool,
303}
304
305pub struct StartedTimelineMarker {
306 name: String,
307 start_time: CrossProcessInstant,
308 start_stack: Option<Vec<()>>,
309}
310
311#[derive(Debug, Deserialize, Serialize)]
312pub struct TimelineMarker {
313 pub name: String,
314 pub start_time: CrossProcessInstant,
315 pub start_stack: Option<Vec<()>>,
316 pub end_time: CrossProcessInstant,
317 pub end_stack: Option<Vec<()>>,
318}
319
320#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
321pub enum TimelineMarkerType {
322 Reflow,
323 DOMEvent,
324}
325
326#[derive(Debug, Deserialize, Serialize)]
327#[serde(rename_all = "camelCase")]
328pub struct NodeStyle {
329 pub name: String,
330 pub value: String,
331 pub priority: String,
332}
333
334#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
335#[serde(tag = "type", rename_all = "camelCase")]
336pub enum AncestorData {
337 Layer {
338 actor_id: Option<String>,
339 value: Option<String>,
340 },
341}
342
343#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
344#[serde(rename_all = "camelCase")]
345pub struct MatchedRule {
346 pub selector: String,
347 pub stylesheet_index: usize,
348 pub block_id: usize,
349 pub ancestor_data: Vec<AncestorData>,
350}
351
352#[derive(Debug, Deserialize, Serialize)]
354#[serde(rename_all = "kebab-case")]
355pub struct ComputedNodeLayout {
356 pub display: String,
357 pub position: String,
358 pub z_index: String,
359 pub box_sizing: String,
360
361 pub margin_top: String,
362 pub margin_right: String,
363 pub margin_bottom: String,
364 pub margin_left: String,
365
366 pub border_top_width: String,
367 pub border_right_width: String,
368 pub border_bottom_width: String,
369 pub border_left_width: String,
370
371 pub padding_top: String,
372 pub padding_right: String,
373 pub padding_bottom: String,
374 pub padding_left: String,
375
376 pub width: f32,
377 pub height: f32,
378}
379
380#[derive(Debug, Default, Deserialize, Serialize)]
381pub struct AutoMargins {
382 pub top: bool,
383 pub right: bool,
384 pub bottom: bool,
385 pub left: bool,
386}
387
388#[derive(Debug, Deserialize, Serialize)]
389pub enum GetHTMLType {
390 OuterHTML,
391 InnerHTML,
392}
393
394#[derive(Debug, Deserialize, Serialize)]
397pub enum DevtoolScriptControlMsg {
398 GetRootNode(PipelineId, GenericSender<Option<NodeInfo>>),
400 GetDocumentElement(PipelineId, GenericSender<Option<NodeInfo>>),
402 GetChildren(PipelineId, String, GenericSender<Option<Vec<NodeInfo>>>),
404 GetAttributeStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
406 GetStylesheetStyle(
408 PipelineId,
409 String,
410 MatchedRule,
411 GenericSender<Option<Vec<NodeStyle>>>,
412 ),
413 GetStyleSheets(PipelineId, GenericSender<Vec<StyleSheetInfo>>),
415 GetStyleSheetText(PipelineId, i32, GenericSender<Option<String>>),
417 GetSelectors(PipelineId, String, GenericSender<Option<Vec<MatchedRule>>>),
420 GetComputedStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
422 GetEventListenerInfo(PipelineId, String, GenericSender<Vec<EventListenerInfo>>),
424 GetLayout(
426 PipelineId,
427 String,
428 GenericSender<Option<(ComputedNodeLayout, AutoMargins)>>,
429 ),
430 GetXPath(PipelineId, String, GenericSender<String>),
432 GetInnerOrOuterHTML(
434 PipelineId,
435 String,
436 GenericSender<Option<String>>,
437 GetHTMLType,
438 ),
439 ModifyAttribute(PipelineId, String, Vec<AttrModification>),
441 ModifyRule(PipelineId, String, Vec<RuleModification>),
443 WantsLiveNotifications(PipelineId, bool),
445 SetTimelineMarkers(
447 PipelineId,
448 Vec<TimelineMarkerType>,
449 GenericSender<Option<TimelineMarker>>,
450 ),
451 DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
453 RequestAnimationFrame(PipelineId, String),
456 NavigateTo(PipelineId, ServoUrl),
459 GoBack(PipelineId),
462 GoForward(PipelineId),
465 Reload(PipelineId),
467 GetCssDatabase(GenericSender<HashMap<String, CssDatabaseProperty>>),
469 SimulateColorScheme(PipelineId, Theme),
471 HighlightDomNode(PipelineId, Option<String>),
473
474 Eval(
475 String,
476 PipelineId,
477 Option<String>,
478 GenericSender<EvaluateJSReply>,
479 ),
480 GetPossibleBreakpoints(u32, GenericSender<Vec<RecommendedBreakpointLocation>>),
481 SetBreakpoint(u32, u32, u32),
482 ClearBreakpoint(u32, u32, u32),
483 Interrupt,
484 Resume(Option<String>, Option<String>),
485 ListFrames(PipelineId, u32, u32, GenericSender<Vec<String>>),
486 GetEnvironment(String, GenericSender<String>),
487 Blackbox(u32, BlackboxCoverage),
488 Unblackbox(u32, BlackboxCoverage),
489}
490
491#[derive(Debug, Deserialize, Serialize)]
492pub enum BlackboxCoverage {
493 Full,
494 Partial((u32, u32), (u32, u32)),
495}
496
497#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
498#[serde(rename_all = "camelCase")]
499pub struct AttrModification {
500 pub attribute_name: String,
501 pub new_value: Option<String>,
502}
503
504#[derive(Clone, Debug, Deserialize, Serialize)]
505#[serde(rename_all = "camelCase")]
506pub struct RuleModification {
507 #[serde(rename = "type")]
508 pub type_: String,
509 pub index: u32,
510 pub name: String,
511 pub value: String,
512 pub priority: String,
513}
514
515#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
516#[serde(rename_all = "camelCase")]
517pub struct StackFrame {
518 pub filename: String,
519 pub function_name: String,
520 pub column_number: u32,
521 pub line_number: u32,
522 }
525
526pub fn get_time_stamp() -> u64 {
527 SystemTime::now()
528 .duration_since(UNIX_EPOCH)
529 .unwrap_or_default()
530 .as_millis() as u64
531}
532
533#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
534#[serde(rename_all = "camelCase")]
535pub struct ConsoleMessageFields {
536 pub level: ConsoleLogLevel,
537 pub filename: String,
538 pub line_number: u32,
539 pub column_number: u32,
540 pub time_stamp: u64,
541}
542
543#[derive(Clone, Debug, Deserialize, Serialize)]
544pub struct ConsoleMessage {
545 pub fields: ConsoleMessageFields,
546 pub arguments: Vec<DebuggerValue>,
547 pub stacktrace: Option<Vec<StackFrame>>,
548}
549
550#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
551#[serde(rename_all = "camelCase")]
552pub struct PageError {
553 pub error_message: String,
554 pub source_name: String,
555 pub line_number: u32,
556 pub column_number: u32,
557 pub time_stamp: u64,
558}
559
560#[derive(Debug, PartialEq, MallocSizeOf)]
561pub struct HttpRequest {
562 pub url: ServoUrl,
563 pub method: Method,
564 pub headers: HeaderMap,
565 pub body: Option<DebugVec>,
566 pub pipeline_id: PipelineId,
567 pub started_date_time: SystemTime,
568 pub time_stamp: i64,
569 pub connect_time: Duration,
570 pub send_time: Duration,
571 pub destination: Destination,
572 pub is_xhr: bool,
573 pub browsing_context_id: BrowsingContextId,
574}
575
576#[derive(Debug, PartialEq, MallocSizeOf)]
577pub struct HttpResponse {
578 #[ignore_malloc_size_of = "Http type"]
579 pub headers: Option<HeaderMap>,
580 pub status: HttpStatus,
581 pub body: Option<DebugVec>,
582 pub from_cache: bool,
583 pub pipeline_id: PipelineId,
584 pub browsing_context_id: BrowsingContextId,
585}
586
587#[derive(Debug, PartialEq)]
588pub struct SecurityInfoUpdate {
589 pub browsing_context_id: BrowsingContextId,
590 pub security_info: Option<TlsSecurityInfo>,
591}
592
593#[derive(Debug)]
594pub enum NetworkEvent {
595 HttpRequest(HttpRequest),
596 HttpRequestUpdate(HttpRequest),
597 HttpResponse(HttpResponse),
598 SecurityInfo(SecurityInfoUpdate),
599}
600
601impl NetworkEvent {
602 pub fn forward_to_devtools(&self) -> bool {
603 match self {
604 NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
605 NetworkEvent::HttpRequestUpdate(_) => true,
606 NetworkEvent::HttpResponse(_) => true,
607 NetworkEvent::SecurityInfo(_) => true,
608 }
609 }
610}
611
612impl TimelineMarker {
613 pub fn start(name: String) -> StartedTimelineMarker {
614 StartedTimelineMarker {
615 name,
616 start_time: CrossProcessInstant::now(),
617 start_stack: None,
618 }
619 }
620}
621
622impl StartedTimelineMarker {
623 pub fn end(self) -> TimelineMarker {
624 TimelineMarker {
625 name: self.name,
626 start_time: self.start_time,
627 start_stack: self.start_stack,
628 end_time: CrossProcessInstant::now(),
629 end_stack: None,
630 }
631 }
632}
633#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
634pub struct WorkerId(pub Uuid);
635impl Display for WorkerId {
636 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
637 write!(f, "{}", self.0)
638 }
639}
640impl FromStr for WorkerId {
641 type Err = uuid::Error;
642
643 fn from_str(s: &str) -> Result<Self, Self::Err> {
644 Ok(Self(s.parse()?))
645 }
646}
647
648#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
649#[serde(rename_all = "camelCase")]
650pub struct CssDatabaseProperty {
651 pub is_inherited: bool,
652 pub values: Vec<String>,
653 pub supports: Vec<String>,
654 pub subproperties: Vec<String>,
655}
656
657#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
658pub enum ShadowRootMode {
659 Open,
660 Closed,
661}
662
663impl fmt::Display for ShadowRootMode {
664 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
665 match self {
666 Self::Open => write!(f, "open"),
667 Self::Closed => write!(f, "close"),
668 }
669 }
670}
671
672#[derive(Debug, Deserialize, Serialize)]
673pub struct SourceInfo {
674 pub url: ServoUrl,
675 pub introduction_type: String,
676 pub inline: bool,
677 pub worker_id: Option<WorkerId>,
678 pub content: Option<String>,
679 pub content_type: Option<String>,
680 pub spidermonkey_id: u32,
681}
682
683#[derive(Clone, Debug, Deserialize, Serialize)]
684#[serde(rename_all = "camelCase")]
685pub struct RecommendedBreakpointLocation {
686 pub script_id: u32,
687 pub offset: u32,
688 pub line_number: u32,
689 pub column_number: u32,
690 pub is_step_start: bool,
691}
692
693#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
694pub struct FrameInfo {
695 pub display_name: Option<String>,
696 pub on_stack: bool,
697 pub oldest: bool,
698 pub this_value: DebuggerValue,
699 pub terminated: bool,
700 pub type_: String,
701 pub url: String,
702}
703
704#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
705pub struct EnvironmentInfo {
706 pub type_: Option<String>,
707 pub scope_kind: Option<String>,
708 pub function_display_name: Option<String>,
709 pub object: Option<DebuggerValue>,
710 pub binding_variables: Vec<PropertyDescriptor>,
711}
712
713#[derive(Clone, Debug, Deserialize, Serialize)]
714pub struct StyleSheetInfo {
715 pub href: Option<String>,
716 pub disabled: bool,
717 pub title: String,
718 pub style_sheet_index: i32,
719 pub system: bool,
720 pub rule_count: u32,
721}
722
723#[derive(Clone, Debug, Deserialize, Serialize)]
724pub struct EventListenerInfo {
725 pub event_type: String,
726 pub capturing: bool,
727}
728
729#[derive(Debug, Deserialize, Serialize)]
730#[serde(rename_all = "camelCase")]
731pub struct PauseReason {
732 #[serde(rename = "type")]
733 pub type_: String,
734 pub on_next: Option<bool>,
735}
736
737#[derive(Debug, Deserialize, Serialize)]
738pub struct FrameOffset {
739 pub actor: String,
740 pub column: u32,
741 pub line: u32,
742}