devtools/actors/
breakpoint.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 devtools_traits::DevtoolScriptControlMsg;
6use malloc_size_of_derive::MallocSizeOf;
7use serde::Deserialize;
8use serde_json::Map;
9
10use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry};
11use crate::actors::browsing_context::BrowsingContextActor;
12use crate::actors::thread::ThreadActor;
13use crate::protocol::ClientRequest;
14use crate::{ActorMsg, EmptyReplyMsg};
15
16#[derive(Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct BreakpointRequestLocation {
19    pub line: u32,
20    pub column: u32,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub source_url: Option<String>,
23}
24
25#[derive(Deserialize)]
26struct BreakpointRequest {
27    location: BreakpointRequestLocation,
28}
29
30#[derive(MallocSizeOf)]
31pub(crate) struct BreakpointListActor {
32    name: String,
33    browsing_context_name: String,
34}
35
36impl Actor for BreakpointListActor {
37    fn name(&self) -> String {
38        self.name.clone()
39    }
40
41    fn handle_message(
42        &self,
43        request: ClientRequest,
44        registry: &crate::actor::ActorRegistry,
45        msg_type: &str,
46        msg: &Map<String, serde_json::Value>,
47        _stream_id: crate::StreamId,
48    ) -> Result<(), ActorError> {
49        match msg_type {
50            // Client wants to set a breakpoint.
51            // Seems to be infallible, unlike the thread actor’s `setBreakpoint`.
52            // <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#breakpoints>
53            "setBreakpoint" => {
54                let msg: BreakpointRequest =
55                    serde_json::from_value(msg.clone().into()).map_err(|_| ActorError::Internal)?;
56                let BreakpointRequestLocation {
57                    line,
58                    column,
59                    source_url,
60                } = msg.location;
61                let source_url = source_url.ok_or(ActorError::Internal)?;
62
63                let browsing_context_actor =
64                    registry.find::<BrowsingContextActor>(&self.browsing_context_name);
65                let thread_actor =
66                    registry.find::<ThreadActor>(&browsing_context_actor.thread_name);
67                let source = thread_actor
68                    .source_manager
69                    .find_source(registry, &source_url)
70                    .ok_or(ActorError::Internal)?;
71
72                if let Some((script_id, offset)) = source.find_offset(line, column) {
73                    source
74                        .script_sender
75                        .send(DevtoolScriptControlMsg::SetBreakpoint(
76                            source.spidermonkey_id,
77                            script_id,
78                            offset,
79                        ))
80                        .map_err(|_| ActorError::Internal)?;
81                }
82
83                let msg = EmptyReplyMsg { from: self.name() };
84                request.reply_final(&msg)?
85            },
86            "setActiveEventBreakpoints" => {
87                let msg = EmptyReplyMsg { from: self.name() };
88                request.reply_final(&msg)?
89            },
90            "removeBreakpoint" => {
91                let msg: BreakpointRequest =
92                    serde_json::from_value(msg.clone().into()).map_err(|_| ActorError::Internal)?;
93                let BreakpointRequestLocation {
94                    line,
95                    column,
96                    source_url,
97                } = msg.location;
98                let source_url = source_url.ok_or(ActorError::Internal)?;
99
100                let browsing_context_actor =
101                    registry.find::<BrowsingContextActor>(&self.browsing_context_name);
102                let thread_actor =
103                    registry.find::<ThreadActor>(&browsing_context_actor.thread_name);
104                let source = thread_actor
105                    .source_manager
106                    .find_source(registry, &source_url)
107                    .ok_or(ActorError::Internal)?;
108                if let Some((script_id, offset)) = source.find_offset(line, column) {
109                    source
110                        .script_sender
111                        .send(DevtoolScriptControlMsg::ClearBreakpoint(
112                            source.spidermonkey_id,
113                            script_id,
114                            offset,
115                        ))
116                        .map_err(|_| ActorError::Internal)?;
117                }
118
119                let msg = EmptyReplyMsg { from: self.name() };
120                request.reply_final(&msg)?
121            },
122            _ => return Err(ActorError::UnrecognizedPacketType),
123        };
124        Ok(())
125    }
126}
127
128impl BreakpointListActor {
129    pub fn register(registry: &ActorRegistry, browsing_context_name: String) -> String {
130        let name = registry.new_name::<Self>();
131        let actor = Self {
132            name: name.clone(),
133            browsing_context_name,
134        };
135        registry.register::<Self>(actor);
136        name
137    }
138}
139
140impl ActorEncode<ActorMsg> for BreakpointListActor {
141    fn encode(&self, _: &ActorRegistry) -> ActorMsg {
142        ActorMsg { actor: self.name() }
143    }
144}