devtools/actors/
stylesheets.rs1use std::sync::Arc;
5
6use devtools_traits::DevtoolScriptControlMsg;
7use log::warn;
8use malloc_size_of_derive::MallocSizeOf;
9use serde::Serialize;
10use serde_json::{Map, Value};
11use servo_base::generic_channel;
12use servo_base::generic_channel::GenericSender;
13
14use crate::StreamId;
15use crate::actor::{Actor, ActorError, ActorRegistry, new_actor_name};
16use crate::actors::browsing_context::BrowsingContextActor;
17use crate::actors::long_string::{LongStringActor, LongStringObj};
18use crate::protocol::ClientRequest;
19
20#[derive(Serialize)]
22#[serde(rename_all = "camelCase")]
23pub(crate) struct StyleSheetData {
24 pub(crate) resource_id: String,
26 pub(crate) browsing_context_id: u32,
28 pub(crate) href: Option<String>,
30 pub(crate) node_href: String,
32 pub(crate) disabled: bool,
34 pub(crate) title: Option<String>,
36 pub(crate) system: bool,
38 pub(crate) is_new: bool,
40 pub(crate) file_name: Option<String>,
42 #[serde(rename = "sourceMapURL")]
44 pub(crate) source_map_url: Option<String>,
45 #[serde(rename = "sourceMapBaseURL")]
46 pub(crate) source_map_base_url: Option<String>,
47 pub(crate) style_sheet_index: i32,
49 pub(crate) constructed: bool,
51 pub(crate) rule_count: u32,
53 pub(crate) at_rules: Vec<String>,
55}
56
57#[derive(Serialize)]
58#[serde(rename_all = "camelCase")]
59struct GetStyleSheetsReply {
60 from: String,
61 style_sheets: Vec<StyleSheetData>,
62}
63
64#[derive(Serialize)]
65#[serde(rename_all = "camelCase")]
66struct GetTextReply {
67 from: String,
68 text: LongStringObj,
69}
70
71#[derive(MallocSizeOf)]
72pub(crate) struct StyleSheetsActor {
73 name: String,
74 script_sender: GenericSender<DevtoolScriptControlMsg>,
75 browsing_context_name: String,
76}
77
78impl Actor for StyleSheetsActor {
79 fn name(&self) -> &str {
80 &self.name
81 }
82 fn handle_message(
83 &self,
84 request: ClientRequest,
85 registry: &ActorRegistry,
86 msg_type: &str,
87 msg: &Map<String, Value>,
88 _id: StreamId,
89 ) -> Result<(), ActorError> {
90 let browsing_context_actor =
91 registry.find::<BrowsingContextActor>(&self.browsing_context_name);
92 match msg_type {
93 "getStyleSheets" => {
94 let style_sheets = self.get_stylesheets_data(&browsing_context_actor);
95 let msg = GetStyleSheetsReply {
96 from: self.name().into(),
97 style_sheets,
98 };
99 request.reply_final(&msg)?
100 },
101 "getText" => {
103 let resource_id = msg.get("resourceId").and_then(|v| v.as_str()).unwrap_or("");
104 let index = resource_id
105 .split('-')
106 .next_back()
107 .unwrap_or("0")
108 .parse::<i32>()
109 .unwrap_or(0);
110 let (tx, rx) = generic_channel::channel().unwrap();
111 let _ = self
112 .script_sender
113 .send(DevtoolScriptControlMsg::GetStyleSheetText(
114 browsing_context_actor.pipeline_id(),
115 index,
116 tx,
117 ));
118 let css_text = rx
119 .recv()
120 .map_err(|_| ActorError::Internal)?
121 .unwrap_or_else(|| {
122 warn!("Stylesheet fetched without text content");
123 "Error fetching CSS text".to_string()
124 });
125 let long_string_actor = LongStringActor::register(registry, css_text);
126 let msg = GetTextReply {
127 from: self.name().into(),
128 text: long_string_actor.long_string_obj(),
129 };
130 request.reply_final(&msg)?
131 },
132 _ => return Err(ActorError::UnrecognizedPacketType),
133 };
134 Ok(())
135 }
136}
137
138impl StyleSheetsActor {
139 pub fn register(
140 registry: &ActorRegistry,
141 script_sender: GenericSender<DevtoolScriptControlMsg>,
142 browsing_context_name: String,
143 ) -> Arc<Self> {
144 let name = new_actor_name::<Self>();
145 let actor = StyleSheetsActor {
146 name,
147 script_sender,
148 browsing_context_name,
149 };
150 registry.register::<Self>(actor)
151 }
152
153 pub(crate) fn get_stylesheets_data(
154 &self,
155 browsing_context_actor: &BrowsingContextActor,
156 ) -> Vec<StyleSheetData> {
157 let (tx, rx) = generic_channel::channel().unwrap();
158 let _ = self
159 .script_sender
160 .send(DevtoolScriptControlMsg::GetStyleSheets(
161 browsing_context_actor.pipeline_id(),
162 tx,
163 ));
164 let style_sheets = rx.recv().unwrap();
165 let url = browsing_context_actor.url();
166 let browsing_context_id = browsing_context_actor.browsing_context_id.value();
167 style_sheets
168 .into_iter()
169 .map(|info| StyleSheetData {
170 resource_id: format!("{}-{}", browsing_context_id, info.style_sheet_index),
171 browsing_context_id,
172 href: info.href.clone(),
173 node_href: url.clone(),
174 disabled: info.disabled,
175 title: (!info.title.is_empty()).then_some(info.title),
176 system: info.system,
177 is_new: false,
178 file_name: None,
179 source_map_url: Some(String::new()),
180 source_map_base_url: Some(info.href.unwrap_or_else(|| url.clone())),
181 style_sheet_index: info.style_sheet_index,
182 constructed: false,
183 rule_count: info.rule_count,
184 at_rules: vec![], })
186 .collect()
187 }
188}