devtools/actors/inspector/
walker.rs1use std::cell::RefCell;
8
9use base::id::PipelineId;
10use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement};
11use devtools_traits::{AttrModification, DevtoolScriptControlMsg};
12use ipc_channel::ipc::{self, IpcSender};
13use serde::Serialize;
14use serde_json::{self, Map, Value};
15
16use crate::actor::{Actor, ActorError, ActorRegistry};
17use crate::actors::inspector::layout::{LayoutInspectorActor, LayoutInspectorActorMsg};
18use crate::actors::inspector::node::{NodeActorMsg, NodeInfoToProtocol};
19use crate::protocol::{ClientRequest, JsonPacketStream};
20use crate::{EmptyReplyMsg, StreamId};
21
22#[derive(Serialize)]
23pub struct WalkerMsg {
24 pub actor: String,
25 pub root: NodeActorMsg,
26}
27
28pub struct WalkerActor {
29 pub name: String,
30 pub script_chan: IpcSender<DevtoolScriptControlMsg>,
31 pub pipeline: PipelineId,
32 pub root_node: NodeActorMsg,
33 pub mutations: RefCell<Vec<(AttrModification, String)>>,
34}
35
36#[derive(Serialize)]
37#[serde(rename_all = "camelCase")]
38struct QuerySelectorReply {
39 from: String,
40 node: NodeActorMsg,
41 new_parents: Vec<NodeActorMsg>,
42}
43
44#[derive(Serialize)]
45struct DocumentElementReply {
46 from: String,
47 node: NodeActorMsg,
48}
49
50#[derive(Serialize)]
51#[serde(rename_all = "camelCase")]
52struct ChildrenReply {
53 has_first: bool,
54 has_last: bool,
55 nodes: Vec<NodeActorMsg>,
56 from: String,
57}
58
59#[derive(Serialize)]
60struct GetLayoutInspectorReply {
61 actor: LayoutInspectorActorMsg,
62 from: String,
63}
64
65#[derive(Serialize)]
66struct WatchRootNodeNotification {
67 #[serde(rename = "type")]
68 type_: String,
69 from: String,
70 node: NodeActorMsg,
71}
72
73#[derive(Serialize)]
74#[serde(rename_all = "camelCase")]
75struct MutationMsg {
76 attribute_name: String,
77 new_value: Option<String>,
78 target: String,
79 #[serde(rename = "type")]
80 type_: String,
81}
82
83#[derive(Serialize)]
84struct GetMutationsReply {
85 from: String,
86 mutations: Vec<MutationMsg>,
87}
88
89#[derive(Serialize)]
90struct GetOffsetParentReply {
91 from: String,
92 node: Option<()>,
93}
94
95#[derive(Serialize)]
96struct NewMutationsNotification {
97 from: String,
98 #[serde(rename = "type")]
99 type_: String,
100}
101
102impl Actor for WalkerActor {
103 fn name(&self) -> String {
104 self.name.clone()
105 }
106
107 fn handle_message(
124 &self,
125 mut request: ClientRequest,
126 registry: &ActorRegistry,
127 msg_type: &str,
128 msg: &Map<String, Value>,
129 _id: StreamId,
130 ) -> Result<(), ActorError> {
131 match msg_type {
132 "children" => {
133 let target = msg
134 .get("node")
135 .ok_or(ActorError::MissingParameter)?
136 .as_str()
137 .ok_or(ActorError::BadParameterType)?;
138 let (tx, rx) = ipc::channel().map_err(|_| ActorError::Internal)?;
139 self.script_chan
140 .send(GetChildren(
141 self.pipeline,
142 registry.actor_to_script(target.into()),
143 tx,
144 ))
145 .map_err(|_| ActorError::Internal)?;
146 let children = rx
147 .recv()
148 .map_err(|_| ActorError::Internal)?
149 .ok_or(ActorError::Internal)?;
150
151 let msg = ChildrenReply {
152 has_first: true,
153 has_last: true,
154 nodes: children
155 .into_iter()
156 .map(|child| {
157 child.encode(
158 registry,
159 self.script_chan.clone(),
160 self.pipeline,
161 self.name(),
162 )
163 })
164 .collect(),
165 from: self.name(),
166 };
167 request.reply_final(&msg)?
168 },
169 "clearPseudoClassLocks" => {
170 let msg = EmptyReplyMsg { from: self.name() };
171 request.reply_final(&msg)?
172 },
173 "documentElement" => {
174 let (tx, rx) = ipc::channel().map_err(|_| ActorError::Internal)?;
175 self.script_chan
176 .send(GetDocumentElement(self.pipeline, tx))
177 .map_err(|_| ActorError::Internal)?;
178 let doc_elem_info = rx
179 .recv()
180 .map_err(|_| ActorError::Internal)?
181 .ok_or(ActorError::Internal)?;
182 let node = doc_elem_info.encode(
183 registry,
184 self.script_chan.clone(),
185 self.pipeline,
186 self.name(),
187 );
188
189 let msg = DocumentElementReply {
190 from: self.name(),
191 node,
192 };
193 request.reply_final(&msg)?
194 },
195 "getLayoutInspector" => {
196 let layout = LayoutInspectorActor::new(registry.new_name("layout"));
198 let actor = layout.encodable();
199 registry.register_later(Box::new(layout));
200
201 let msg = GetLayoutInspectorReply {
202 from: self.name(),
203 actor,
204 };
205 request.reply_final(&msg)?
206 },
207 "getMutations" => {
208 let msg = GetMutationsReply {
209 from: self.name(),
210 mutations: self
211 .mutations
212 .borrow_mut()
213 .drain(..)
214 .map(|(mutation, target)| MutationMsg {
215 attribute_name: mutation.attribute_name,
216 new_value: mutation.new_value,
217 target,
218 type_: "attributes".into(),
219 })
220 .collect(),
221 };
222 request.reply_final(&msg)?
223 },
224 "getOffsetParent" => {
225 let msg = GetOffsetParentReply {
226 from: self.name(),
227 node: None,
228 };
229 request.reply_final(&msg)?
230 },
231 "querySelector" => {
232 let selector = msg
233 .get("selector")
234 .ok_or(ActorError::MissingParameter)?
235 .as_str()
236 .ok_or(ActorError::BadParameterType)?;
237 let node = msg
238 .get("node")
239 .ok_or(ActorError::MissingParameter)?
240 .as_str()
241 .ok_or(ActorError::BadParameterType)?;
242 let mut hierarchy = find_child(
243 &self.script_chan,
244 self.pipeline,
245 &self.name,
246 registry,
247 node,
248 vec![],
249 |msg| msg.display_name == selector,
250 )
251 .map_err(|_| ActorError::Internal)?;
252 hierarchy.reverse();
253 let node = hierarchy.pop().ok_or(ActorError::Internal)?;
254
255 let msg = QuerySelectorReply {
256 from: self.name(),
257 node,
258 new_parents: hierarchy,
259 };
260 request.reply_final(&msg)?
261 },
262 "watchRootNode" => {
263 let msg = WatchRootNodeNotification {
264 type_: "root-available".into(),
265 from: self.name(),
266 node: self.root_node.clone(),
267 };
268 let _ = request.write_json_packet(&msg);
269
270 let msg = EmptyReplyMsg { from: self.name() };
271 request.reply_final(&msg)?
272 },
273 _ => return Err(ActorError::UnrecognizedPacketType),
274 };
275 Ok(())
276 }
277}
278
279impl WalkerActor {
280 pub(crate) fn new_mutations(
281 &self,
282 request: &mut ClientRequest,
283 target: &str,
284 modifications: &[AttrModification],
285 ) {
286 {
287 let mut mutations = self.mutations.borrow_mut();
288 mutations.extend(modifications.iter().cloned().map(|m| (m, target.into())));
289 }
290 let _ = request.write_json_packet(&NewMutationsNotification {
291 from: self.name(),
292 type_: "newMutations".into(),
293 });
294 }
295}
296
297pub fn find_child(
301 script_chan: &IpcSender<DevtoolScriptControlMsg>,
302 pipeline: PipelineId,
303 name: &str,
304 registry: &ActorRegistry,
305 node: &str,
306 mut hierarchy: Vec<NodeActorMsg>,
307 compare_fn: impl Fn(&NodeActorMsg) -> bool + Clone,
308) -> Result<Vec<NodeActorMsg>, Vec<NodeActorMsg>> {
309 let (tx, rx) = ipc::channel().unwrap();
310 script_chan
311 .send(GetChildren(
312 pipeline,
313 registry.actor_to_script(node.into()),
314 tx,
315 ))
316 .unwrap();
317 let children = rx.recv().unwrap().ok_or(vec![])?;
318
319 for child in children {
320 let msg = child.encode(registry, script_chan.clone(), pipeline, name.into());
321 if compare_fn(&msg) {
322 hierarchy.push(msg);
323 return Ok(hierarchy);
324 };
325
326 if msg.num_children == 0 {
327 continue;
328 }
329
330 match find_child(
331 script_chan,
332 pipeline,
333 name,
334 registry,
335 &msg.actor,
336 hierarchy,
337 compare_fn.clone(),
338 ) {
339 Ok(mut hierarchy) => {
340 hierarchy.push(msg);
341 return Ok(hierarchy);
342 },
343 Err(e) => {
344 hierarchy = e;
345 },
346 }
347 }
348 Err(hierarchy)
349}