1#![crate_name = "devtools_traits"]
10#![crate_type = "rlib"]
11#![deny(unsafe_code)]
12
13use core::fmt;
14use std::collections::HashMap;
15use std::fmt::Display;
16use std::net::TcpStream;
17use std::str::FromStr;
18use std::time::{Duration, SystemTime, UNIX_EPOCH};
19
20use base::cross_process_instant::CrossProcessInstant;
21use base::id::{BrowsingContextId, PipelineId, WebViewId};
22use bitflags::bitflags;
23use embedder_traits::Theme;
24use http::{HeaderMap, Method};
25use ipc_channel::ipc::IpcSender;
26use malloc_size_of_derive::MallocSizeOf;
27use net_traits::DebugVec;
28use net_traits::http_status::HttpStatus;
29use net_traits::request::Destination;
30use serde::{Deserialize, Serialize};
31use servo_url::ServoUrl;
32use uuid::Uuid;
33
34#[derive(Clone, Debug, Deserialize, Serialize)]
37pub struct DevtoolsPageInfo {
38 pub title: String,
39 pub url: ServoUrl,
40 pub is_top_level_global: bool,
41}
42
43#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
44pub struct CSSError {
45 pub filename: String,
46 pub line: u32,
47 pub column: u32,
48 pub msg: String,
49}
50
51#[derive(Debug)]
54pub enum DevtoolsControlMsg {
55 FromChrome(ChromeToDevtoolsControlMsg),
57 FromScript(ScriptToDevtoolsControlMsg),
59}
60
61#[expect(clippy::large_enum_variant)]
64#[derive(Debug)]
65pub enum ChromeToDevtoolsControlMsg {
66 AddClient(TcpStream),
68 ServerExitMsg,
70 NetworkEvent(String, NetworkEvent),
73}
74
75#[derive(Debug, Deserialize, Serialize)]
77pub enum NavigationState {
78 Start(ServoUrl),
80 Stop(PipelineId, DevtoolsPageInfo),
82}
83
84#[derive(Debug, Deserialize, Serialize)]
85pub enum ScriptToDevtoolsControlMsg {
87 NewGlobal(
90 (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
91 IpcSender<DevtoolScriptControlMsg>,
92 DevtoolsPageInfo,
93 ),
94 Navigate(BrowsingContextId, NavigationState),
96 ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
98 FramerateTick(String, f64),
101
102 ReportCSSError(PipelineId, CSSError),
104
105 ReportPageError(PipelineId, PageError),
107
108 TitleChanged(PipelineId, String),
110
111 CreateSourceActor(IpcSender<DevtoolScriptControlMsg>, PipelineId, SourceInfo),
113
114 UpdateSourceContent(PipelineId, String),
115}
116
117#[derive(Debug, Deserialize, Serialize)]
120pub enum EvaluateJSReply {
121 VoidValue,
122 NullValue,
123 BooleanValue(bool),
124 NumberValue(f64),
125 StringValue(String),
126 ActorValue { class: String, uuid: String },
127}
128
129#[derive(Debug, Deserialize, Serialize)]
130pub struct AttrInfo {
131 pub namespace: String,
132 pub name: String,
133 pub value: String,
134}
135
136#[derive(Debug, Deserialize, Serialize)]
137#[serde(rename_all = "camelCase")]
138pub struct NodeInfo {
139 pub unique_id: String,
140 pub host: Option<String>,
141 #[serde(rename = "baseURI")]
142 pub base_uri: String,
143 pub parent: String,
144 pub node_type: u16,
145 pub node_name: String,
146 pub node_value: Option<String>,
147 pub num_children: usize,
148 pub attrs: Vec<AttrInfo>,
149 pub is_top_level_document: bool,
150 pub shadow_root_mode: Option<ShadowRootMode>,
151 pub is_shadow_host: bool,
152 pub display: Option<String>,
153 pub is_displayed: bool,
157
158 pub doctype_name: Option<String>,
160
161 pub doctype_public_identifier: Option<String>,
163
164 pub doctype_system_identifier: Option<String>,
166}
167
168pub struct StartedTimelineMarker {
169 name: String,
170 start_time: CrossProcessInstant,
171 start_stack: Option<Vec<()>>,
172}
173
174#[derive(Debug, Deserialize, Serialize)]
175pub struct TimelineMarker {
176 pub name: String,
177 pub start_time: CrossProcessInstant,
178 pub start_stack: Option<Vec<()>>,
179 pub end_time: CrossProcessInstant,
180 pub end_stack: Option<Vec<()>>,
181}
182
183#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
184pub enum TimelineMarkerType {
185 Reflow,
186 DOMEvent,
187}
188
189#[derive(Debug, Deserialize, Serialize)]
190#[serde(rename_all = "camelCase")]
191pub struct NodeStyle {
192 pub name: String,
193 pub value: String,
194 pub priority: String,
195}
196
197#[derive(Debug, Deserialize, Serialize)]
199#[serde(rename_all = "camelCase")]
200pub struct ComputedNodeLayout {
201 pub display: String,
202 pub position: String,
203 pub z_index: String,
204 pub box_sizing: String,
205
206 pub auto_margins: AutoMargins,
207 pub margin_top: String,
208 pub margin_right: String,
209 pub margin_bottom: String,
210 pub margin_left: String,
211
212 pub border_top_width: String,
213 pub border_right_width: String,
214 pub border_bottom_width: String,
215 pub border_left_width: String,
216
217 pub padding_top: String,
218 pub padding_right: String,
219 pub padding_bottom: String,
220 pub padding_left: String,
221
222 pub width: f32,
223 pub height: f32,
224}
225
226#[derive(Debug, Deserialize, Serialize)]
227pub struct AutoMargins {
228 pub top: bool,
229 pub right: bool,
230 pub bottom: bool,
231 pub left: bool,
232}
233
234#[derive(Debug, Deserialize, Serialize)]
237pub enum DevtoolScriptControlMsg {
238 EvaluateJS(PipelineId, String, IpcSender<EvaluateJSReply>),
240 GetRootNode(PipelineId, IpcSender<Option<NodeInfo>>),
242 GetDocumentElement(PipelineId, IpcSender<Option<NodeInfo>>),
244 GetChildren(PipelineId, String, IpcSender<Option<Vec<NodeInfo>>>),
246 GetAttributeStyle(PipelineId, String, IpcSender<Option<Vec<NodeStyle>>>),
248 GetStylesheetStyle(
250 PipelineId,
251 String,
252 String,
253 usize,
254 IpcSender<Option<Vec<NodeStyle>>>,
255 ),
256 GetSelectors(PipelineId, String, IpcSender<Option<Vec<(String, usize)>>>),
259 GetComputedStyle(PipelineId, String, IpcSender<Option<Vec<NodeStyle>>>),
261 GetLayout(PipelineId, String, IpcSender<Option<ComputedNodeLayout>>),
263 GetXPath(PipelineId, String, IpcSender<String>),
265 ModifyAttribute(PipelineId, String, Vec<AttrModification>),
267 ModifyRule(PipelineId, String, Vec<RuleModification>),
269 WantsLiveNotifications(PipelineId, bool),
271 SetTimelineMarkers(
273 PipelineId,
274 Vec<TimelineMarkerType>,
275 IpcSender<Option<TimelineMarker>>,
276 ),
277 DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
279 RequestAnimationFrame(PipelineId, String),
282 Reload(PipelineId),
284 GetCssDatabase(IpcSender<HashMap<String, CssDatabaseProperty>>),
286 SimulateColorScheme(PipelineId, Theme),
288 HighlightDomNode(PipelineId, Option<String>),
290
291 GetPossibleBreakpoints(u32, IpcSender<Vec<RecommendedBreakpointLocation>>),
292}
293
294#[derive(Clone, Debug, Deserialize, Serialize)]
295#[serde(rename_all = "camelCase")]
296pub struct AttrModification {
297 pub attribute_name: String,
298 pub new_value: Option<String>,
299}
300
301#[derive(Clone, Debug, Deserialize, Serialize)]
302#[serde(rename_all = "camelCase")]
303pub struct RuleModification {
304 #[serde(rename = "type")]
305 pub type_: String,
306 pub index: u32,
307 pub name: String,
308 pub value: String,
309 pub priority: String,
310}
311
312#[derive(Clone, Debug, Deserialize, Serialize)]
313pub enum LogLevel {
314 Log,
315 Debug,
316 Info,
317 Warn,
318 Error,
319 Clear,
320 Trace,
321}
322
323impl From<LogLevel> for log::Level {
324 fn from(value: LogLevel) -> Self {
325 match value {
326 LogLevel::Log => log::Level::Info,
327 LogLevel::Clear => log::Level::Info,
328
329 LogLevel::Debug => log::Level::Debug,
330 LogLevel::Info => log::Level::Info,
331 LogLevel::Warn => log::Level::Warn,
332 LogLevel::Error => log::Level::Error,
333 LogLevel::Trace => log::Level::Trace,
334 }
335 }
336}
337
338#[derive(Clone, Debug, Deserialize, Serialize)]
340#[serde(rename_all = "camelCase")]
341pub struct ConsoleMessage {
342 pub log_level: LogLevel,
343 pub filename: String,
344 pub line_number: usize,
345 pub column_number: usize,
346 pub arguments: Vec<ConsoleMessageArgument>,
347 pub stacktrace: Option<Vec<StackFrame>>,
348}
349
350#[derive(Clone, Debug, Deserialize, Serialize)]
351pub enum ConsoleMessageArgument {
352 String(String),
353 Integer(i32),
354 Number(f64),
355}
356
357#[derive(Clone, Debug, Deserialize, Serialize)]
358pub struct StackFrame {
359 pub filename: String,
360
361 #[serde(rename = "functionName")]
362 pub function_name: String,
363
364 #[serde(rename = "columnNumber")]
365 pub column_number: u32,
366
367 #[serde(rename = "lineNumber")]
368 pub line_number: u32,
369}
370
371bitflags! {
372 #[derive(Deserialize, Serialize)]
373 pub struct CachedConsoleMessageTypes: u8 {
374 const PAGE_ERROR = 1 << 0;
375 const CONSOLE_API = 1 << 1;
376 }
377}
378
379#[derive(Clone, Debug, Deserialize, Serialize)]
380#[serde(rename_all = "camelCase")]
381pub struct PageError {
382 #[serde(rename = "_type")]
383 pub type_: String,
384 pub error_message: String,
385 pub source_name: String,
386 pub line_text: String,
387 pub line_number: u32,
388 pub column_number: u32,
389 pub category: String,
390 pub time_stamp: u64,
391 pub error: bool,
392 pub warning: bool,
393 pub exception: bool,
394 pub strict: bool,
395 pub private: bool,
396}
397
398#[derive(Clone, Debug, Deserialize, Serialize)]
400pub struct ConsoleLog {
401 pub level: String,
402 pub filename: String,
403 pub line_number: u32,
404 pub column_number: u32,
405 pub time_stamp: u64,
406 pub arguments: Vec<ConsoleArgument>,
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub stacktrace: Option<Vec<StackFrame>>,
409}
410
411impl From<ConsoleMessage> for ConsoleLog {
412 fn from(value: ConsoleMessage) -> Self {
413 let level = match value.log_level {
414 LogLevel::Debug => "debug",
415 LogLevel::Info => "info",
416 LogLevel::Warn => "warn",
417 LogLevel::Error => "error",
418 LogLevel::Clear => "clear",
419 LogLevel::Trace => "trace",
420 LogLevel::Log => "log",
421 }
422 .to_owned();
423
424 let time_stamp = SystemTime::now()
425 .duration_since(UNIX_EPOCH)
426 .unwrap_or_default()
427 .as_millis() as u64;
428
429 Self {
430 level,
431 filename: value.filename,
432 line_number: value.line_number as u32,
433 column_number: value.column_number as u32,
434 time_stamp,
435 arguments: value.arguments.into_iter().map(|arg| arg.into()).collect(),
436 stacktrace: value.stacktrace,
437 }
438 }
439}
440
441#[derive(Debug, Deserialize, Serialize)]
442pub enum CachedConsoleMessage {
443 PageError(PageError),
444 ConsoleLog(ConsoleLog),
445}
446
447#[derive(Debug, PartialEq)]
448pub struct HttpRequest {
449 pub url: ServoUrl,
450 pub method: Method,
451 pub headers: HeaderMap,
452 pub body: Option<DebugVec>,
453 pub pipeline_id: PipelineId,
454 pub started_date_time: SystemTime,
455 pub time_stamp: i64,
456 pub connect_time: Duration,
457 pub send_time: Duration,
458 pub destination: Destination,
459 pub is_xhr: bool,
460 pub browsing_context_id: BrowsingContextId,
461}
462
463#[derive(Debug, PartialEq)]
464pub struct HttpResponse {
465 pub headers: Option<HeaderMap>,
466 pub status: HttpStatus,
467 pub body: Option<DebugVec>,
468 pub from_cache: bool,
469 pub pipeline_id: PipelineId,
470 pub browsing_context_id: BrowsingContextId,
471}
472
473#[derive(Debug)]
474pub enum NetworkEvent {
475 HttpRequest(HttpRequest),
476 HttpRequestUpdate(HttpRequest),
477 HttpResponse(HttpResponse),
478}
479
480impl NetworkEvent {
481 pub fn forward_to_devtools(&self) -> bool {
482 !matches!(self, NetworkEvent::HttpRequest(http_request) if http_request.url.scheme() == "data") ||
483 match self {
484 NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
485 NetworkEvent::HttpRequestUpdate(..) => true,
486 NetworkEvent::HttpResponse(..) => true,
487 }
488 }
489}
490
491impl TimelineMarker {
492 pub fn start(name: String) -> StartedTimelineMarker {
493 StartedTimelineMarker {
494 name,
495 start_time: CrossProcessInstant::now(),
496 start_stack: None,
497 }
498 }
499}
500
501impl StartedTimelineMarker {
502 pub fn end(self) -> TimelineMarker {
503 TimelineMarker {
504 name: self.name,
505 start_time: self.start_time,
506 start_stack: self.start_stack,
507 end_time: CrossProcessInstant::now(),
508 end_stack: None,
509 }
510 }
511}
512#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
513pub struct WorkerId(pub Uuid);
514impl Display for WorkerId {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 write!(f, "{}", self.0)
517 }
518}
519impl FromStr for WorkerId {
520 type Err = uuid::Error;
521
522 fn from_str(s: &str) -> Result<Self, Self::Err> {
523 Ok(Self(s.parse()?))
524 }
525}
526
527#[derive(Debug, Deserialize, Serialize)]
528#[serde(rename_all = "camelCase")]
529pub struct CssDatabaseProperty {
530 pub is_inherited: bool,
531 pub values: Vec<String>,
532 pub supports: Vec<String>,
533 pub subproperties: Vec<String>,
534}
535
536#[derive(Clone, Debug, Deserialize, Serialize)]
537#[serde(untagged)]
538pub enum ConsoleArgument {
539 String(String),
540 Integer(i32),
541 Number(f64),
542}
543
544impl From<ConsoleMessageArgument> for ConsoleArgument {
545 fn from(value: ConsoleMessageArgument) -> Self {
546 match value {
547 ConsoleMessageArgument::String(string) => Self::String(string),
548 ConsoleMessageArgument::Integer(integer) => Self::Integer(integer),
549 ConsoleMessageArgument::Number(number) => Self::Number(number),
550 }
551 }
552}
553
554impl From<String> for ConsoleMessageArgument {
555 fn from(value: String) -> Self {
556 Self::String(value)
557 }
558}
559
560pub struct ConsoleMessageBuilder {
561 level: LogLevel,
562 filename: String,
563 line_number: u32,
564 column_number: u32,
565 arguments: Vec<ConsoleMessageArgument>,
566 stack_trace: Option<Vec<StackFrame>>,
567}
568
569impl ConsoleMessageBuilder {
570 pub fn new(level: LogLevel, filename: String, line_number: u32, column_number: u32) -> Self {
571 Self {
572 level,
573 filename,
574 line_number,
575 column_number,
576 arguments: vec![],
577 stack_trace: None,
578 }
579 }
580
581 pub fn attach_stack_trace(&mut self, stack_trace: Vec<StackFrame>) -> &mut Self {
582 self.stack_trace = Some(stack_trace);
583 self
584 }
585
586 pub fn add_argument(&mut self, argument: ConsoleMessageArgument) -> &mut Self {
587 self.arguments.push(argument);
588 self
589 }
590
591 pub fn finish(self) -> ConsoleMessage {
592 ConsoleMessage {
593 log_level: self.level,
594 filename: self.filename,
595 line_number: self.line_number as usize,
596 column_number: self.column_number as usize,
597 arguments: self.arguments,
598 stacktrace: self.stack_trace,
599 }
600 }
601}
602
603#[derive(Debug, Deserialize, Serialize)]
604pub enum ShadowRootMode {
605 Open,
606 Closed,
607}
608
609impl fmt::Display for ShadowRootMode {
610 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
611 match self {
612 Self::Open => write!(f, "open"),
613 Self::Closed => write!(f, "close"),
614 }
615 }
616}
617
618#[derive(Debug, Deserialize, Serialize)]
619pub struct SourceInfo {
620 pub url: ServoUrl,
621 pub introduction_type: String,
622 pub inline: bool,
623 pub worker_id: Option<WorkerId>,
624 pub content: Option<String>,
625 pub content_type: Option<String>,
626 pub spidermonkey_id: u32,
627}
628
629#[derive(Clone, Debug, Deserialize, Serialize)]
630#[serde(rename_all = "camelCase")]
631pub struct RecommendedBreakpointLocation {
632 pub offset: u32,
633 pub line_number: u32,
634 pub column_number: u32,
635 pub is_step_start: bool,
636}