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(GetEnvironmentRequest, GenericSender<String>),
487 Blackbox(u32, BlackboxCoverage),
488 Unblackbox(u32, BlackboxCoverage),
489}
490
491#[derive(Debug, Deserialize, Serialize)]
492pub enum GetEnvironmentRequest {
493 Global(PipelineId),
494 Frame(String),
495}
496
497#[derive(Debug, Deserialize, Serialize)]
498pub enum BlackboxCoverage {
499 Full,
500 Partial((u32, u32), (u32, u32)),
501}
502
503#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
504#[serde(rename_all = "camelCase")]
505pub struct AttrModification {
506 pub attribute_name: String,
507 pub new_value: Option<String>,
508}
509
510#[derive(Clone, Debug, Deserialize, Serialize)]
511#[serde(rename_all = "camelCase")]
512pub struct RuleModification {
513 #[serde(rename = "type")]
514 pub type_: String,
515 pub index: u32,
516 pub name: String,
517 pub value: String,
518 pub priority: String,
519}
520
521#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
522#[serde(rename_all = "camelCase")]
523pub struct StackFrame {
524 pub filename: String,
525 pub function_name: String,
526 pub column_number: u32,
527 pub line_number: u32,
528 }
531
532pub fn get_time_stamp() -> u64 {
533 SystemTime::now()
534 .duration_since(UNIX_EPOCH)
535 .unwrap_or_default()
536 .as_millis() as u64
537}
538
539#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
540#[serde(rename_all = "camelCase")]
541pub struct ConsoleMessageFields {
542 pub level: ConsoleLogLevel,
543 pub filename: String,
544 pub line_number: u32,
545 pub column_number: u32,
546 pub time_stamp: u64,
547}
548
549#[derive(Clone, Debug, Deserialize, Serialize)]
550pub struct ConsoleMessage {
551 pub fields: ConsoleMessageFields,
552 pub arguments: Vec<DebuggerValue>,
553 pub stacktrace: Option<Vec<StackFrame>>,
554}
555
556#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
557#[serde(rename_all = "camelCase")]
558pub struct PageError {
559 pub error_message: String,
560 pub source_name: String,
561 pub line_number: u32,
562 pub column_number: u32,
563 pub time_stamp: u64,
564}
565
566#[derive(Debug, PartialEq, MallocSizeOf)]
567pub struct HttpRequest {
568 pub url: ServoUrl,
569 pub method: Method,
570 pub headers: HeaderMap,
571 pub body: Option<DebugVec>,
572 pub pipeline_id: PipelineId,
573 pub started_date_time: SystemTime,
574 pub time_stamp: i64,
575 pub connect_time: Duration,
576 pub send_time: Duration,
577 pub destination: Destination,
578 pub is_xhr: bool,
579 pub browsing_context_id: BrowsingContextId,
580}
581
582#[derive(Debug, PartialEq, MallocSizeOf)]
583pub struct HttpResponse {
584 #[ignore_malloc_size_of = "Http type"]
585 pub headers: Option<HeaderMap>,
586 pub status: HttpStatus,
587 pub body: Option<DebugVec>,
588 pub from_cache: bool,
589 pub pipeline_id: PipelineId,
590 pub browsing_context_id: BrowsingContextId,
591}
592
593#[derive(Debug, PartialEq)]
594pub struct SecurityInfoUpdate {
595 pub browsing_context_id: BrowsingContextId,
596 pub security_info: Option<TlsSecurityInfo>,
597}
598
599#[derive(Debug)]
600pub enum NetworkEvent {
601 HttpRequest(HttpRequest),
602 HttpRequestUpdate(HttpRequest),
603 HttpResponse(HttpResponse),
604 SecurityInfo(SecurityInfoUpdate),
605}
606
607impl NetworkEvent {
608 pub fn forward_to_devtools(&self) -> bool {
609 match self {
610 NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
611 NetworkEvent::HttpRequestUpdate(_) => true,
612 NetworkEvent::HttpResponse(_) => true,
613 NetworkEvent::SecurityInfo(_) => true,
614 }
615 }
616}
617
618impl TimelineMarker {
619 pub fn start(name: String) -> StartedTimelineMarker {
620 StartedTimelineMarker {
621 name,
622 start_time: CrossProcessInstant::now(),
623 start_stack: None,
624 }
625 }
626}
627
628impl StartedTimelineMarker {
629 pub fn end(self) -> TimelineMarker {
630 TimelineMarker {
631 name: self.name,
632 start_time: self.start_time,
633 start_stack: self.start_stack,
634 end_time: CrossProcessInstant::now(),
635 end_stack: None,
636 }
637 }
638}
639#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
640pub struct WorkerId(pub Uuid);
641impl Display for WorkerId {
642 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
643 write!(f, "{}", self.0)
644 }
645}
646impl FromStr for WorkerId {
647 type Err = uuid::Error;
648
649 fn from_str(s: &str) -> Result<Self, Self::Err> {
650 Ok(Self(s.parse()?))
651 }
652}
653
654#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
655#[serde(rename_all = "camelCase")]
656pub struct CssDatabaseProperty {
657 pub is_inherited: bool,
658 pub values: Vec<String>,
659 pub supports: Vec<String>,
660 pub subproperties: Vec<String>,
661}
662
663#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
664pub enum ShadowRootMode {
665 Open,
666 Closed,
667}
668
669impl fmt::Display for ShadowRootMode {
670 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
671 match self {
672 Self::Open => write!(f, "open"),
673 Self::Closed => write!(f, "close"),
674 }
675 }
676}
677
678#[derive(Debug, Deserialize, Serialize)]
679pub struct SourceInfo {
680 pub url: ServoUrl,
681 pub introduction_type: String,
682 pub inline: bool,
683 pub worker_id: Option<WorkerId>,
684 pub content: Option<String>,
685 pub content_type: Option<String>,
686 pub spidermonkey_id: u32,
687}
688
689#[derive(Clone, Debug, Deserialize, Serialize)]
690#[serde(rename_all = "camelCase")]
691pub struct RecommendedBreakpointLocation {
692 pub script_id: u32,
693 pub offset: u32,
694 pub line_number: u32,
695 pub column_number: u32,
696 pub is_step_start: bool,
697}
698
699#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
700pub struct FrameInfo {
701 pub display_name: Option<String>,
702 pub on_stack: bool,
703 pub oldest: bool,
704 pub this_value: DebuggerValue,
705 pub terminated: bool,
706 pub type_: String,
707 pub url: String,
708}
709
710#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
711pub struct EnvironmentInfo {
712 pub type_: Option<String>,
713 pub scope_kind: Option<String>,
714 pub function_display_name: Option<String>,
715 pub object: Option<DebuggerValue>,
716 pub binding_variables: Vec<PropertyDescriptor>,
717}
718
719#[derive(Clone, Debug, Deserialize, Serialize)]
720pub struct StyleSheetInfo {
721 pub href: Option<String>,
722 pub disabled: bool,
723 pub title: String,
724 pub style_sheet_index: i32,
725 pub system: bool,
726 pub rule_count: u32,
727}
728
729#[derive(Clone, Debug, Deserialize, Serialize)]
730pub struct EventListenerInfo {
731 pub event_type: String,
732 pub capturing: bool,
733}
734
735#[derive(Debug, Deserialize, Serialize)]
736#[serde(rename_all = "camelCase")]
737pub struct PauseReason {
738 #[serde(rename = "type")]
739 pub type_: String,
740 pub on_next: Option<bool>,
741}
742
743#[derive(Debug, Deserialize, Serialize)]
744pub struct FrameOffset {
745 pub actor: String,
746 pub column: u32,
747 pub line: u32,
748}