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::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// Information would be attached to NewGlobal to be received and show in devtools.
43// Extend these fields if we need more information.
44#[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/// Messages to instruct the devtools server to update its known actors/state
61/// according to changes in the browser.
62#[derive(Debug)]
63pub enum DevtoolsControlMsg {
64    /// Messages from threads in the chrome process (resource/constellation/devtools)
65    FromChrome(ChromeToDevtoolsControlMsg),
66    /// Messages from script threads
67    FromScript(ScriptToDevtoolsControlMsg),
68    /// Sent when a devtools client thread terminates.
69    ClientExited,
70}
71
72/// Events that the devtools server must act upon.
73// FIXME: https://github.com/servo/servo/issues/34591
74#[expect(clippy::large_enum_variant)]
75#[derive(Debug)]
76pub enum ChromeToDevtoolsControlMsg {
77    /// A new client has connected to the server.
78    AddClient(TcpStream),
79    /// The browser is shutting down.
80    ServerExitMsg,
81    /// A network event occurred (request, reply, etc.). The actor with the
82    /// provided name should be notified.
83    NetworkEvent(String, NetworkEvent),
84    /// Perform a memory report.
85    CollectMemoryReport(ReportsChan),
86}
87
88/// The state of a page navigation.
89#[derive(Debug, Deserialize, Serialize)]
90pub enum NavigationState {
91    /// A browsing context is about to navigate to a given URL.
92    Start(ServoUrl),
93    /// A browsing context has completed navigating to the provided pipeline.
94    Stop(PipelineId, DevtoolsPageInfo),
95}
96
97#[derive(Debug, Deserialize, Serialize)]
98/// Events that the devtools server must act upon.
99pub enum ScriptToDevtoolsControlMsg {
100    /// A new global object was created, associated with a particular pipeline.
101    /// The means of communicating directly with it are provided.
102    NewGlobal(
103        (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
104        GenericSender<DevtoolScriptControlMsg>,
105        DevtoolsPageInfo,
106    ),
107    /// The given browsing context is performing a navigation.
108    Navigate(BrowsingContextId, NavigationState),
109    /// A particular page has invoked the console API.
110    ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
111    /// Request to clear the console for a given pipeline.
112    ClearConsole(PipelineId, Option<WorkerId>),
113    /// An animation frame with the given timestamp was processed in a script thread.
114    /// The actor with the provided name should be notified.
115    FramerateTick(String, f64),
116
117    /// Report a CSS parse error for the given pipeline
118    ReportCSSError(PipelineId, CSSError),
119
120    /// Report a page error for the given pipeline
121    ReportPageError(PipelineId, PageError),
122
123    /// Report a page title change
124    TitleChanged(PipelineId, String),
125
126    /// Get source information from script
127    CreateSourceActor(
128        GenericSender<DevtoolScriptControlMsg>,
129        PipelineId,
130        SourceInfo,
131    ),
132
133    UpdateSourceContent(PipelineId, String),
134
135    DomMutation(PipelineId, DomMutation),
136
137    /// The debugger is paused, sending frame information.
138    DebuggerPause(PipelineId, FrameOffset, PauseReason),
139
140    /// Get frame information from script
141    CreateFrameActor(GenericSender<String>, PipelineId, FrameInfo),
142
143    /// Get environment information from script
144    CreateEnvironmentActor(
145        GenericSender<String>,
146        EnvironmentInfo,
147        Option<String>,
148        Option<String>,
149    ),
150}
151
152#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
153pub enum DomMutation {
154    AttributeModified {
155        node: String,
156        attribute_name: String,
157        new_value: Option<String>,
158    },
159}
160
161#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
162#[serde(rename_all = "camelCase")]
163pub struct ObjectPreview {
164    pub kind: String,
165    pub size: Option<u32>,
166    pub entries: Option<Vec<(DebuggerValue, DebuggerValue)>>,
167    pub own_properties: Option<Vec<PropertyDescriptor>>,
168    pub own_properties_length: Option<u32>,
169    pub function: Option<FunctionPreview>,
170    pub array_length: Option<u32>,
171    pub items: Option<Vec<DebuggerValue>>,
172}
173
174#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
175#[serde(rename_all = "camelCase")]
176pub struct FunctionPreview {
177    pub name: Option<String>,
178    pub display_name: Option<String>,
179    pub parameter_names: Vec<String>,
180    pub is_async: Option<bool>,
181    pub is_generator: Option<bool>,
182}
183
184fn debugger_value_uuid() -> String {
185    Uuid::new_v4().to_string()
186}
187
188struct DebuggerNumberVisitor;
189
190impl Visitor<'_> for DebuggerNumberVisitor {
191    type Value = f64;
192
193    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
194        formatter.write_str("a debugger value number")
195    }
196
197    fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
198        Ok(value)
199    }
200
201    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
202        Ok(value as f64)
203    }
204
205    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
206        Ok(value as f64)
207    }
208
209    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
210    where
211        E: Error,
212    {
213        value.parse().map_err(E::custom)
214    }
215}
216
217fn deserialize_debugger_number<'de, D>(deserializer: D) -> Result<f64, D::Error>
218where
219    D: serde::Deserializer<'de>,
220{
221    // `DebuggerValue` is also sent over Servo IPC, not only through debugger.js.
222    if !deserializer.is_human_readable() {
223        return f64::deserialize(deserializer);
224    }
225
226    deserializer.deserialize_any(DebuggerNumberVisitor)
227}
228
229#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
230#[serde(rename_all_fields = "camelCase")]
231pub enum DebuggerValue {
232    VoidValue,
233    NullValue(bool),
234    BooleanValue(bool),
235    NumberValue(#[serde(deserialize_with = "deserialize_debugger_number")] f64),
236    StringValue(String),
237    ObjectValue {
238        #[serde(default = "debugger_value_uuid")]
239        uuid: String,
240        class: String,
241        own_property_length: Option<u32>,
242        preview: Option<Box<ObjectPreview>>,
243    },
244}
245
246/// <https://searchfox.org/mozilla-central/source/devtools/server/actors/object/property-iterator.js#51>
247#[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    /// Whether this node is currently displayed.
291    ///
292    /// For example, the node might have `display: none`.
293    pub is_displayed: bool,
294
295    /// The `DOCTYPE` name if this is a `DocumentType` node, `None` otherwise
296    pub doctype_name: Option<String>,
297
298    /// The `DOCTYPE` public identifier if this is a `DocumentType` node , `None` otherwise
299    pub doctype_public_identifier: Option<String>,
300
301    /// The `DOCTYPE` system identifier if this is a `DocumentType` node, `None` otherwise
302    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/// The properties of a DOM node as computed by layout.
355#[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/// Messages to process in a particular script thread, as instructed by a devtools client.
397/// TODO: better error handling, e.g. if pipeline id lookup fails?
398#[derive(Debug, Deserialize, Serialize)]
399pub enum DevtoolScriptControlMsg {
400    /// Retrieve the details of the root node (ie. the document) for the given pipeline.
401    GetRootNode(PipelineId, GenericSender<Option<NodeInfo>>),
402    /// Retrieve the details of the document element for the given pipeline.
403    GetDocumentElement(PipelineId, GenericSender<Option<NodeInfo>>),
404    /// Retrieve the details of the child nodes of the given node in the given pipeline.
405    GetChildren(PipelineId, String, GenericSender<Option<Vec<NodeInfo>>>),
406    /// Retrieve the CSS style properties defined in the attribute tag for the given node.
407    GetAttributeStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
408    /// Retrieve the CSS style properties defined in an stylesheet for the given selector.
409    GetStylesheetStyle(
410        PipelineId,
411        String,
412        MatchedRule,
413        GenericSender<Option<Vec<NodeStyle>>>,
414    ),
415    /// Retrieve the list of stylesheets for the given pipeline and node.
416    GetStyleSheets(PipelineId, GenericSender<Vec<StyleSheetInfo>>),
417    /// Retrieve the actual CSS text for the stylesheet with the given node ID and index.
418    GetStyleSheetText(PipelineId, i32, GenericSender<Option<String>>),
419    /// Retrieves the CSS selectors for the given node. A selector is comprised of the text
420    /// of the selector and the id of the stylesheet that contains it.
421    GetSelectors(PipelineId, String, GenericSender<Option<Vec<MatchedRule>>>),
422    /// Retrieve the computed CSS style properties for the given node.
423    GetComputedStyle(PipelineId, String, GenericSender<Option<Vec<NodeStyle>>>),
424    /// Get information about event listeners on a node.
425    GetEventListenerInfo(PipelineId, String, GenericSender<Vec<EventListenerInfo>>),
426    /// Retrieve the computed layout properties of the given node in the given pipeline.
427    GetLayout(
428        PipelineId,
429        String,
430        GenericSender<Option<(ComputedNodeLayout, AutoMargins)>>,
431    ),
432    /// Get a unique XPath selector for the node.
433    GetXPath(PipelineId, String, GenericSender<String>),
434    /// Get inner/outer HTML on a node.
435    GetInnerOrOuterHTML(
436        PipelineId,
437        String,
438        GenericSender<Option<String>>,
439        GetHTMLType,
440    ),
441    /// Update a given node's attributes with a list of modifications.
442    ModifyAttribute(PipelineId, String, Vec<AttrModification>),
443    /// Update a given node's style rules with a list of modifications.
444    ModifyRule(PipelineId, String, Vec<RuleModification>),
445    /// Request live console messages for a given pipeline (true if desired, false otherwise).
446    WantsLiveNotifications(PipelineId, bool),
447    /// Request live notifications for a given set of timeline events for a given pipeline.
448    SetTimelineMarkers(
449        PipelineId,
450        Vec<TimelineMarkerType>,
451        GenericSender<Option<TimelineMarker>>,
452    ),
453    /// Withdraw request for live timeline notifications for a given pipeline.
454    DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
455    /// Request a callback directed at the given actor name from the next animation frame
456    /// executed in the given pipeline.
457    RequestAnimationFrame(PipelineId, String),
458    /// Direct the WebView containing the given pipeline to load a new URL,
459    /// as if it was typed by the user.
460    NavigateTo(PipelineId, ServoUrl),
461    /// Direct the WebView containing the given pipeline to traverse history backward
462    /// up to one step.
463    GoBack(PipelineId),
464    /// Direct the WebView containing the given pipeline to traverse history forward
465    /// up to one step.
466    GoForward(PipelineId),
467    /// Direct the given pipeline to reload the current page.
468    Reload(PipelineId),
469    /// Gets the list of all allowed CSS rules and possible values.
470    GetCssDatabase(GenericSender<HashMap<String, CssDatabaseProperty>>),
471    /// Simulates a light or dark color scheme for the given pipeline
472    SimulateColorScheme(PipelineId, Theme),
473    /// Highlight the given DOM node
474    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(String, GenericSender<String>),
489    Blackbox(u32, BlackboxCoverage),
490    Unblackbox(u32, BlackboxCoverage),
491}
492
493#[derive(Debug, Deserialize, Serialize)]
494pub enum BlackboxCoverage {
495    Full,
496    Partial((u32, u32), (u32, u32)),
497}
498
499#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
500#[serde(rename_all = "camelCase")]
501pub struct AttrModification {
502    pub attribute_name: String,
503    pub new_value: Option<String>,
504}
505
506#[derive(Clone, Debug, Deserialize, Serialize)]
507#[serde(rename_all = "camelCase")]
508pub struct RuleModification {
509    #[serde(rename = "type")]
510    pub type_: String,
511    pub index: u32,
512    pub name: String,
513    pub value: String,
514    pub priority: String,
515}
516
517#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
518#[serde(rename_all = "camelCase")]
519pub struct StackFrame {
520    pub filename: String,
521    pub function_name: String,
522    pub column_number: u32,
523    pub line_number: u32,
524    // Not implemented in Servo
525    // source_id
526}
527
528pub fn get_time_stamp() -> u64 {
529    SystemTime::now()
530        .duration_since(UNIX_EPOCH)
531        .unwrap_or_default()
532        .as_millis() as u64
533}
534
535#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
536#[serde(rename_all = "camelCase")]
537pub struct ConsoleMessageFields {
538    pub level: ConsoleLogLevel,
539    pub filename: String,
540    pub line_number: u32,
541    pub column_number: u32,
542    pub time_stamp: u64,
543}
544
545#[derive(Clone, Debug, Deserialize, Serialize)]
546pub struct ConsoleMessage {
547    pub fields: ConsoleMessageFields,
548    pub arguments: Vec<DebuggerValue>,
549    pub stacktrace: Option<Vec<StackFrame>>,
550}
551
552#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
553#[serde(rename_all = "camelCase")]
554pub struct PageError {
555    pub error_message: String,
556    pub source_name: String,
557    pub line_number: u32,
558    pub column_number: u32,
559    pub time_stamp: u64,
560}
561
562#[derive(Debug, PartialEq, MallocSizeOf)]
563pub struct HttpRequest {
564    pub url: ServoUrl,
565    pub method: Method,
566    pub headers: HeaderMap,
567    pub body: Option<DebugVec>,
568    pub pipeline_id: PipelineId,
569    pub started_date_time: SystemTime,
570    pub time_stamp: i64,
571    pub connect_time: Duration,
572    pub send_time: Duration,
573    pub destination: Destination,
574    pub is_xhr: bool,
575    pub browsing_context_id: BrowsingContextId,
576}
577
578#[derive(Debug, PartialEq, MallocSizeOf)]
579pub struct HttpResponse {
580    #[ignore_malloc_size_of = "Http type"]
581    pub headers: Option<HeaderMap>,
582    pub status: HttpStatus,
583    pub body: Option<DebugVec>,
584    pub from_cache: bool,
585    pub pipeline_id: PipelineId,
586    pub browsing_context_id: BrowsingContextId,
587}
588
589#[derive(Debug, PartialEq)]
590pub struct SecurityInfoUpdate {
591    pub browsing_context_id: BrowsingContextId,
592    pub security_info: Option<TlsSecurityInfo>,
593}
594
595#[derive(Debug)]
596pub enum NetworkEvent {
597    HttpRequest(HttpRequest),
598    HttpRequestUpdate(HttpRequest),
599    HttpResponse(HttpResponse),
600    SecurityInfo(SecurityInfoUpdate),
601}
602
603impl NetworkEvent {
604    pub fn forward_to_devtools(&self) -> bool {
605        match self {
606            NetworkEvent::HttpRequest(http_request) => http_request.url.scheme() != "data",
607            NetworkEvent::HttpRequestUpdate(_) => true,
608            NetworkEvent::HttpResponse(_) => true,
609            NetworkEvent::SecurityInfo(_) => true,
610        }
611    }
612}
613
614impl TimelineMarker {
615    pub fn start(name: String) -> StartedTimelineMarker {
616        StartedTimelineMarker {
617            name,
618            start_time: CrossProcessInstant::now(),
619            start_stack: None,
620        }
621    }
622}
623
624impl StartedTimelineMarker {
625    pub fn end(self) -> TimelineMarker {
626        TimelineMarker {
627            name: self.name,
628            start_time: self.start_time,
629            start_stack: self.start_stack,
630            end_time: CrossProcessInstant::now(),
631            end_stack: None,
632        }
633    }
634}
635#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
636pub struct WorkerId(pub Uuid);
637impl Display for WorkerId {
638    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
639        write!(f, "{}", self.0)
640    }
641}
642impl FromStr for WorkerId {
643    type Err = uuid::Error;
644
645    fn from_str(s: &str) -> Result<Self, Self::Err> {
646        Ok(Self(s.parse()?))
647    }
648}
649
650#[derive(Debug, Deserialize, Serialize, MallocSizeOf)]
651#[serde(rename_all = "camelCase")]
652pub struct CssDatabaseProperty {
653    pub is_inherited: bool,
654    pub values: Vec<String>,
655    pub supports: Vec<String>,
656    pub subproperties: Vec<String>,
657}
658
659#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
660pub enum ShadowRootMode {
661    Open,
662    Closed,
663}
664
665impl fmt::Display for ShadowRootMode {
666    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667        match self {
668            Self::Open => write!(f, "open"),
669            Self::Closed => write!(f, "close"),
670        }
671    }
672}
673
674#[derive(Debug, Deserialize, Serialize)]
675pub struct SourceInfo {
676    pub url: ServoUrl,
677    pub introduction_type: String,
678    pub inline: bool,
679    pub worker_id: Option<WorkerId>,
680    pub content: Option<String>,
681    pub content_type: Option<String>,
682    pub spidermonkey_id: u32,
683}
684
685#[derive(Clone, Debug, Deserialize, Serialize)]
686#[serde(rename_all = "camelCase")]
687pub struct RecommendedBreakpointLocation {
688    pub script_id: u32,
689    pub offset: u32,
690    pub line_number: u32,
691    pub column_number: u32,
692    pub is_step_start: bool,
693}
694
695#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
696pub struct FrameInfo {
697    pub display_name: Option<String>,
698    pub on_stack: bool,
699    pub oldest: bool,
700    pub this_value: DebuggerValue,
701    pub terminated: bool,
702    pub type_: String,
703    pub url: String,
704}
705
706#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
707pub struct EnvironmentInfo {
708    pub type_: Option<String>,
709    pub scope_kind: Option<String>,
710    pub function_display_name: Option<String>,
711    pub object: Option<DebuggerValue>,
712    pub binding_variables: Vec<PropertyDescriptor>,
713}
714
715#[derive(Clone, Debug, Deserialize, Serialize)]
716pub struct StyleSheetInfo {
717    pub href: Option<String>,
718    pub disabled: bool,
719    pub title: String,
720    pub style_sheet_index: i32,
721    pub system: bool,
722    pub rule_count: u32,
723}
724
725#[derive(Clone, Debug, Deserialize, Serialize)]
726pub struct EventListenerInfo {
727    pub event_type: String,
728    pub capturing: bool,
729}
730
731#[derive(Debug, Deserialize, Serialize)]
732#[serde(rename_all = "camelCase")]
733pub struct PauseReason {
734    #[serde(rename = "type")]
735    pub type_: String,
736    pub on_next: Option<bool>,
737}
738
739#[derive(Debug, Deserialize, Serialize)]
740pub struct FrameOffset {
741    pub actor: String,
742    pub column: u32,
743    pub line: u32,
744}