use std::collections::HashMap;
use std::net::TcpStream;
use devtools_traits::DevtoolScriptControlMsg::{
GetAttributeStyle, GetComputedStyle, GetDocumentElement, GetStylesheetStyle, ModifyRule,
};
use ipc_channel::ipc;
use serde::Serialize;
use serde_json::{Map, Value};
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::inspector::node::NodeActor;
use crate::actors::inspector::walker::WalkerActor;
use crate::protocol::JsonPacketStream;
use crate::StreamId;
const ELEMENT_STYLE_TYPE: u32 = 100;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AppliedRule {
actor: String,
ancestor_data: Vec<()>,
authored_text: String,
css_text: String,
pub declarations: Vec<AppliedDeclaration>,
href: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
selectors: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
selectors_specificity: Vec<u32>,
#[serde(rename = "type")]
type_: u32,
traits: StyleRuleActorTraits,
}
#[derive(Serialize)]
pub struct IsUsed {
pub used: bool,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AppliedDeclaration {
colon_offsets: Vec<i32>,
is_name_valid: bool,
is_used: IsUsed,
is_valid: bool,
name: String,
offsets: Vec<i32>,
priority: String,
terminator: String,
value: String,
}
#[derive(Serialize)]
pub struct ComputedDeclaration {
matched: bool,
value: String,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StyleRuleActorTraits {
pub can_set_rule_text: bool,
}
#[derive(Serialize)]
pub struct StyleRuleActorMsg {
from: String,
rule: Option<AppliedRule>,
}
pub struct StyleRuleActor {
name: String,
node: String,
selector: Option<(String, usize)>,
}
impl Actor for StyleRuleActor {
fn name(&self) -> String {
self.name.clone()
}
fn handle_message(
&self,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
stream: &mut TcpStream,
_id: StreamId,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"setRuleText" => {
let mods = msg.get("modifications").ok_or(())?.as_array().ok_or(())?;
let modifications: Vec<_> = mods
.iter()
.filter_map(|json_mod| {
serde_json::from_str(&serde_json::to_string(json_mod).ok()?).ok()
})
.collect();
let node = registry.find::<NodeActor>(&self.node);
let walker = registry.find::<WalkerActor>(&node.walker);
walker
.script_chan
.send(ModifyRule(
walker.pipeline,
registry.actor_to_script(self.node.clone()),
modifications,
))
.map_err(|_| ())?;
let _ = stream.write_json_packet(&self.encodable(registry));
ActorMessageStatus::Processed
},
_ => ActorMessageStatus::Ignored,
})
}
}
impl StyleRuleActor {
pub fn new(name: String, node: String, selector: Option<(String, usize)>) -> Self {
Self {
name,
node,
selector,
}
}
pub fn applied(&self, registry: &ActorRegistry) -> Option<AppliedRule> {
let node = registry.find::<NodeActor>(&self.node);
let walker = registry.find::<WalkerActor>(&node.walker);
let (document_sender, document_receiver) = ipc::channel().ok()?;
walker
.script_chan
.send(GetDocumentElement(walker.pipeline, document_sender))
.ok()?;
let node = document_receiver.recv().ok()??;
let (style_sender, style_receiver) = ipc::channel().ok()?;
let req = match &self.selector {
Some(selector) => {
let (selector, stylesheet) = selector.clone();
GetStylesheetStyle(
walker.pipeline,
registry.actor_to_script(self.node.clone()),
selector,
stylesheet,
style_sender,
)
},
None => GetAttributeStyle(
walker.pipeline,
registry.actor_to_script(self.node.clone()),
style_sender,
),
};
walker.script_chan.send(req).ok()?;
let style = style_receiver.recv().ok()??;
Some(AppliedRule {
actor: self.name(),
ancestor_data: vec![], authored_text: "".into(),
css_text: "".into(), declarations: style
.into_iter()
.map(|decl| {
AppliedDeclaration {
colon_offsets: vec![],
is_name_valid: true,
is_used: IsUsed { used: true },
is_valid: true,
name: decl.name,
offsets: vec![], priority: decl.priority,
terminator: "".into(),
value: decl.value,
}
})
.collect(),
href: node.base_uri.clone(),
selectors: self.selector.iter().map(|(s, _)| s).cloned().collect(),
selectors_specificity: self.selector.iter().map(|_| 1).collect(),
type_: ELEMENT_STYLE_TYPE,
traits: StyleRuleActorTraits {
can_set_rule_text: true,
},
})
}
pub fn computed(
&self,
registry: &ActorRegistry,
) -> Option<HashMap<String, ComputedDeclaration>> {
let node = registry.find::<NodeActor>(&self.node);
let walker = registry.find::<WalkerActor>(&node.walker);
let (style_sender, style_receiver) = ipc::channel().ok()?;
walker
.script_chan
.send(GetComputedStyle(
walker.pipeline,
registry.actor_to_script(self.node.clone()),
style_sender,
))
.ok()?;
let style = style_receiver.recv().ok()??;
Some(
style
.into_iter()
.map(|s| {
(
s.name,
ComputedDeclaration {
matched: true,
value: s.value,
},
)
})
.collect(),
)
}
pub fn encodable(&self, registry: &ActorRegistry) -> StyleRuleActorMsg {
StyleRuleActorMsg {
from: self.name(),
rule: self.applied(registry),
}
}
}