Skip to main content

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