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