1use std::collections::HashMap;
9
10use atomic_refcell::AtomicRefCell;
11use devtools_traits::{
12 AttrModification, DevtoolScriptControlMsg, EventListenerInfo, MatchedRule, NodeInfo,
13 ShadowRootMode,
14};
15use malloc_size_of_derive::MallocSizeOf;
16use serde::Serialize;
17use serde_json::{self, Map, Value};
18use servo_base::generic_channel::{self, GenericSender};
19use servo_base::id::PipelineId;
20
21use crate::actor::{Actor, ActorError, ActorRegistry};
22use crate::protocol::ClientRequest;
23use crate::{EmptyReplyMsg, StreamId};
24
25const TEXT_NODE: u16 = 3;
28
29const MAX_INLINE_LENGTH: usize = 50;
31
32#[derive(Serialize)]
33struct GetEventListenerInfoReply {
34 from: String,
35 events: Vec<DevtoolsEventListenerInfo>,
36}
37
38#[derive(Serialize)]
39#[serde(rename_all = "camelCase")]
40struct DevtoolsEventListenerInfo {
41 r#type: String,
42 handler: String,
43 origin: String,
44 tags: String,
45 capturing: bool,
46 hide: Value,
48 native: bool,
49 source_actor: String,
50 enabled: bool,
51 is_user_defined: bool,
52 event_listener_info_id: String,
53}
54
55#[derive(Serialize)]
56struct GetUniqueSelectorReply {
57 from: String,
58 value: String,
59}
60
61#[derive(Serialize)]
62struct GetXPathReply {
63 from: String,
64 value: String,
65}
66
67#[derive(Clone, Serialize)]
68struct AttrMsg {
69 name: String,
70 value: String,
71}
72
73#[derive(Clone, Serialize)]
74#[serde(rename_all = "camelCase")]
75pub(crate) struct NodeActorMsg {
76 pub actor: String,
77
78 host: Option<String>,
81 #[serde(rename = "baseURI")]
82 base_uri: String,
83 causes_overflow: bool,
84 container_type: Option<()>,
85 pub display_name: String,
86 display_type: Option<String>,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 inline_text_child: Option<Box<NodeActorMsg>>,
89 is_after_pseudo_element: bool,
90 is_anonymous: bool,
91 is_before_pseudo_element: bool,
92 is_direct_shadow_host_child: Option<bool>,
93 is_displayed: bool,
97 #[serde(rename = "isInHTMLDocument")]
98 is_in_html_document: Option<bool>,
99 is_marker_pseudo_element: bool,
100 is_native_anonymous: bool,
101 is_scrollable: bool,
102 is_shadow_host: bool,
103 is_shadow_root: bool,
104 is_top_level_document: bool,
105 node_name: String,
106 node_type: u16,
107 node_value: Option<String>,
108 pub num_children: usize,
109 #[serde(skip_serializing_if = "String::is_empty")]
110 parent: String,
111 shadow_root_mode: Option<String>,
112 traits: HashMap<String, ()>,
113 attrs: Vec<AttrMsg>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 name: Option<String>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 public_id: Option<String>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 system_id: Option<String>,
126
127 has_event_listeners: bool,
128}
129
130#[derive(MallocSizeOf)]
131pub(crate) struct NodeActor {
132 name: String,
133 pub script_chan: GenericSender<DevtoolScriptControlMsg>,
134 pub pipeline: PipelineId,
135 pub walker: String,
136 pub style_rules: AtomicRefCell<HashMap<MatchedRule, String>>,
137}
138
139impl Actor for NodeActor {
140 fn name(&self) -> String {
141 self.name.clone()
142 }
143
144 fn handle_message(
151 &self,
152 request: ClientRequest,
153 registry: &ActorRegistry,
154 msg_type: &str,
155 msg: &Map<String, Value>,
156 _id: StreamId,
157 ) -> Result<(), ActorError> {
158 match msg_type {
159 "modifyAttributes" => {
160 let mods = msg
161 .get("modifications")
162 .ok_or(ActorError::MissingParameter)?
163 .as_array()
164 .ok_or(ActorError::BadParameterType)?;
165 let modifications: Vec<AttrModification> = mods
166 .iter()
167 .filter_map(|json_mod| {
168 serde_json::from_str(&serde_json::to_string(json_mod).ok()?).ok()
169 })
170 .collect();
171
172 self.script_chan
173 .send(DevtoolScriptControlMsg::ModifyAttribute(
174 self.pipeline,
175 registry.actor_to_script(self.name()),
176 modifications,
177 ))
178 .map_err(|_| ActorError::Internal)?;
179
180 let reply = EmptyReplyMsg { from: self.name() };
181 request.reply_final(&reply)?
182 },
183 "getEventListenerInfo" => {
184 let target = msg
185 .get("to")
186 .ok_or(ActorError::MissingParameter)?
187 .as_str()
188 .ok_or(ActorError::BadParameterType)?;
189
190 let (tx, rx) = generic_channel::channel().ok_or(ActorError::Internal)?;
191 self.script_chan
192 .send(DevtoolScriptControlMsg::GetEventListenerInfo(
193 self.pipeline,
194 registry.actor_to_script(target.to_owned()),
195 tx,
196 ))
197 .unwrap();
198 let event_listeners = rx.recv().map_err(|_| ActorError::Internal)?;
199
200 let msg = GetEventListenerInfoReply {
201 from: self.name(),
202 events: event_listeners.into_iter().map(From::from).collect(),
203 };
204 request.reply_final(&msg)?
205 },
206 "getUniqueSelector" => {
207 let (tx, rx) = generic_channel::channel().unwrap();
208 self.script_chan
209 .send(DevtoolScriptControlMsg::GetDocumentElement(
210 self.pipeline,
211 tx,
212 ))
213 .unwrap();
214 let doc_elem_info = rx
215 .recv()
216 .map_err(|_| ActorError::Internal)?
217 .ok_or(ActorError::Internal)?;
218 let node = doc_elem_info.encode(
219 registry,
220 self.script_chan.clone(),
221 self.pipeline,
222 self.walker.clone(),
223 );
224
225 let msg = GetUniqueSelectorReply {
226 from: self.name(),
227 value: node.display_name,
228 };
229 request.reply_final(&msg)?
230 },
231 "getXPath" => {
232 let target = msg
233 .get("to")
234 .ok_or(ActorError::MissingParameter)?
235 .as_str()
236 .ok_or(ActorError::BadParameterType)?;
237
238 let (tx, rx) = generic_channel::channel().unwrap();
239 self.script_chan
240 .send(DevtoolScriptControlMsg::GetXPath(
241 self.pipeline,
242 registry.actor_to_script(target.to_owned()),
243 tx,
244 ))
245 .unwrap();
246
247 let xpath_selector = rx.recv().map_err(|_| ActorError::Internal)?;
248 let msg = GetXPathReply {
249 from: self.name(),
250 value: xpath_selector,
251 };
252 request.reply_final(&msg)?
253 },
254
255 _ => return Err(ActorError::UnrecognizedPacketType),
256 };
257 Ok(())
258 }
259}
260
261impl NodeActor {
262 pub fn register(
263 registry: &ActorRegistry,
264 script_id: String,
265 script_chan: GenericSender<DevtoolScriptControlMsg>,
266 pipeline: PipelineId,
267 walker: String,
268 ) -> String {
269 let name = registry.new_name::<Self>();
270
271 registry.register_script_actor(script_id, name.clone());
272
273 let actor = Self {
274 name: name.clone(),
275 script_chan,
276 pipeline,
277 walker,
278 style_rules: AtomicRefCell::new(HashMap::new()),
279 };
280
281 registry.register(actor);
282 name
283 }
284}
285pub trait NodeInfoToProtocol {
286 fn encode(
287 self,
288 registry: &ActorRegistry,
289 script_chan: GenericSender<DevtoolScriptControlMsg>,
290 pipeline: PipelineId,
291 walker: String,
292 ) -> NodeActorMsg;
293}
294
295impl NodeInfoToProtocol for NodeInfo {
296 fn encode(
297 self,
298 registry: &ActorRegistry,
299 script_chan: GenericSender<DevtoolScriptControlMsg>,
300 pipeline: PipelineId,
301 walker: String,
302 ) -> NodeActorMsg {
303 let get_or_register_node_actor = |id: &str| {
304 if !registry.script_actor_registered(id.to_string()) {
305 NodeActor::register(
306 registry,
307 id.to_string(),
308 script_chan.clone(),
309 pipeline,
310 walker.clone(),
311 )
312 } else {
313 registry.script_to_actor(id.to_string())
314 }
315 };
316
317 let actor = get_or_register_node_actor(&self.unique_id);
318 let host = self
319 .host
320 .as_ref()
321 .map(|host_id| get_or_register_node_actor(host_id));
322
323 let name = registry.actor_to_script(actor.clone());
324
325 let inline_text_child = (|| {
328 if self.num_children != 1 || self.node_name == "SLOT" {
330 return None;
331 }
332
333 let (tx, rx) = generic_channel::channel()?;
334 script_chan
335 .send(DevtoolScriptControlMsg::GetChildren(
336 pipeline,
337 name.clone(),
338 tx,
339 ))
340 .unwrap();
341 let mut children = rx.recv().ok()??;
342
343 let child = children.pop()?;
344 let msg = child.encode(registry, script_chan.clone(), pipeline, walker);
345
346 if msg.node_type != TEXT_NODE {
348 return None;
349 }
350
351 if msg.node_value.clone().unwrap_or_default().len() > MAX_INLINE_LENGTH {
353 return None;
354 }
355
356 Some(Box::new(msg))
357 })();
358
359 NodeActorMsg {
360 actor,
361 host,
362 base_uri: self.base_uri,
363 causes_overflow: false,
364 container_type: None,
365 display_name: self.node_name.clone().to_lowercase(),
366 display_type: self.display,
367 inline_text_child,
368 is_after_pseudo_element: false,
369 is_anonymous: false,
370 is_before_pseudo_element: false,
371 is_direct_shadow_host_child: None,
372 is_displayed: self.is_displayed,
373 is_in_html_document: Some(true),
374 is_marker_pseudo_element: false,
375 is_native_anonymous: false,
376 is_scrollable: false,
377 is_shadow_host: self.is_shadow_host,
378 is_shadow_root: self.shadow_root_mode.is_some(),
379 is_top_level_document: self.is_top_level_document,
380 node_name: self.node_name,
381 node_type: self.node_type,
382 node_value: self.node_value,
383 num_children: self.num_children,
384 parent: registry.script_to_actor(self.parent.clone()),
385 shadow_root_mode: self
386 .shadow_root_mode
387 .as_ref()
388 .map(ShadowRootMode::to_string),
389 traits: HashMap::new(),
390 attrs: self
391 .attrs
392 .into_iter()
393 .map(|attr| AttrMsg {
394 name: attr.name,
395 value: attr.value,
396 })
397 .collect(),
398 name: self.doctype_name,
399 public_id: self.doctype_public_identifier,
400 system_id: self.doctype_system_identifier,
401 has_event_listeners: self.has_event_listeners,
402 }
403 }
404}
405
406impl From<EventListenerInfo> for DevtoolsEventListenerInfo {
407 fn from(event_listener_info: EventListenerInfo) -> Self {
408 Self {
409 r#type: event_listener_info.event_type,
410 handler: "todo".to_owned(),
411 capturing: event_listener_info.capturing,
412 origin: "todo".to_owned(),
413 tags: "".to_owned(),
414 hide: Value::Object(Default::default()),
415 native: false,
416 source_actor: "todo".to_owned(),
417 enabled: true,
418 is_user_defined: false,
419 event_listener_info_id: "todo".to_owned(),
420 }
421 }
422}