use devtools_traits::DevtoolScriptControlMsg;
use log::warn;
use malloc_size_of_derive::MallocSizeOf;
use serde::Serialize;
use serde_json::{Map, Value};
use servo_base::generic_channel;
use servo_base::generic_channel::GenericSender;
use crate::StreamId;
use crate::actor::{Actor, ActorError, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::long_string::{LongStringActor, LongStringObj};
use crate::protocol::ClientRequest;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct StyleSheetData {
pub(crate) resource_id: String,
pub(crate) browsing_context_id: u32,
pub(crate) href: Option<String>,
pub(crate) node_href: String,
pub(crate) disabled: bool,
pub(crate) title: Option<String>,
pub(crate) system: bool,
pub(crate) is_new: bool,
pub(crate) file_name: Option<String>,
#[serde(rename = "sourceMapURL")]
pub(crate) source_map_url: Option<String>,
#[serde(rename = "sourceMapBaseURL")]
pub(crate) source_map_base_url: Option<String>,
pub(crate) style_sheet_index: i32,
pub(crate) constructed: bool,
pub(crate) rule_count: u32,
pub(crate) at_rules: Vec<String>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct GetStyleSheetsReply {
from: String,
style_sheets: Vec<StyleSheetData>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct GetTextReply {
from: String,
text: LongStringObj,
}
#[derive(MallocSizeOf)]
pub(crate) struct StyleSheetsActor {
name: String,
script_sender: GenericSender<DevtoolScriptControlMsg>,
browsing_context_name: String,
}
impl Actor for StyleSheetsActor {
fn name(&self) -> String {
self.name.clone()
}
fn handle_message(
&self,
request: ClientRequest,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
_id: StreamId,
) -> Result<(), ActorError> {
let browsing_context_actor =
registry.find::<BrowsingContextActor>(&self.browsing_context_name);
match msg_type {
"getStyleSheets" => {
let style_sheets = self.get_stylesheets_data(&browsing_context_actor);
let msg = GetStyleSheetsReply {
from: self.name(),
style_sheets,
};
request.reply_final(&msg)?
},
"getText" => {
let resource_id = msg.get("resourceId").and_then(|v| v.as_str()).unwrap_or("");
let index = resource_id
.split('-')
.next_back()
.unwrap_or("0")
.parse::<i32>()
.unwrap_or(0);
let (tx, rx) = generic_channel::channel().unwrap();
let _ = self
.script_sender
.send(DevtoolScriptControlMsg::GetStyleSheetText(
browsing_context_actor.pipeline_id(),
index,
tx,
));
let css_text = rx
.recv()
.map_err(|_| ActorError::Internal)?
.unwrap_or_else(|| {
warn!("Stylesheet fetched without text content");
"Error fetching CSS text".to_string()
});
let long_string_name = LongStringActor::register(registry, css_text);
let long_string_actor = registry.find::<LongStringActor>(&long_string_name);
let msg = GetTextReply {
from: self.name(),
text: long_string_actor.long_string_obj(),
};
request.reply_final(&msg)?
},
_ => return Err(ActorError::UnrecognizedPacketType),
};
Ok(())
}
}
impl StyleSheetsActor {
pub fn register(
registry: &ActorRegistry,
script_sender: GenericSender<DevtoolScriptControlMsg>,
browsing_context_name: String,
) -> String {
let name = registry.new_name::<Self>();
let actor = StyleSheetsActor {
name: name.clone(),
script_sender,
browsing_context_name,
};
registry.register::<Self>(actor);
name
}
pub(crate) fn get_stylesheets_data(
&self,
browsing_context_actor: &BrowsingContextActor,
) -> Vec<StyleSheetData> {
let (tx, rx) = generic_channel::channel().unwrap();
let _ = self
.script_sender
.send(DevtoolScriptControlMsg::GetStyleSheets(
browsing_context_actor.pipeline_id(),
tx,
));
let style_sheets = rx.recv().unwrap();
let url = browsing_context_actor.url();
let browsing_context_id = browsing_context_actor.browsing_context_id.value();
style_sheets
.into_iter()
.map(|info| StyleSheetData {
resource_id: format!("{}-{}", browsing_context_id, info.style_sheet_index),
browsing_context_id,
href: info.href.clone(),
node_href: url.clone(),
disabled: info.disabled,
title: (!info.title.is_empty()).then_some(info.title),
system: info.system,
is_new: false,
file_name: None,
source_map_url: Some("".to_string()),
source_map_base_url: Some(info.href.unwrap_or_else(|| url.clone())),
style_sheet_index: info.style_sheet_index,
constructed: false,
rule_count: info.rule_count,
at_rules: vec![], })
.collect()
}
}