Skip to main content

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