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