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 ModifyAttribute(PipelineId, String, Vec<AttrModification>),
264 ModifyRule(PipelineId, String, Vec<RuleModification>),
266 WantsLiveNotifications(PipelineId, bool),
268 SetTimelineMarkers(
270 PipelineId,
271 Vec<TimelineMarkerType>,
272 IpcSender<Option<TimelineMarker>>,
273 ),
274 DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
276 RequestAnimationFrame(PipelineId, String),
279 Reload(PipelineId),
281 GetCssDatabase(IpcSender<HashMap<String, CssDatabaseProperty>>),
283 SimulateColorScheme(PipelineId, Theme),
285 HighlightDomNode(PipelineId, Option<String>),
287
288 GetPossibleBreakpoints(u32, IpcSender<Vec<RecommendedBreakpointLocation>>),
289}
290
291#[derive(Clone, Debug, Deserialize, Serialize)]
292#[serde(rename_all = "camelCase")]
293pub struct AttrModification {
294 pub attribute_name: String,
295 pub new_value: Option<String>,
296}
297
298#[derive(Clone, Debug, Deserialize, Serialize)]
299#[serde(rename_all = "camelCase")]
300pub struct RuleModification {
301 #[serde(rename = "type")]
302 pub type_: String,
303 pub index: u32,
304 pub name: String,
305 pub value: String,
306 pub priority: String,
307}
308
309#[derive(Clone, Debug, Deserialize, Serialize)]
310pub enum LogLevel {
311 Log,
312 Debug,
313 Info,
314 Warn,
315 Error,
316 Clear,
317 Trace,
318}
319
320impl From<LogLevel> for log::Level {
321 fn from(value: LogLevel) -> Self {
322 match value {
323 LogLevel::Log => log::Level::Info,
324 LogLevel::Clear => log::Level::Info,
325
326 LogLevel::Debug => log::Level::Debug,
327 LogLevel::Info => log::Level::Info,
328 LogLevel::Warn => log::Level::Warn,
329 LogLevel::Error => log::Level::Error,
330 LogLevel::Trace => log::Level::Trace,
331 }
332 }
333}
334
335#[derive(Clone, Debug, Deserialize, Serialize)]
337#[serde(rename_all = "camelCase")]
338pub struct ConsoleMessage {
339 pub log_level: LogLevel,
340 pub filename: String,
341 pub line_number: usize,
342 pub column_number: usize,
343 pub arguments: Vec<ConsoleMessageArgument>,
344 pub stacktrace: Option<Vec<StackFrame>>,
345}
346
347#[derive(Clone, Debug, Deserialize, Serialize)]
348pub enum ConsoleMessageArgument {
349 String(String),
350 Integer(i32),
351 Number(f64),
352}
353
354#[derive(Clone, Debug, Deserialize, Serialize)]
355pub struct StackFrame {
356 pub filename: String,
357
358 #[serde(rename = "functionName")]
359 pub function_name: String,
360
361 #[serde(rename = "columnNumber")]
362 pub column_number: u32,
363
364 #[serde(rename = "lineNumber")]
365 pub line_number: u32,
366}
367
368bitflags! {
369 #[derive(Deserialize, Serialize)]
370 pub struct CachedConsoleMessageTypes: u8 {
371 const PAGE_ERROR = 1 << 0;
372 const CONSOLE_API = 1 << 1;
373 }
374}
375
376#[derive(Clone, Debug, Deserialize, Serialize)]
377#[serde(rename_all = "camelCase")]
378pub struct PageError {
379 #[serde(rename = "_type")]
380 pub type_: String,
381 pub error_message: String,
382 pub source_name: String,
383 pub line_text: String,
384 pub line_number: u32,
385 pub column_number: u32,
386 pub category: String,
387 pub time_stamp: u64,
388 pub error: bool,
389 pub warning: bool,
390 pub exception: bool,
391 pub strict: bool,
392 pub private: bool,
393}
394
395#[derive(Clone, Debug, Deserialize, Serialize)]
397pub struct ConsoleLog {
398 pub level: String,
399 pub filename: String,
400 pub line_number: u32,
401 pub column_number: u32,
402 pub time_stamp: u64,
403 pub arguments: Vec<ConsoleArgument>,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub stacktrace: Option<Vec<StackFrame>>,
406}
407
408impl From<ConsoleMessage> for ConsoleLog {
409 fn from(value: ConsoleMessage) -> Self {
410 let level = match value.log_level {
411 LogLevel::Debug => "debug",
412 LogLevel::Info => "info",
413 LogLevel::Warn => "warn",
414 LogLevel::Error => "error",
415 LogLevel::Clear => "clear",
416 LogLevel::Trace => "trace",
417 LogLevel::Log => "log",
418 }
419 .to_owned();
420
421 let time_stamp = SystemTime::now()
422 .duration_since(UNIX_EPOCH)
423 .unwrap_or_default()
424 .as_millis() as u64;
425
426 Self {
427 level,
428 filename: value.filename,
429 line_number: value.line_number as u32,
430 column_number: value.column_number as u32,
431 time_stamp,
432 arguments: value.arguments.into_iter().map(|arg| arg.into()).collect(),
433 stacktrace: value.stacktrace,
434 }
435 }
436}
437
438#[derive(Debug, Deserialize, Serialize)]
439pub enum CachedConsoleMessage {
440 PageError(PageError),
441 ConsoleLog(ConsoleLog),
442}
443
444#[derive(Debug, PartialEq)]
445pub struct HttpRequest {
446 pub url: ServoUrl,
447 pub method: Method,
448 pub headers: HeaderMap,
449 pub body: Option<Vec<u8>>,
450 pub pipeline_id: PipelineId,
451 pub started_date_time: SystemTime,
452 pub time_stamp: i64,
453 pub connect_time: Duration,
454 pub send_time: Duration,
455 pub destination: Destination,
456 pub is_xhr: bool,
457 pub browsing_context_id: BrowsingContextId,
458}
459
460#[derive(Debug, PartialEq)]
461pub struct HttpResponse {
462 pub headers: Option<HeaderMap>,
463 pub status: HttpStatus,
464 pub body: Option<Vec<u8>>,
465 pub pipeline_id: PipelineId,
466 pub browsing_context_id: BrowsingContextId,
467}
468
469#[derive(Debug)]
470pub enum NetworkEvent {
471 HttpRequest(HttpRequest),
472 HttpRequestUpdate(HttpRequest),
473 HttpResponse(HttpResponse),
474}
475
476impl TimelineMarker {
477 pub fn start(name: String) -> StartedTimelineMarker {
478 StartedTimelineMarker {
479 name,
480 start_time: CrossProcessInstant::now(),
481 start_stack: None,
482 }
483 }
484}
485
486impl StartedTimelineMarker {
487 pub fn end(self) -> TimelineMarker {
488 TimelineMarker {
489 name: self.name,
490 start_time: self.start_time,
491 start_stack: self.start_stack,
492 end_time: CrossProcessInstant::now(),
493 end_stack: None,
494 }
495 }
496}
497#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
498pub struct WorkerId(pub Uuid);
499impl Display for WorkerId {
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 write!(f, "{}", self.0)
502 }
503}
504impl FromStr for WorkerId {
505 type Err = uuid::Error;
506
507 fn from_str(s: &str) -> Result<Self, Self::Err> {
508 Ok(Self(s.parse()?))
509 }
510}
511
512#[derive(Debug, Deserialize, Serialize)]
513#[serde(rename_all = "camelCase")]
514pub struct CssDatabaseProperty {
515 pub is_inherited: bool,
516 pub values: Vec<String>,
517 pub supports: Vec<String>,
518 pub subproperties: Vec<String>,
519}
520
521#[derive(Clone, Debug, Deserialize, Serialize)]
522#[serde(untagged)]
523pub enum ConsoleArgument {
524 String(String),
525 Integer(i32),
526 Number(f64),
527}
528
529impl From<ConsoleMessageArgument> for ConsoleArgument {
530 fn from(value: ConsoleMessageArgument) -> Self {
531 match value {
532 ConsoleMessageArgument::String(string) => Self::String(string),
533 ConsoleMessageArgument::Integer(integer) => Self::Integer(integer),
534 ConsoleMessageArgument::Number(number) => Self::Number(number),
535 }
536 }
537}
538
539impl From<String> for ConsoleMessageArgument {
540 fn from(value: String) -> Self {
541 Self::String(value)
542 }
543}
544
545pub struct ConsoleMessageBuilder {
546 level: LogLevel,
547 filename: String,
548 line_number: u32,
549 column_number: u32,
550 arguments: Vec<ConsoleMessageArgument>,
551 stack_trace: Option<Vec<StackFrame>>,
552}
553
554impl ConsoleMessageBuilder {
555 pub fn new(level: LogLevel, filename: String, line_number: u32, column_number: u32) -> Self {
556 Self {
557 level,
558 filename,
559 line_number,
560 column_number,
561 arguments: vec![],
562 stack_trace: None,
563 }
564 }
565
566 pub fn attach_stack_trace(&mut self, stack_trace: Vec<StackFrame>) -> &mut Self {
567 self.stack_trace = Some(stack_trace);
568 self
569 }
570
571 pub fn add_argument(&mut self, argument: ConsoleMessageArgument) -> &mut Self {
572 self.arguments.push(argument);
573 self
574 }
575
576 pub fn finish(self) -> ConsoleMessage {
577 ConsoleMessage {
578 log_level: self.level,
579 filename: self.filename,
580 line_number: self.line_number as usize,
581 column_number: self.column_number as usize,
582 arguments: self.arguments,
583 stacktrace: self.stack_trace,
584 }
585 }
586}
587
588#[derive(Debug, Deserialize, Serialize)]
589pub enum ShadowRootMode {
590 Open,
591 Closed,
592}
593
594impl fmt::Display for ShadowRootMode {
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 match self {
597 Self::Open => write!(f, "open"),
598 Self::Closed => write!(f, "close"),
599 }
600 }
601}
602
603#[derive(Debug, Deserialize, Serialize)]
604pub struct SourceInfo {
605 pub url: ServoUrl,
606 pub introduction_type: String,
607 pub inline: bool,
608 pub worker_id: Option<WorkerId>,
609 pub content: Option<String>,
610 pub content_type: Option<String>,
611 pub spidermonkey_id: u32,
612}
613
614#[derive(Clone, Debug, Deserialize, Serialize)]
615#[serde(rename_all = "camelCase")]
616pub struct RecommendedBreakpointLocation {
617 pub offset: u32,
618 pub line_number: u32,
619 pub column_number: u32,
620 pub is_step_start: bool,
621}