devtools/actors/inspector/
style_rule.rs1use std::collections::HashMap;
10
11use devtools_traits::DevtoolScriptControlMsg::{
12 GetAttributeStyle, GetComputedStyle, GetDocumentElement, GetStylesheetStyle, ModifyRule,
13};
14use ipc_channel::ipc;
15use serde::Serialize;
16use serde_json::{Map, Value};
17
18use crate::StreamId;
19use crate::actor::{Actor, ActorError, ActorRegistry};
20use crate::actors::inspector::node::NodeActor;
21use crate::actors::inspector::walker::WalkerActor;
22use crate::protocol::ClientRequest;
23
24const ELEMENT_STYLE_TYPE: u32 = 100;
25
26#[derive(Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct AppliedRule {
29 actor: String,
30 ancestor_data: Vec<()>,
31 authored_text: String,
32 css_text: String,
33 pub declarations: Vec<AppliedDeclaration>,
34 href: String,
35 #[serde(skip_serializing_if = "Vec::is_empty")]
36 selectors: Vec<String>,
37 #[serde(skip_serializing_if = "Vec::is_empty")]
38 selectors_specificity: Vec<u32>,
39 #[serde(rename = "type")]
40 type_: u32,
41 traits: StyleRuleActorTraits,
42}
43
44#[derive(Serialize)]
45pub struct IsUsed {
46 pub used: bool,
47}
48
49#[derive(Serialize)]
50#[serde(rename_all = "camelCase")]
51pub struct AppliedDeclaration {
52 colon_offsets: Vec<i32>,
53 is_name_valid: bool,
54 is_used: IsUsed,
55 is_valid: bool,
56 name: String,
57 offsets: Vec<i32>,
58 priority: String,
59 terminator: String,
60 value: String,
61}
62
63#[derive(Serialize)]
64pub struct ComputedDeclaration {
65 matched: bool,
66 value: String,
67}
68
69#[derive(Serialize)]
70#[serde(rename_all = "camelCase")]
71pub struct StyleRuleActorTraits {
72 pub can_set_rule_text: bool,
73}
74
75#[derive(Serialize)]
76pub struct StyleRuleActorMsg {
77 from: String,
78 rule: Option<AppliedRule>,
79}
80
81pub struct StyleRuleActor {
82 name: String,
83 node: String,
84 selector: Option<(String, usize)>,
85}
86
87impl Actor for StyleRuleActor {
88 fn name(&self) -> String {
89 self.name.clone()
90 }
91
92 fn handle_message(
99 &self,
100 request: ClientRequest,
101 registry: &ActorRegistry,
102 msg_type: &str,
103 msg: &Map<String, Value>,
104 _id: StreamId,
105 ) -> Result<(), ActorError> {
106 match msg_type {
107 "setRuleText" => {
108 let mods = msg
110 .get("modifications")
111 .ok_or(ActorError::MissingParameter)?
112 .as_array()
113 .ok_or(ActorError::BadParameterType)?;
114 let modifications: Vec<_> = mods
115 .iter()
116 .filter_map(|json_mod| {
117 serde_json::from_str(&serde_json::to_string(json_mod).ok()?).ok()
118 })
119 .collect();
120
121 let node = registry.find::<NodeActor>(&self.node);
123 let walker = registry.find::<WalkerActor>(&node.walker);
124 walker
125 .script_chan
126 .send(ModifyRule(
127 walker.pipeline,
128 registry.actor_to_script(self.node.clone()),
129 modifications,
130 ))
131 .map_err(|_| ActorError::Internal)?;
132
133 request.reply_final(&self.encodable(registry))?
134 },
135 _ => return Err(ActorError::UnrecognizedPacketType),
136 };
137 Ok(())
138 }
139}
140
141impl StyleRuleActor {
142 pub fn new(name: String, node: String, selector: Option<(String, usize)>) -> Self {
143 Self {
144 name,
145 node,
146 selector,
147 }
148 }
149
150 pub fn applied(&self, registry: &ActorRegistry) -> Option<AppliedRule> {
151 let node = registry.find::<NodeActor>(&self.node);
152 let walker = registry.find::<WalkerActor>(&node.walker);
153
154 let (document_sender, document_receiver) = ipc::channel().ok()?;
155 walker
156 .script_chan
157 .send(GetDocumentElement(walker.pipeline, document_sender))
158 .ok()?;
159 let node = document_receiver.recv().ok()??;
160
161 let (style_sender, style_receiver) = ipc::channel().ok()?;
164 let req = match &self.selector {
165 Some(selector) => {
166 let (selector, stylesheet) = selector.clone();
167 GetStylesheetStyle(
168 walker.pipeline,
169 registry.actor_to_script(self.node.clone()),
170 selector,
171 stylesheet,
172 style_sender,
173 )
174 },
175 None => GetAttributeStyle(
176 walker.pipeline,
177 registry.actor_to_script(self.node.clone()),
178 style_sender,
179 ),
180 };
181 walker.script_chan.send(req).ok()?;
182 let style = style_receiver.recv().ok()??;
183
184 Some(AppliedRule {
185 actor: self.name(),
186 ancestor_data: vec![], authored_text: "".into(),
188 css_text: "".into(), declarations: style
190 .into_iter()
191 .map(|decl| {
192 AppliedDeclaration {
193 colon_offsets: vec![],
194 is_name_valid: true,
195 is_used: IsUsed { used: true },
196 is_valid: true,
197 name: decl.name,
198 offsets: vec![], priority: decl.priority,
200 terminator: "".into(),
201 value: decl.value,
202 }
203 })
204 .collect(),
205 href: node.base_uri.clone(),
206 selectors: self.selector.iter().map(|(s, _)| s).cloned().collect(),
207 selectors_specificity: self.selector.iter().map(|_| 1).collect(),
208 type_: ELEMENT_STYLE_TYPE,
209 traits: StyleRuleActorTraits {
210 can_set_rule_text: true,
211 },
212 })
213 }
214
215 pub fn computed(
216 &self,
217 registry: &ActorRegistry,
218 ) -> Option<HashMap<String, ComputedDeclaration>> {
219 let node = registry.find::<NodeActor>(&self.node);
220 let walker = registry.find::<WalkerActor>(&node.walker);
221
222 let (style_sender, style_receiver) = ipc::channel().ok()?;
223 walker
224 .script_chan
225 .send(GetComputedStyle(
226 walker.pipeline,
227 registry.actor_to_script(self.node.clone()),
228 style_sender,
229 ))
230 .ok()?;
231 let style = style_receiver.recv().ok()??;
232
233 Some(
234 style
235 .into_iter()
236 .map(|s| {
237 (
238 s.name,
239 ComputedDeclaration {
240 matched: true,
241 value: s.value,
242 },
243 )
244 })
245 .collect(),
246 )
247 }
248
249 pub fn encodable(&self, registry: &ActorRegistry) -> StyleRuleActorMsg {
250 StyleRuleActorMsg {
251 from: self.name(),
252 rule: self.applied(registry),
253 }
254 }
255}