devtools/actors/
frame.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
5use atomic_refcell::AtomicRefCell;
6use devtools_traits::{DevtoolScriptControlMsg, FrameInfo};
7use malloc_size_of_derive::MallocSizeOf;
8use serde::Serialize;
9use serde_json::{Map, Value};
10use servo_base::generic_channel::channel;
11
12use crate::StreamId;
13use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry};
14use crate::actors::environment::{EnvironmentActor, EnvironmentActorMsg};
15use crate::actors::object::ObjectActor;
16use crate::actors::source::SourceActor;
17use crate::protocol::{ClientRequest, JsonPacketStream};
18
19#[derive(Serialize)]
20struct FrameEnvironmentReply {
21    from: String,
22    #[serde(flatten)]
23    environment: EnvironmentActorMsg,
24}
25
26#[derive(Serialize)]
27#[serde(rename_all = "kebab-case")]
28pub enum FrameState {
29    OnStack,
30    _Suspended,
31    _Dead,
32}
33
34#[derive(Serialize)]
35pub(crate) struct FrameWhere {
36    actor: String,
37    line: u32,
38    column: u32,
39}
40
41#[derive(Serialize)]
42#[serde(rename_all = "camelCase")]
43pub(crate) struct FrameActorMsg {
44    actor: String,
45    #[serde(rename = "type")]
46    type_: String,
47    arguments: Vec<Value>,
48    async_cause: Option<String>,
49    display_name: String,
50    oldest: bool,
51    state: FrameState,
52    this: Value,
53    #[serde(rename = "where")]
54    where_: FrameWhere,
55}
56
57/// Represents an stack frame. Used by `ThreadActor` when replying to interrupt messages.
58/// <https://searchfox.org/firefox-main/source/devtools/server/actors/frame.js>
59#[derive(MallocSizeOf)]
60pub(crate) struct FrameActor {
61    name: String,
62    object_actor: String,
63    source_actor: String,
64    frame_result: FrameInfo,
65    current_offset: AtomicRefCell<(u32, u32)>,
66}
67
68impl Actor for FrameActor {
69    fn name(&self) -> String {
70        self.name.clone()
71    }
72
73    // https://searchfox.org/firefox-main/source/devtools/shared/specs/frame.js
74    fn handle_message(
75        &self,
76        mut request: ClientRequest,
77        registry: &ActorRegistry,
78        msg_type: &str,
79        _msg: &Map<String, Value>,
80        _id: StreamId,
81    ) -> Result<(), ActorError> {
82        match msg_type {
83            "getEnvironment" => {
84                let Some((tx, rx)) = channel() else {
85                    return Err(ActorError::Internal);
86                };
87                let source = registry.find::<SourceActor>(&self.source_actor);
88                source
89                    .script_sender
90                    .send(DevtoolScriptControlMsg::GetEnvironment(self.name(), tx))
91                    .map_err(|_| ActorError::Internal)?;
92                let environment_name = rx.recv().map_err(|_| ActorError::Internal)?;
93
94                let msg = FrameEnvironmentReply {
95                    from: self.name(),
96                    environment: registry.encode::<EnvironmentActor, _>(&environment_name),
97                };
98                // This reply has a `type` field but it doesn't need a followup,
99                // unlike most messages. We need to skip the validity check.
100                request.write_json_packet(&msg)?;
101                request.mark_handled();
102            },
103            _ => return Err(ActorError::UnrecognizedPacketType),
104        };
105        Ok(())
106    }
107}
108
109impl FrameActor {
110    pub fn register(
111        registry: &ActorRegistry,
112        source_actor: String,
113        frame_result: FrameInfo,
114    ) -> String {
115        let object_name = ObjectActor::register(registry, None, "Object".to_owned(), None);
116
117        let name = registry.new_name::<Self>();
118        let actor = Self {
119            name: name.clone(),
120            object_actor: object_name,
121            source_actor,
122            frame_result,
123            current_offset: Default::default(),
124        };
125        registry.register::<Self>(actor);
126        name
127    }
128
129    pub(crate) fn set_offset(&self, column: u32, line: u32) {
130        *self.current_offset.borrow_mut() = (column, line);
131    }
132}
133
134impl ActorEncode<FrameActorMsg> for FrameActor {
135    fn encode(&self, registry: &ActorRegistry) -> FrameActorMsg {
136        // TODO: Handle other states
137        let state = FrameState::OnStack;
138        let async_cause = if let FrameState::OnStack = state {
139            None
140        } else {
141            Some("await".into())
142        };
143        let (column, line) = *self.current_offset.borrow();
144        // <https://searchfox.org/firefox-main/source/devtools/docs/user/debugger-api/debugger.frame/index.rst>
145        FrameActorMsg {
146            actor: self.name(),
147            type_: self.frame_result.type_.clone(),
148            arguments: vec![],
149            async_cause,
150            // TODO: Should be optional
151            display_name: self.frame_result.display_name.clone(),
152            this: registry.encode::<ObjectActor, _>(&self.object_actor),
153            oldest: self.frame_result.oldest,
154            state,
155            where_: FrameWhere {
156                actor: self.source_actor.clone(),
157                line,
158                column,
159            },
160        }
161    }
162}