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