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