1use std::collections::HashMap;
9use std::collections::hash_map::Entry;
10use std::iter::once;
11
12use base::id::PipelineId;
13use devtools_traits::DevtoolScriptControlMsg::{GetLayout, GetSelectors};
14use devtools_traits::{ComputedNodeLayout, DevtoolScriptControlMsg};
15use ipc_channel::ipc::{self, IpcSender};
16use serde::Serialize;
17use serde_json::{self, Map, Value};
18
19use crate::StreamId;
20use crate::actor::{Actor, ActorError, ActorRegistry};
21use crate::actors::inspector::node::NodeActor;
22use crate::actors::inspector::style_rule::{AppliedRule, ComputedDeclaration, StyleRuleActor};
23use crate::actors::inspector::walker::{WalkerActor, find_child};
24use crate::protocol::ClientRequest;
25
26#[derive(Serialize)]
27struct GetAppliedReply {
28 entries: Vec<AppliedEntry>,
29 from: String,
30}
31
32#[derive(Serialize)]
33struct GetComputedReply {
34 computed: HashMap<String, ComputedDeclaration>,
35 from: String,
36}
37
38#[derive(Serialize)]
39#[serde(rename_all = "camelCase")]
40struct AppliedEntry {
41 rule: AppliedRule,
42 pseudo_element: Option<()>,
43 is_system: bool,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 inherited: Option<String>,
46}
47
48#[derive(Serialize)]
49#[serde(rename_all = "kebab-case")]
50struct GetLayoutReply {
51 from: String,
52
53 display: String,
54 position: String,
55 z_index: String,
56 box_sizing: String,
57
58 auto_margins: serde_json::value::Value,
61 margin_top: String,
62 margin_right: String,
63 margin_bottom: String,
64 margin_left: String,
65
66 border_top_width: String,
67 border_right_width: String,
68 border_bottom_width: String,
69 border_left_width: String,
70
71 padding_top: String,
72 padding_right: String,
73 padding_bottom: String,
74 padding_left: String,
75
76 width: f32,
77 height: f32,
78}
79
80#[derive(Serialize)]
81pub struct IsPositionEditableReply {
82 pub from: String,
83 pub value: bool,
84}
85
86#[derive(Serialize)]
87pub struct PageStyleMsg {
88 pub actor: String,
89 pub traits: HashMap<String, bool>,
90}
91
92pub struct PageStyleActor {
93 pub name: String,
94 pub script_chan: IpcSender<DevtoolScriptControlMsg>,
95 pub pipeline: PipelineId,
96}
97
98impl Actor for PageStyleActor {
99 fn name(&self) -> String {
100 self.name.clone()
101 }
102
103 fn handle_message(
115 &self,
116 request: ClientRequest,
117 registry: &ActorRegistry,
118 msg_type: &str,
119 msg: &Map<String, Value>,
120 _id: StreamId,
121 ) -> Result<(), ActorError> {
122 match msg_type {
123 "getApplied" => self.get_applied(request, msg, registry),
124 "getComputed" => self.get_computed(request, msg, registry),
125 "getLayout" => self.get_layout(request, msg, registry),
126 "isPositionEditable" => self.is_position_editable(request),
127 _ => Err(ActorError::UnrecognizedPacketType),
128 }
129 }
130}
131
132impl PageStyleActor {
133 fn get_applied(
134 &self,
135 request: ClientRequest,
136 msg: &Map<String, Value>,
137 registry: &ActorRegistry,
138 ) -> Result<(), ActorError> {
139 let target = msg
140 .get("node")
141 .ok_or(ActorError::MissingParameter)?
142 .as_str()
143 .ok_or(ActorError::BadParameterType)?;
144 let node = registry.find::<NodeActor>(target);
145 let walker = registry.find::<WalkerActor>(&node.walker);
146 let entries: Vec<_> = find_child(
147 &node.script_chan,
148 node.pipeline,
149 target,
150 registry,
151 &walker.root_node.actor,
152 vec![],
153 |msg| msg.actor == target,
154 )
155 .unwrap_or_default()
156 .into_iter()
157 .flat_map(|node| {
158 let inherited = (node.actor != target).then(|| node.actor.clone());
159 let node_actor = registry.find::<NodeActor>(&node.actor);
160
161 let selectors = (|| {
163 let (selectors_sender, selector_receiver) = ipc::channel().ok()?;
164 walker
165 .script_chan
166 .send(GetSelectors(
167 walker.pipeline,
168 registry.actor_to_script(node.actor.clone()),
169 selectors_sender,
170 ))
171 .ok()?;
172 selector_receiver.recv().ok()?
173 })()
174 .unwrap_or_default();
175
176 once(("".into(), usize::MAX))
180 .chain(selectors)
181 .filter_map(move |selector| {
182 let rule = match node_actor.style_rules.borrow_mut().entry(selector) {
183 Entry::Vacant(e) => {
184 let name = registry.new_name("style-rule");
185 let actor = StyleRuleActor::new(
186 name.clone(),
187 node_actor.name(),
188 (!e.key().0.is_empty()).then_some(e.key().clone()),
189 );
190 let rule = actor.applied(registry)?;
191
192 registry.register_later(Box::new(actor));
193 e.insert(name);
194 rule
195 },
196 Entry::Occupied(e) => {
197 let actor = registry.find::<StyleRuleActor>(e.get());
198 actor.applied(registry)?
199 },
200 };
201 if inherited.is_some() && rule.declarations.is_empty() {
202 return None;
203 }
204
205 Some(AppliedEntry {
206 rule,
207 pseudo_element: None,
209 is_system: false,
210 inherited: inherited.clone(),
211 })
212 })
213 })
214 .collect();
215 let msg = GetAppliedReply {
216 entries,
217 from: self.name(),
218 };
219 request.reply_final(&msg)
220 }
221
222 fn get_computed(
223 &self,
224 request: ClientRequest,
225 msg: &Map<String, Value>,
226 registry: &ActorRegistry,
227 ) -> Result<(), ActorError> {
228 let target = msg
229 .get("node")
230 .ok_or(ActorError::MissingParameter)?
231 .as_str()
232 .ok_or(ActorError::BadParameterType)?;
233 let node_actor = registry.find::<NodeActor>(target);
234 let computed = (|| match node_actor
235 .style_rules
236 .borrow_mut()
237 .entry(("".into(), usize::MAX))
238 {
239 Entry::Vacant(e) => {
240 let name = registry.new_name("style-rule");
241 let actor = StyleRuleActor::new(name.clone(), target.into(), None);
242 let computed = actor.computed(registry)?;
243 registry.register_later(Box::new(actor));
244 e.insert(name);
245 Some(computed)
246 },
247 Entry::Occupied(e) => {
248 let actor = registry.find::<StyleRuleActor>(e.get());
249 Some(actor.computed(registry)?)
250 },
251 })()
252 .unwrap_or_default();
253 let msg = GetComputedReply {
254 computed,
255 from: self.name(),
256 };
257 request.reply_final(&msg)
258 }
259
260 fn get_layout(
261 &self,
262 request: ClientRequest,
263 msg: &Map<String, Value>,
264 registry: &ActorRegistry,
265 ) -> Result<(), ActorError> {
266 let target = msg
267 .get("node")
268 .ok_or(ActorError::MissingParameter)?
269 .as_str()
270 .ok_or(ActorError::BadParameterType)?;
271 let (computed_node_sender, computed_node_receiver) =
272 ipc::channel().map_err(|_| ActorError::Internal)?;
273 self.script_chan
274 .send(GetLayout(
275 self.pipeline,
276 registry.actor_to_script(target.to_owned()),
277 computed_node_sender,
278 ))
279 .unwrap();
280 let ComputedNodeLayout {
281 display,
282 position,
283 z_index,
284 box_sizing,
285 auto_margins,
286 margin_top,
287 margin_right,
288 margin_bottom,
289 margin_left,
290 border_top_width,
291 border_right_width,
292 border_bottom_width,
293 border_left_width,
294 padding_top,
295 padding_right,
296 padding_bottom,
297 padding_left,
298 width,
299 height,
300 } = computed_node_receiver
301 .recv()
302 .map_err(|_| ActorError::Internal)?
303 .ok_or(ActorError::Internal)?;
304 let msg_auto_margins = msg
305 .get("autoMargins")
306 .and_then(Value::as_bool)
307 .unwrap_or(false);
308 let msg = GetLayoutReply {
309 from: self.name(),
310 display,
311 position,
312 z_index,
313 box_sizing,
314 auto_margins: if msg_auto_margins {
315 let mut m = Map::new();
316 let auto = serde_json::value::Value::String("auto".to_owned());
317 if auto_margins.top {
318 m.insert("top".to_owned(), auto.clone());
319 }
320 if auto_margins.right {
321 m.insert("right".to_owned(), auto.clone());
322 }
323 if auto_margins.bottom {
324 m.insert("bottom".to_owned(), auto.clone());
325 }
326 if auto_margins.left {
327 m.insert("left".to_owned(), auto);
328 }
329 serde_json::value::Value::Object(m)
330 } else {
331 serde_json::value::Value::Null
332 },
333 margin_top,
334 margin_right,
335 margin_bottom,
336 margin_left,
337 border_top_width,
338 border_right_width,
339 border_bottom_width,
340 border_left_width,
341 padding_top,
342 padding_right,
343 padding_bottom,
344 padding_left,
345 width,
346 height,
347 };
348 let msg = serde_json::to_string(&msg).map_err(|_| ActorError::Internal)?;
349 let msg = serde_json::from_str::<Value>(&msg).map_err(|_| ActorError::Internal)?;
350 request.reply_final(&msg)
351 }
352
353 fn is_position_editable(&self, request: ClientRequest) -> Result<(), ActorError> {
354 let msg = IsPositionEditableReply {
355 from: self.name(),
356 value: false,
357 };
358 request.reply_final(&msg)
359 }
360}