1use std::sync::Arc;
8
9use atomic_refcell::AtomicRefCell;
10use devtools_traits::DevtoolScriptControlMsg::{
11 GetChildren, GetDocumentElement, GetInnerOrOuterHTML, GetRootNode,
12};
13use devtools_traits::{DomMutation, GetHTMLType};
14use malloc_size_of_derive::MallocSizeOf;
15use serde::Serialize;
16use serde_json::{self, Map, Value};
17use servo_base::generic_channel;
18
19use crate::actor::{
20 Actor, ActorEncode, ActorError, ActorRegistry, DowncastableActorArc, new_actor_name,
21};
22use crate::actors::browsing_context::BrowsingContextActor;
23use crate::actors::inspector::layout::LayoutInspectorActor;
24use crate::actors::inspector::node::{NodeActor, NodeActorMsg};
25use crate::actors::long_string::{LongStringActor, LongStringObj};
26use crate::protocol::{ClientRequest, DevtoolsConnection, JsonPacketStream};
27use crate::{ActorMsg, EmptyReplyMsg, StreamId};
28
29#[derive(Serialize)]
30pub(crate) struct WalkerMsg {
31 actor: String,
32 root: NodeActorMsg,
33}
34
35#[derive(MallocSizeOf)]
36pub(crate) struct WalkerActor {
37 pub name: String,
38 pub mutations: AtomicRefCell<Vec<DomMutation>>,
39 pub browsing_context_name: String,
41}
42
43#[derive(Serialize)]
44#[serde(rename_all = "camelCase")]
45struct QuerySelectorReply {
46 from: String,
47 node: NodeActorMsg,
48 new_parents: Vec<NodeActorMsg>,
49}
50
51#[derive(Serialize)]
52struct DocumentElementReply {
53 from: String,
54 node: NodeActorMsg,
55}
56
57#[derive(Serialize)]
58#[serde(rename_all = "camelCase")]
59struct ChildrenReply {
60 has_first: bool,
61 has_last: bool,
62 nodes: Vec<NodeActorMsg>,
63 from: String,
64}
65
66#[derive(Serialize)]
67struct GetLayoutInspectorReply {
68 from: String,
69 actor: ActorMsg,
70}
71
72#[derive(Serialize)]
73struct WatchRootNodeNotification {
74 #[serde(rename = "type")]
75 type_: String,
76 from: String,
77 node: NodeActorMsg,
78}
79
80#[derive(Serialize)]
81#[serde(rename_all = "camelCase")]
82struct MutationMsg {
83 #[serde(flatten)]
84 variant: MutationVariant,
85 #[serde(rename = "type")]
86 type_: String,
87 target: String,
88}
89
90#[derive(Serialize)]
91#[serde(untagged)]
92enum MutationVariant {
93 AttributeModified {
94 #[serde(rename = "attributeName")]
95 attribute_name: String,
96 #[serde(rename = "newValue")]
97 new_value: Option<String>,
98 },
99}
100
101#[derive(Serialize)]
102struct GetMutationsReply {
103 from: String,
104 mutations: Vec<MutationMsg>,
105}
106
107#[derive(Serialize)]
108struct GetOffsetParentReply {
109 from: String,
110 node: Option<()>,
111}
112
113#[derive(Serialize)]
114struct NewMutationsNotification {
115 from: String,
116 #[serde(rename = "type")]
117 type_: String,
118}
119
120#[derive(Serialize)]
121struct GetInnerOrOuterHTMLReply {
122 from: String,
123 value: LongStringObj,
124}
125
126impl Actor for WalkerActor {
127 fn name(&self) -> &str {
128 &self.name
129 }
130
131 fn handle_message(
152 &self,
153 mut request: ClientRequest,
154 registry: &ActorRegistry,
155 msg_type: &str,
156 msg: &Map<String, Value>,
157 _id: StreamId,
158 ) -> Result<(), ActorError> {
159 let browsing_context_actor = self.browsing_context_actor(registry);
160 match msg_type {
161 "children" => {
162 let target = msg
163 .get("node")
164 .ok_or(ActorError::MissingParameter)?
165 .as_str()
166 .ok_or(ActorError::BadParameterType)?;
167 let Some((tx, rx)) = generic_channel::channel() else {
168 return Err(ActorError::Internal);
169 };
170 browsing_context_actor
171 .script_chan()
172 .send(GetChildren(
173 browsing_context_actor.pipeline_id(),
174 registry.actor_to_script(target.into()),
175 tx,
176 ))
177 .map_err(|_| ActorError::Internal)?;
178 let children = rx
179 .recv()
180 .map_err(|_| ActorError::Internal)?
181 .ok_or(ActorError::Internal)?;
182
183 let msg = ChildrenReply {
184 has_first: true,
185 has_last: true,
186 nodes: children
187 .into_iter()
188 .map(|child| {
189 let node_actor =
190 NodeActor::register_or_update(registry, &self.name, child);
191 node_actor.encode(registry)
192 })
193 .collect(),
194 from: self.name().into(),
195 };
196 request.reply_final(&msg)?
197 },
198 "clearPseudoClassLocks" => {
199 let msg = EmptyReplyMsg {
200 from: self.name().into(),
201 };
202 request.reply_final(&msg)?
203 },
204 "documentElement" => {
205 let Some((tx, rx)) = generic_channel::channel() else {
206 return Err(ActorError::Internal);
207 };
208 browsing_context_actor
209 .script_chan()
210 .send(GetDocumentElement(browsing_context_actor.pipeline_id(), tx))
211 .map_err(|_| ActorError::Internal)?;
212 let node_info = rx
213 .recv()
214 .map_err(|_| ActorError::Internal)?
215 .ok_or(ActorError::Internal)?;
216
217 let node_actor = NodeActor::register_or_update(registry, &self.name, node_info);
218 let node = node_actor.encode(registry);
219
220 let msg = DocumentElementReply {
221 from: self.name().into(),
222 node,
223 };
224 request.reply_final(&msg)?
225 },
226 "getLayoutInspector" => {
227 let layout_inspector_actor = LayoutInspectorActor::register(registry);
228 let msg = GetLayoutInspectorReply {
229 from: self.name().into(),
230 actor: layout_inspector_actor.encode(registry),
231 };
232 request.reply_final(&msg)?
233 },
234 "getMutations" => self.handle_get_mutations(request, registry)?,
235 "getOffsetParent" => {
236 let msg = GetOffsetParentReply {
237 from: self.name().into(),
238 node: None,
239 };
240 request.reply_final(&msg)?
241 },
242 "querySelector" => {
243 let selector = msg
244 .get("selector")
245 .ok_or(ActorError::MissingParameter)?
246 .as_str()
247 .ok_or(ActorError::BadParameterType)?;
248 let node_name = msg
249 .get("node")
250 .ok_or(ActorError::MissingParameter)?
251 .as_str()
252 .ok_or(ActorError::BadParameterType)?;
253 let mut hierarchy = find_child(&self.name, registry, node_name, vec![], |msg| {
254 msg.display_name == selector
255 })
256 .map_err(|_| ActorError::Internal)?;
257 hierarchy.reverse();
258 let node = hierarchy.pop().ok_or(ActorError::Internal)?;
259
260 let msg = QuerySelectorReply {
261 from: self.name().into(),
262 node,
263 new_parents: hierarchy,
264 };
265 request.reply_final(&msg)?
266 },
267 "watchRootNode" => {
268 let msg = WatchRootNodeNotification {
269 type_: "root-available".into(),
270 from: self.name().into(),
271 node: self.root(registry)?,
272 };
273 let _ = request.write_json_packet(&msg);
274
275 let msg = EmptyReplyMsg {
276 from: self.name().into(),
277 };
278 request.reply_final(&msg)?
279 },
280 "outerHTML" => {
281 self.handle_get_inner_or_outer_html(request, registry, msg, GetHTMLType::OuterHTML)?
282 },
283 "innerHTML" => {
284 self.handle_get_inner_or_outer_html(request, registry, msg, GetHTMLType::InnerHTML)?
285 },
286 _ => return Err(ActorError::UnrecognizedPacketType),
287 };
288 Ok(())
289 }
290}
291
292impl WalkerActor {
293 pub fn register(registry: &ActorRegistry, browsing_context_name: String) -> Arc<Self> {
294 let name = new_actor_name::<WalkerActor>();
295 let actor = WalkerActor {
296 name,
297 mutations: AtomicRefCell::new(vec![]),
298 browsing_context_name,
299 };
300 registry.register::<Self>(actor)
301 }
302
303 pub(crate) fn browsing_context_actor(
304 &self,
305 registry: &ActorRegistry,
306 ) -> DowncastableActorArc<BrowsingContextActor> {
307 registry.find::<BrowsingContextActor>(&self.browsing_context_name)
308 }
309
310 pub(crate) fn root(&self, registry: &ActorRegistry) -> Result<NodeActorMsg, ActorError> {
311 let browsing_context_actor = self.browsing_context_actor(registry);
312 let pipeline = browsing_context_actor.pipeline_id();
313 let (tx, rx) = generic_channel::channel().ok_or(ActorError::Internal)?;
314 browsing_context_actor
315 .script_chan()
316 .send(GetRootNode(pipeline, tx))
317 .map_err(|_| ActorError::Internal)?;
318 let node_info = rx
319 .recv()
320 .map_err(|_| ActorError::Internal)?
321 .ok_or(ActorError::Internal)?;
322
323 let node_actor = NodeActor::register_or_update(registry, &self.name, node_info);
324 Ok(node_actor.encode(registry))
325 }
326
327 pub(crate) fn handle_dom_mutation(
328 &self,
329 dom_mutation: DomMutation,
330 stream: &mut DevtoolsConnection,
331 ) -> Result<(), ActorError> {
332 let mut pending_mutations = self.mutations.borrow_mut();
333
334 let DomMutation::AttributeModified {
337 node,
338 attribute_name,
339 ..
340 } = &dom_mutation;
341 pending_mutations.retain(|pending_mutation| match pending_mutation {
342 DomMutation::AttributeModified {
343 node: old_node,
344 attribute_name: old_attribute_name,
345 ..
346 } => old_node != node || old_attribute_name != attribute_name,
347 });
348
349 pending_mutations.push(dom_mutation);
350
351 stream.write_json_packet(&NewMutationsNotification {
352 from: self.name().into(),
353 type_: "newMutations".into(),
354 })
355 }
356
357 fn handle_get_mutations(
359 &self,
360 request: ClientRequest,
361 registry: &ActorRegistry,
362 ) -> Result<(), ActorError> {
363 let msg = GetMutationsReply {
364 from: self.name().into(),
365 mutations: self
366 .mutations
367 .borrow_mut()
368 .drain(..)
369 .map(|mutation| match mutation {
370 DomMutation::AttributeModified {
371 node,
372 attribute_name,
373 new_value,
374 } => MutationMsg {
375 variant: MutationVariant::AttributeModified {
376 attribute_name,
377 new_value,
378 },
379 target: registry.script_to_actor(&node),
380 type_: "attributes".to_owned(),
381 },
382 })
383 .collect(),
384 };
385
386 request.reply_final(&msg)
387 }
388
389 fn handle_get_inner_or_outer_html(
390 &self,
391 request: ClientRequest,
392 registry: &ActorRegistry,
393 msg: &Map<String, Value>,
394 html_type: GetHTMLType,
395 ) -> Result<(), ActorError> {
396 let target = msg
397 .get("node")
398 .ok_or(ActorError::MissingParameter)?
399 .as_str()
400 .ok_or(ActorError::BadParameterType)?;
401
402 let Some((tx, rx)) = generic_channel::channel() else {
403 return Err(ActorError::Internal);
404 };
405
406 let browsing_context_actor = self.browsing_context_actor(registry);
407
408 let handle_get_inner_or_outer_html = GetInnerOrOuterHTML(
409 browsing_context_actor.pipeline_id(),
410 registry.actor_to_script(target.into()),
411 tx,
412 html_type,
413 );
414
415 browsing_context_actor
416 .script_chan()
417 .send(handle_get_inner_or_outer_html)
418 .map_err(|_| ActorError::Internal)?;
419
420 let html_text = rx
421 .recv()
422 .map_err(|_| ActorError::Internal)?
423 .ok_or(ActorError::Internal)?;
424
425 let long_string_actor = LongStringActor::register(registry, html_text);
426
427 let msg = GetInnerOrOuterHTMLReply {
428 from: self.name().into(),
429 value: long_string_actor.long_string_obj(),
430 };
431
432 request.reply_final(&msg)
433 }
434}
435
436pub fn find_child(
440 walker_name: &str,
441 registry: &ActorRegistry,
442 node_name: &str,
443 mut hierarchy: Vec<NodeActorMsg>,
444 compare_fn: impl Fn(&NodeActorMsg) -> bool + Clone,
445) -> Result<Vec<NodeActorMsg>, Vec<NodeActorMsg>> {
446 let walker = registry.find::<WalkerActor>(walker_name);
447 let browsing_context = walker.browsing_context_actor(registry);
448 let script_chan = browsing_context.script_chan();
449 let pipeline = browsing_context.pipeline_id();
450
451 let (tx, rx) = generic_channel::channel().unwrap();
452 script_chan
453 .send(GetChildren(
454 pipeline,
455 registry.actor_to_script(node_name.into()),
456 tx,
457 ))
458 .unwrap();
459 let children = rx.recv().unwrap().ok_or(vec![])?;
460
461 for child in children {
462 let node_actor = NodeActor::register_or_update(registry, walker_name, child);
463 let msg = node_actor.encode(registry);
464
465 if compare_fn(&msg) {
466 hierarchy.push(msg);
467 return Ok(hierarchy);
468 };
469
470 if msg.num_children == 0 {
471 continue;
472 }
473
474 match find_child(
475 walker_name,
476 registry,
477 &msg.actor,
478 hierarchy,
479 compare_fn.clone(),
480 ) {
481 Ok(mut hierarchy) => {
482 hierarchy.push(msg);
483 return Ok(hierarchy);
484 },
485 Err(e) => {
486 hierarchy = e;
487 },
488 }
489 }
490 Err(hierarchy)
491}
492
493impl ActorEncode<WalkerMsg> for WalkerActor {
494 fn encode(&self, registry: &ActorRegistry) -> WalkerMsg {
495 WalkerMsg {
496 actor: self.name().into(),
497 root: self.root(registry).unwrap(),
498 }
499 }
500}