Skip to main content

devtools_traits/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! This module contains shared types and messages for use by devtools/script.
6//! The traits are here instead of in script so that the devtools crate can be
7//! modified independently of the rest of Servo.
8//!
9//! Since these types can be sent through the IPC channel and use non
10//! self-describing serializers, the `flatten`, `skip*`, `tag` and `untagged`
11//! serde annotations are not supported. Types like `serde_json::Value` aren't
12//! supported either. For JSON serialization it is preferred to use a wrapper
13//! struct in the devtools crate instead.
14
15#![crate_name = "devtools_traits"]
16#![crate_type = "rlib"]
17#![deny(unsafe_code)]
18
19use core::fmt;
20use std::collections::HashMap;
21use std::net::TcpStream;
22use std::time::{Duration, SystemTime, UNIX_EPOCH};
23
24pub use embedder_traits::ConsoleLogLevel;
25use embedder_traits::Theme;
26use http::{HeaderMap, Method};
27use malloc_size_of_derive::MallocSizeOf;
28use net_traits::TlsSecurityInfo;
29use net_traits::http_status::HttpStatus;
30use net_traits::request::Destination;
31use profile_traits::mem::ReportsChan;
32use serde::de::{Error, Visitor};
33use serde::{Deserialize, Serialize};
34use servo_base::cross_process_instant::CrossProcessInstant;
35use servo_base::generic_channel::GenericSender;
36pub use servo_base::id::WorkerId;
37use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
38use servo_url::ServoUrl;
39use uuid::Uuid;
40
41// Information would be attached to NewGlobal to be received and show in devtools.
42// Extend these fields if we need more information.
43#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
44pub struct DevtoolsPageInfo {
45    pub title: String,
46    pub url: ServoUrl,
47    pub is_top_level_global: bool,
48    pub is_service_worker: bool,
49}
50
51#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
52pub struct CSSError {
53    pub filename: String,
54    pub line: u32,
55    pub column: u32,
56    pub msg: String,
57}
58
59/// Messages to instruct the devtools server to update its known actors/state
60/// according to changes in the browser.
61#[derive(Debug)]
62pub enum DevtoolsControlMsg {
63    /// Messages from threads in the chrome process (resource/constellation/devtools)
64    FromChrome(ChromeToDevtoolsControlMsg),
65    /// Messages from script threads
66    FromScript(ScriptToDevtoolsControlMsg),
67    /// Sent when a devtools client thread terminates.
68    ClientExited,
69}
70
71/// Events that the devtools server must act upon.
72// FIXME: https://github.com/servo/servo/issues/34591
73#[expect(clippy::large_enum_variant)]
74#[derive(Debug)]
75pub enum ChromeToDevtoolsControlMsg {
76    /// A new client has connected to the server.
77    AddClient(TcpStream),
78    /// The browser is shutting down.
79    ServerExitMsg,
80    /// A network event occurred (request, reply, etc.). The actor with the
81    /// provided name should be notified.
82    NetworkEvent(Uuid, NetworkEvent),
83    /// Perform a memory report.
84    CollectMemoryReport(ReportsChan),
85}
86
87/// The state of a page navigation.
88#[derive(Debug, Deserialize, Serialize)]
89pub enum NavigationState {
90    /// A browsing context is about to navigate to a given URL.
91    Start(ServoUrl),
92    /// A browsing context has completed navigating to the provided pipeline.
93    Stop(PipelineId, DevtoolsPageInfo),
94}
95
96#[derive(Debug, Deserialize, Serialize)]
97/// Events that the devtools server must act upon.
98pub enum ScriptToDevtoolsControlMsg {
99    /// A new global object was created, associated with a particular pipeline.
100    /// The means of communicating directly with it are provided.
101    NewGlobal(
102        (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
103        GenericSender<DevtoolScriptControlMsg>,
104        DevtoolsPageInfo,
105    ),
106    /// The given browsing context is performing a navigation.
107    Navigate(BrowsingContextId, NavigationState),
108    /// A particular page has invoked the console API.
109    ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
110    /// Request to clear the console for a given pipeline.
111    ClearConsole(PipelineId, Option<WorkerId>),
112    /// An animation frame with the given timestamp was processed in a script thread.
113    /// The actor with the provided name should be notified.
114    FramerateTick(String, f64),
115
116    /// Report a CSS parse error for the given pipeline
117    ReportCSSError(PipelineId, CSSError),
118
119    /// Report a page error for the given pipeline
120    ReportPageError(PipelineId, PageError),
121
122    /// Report a page title change
123    TitleChanged(PipelineId, String),
124
125    /// Get source information from script
126    CreateSourceActor(
127        GenericSender<DevtoolScriptControlMsg>,
128        PipelineId,
129        SourceInfo,
130    ),
131
132    UpdateSourceContent(PipelineId, String),
133
134    DomMutation(PipelineId, DomMutation),
135
136    /// The debugger is paused, sending frame information.
137    DebuggerPause(PipelineId, FrameOffset, PauseReason),
138
139    /// Get frame information from script
140    CreateFrameActor(GenericSender<String>, PipelineId, FrameInfo),
141
142    /// Get object information from script
143    CreateObjectActor(GenericSender<String>, DebuggerValue),
144
145    /// Get environment information from script
146    CreateEnvironmentActor(
147        GenericSender<String>,
148        EnvironmentInfo,
149        Option<String>,
150        Option<String>,
151    ),
152}
153
154#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
155pub enum DomMutation {
156    AttributeModified {
157        node: String,
158        attribute_name: String,
159        new_value: Option<String>,
160    },
161}
162
163#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
164#[serde(rename_all = "camelCase")]
165pub struct ObjectPreview {
166    pub kind: String,
167    pub size: Option<u32>,
168    pub entries: Option<Vec<(DebuggerValue, DebuggerValue)>>,
169    pub own_properties: Option<Vec<PropertyDescriptor>>,
170    pub own_properties_length: Option<u32>,
171    pub function: Option<FunctionPreview>,
172    pub array_length: Option<u32>,
173    pub items: Option<Vec<DebuggerValue>>,
174}
175
176#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
177#[serde(rename_all = "camelCase")]
178pub struct FunctionPreview {
179    pub name: Option<String>,
180    pub display_name: Option<String>,
181    pub parameter_names: Vec<String>,
182    pub is_async: Option<bool>,
183    pub is_generator: Option<bool>,
184}
185
186struct DebuggerNumberVisitor;
187
188impl Visitor<'_> for DebuggerNumberVisitor {
189    type Value = f64;
190
191    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
192        formatter.write_str("a debugger value number")
193    }
194
195    fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
196        Ok(value)
197    }
198
199    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
200        Ok(value as f64)
201    }
202
203    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
204        Ok(value as f64)
205    }
206
207    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
208    where
209        E: Error,
210    {
211        value.parse().map_err(E::custom)
212    }
213}
214
215fn deserialize_debugger_number<'de, D>(deserializer: D) -> Result<f64, D::Error>
216where
217    D: serde::Deserializer<'de>,
218{
219    // `DebuggerValue` is also sent over Servo IPC, not only through debugger.js.
220    if !deserializer.is_human_readable() {
221        return f64::deserialize(deserializer);
222    }
223
224    deserializer.deserialize_any(DebuggerNumberVisitor)
225}
226
227#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
228#[serde(rename_all_fields = "camelCase")]
229pub enum DebuggerValue {
230    VoidValue,
231    NullValue(bool),
232    BooleanValue(bool),
233    NumberValue(#[serde(deserialize_with = "deserialize_debugger_number")] f64),
234    StringValue(String),
235    ObjectValue {
236        actor: Option<String>,
237        class: String,
238        own_property_length: Option<u32>,
239        preview: Option<Box<ObjectPreview>>,
240        #[serde(default, skip_serializing_if = "Option::is_none")]
241        prototype: Option<Box<DebuggerValue>>,
242    },
243}
244
245/// <https://searchfox.org/mozilla-central/source/devtools/server/actors/object/property-iterator.js#51>
246#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
247#[serde(rename_all = "camelCase")]
248pub struct PropertyDescriptor {
249    pub name: String,
250    pub value: DebuggerValue,
251    pub configurable: bool,
252    pub enumerable: bool,
253    pub writable: bool,
254    pub is_accessor: bool,
255}
256
257#[derive(Debug, Deserialize, Serialize)]
258#[serde(rename_all = "camelCase")]
259pub struct EvaluateJSReply {
260    pub value: DebuggerValue,
261    pub exception_message: Option<String>,
262    pub has_exception: bool,
263}
264
265#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
266pub struct AttrInfo {
267    pub namespace: String,
268    pub name: String,
269    pub value: String,
270}
271
272#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
273#[serde(rename_all = "camelCase")]
274pub struct NodeInfo {
275    pub unique_id: String,
276    pub host: Option<String>,
277    #[serde(rename = "baseURI")]
278    pub base_uri: String,
279    pub parent: String,
280    pub node_type: u16,
281    pub node_name: String,
282    pub node_value: Option<String>,
283    pub num_children: usize,
284    pub attrs: Vec<AttrInfo>,
285    pub is_top_level_document: bool,
286    pub shadow_root_mode: Option<ShadowRootMode>,
287    pub is_shadow_host: bool,
288    pub display: Option<String>,
289    /// Whether this node is currently displayed.
290    ///
291    /// For example, the node might have `display: none`.
292    pub is_displayed: bool,
293
294    /// The `DOCTYPE` name if this is a `DocumentType` node, `None` otherwise
295    pub doctype_name: Option<String>,
296
297    /// The `DOCTYPE` public identifier if this is a `DocumentType` node , `None` otherwise
298    pub doctype_public_identifier: Option<String>,
299
300    /// The `DOCTYPE` system identifier if this is a `DocumentType` node, `None` otherwise
301    pub doctype_system_identifier: Option<String>,
302
303    pub has_event_listeners: bool,
304}
305
306pub struct StartedTimelineMarker {
307    name: String,
308    start_time: CrossProcessInstant,
309    start_stack: Option<Vec<()>>,
310}
311
312#[derive(Debug, Deserialize, Serialize)]
313pub struct TimelineMarker {
314    pub name: String,
315    pub start_time: CrossProcessInstant,
316    pub start_stack: Option<Vec<()>>,
317    pub end_time: CrossProcessInstant,
318    pub end_stack: Option<Vec<()>>,
319}
320
321#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
322pub enum TimelineMarkerType {
323    Reflow,
324    DOMEvent,
325}
326
327#[derive(Debug, Deserialize, Serialize)]
328#[serde(rename_all = "camelCase")]
329pub struct NodeStyle {
330    pub name: String,
331    pub value: String,
332    pub priority: String,
333}
334
335#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
336#[serde(tag = "type", rename_all = "camelCase")]
337pub enum AncestorData {
338    Layer {
339        actor_id: Option<String>,
340        value: Option<String>,
341    },
342}
343
344#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf, PartialEq, Eq, Hash)]
345#[serde(rename_all = "camelCase")]
346pub struct MatchedRule {
347    pub selector: String,
348    pub stylesheet_index: usize,
349    pub block_id: usize,
350    pub ancestor_data: Vec<AncestorData>,
351}
352
353/// The properties of a DOM node as computed by layout.
354#[derive(Debug, Deserialize, Serialize)]
355#[serde(rename_all = "kebab-case")]
356pub struct ComputedNodeLayout {
357    pub display: String,
358    pub position: String,
359    pub z_index: String,
360    pub box_sizing: String,
361
362    pub margin_top: String,
363    pub margin_right: String,
364    pub margin_bottom: String,
365    pub margin_left: String,
366
367    pub border_top_width: String,
368    pub border_right_width: String,
369    pub border_bottom_width: String,
370    pub border_left_width: String,
371
372    pub padding_top: String,
373    pub padding_right: String,
374    pub padding_bottom: String,
375    pub padding_left: String,
376
377    pub width: f32,
378    pub height: f32,
379}
380
381#[derive(Debug, Default, Deserialize, Serialize)]
382pub struct AutoMargins {
383    pub top: bool,
384    pub right: bool,
385    pub bottom: bool,
386    pub left: bool,
387}
388
389#[derive(Debug, Deserialize, Serialize)]
390pub enum GetHTMLType {
391    OuterHTML,
392    InnerHTML,
393}
394
395/// Messages to process in a particular script thread, as instructed by a devtools client.
396/// TODO: better error handling, e.g. if pipeline id lookup fails?
397#[derive(Debug, Deserialize, Serialize)]
398pub enum DevtoolScriptControlMsg {
399    /// Retrieve the details of the root node (ie. the document) for the given pipeline.
400    GetRootNode(PipelineId, GenericSender<Option<NodeInfo>>),
401    /// Retrieve the details of the document element for the given pipeline.
402    GetDocumentElement(PipelineId, GenericSender<Option<NodeInfo>>),
403    /// Retrieve the details of the child nodes of the given node in the given pipeline.
404    GetChildren(PipelineId, String, GenericSender<Option<Vec<NodeInfo>>>),
405    /// Retrieve the CSS style properties defined in the attribute tag for the given node.
406    GetAttributeStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
407    /// Retrieve the CSS style properties defined in an stylesheet for the given selector.
408    GetStylesheetStyle(
409        PipelineId,
410        String,
411        MatchedRule,
412        GenericSender<Option<Vec<NodeStyle>>>,
413    ),
414    /// Retrieve the list of stylesheets for the given pipeline and node.
415    GetStyleSheets(PipelineId, GenericSender<Vec<StyleSheetInfo>>),
416    /// Retrieve the actual CSS text for the stylesheet with the given node ID and index.
417    GetStyleSheetText(PipelineId, i32, GenericSender<Option<String>>),
418    /// Retrieves the CSS selectors for the given node. A selector is comprised of the text
419    /// of the selector and the id of the stylesheet that contains it.
420    GetSelectors(PipelineId, String, GenericSender<Option<Vec<MatchedRule>>>),
421    /// Retrieve the computed CSS style properties for the given node.
422    GetComputedStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
423    /// Get information about event listeners on a node.
424    GetEventListenerInfo(PipelineId, String, GenericSender<Vec<EventListenerInfo>>),
425    /// Retrieve the computed layout properties of the given node in the given pipeline.
426    GetLayout(
427        PipelineId,
428        String,
429        GenericSender<Option<(ComputedNodeLayout, AutoMargins)>>,
430    ),
431    /// Get a unique XPath selector for the node.
432    GetXPath(PipelineId, String, GenericSender<String>),
433    /// Get inner/outer HTML on a node.
434    GetInnerOrOuterHTML(
435        PipelineId,
436        String,
437        GenericSender<Option<String>>,
438        GetHTMLType,
439    ),
440    /// Update a given node's attributes with a list of modifications.
441    ModifyAttribute(PipelineId, String, Vec<AttrModification>),
442    /// Update a given node's style rules with a list of modifications.
443    ModifyRule(PipelineId, String, Vec<RuleModification>),
444    /// Request live console messages for a given pipeline (true if desired, false otherwise).
445    WantsLiveNotifications(PipelineId, bool),
446    /// Request live notifications for a given set of timeline events for a given pipeline.
447    SetTimelineMarkers(
448        PipelineId,
449        Vec<TimelineMarkerType>,
450        GenericSender<Option<TimelineMarker>>,
451    ),
452    /// Withdraw request for live timeline notifications for a given pipeline.
453    DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
454    /// Request a callback directed at the given actor name from the next animation frame
455    /// executed in the given pipeline.
456    RequestAnimationFrame(PipelineId, String),
457    /// Direct the WebView containing the given pipeline to load a new URL,
458    /// as if it was typed by the user.
459    NavigateTo(PipelineId, ServoUrl),
460    /// Direct the WebView containing the given pipeline to traverse history backward
461    /// up to one step.
462    GoBack(PipelineId),
463    /// Direct the WebView containing the given pipeline to traverse history forward
464    /// up to one step.
465    GoForward(PipelineId),
466    /// Direct the given pipeline to reload the current page.
467    Reload(PipelineId),
468    /// Gets the list of all allowed CSS rules and possible values.
469    GetCssDatabase(GenericSender<HashMap<String, CssDatabaseProperty>>),
470    /// Simulates a light or dark color scheme for the given pipeline
471    SimulateColorScheme(PipelineId, Option<Theme>),
472    /// Highlight the given DOM node
473    HighlightDomNode(PipelineId, Option<String>),
474
475    Eval(
476        String,
477        PipelineId,
478        Option<String>,
479        bool,
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    // Not implemented in Servo
531    // source_id
532}
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<bytes::Bytes>,
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    pub headers: Option<HeaderMap>,
587    pub status: HttpStatus,
588    pub body: Option<bytes::Bytes>,
589    pub from_cache: bool,
590    pub pipeline_id: PipelineId,
591    pub browsing_context_id: BrowsingContextId,
592}
593
594#[derive(Debug, PartialEq)]
595pub struct SecurityInfoUpdate {
596    pub browsing_context_id: BrowsingContextId,
597    pub security_info: Option<TlsSecurityInfo>,
598}
599
600#[derive(Debug)]
601pub enum NetworkEvent {
602    HttpRequest(HttpRequest),
603    HttpRequestUpdate(HttpRequest),
604    HttpResponse(HttpResponse),
605    SecurityInfo(SecurityInfoUpdate),
606}
607
608impl NetworkEvent {
609    pub fn forward_to_devtools(&self) -> bool {
610        match self {
611            NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
612            NetworkEvent::HttpRequestUpdate(_) => true,
613            NetworkEvent::HttpResponse(_) => true,
614            NetworkEvent::SecurityInfo(_) => true,
615        }
616    }
617}
618
619impl TimelineMarker {
620    pub fn start(name: String) -> StartedTimelineMarker {
621        StartedTimelineMarker {
622            name,
623            start_time: CrossProcessInstant::now(),
624            start_stack: None,
625        }
626    }
627}
628
629impl StartedTimelineMarker {
630    pub fn end(self) -> TimelineMarker {
631        TimelineMarker {
632            name: self.name,
633            start_time: self.start_time,
634            start_stack: self.start_stack,
635            end_time: CrossProcessInstant::now(),
636            end_stack: None,
637        }
638    }
639}
640#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
641#[serde(rename_all = "camelCase")]
642pub struct CssDatabaseProperty {
643    pub is_inherited: bool,
644    pub values: Vec<String>,
645    pub supports: Vec<String>,
646    pub subproperties: Vec<String>,
647}
648
649#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
650pub enum ShadowRootMode {
651    Open,
652    Closed,
653}
654
655impl fmt::Display for ShadowRootMode {
656    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
657        match self {
658            Self::Open => write!(f, "open"),
659            Self::Closed => write!(f, "close"),
660        }
661    }
662}
663
664#[derive(Debug, Deserialize, Serialize)]
665pub struct SourceInfo {
666    pub url: ServoUrl,
667    pub introduction_type: String,
668    pub inline: bool,
669    pub worker_id: Option<WorkerId>,
670    pub content: Option<String>,
671    pub content_type: Option<String>,
672    pub spidermonkey_id: u32,
673}
674
675#[derive(Clone, Debug, Deserialize, Serialize)]
676#[serde(rename_all = "camelCase")]
677pub struct RecommendedBreakpointLocation {
678    pub script_id: u32,
679    pub offset: u32,
680    pub line_number: u32,
681    pub column_number: u32,
682    pub is_step_start: bool,
683}
684
685#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
686pub struct FrameInfo {
687    pub display_name: Option<String>,
688    pub on_stack: bool,
689    pub oldest: bool,
690    pub this_value: DebuggerValue,
691    pub terminated: bool,
692    pub type_: String,
693    pub url: String,
694}
695
696#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
697pub struct EnvironmentInfo {
698    pub type_: Option<String>,
699    pub scope_kind: Option<String>,
700    pub function_display_name: Option<String>,
701    pub object: Option<DebuggerValue>,
702    pub binding_variables: Vec<PropertyDescriptor>,
703}
704
705#[derive(Clone, Debug, Deserialize, Serialize)]
706pub struct StyleSheetInfo {
707    pub href: Option<String>,
708    pub disabled: bool,
709    pub title: String,
710    pub style_sheet_index: i32,
711    pub system: bool,
712    pub rule_count: u32,
713}
714
715#[derive(Clone, Debug, Deserialize, Serialize)]
716pub struct EventListenerInfo {
717    pub event_type: String,
718    pub capturing: bool,
719}
720
721#[derive(Debug, Deserialize, Serialize)]
722#[serde(rename_all = "camelCase")]
723pub struct PauseReason {
724    #[serde(rename = "type")]
725    pub type_: String,
726    pub on_next: Option<bool>,
727}
728
729#[derive(Debug, Deserialize, Serialize)]
730pub struct FrameOffset {
731    pub actor: String,
732    pub column: u32,
733    pub line: u32,
734}