devtools/actors/inspector/
highlighter.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//! Handles highlighting selected DOM nodes in the inspector. At the moment it only replies and
6//! changes nothing on Servo's side.
7
8use devtools_traits::DevtoolScriptControlMsg;
9use malloc_size_of_derive::MallocSizeOf;
10use serde::Serialize;
11use serde_json::{self, Map, Value};
12
13use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry};
14use crate::actors::browsing_context::BrowsingContextActor;
15use crate::actors::inspector::InspectorActor;
16use crate::protocol::ClientRequest;
17use crate::{ActorMsg, EmptyReplyMsg, StreamId};
18
19#[derive(MallocSizeOf)]
20pub(crate) struct HighlighterActor {
21    pub name: String,
22    pub browsing_context_name: String,
23}
24
25#[derive(Serialize)]
26struct ShowReply {
27    from: String,
28    value: bool,
29}
30
31impl Actor for HighlighterActor {
32    fn name(&self) -> String {
33        self.name.clone()
34    }
35
36    /// The highligher actor can handle the following messages:
37    ///
38    /// - `show`: Enables highlighting for the selected node
39    ///
40    /// - `hide`: Disables highlighting for the selected node
41    ///
42    /// - `finalize`: Performs cleanup for this actor; currently a no-op
43    fn handle_message(
44        &self,
45        request: ClientRequest,
46        registry: &ActorRegistry,
47        msg_type: &str,
48        msg: &Map<String, Value>,
49        _id: StreamId,
50    ) -> Result<(), ActorError> {
51        match msg_type {
52            "show" => {
53                let Some(node_actor) = msg.get("node") else {
54                    return Err(ActorError::MissingParameter);
55                };
56
57                let Some(node_actor_name) = node_actor.as_str() else {
58                    return Err(ActorError::BadParameterType);
59                };
60
61                if node_actor_name.starts_with(ActorRegistry::base_name::<InspectorActor>()) {
62                    // TODO: For some reason, the client initially asks us to highlight
63                    // the inspector? Investigate what this is supposed to mean.
64                    let msg = ShowReply {
65                        from: self.name(),
66                        value: false,
67                    };
68                    return request.reply_final(&msg);
69                }
70
71                self.instruct_script_thread_to_highlight_node(
72                    Some(node_actor_name.to_owned()),
73                    registry,
74                );
75                let msg = ShowReply {
76                    from: self.name(),
77                    value: true,
78                };
79                request.reply_final(&msg)?
80            },
81
82            "hide" => {
83                self.instruct_script_thread_to_highlight_node(None, registry);
84
85                let msg = EmptyReplyMsg { from: self.name() };
86                request.reply_final(&msg)?
87            },
88
89            "finalize" => {
90                request.mark_handled();
91            },
92
93            _ => return Err(ActorError::UnrecognizedPacketType),
94        };
95        Ok(())
96    }
97}
98
99impl HighlighterActor {
100    fn instruct_script_thread_to_highlight_node(
101        &self,
102        node_name: Option<String>,
103        registry: &ActorRegistry,
104    ) {
105        let node_id = node_name.map(|node_name| registry.actor_to_script(node_name));
106        let browsing_context_actor =
107            registry.find::<BrowsingContextActor>(&self.browsing_context_name);
108        browsing_context_actor
109            .script_chan()
110            .send(DevtoolScriptControlMsg::HighlightDomNode(
111                browsing_context_actor.pipeline_id(),
112                node_id,
113            ))
114            .unwrap();
115    }
116}
117
118impl ActorEncode<ActorMsg> for HighlighterActor {
119    fn encode(&self, _: &ActorRegistry) -> ActorMsg {
120        ActorMsg { actor: self.name() }
121    }
122}