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