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