devtools/actors/inspector/
style_rule.rs1use std::collections::HashMap;
10use std::sync::Arc;
11
12use devtools_traits::DevtoolScriptControlMsg::{
13 GetAttributeStyle, GetComputedStyle, GetDocumentElement, GetStylesheetStyle, ModifyRule,
14};
15use devtools_traits::{AncestorData, MatchedRule};
16use malloc_size_of_derive::MallocSizeOf;
17use serde::Serialize;
18use serde_json::{Map, Value};
19use servo_base::generic_channel;
20
21use crate::StreamId;
22use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry, new_actor_name};
23use crate::actors::inspector::node::NodeActor;
24use crate::actors::inspector::walker::WalkerActor;
25use crate::protocol::ClientRequest;
26
27const ELEMENT_STYLE_TYPE: u32 = 100;
28
29#[derive(Serialize)]
30#[serde(rename_all = "camelCase")]
31pub(crate) struct AppliedRule {
32 actor: String,
33 ancestor_data: Vec<AncestorData>,
34 authored_text: String,
35 css_text: String,
36 pub declarations: Vec<AppliedDeclaration>,
37 href: String,
38 #[serde(skip_serializing_if = "Vec::is_empty")]
39 selectors: Vec<String>,
40 #[serde(skip_serializing_if = "Vec::is_empty")]
41 selectors_specificity: Vec<u32>,
42 #[serde(rename = "type")]
43 type_: u32,
44 traits: StyleRuleActorTraits,
45}
46
47#[derive(Serialize)]
48pub(crate) struct IsUsed {
49 used: bool,
50}
51
52#[derive(Serialize)]
53#[serde(rename_all = "camelCase")]
54pub(crate) struct AppliedDeclaration {
55 colon_offsets: Vec<i32>,
56 is_name_valid: bool,
57 is_used: IsUsed,
58 is_valid: bool,
59 name: String,
60 offsets: Vec<i32>,
61 priority: String,
62 terminator: String,
63 value: String,
64}
65
66#[derive(Serialize)]
67pub(crate) struct ComputedDeclaration {
68 matched: bool,
69 value: String,
70}
71
72#[derive(Serialize)]
73#[serde(rename_all = "camelCase")]
74pub(crate) struct StyleRuleActorTraits {
75 pub can_set_rule_text: bool,
76}
77
78#[derive(Serialize)]
79pub(crate) struct StyleRuleActorMsg {
80 from: String,
81 rule: Option<AppliedRule>,
82}
83
84#[derive(MallocSizeOf)]
85pub(crate) struct StyleRuleActor {
86 name: String,
87 node_name: String,
88 selector: Option<MatchedRule>,
89}
90
91impl Actor for StyleRuleActor {
92 fn name(&self) -> &str {
93 &self.name
94 }
95
96 fn handle_message(
103 &self,
104 request: ClientRequest,
105 registry: &ActorRegistry,
106 msg_type: &str,
107 msg: &Map<String, Value>,
108 _id: StreamId,
109 ) -> Result<(), ActorError> {
110 match msg_type {
111 "setRuleText" => {
112 let mods = msg
114 .get("modifications")
115 .ok_or(ActorError::MissingParameter)?
116 .as_array()
117 .ok_or(ActorError::BadParameterType)?;
118 let modifications: Vec<_> = mods
119 .iter()
120 .filter_map(|json_mod| {
121 serde_json::from_str(&serde_json::to_string(json_mod).ok()?).ok()
122 })
123 .collect();
124
125 let node_actor = registry.find::<NodeActor>(&self.node_name);
127 let walker = registry.find::<WalkerActor>(&node_actor.walker_name);
128 let browsing_context_actor = walker.browsing_context_actor(registry);
129 browsing_context_actor
130 .script_chan()
131 .send(ModifyRule(
132 browsing_context_actor.pipeline_id(),
133 registry.actor_to_script(self.node_name.clone()),
134 modifications,
135 ))
136 .map_err(|_| ActorError::Internal)?;
137
138 request.reply_final(&self.encode(registry))?
139 },
140 _ => return Err(ActorError::UnrecognizedPacketType),
141 };
142 Ok(())
143 }
144}
145
146impl StyleRuleActor {
147 pub fn register(
148 registry: &ActorRegistry,
149 node: String,
150 selector: Option<MatchedRule>,
151 ) -> Arc<Self> {
152 let name = new_actor_name::<Self>();
153 let actor = Self {
154 name,
155 node_name: node,
156 selector,
157 };
158 registry.register::<Self>(actor)
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_name);
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().into(),
197 ancestor_data: self
198 .selector
199 .as_ref()
200 .map(|r| r.ancestor_data.clone())
201 .unwrap_or_default(),
202 authored_text: String::new(),
203 css_text: String::new(), 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: String::new(),
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_name);
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().into(),
270 rule: self.applied(registry),
271 }
272 }
273}