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