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