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::TlsSecurityInfo;
31use net_traits::http_status::HttpStatus;
32use net_traits::request::Destination;
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 bool,
479 GenericSender<EvaluateJSReply>,
480 ),
481 GetPossibleBreakpoints(u32, GenericSender<Vec<RecommendedBreakpointLocation>>),
482 SetBreakpoint(u32, u32, u32),
483 ClearBreakpoint(u32, u32, u32),
484 Interrupt,
485 Resume(Option<String>, Option<String>),
486 ListFrames(PipelineId, u32, u32, GenericSender<Vec<String>>),
487 GetEnvironment(GetEnvironmentRequest, GenericSender<String>),
488 Blackbox(u32, BlackboxCoverage),
489 Unblackbox(u32, BlackboxCoverage),
490}
491
492#[derive(Debug, Deserialize, Serialize)]
493pub enum GetEnvironmentRequest {
494 Global(PipelineId),
495 Frame(String),
496}
497
498#[derive(Debug, Deserialize, Serialize)]
499pub enum BlackboxCoverage {
500 Full,
501 Partial((u32, u32), (u32, u32)),
502}
503
504#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
505#[serde(rename_all = "camelCase")]
506pub struct AttrModification {
507 pub attribute_name: String,
508 pub new_value: Option<String>,
509}
510
511#[derive(Clone, Debug, Deserialize, Serialize)]
512#[serde(rename_all = "camelCase")]
513pub struct RuleModification {
514 #[serde(rename = "type")]
515 pub type_: String,
516 pub index: u32,
517 pub name: String,
518 pub value: String,
519 pub priority: String,
520}
521
522#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
523#[serde(rename_all = "camelCase")]
524pub struct StackFrame {
525 pub filename: String,
526 pub function_name: String,
527 pub column_number: u32,
528 pub line_number: u32,
529 }
532
533pub fn get_time_stamp() -> u64 {
534 SystemTime::now()
535 .duration_since(UNIX_EPOCH)
536 .unwrap_or_default()
537 .as_millis() as u64
538}
539
540#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
541#[serde(rename_all = "camelCase")]
542pub struct ConsoleMessageFields {
543 pub level: ConsoleLogLevel,
544 pub filename: String,
545 pub line_number: u32,
546 pub column_number: u32,
547 pub time_stamp: u64,
548}
549
550#[derive(Clone, Debug, Deserialize, Serialize)]
551pub struct ConsoleMessage {
552 pub fields: ConsoleMessageFields,
553 pub arguments: Vec<DebuggerValue>,
554 pub stacktrace: Option<Vec<StackFrame>>,
555}
556
557#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
558#[serde(rename_all = "camelCase")]
559pub struct PageError {
560 pub error_message: String,
561 pub source_name: String,
562 pub line_number: u32,
563 pub column_number: u32,
564 pub time_stamp: u64,
565}
566
567#[derive(Debug, PartialEq, MallocSizeOf)]
568pub struct HttpRequest {
569 pub url: ServoUrl,
570 pub method: Method,
571 pub headers: HeaderMap,
572 pub body: Option<bytes::Bytes>,
573 pub pipeline_id: PipelineId,
574 pub started_date_time: SystemTime,
575 pub time_stamp: i64,
576 pub connect_time: Duration,
577 pub send_time: Duration,
578 pub destination: Destination,
579 pub is_xhr: bool,
580 pub browsing_context_id: BrowsingContextId,
581}
582
583#[derive(Debug, PartialEq, MallocSizeOf)]
584pub struct HttpResponse {
585 #[ignore_malloc_size_of = "Http type"]
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(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
641pub struct WorkerId(pub Uuid);
642impl Display for WorkerId {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 write!(f, "{}", self.0)
645 }
646}
647impl FromStr for WorkerId {
648 type Err = uuid::Error;
649
650 fn from_str(s: &str) -> Result<Self, Self::Err> {
651 Ok(Self(s.parse()?))
652 }
653}
654
655#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
656#[serde(rename_all = "camelCase")]
657pub struct CssDatabaseProperty {
658 pub is_inherited: bool,
659 pub values: Vec<String>,
660 pub supports: Vec<String>,
661 pub subproperties: Vec<String>,
662}
663
664#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
665pub enum ShadowRootMode {
666 Open,
667 Closed,
668}
669
670impl fmt::Display for ShadowRootMode {
671 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672 match self {
673 Self::Open => write!(f, "open"),
674 Self::Closed => write!(f, "close"),
675 }
676 }
677}
678
679#[derive(Debug, Deserialize, Serialize)]
680pub struct SourceInfo {
681 pub url: ServoUrl,
682 pub introduction_type: String,
683 pub inline: bool,
684 pub worker_id: Option<WorkerId>,
685 pub content: Option<String>,
686 pub content_type: Option<String>,
687 pub spidermonkey_id: u32,
688}
689
690#[derive(Clone, Debug, Deserialize, Serialize)]
691#[serde(rename_all = "camelCase")]
692pub struct RecommendedBreakpointLocation {
693 pub script_id: u32,
694 pub offset: u32,
695 pub line_number: u32,
696 pub column_number: u32,
697 pub is_step_start: bool,
698}
699
700#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
701pub struct FrameInfo {
702 pub display_name: Option<String>,
703 pub on_stack: bool,
704 pub oldest: bool,
705 pub this_value: DebuggerValue,
706 pub terminated: bool,
707 pub type_: String,
708 pub url: String,
709}
710
711#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
712pub struct EnvironmentInfo {
713 pub type_: Option<String>,
714 pub scope_kind: Option<String>,
715 pub function_display_name: Option<String>,
716 pub object: Option<DebuggerValue>,
717 pub binding_variables: Vec<PropertyDescriptor>,
718}
719
720#[derive(Clone, Debug, Deserialize, Serialize)]
721pub struct StyleSheetInfo {
722 pub href: Option<String>,
723 pub disabled: bool,
724 pub title: String,
725 pub style_sheet_index: i32,
726 pub system: bool,
727 pub rule_count: u32,
728}
729
730#[derive(Clone, Debug, Deserialize, Serialize)]
731pub struct EventListenerInfo {
732 pub event_type: String,
733 pub capturing: bool,
734}
735
736#[derive(Debug, Deserialize, Serialize)]
737#[serde(rename_all = "camelCase")]
738pub struct PauseReason {
739 #[serde(rename = "type")]
740 pub type_: String,
741 pub on_next: Option<bool>,
742}
743
744#[derive(Debug, Deserialize, Serialize)]
745pub struct FrameOffset {
746 pub actor: String,
747 pub column: u32,
748 pub line: u32,
749}