use std::collections::HashMap;
use atomic_refcell::AtomicRefCell;
use malloc_size_of_derive::MallocSizeOf;
use serde::Serialize;
use serde_json::{Map, Value};
use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::device::DeviceActor;
use crate::actors::performance::PerformanceActor;
use crate::actors::preference::PreferenceActor;
use crate::actors::process::{ProcessActor, ProcessActorMsg};
use crate::actors::tab::{TabDescriptorActor, TabDescriptorActorMsg};
use crate::actors::worker::{WorkerTargetActor, WorkerTargetActorMsg};
use crate::protocol::{ActorDescription, ClientRequest};
use crate::{EmptyReplyMsg, StreamId};
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ServiceWorkerInfo {
actor: String,
url: String,
state: u32,
state_text: String,
id: String,
fetch: bool,
traits: HashMap<&'static str, bool>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ServiceWorkerRegistrationMsg {
actor: String,
scope: String,
url: String,
registration_state: String,
last_update_time: u64,
traits: HashMap<&'static str, bool>,
evaluating_worker: Option<ServiceWorkerInfo>,
installing_worker: Option<ServiceWorkerInfo>,
waiting_worker: Option<ServiceWorkerInfo>,
active_worker: Option<ServiceWorkerInfo>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct RootTraits {
sources: bool,
highlightable: bool,
custom_highlighters: bool,
network_monitor: bool,
resources: HashMap<&'static str, bool>,
}
#[derive(Serialize)]
struct ListAddonsReply {
from: String,
addons: Vec<AddonMsg>,
}
#[derive(Serialize)]
enum AddonMsg {}
#[derive(Clone, Default, Serialize, MallocSizeOf)]
#[serde(rename_all = "camelCase")]
struct GlobalActors {
device_actor: String,
perf_actor: String,
preference_actor: String,
}
#[derive(Serialize)]
struct GetRootReply {
from: String,
#[serde(flatten)]
global_actors: GlobalActors,
}
#[derive(Serialize)]
struct ListTabsReply {
from: String,
tabs: Vec<TabDescriptorActorMsg>,
}
#[derive(Serialize)]
struct GetTabReply {
from: String,
tab: TabDescriptorActorMsg,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct RootActorMsg {
from: String,
application_type: String,
traits: RootTraits,
}
#[derive(Serialize)]
pub(crate) struct ProtocolDescriptionReply {
from: String,
types: Types,
}
#[derive(Serialize)]
struct ListWorkersReply {
from: String,
workers: Vec<WorkerTargetActorMsg>,
}
#[derive(Serialize)]
struct ListServiceWorkerRegistrationsReply {
from: String,
registrations: Vec<ServiceWorkerRegistrationMsg>,
}
#[derive(Serialize)]
pub(crate) struct Types {
performance: ActorDescription,
device: ActorDescription,
}
#[derive(Serialize)]
struct ListProcessesResponse {
from: String,
processes: Vec<ProcessActorMsg>,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DescriptorTraits {
pub(crate) watcher: bool,
pub(crate) supports_reload_descriptor: bool,
pub(crate) supports_navigation: bool,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct GetProcessResponse {
from: String,
process_descriptor: ProcessActorMsg,
}
#[derive(MallocSizeOf)]
pub(crate) struct RootActor {
name: String,
active_tab: AtomicRefCell<Option<String>>,
global_actors: GlobalActors,
process_name: String,
pub tabs: AtomicRefCell<Vec<String>>,
pub workers: AtomicRefCell<Vec<String>>,
pub service_workers: AtomicRefCell<Vec<String>>,
}
impl Actor for RootActor {
fn name(&self) -> &str {
&self.name
}
fn handle_message(
&self,
request: ClientRequest,
registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
_id: StreamId,
) -> Result<(), ActorError> {
match msg_type {
"connect" => {
let message = EmptyReplyMsg {
from: self.name().into(),
};
request.reply_final(&message)?
},
"getProcess" => {
let process_descriptor = registry.encode::<ProcessActor, _>(&self.process_name);
let reply = GetProcessResponse {
from: self.name().into(),
process_descriptor,
};
request.reply_final(&reply)?
},
"getRoot" => {
let reply = GetRootReply {
from: self.name().into(),
global_actors: self.global_actors.clone(),
};
request.reply_final(&reply)?
},
"getTab" => {
let browser_id = msg
.get("browserId")
.ok_or(ActorError::MissingParameter)?
.as_u64()
.ok_or(ActorError::BadParameterType)?;
let Some(tab) = self.get_tab_msg_by_browser_id(registry, browser_id as u32) else {
return Err(ActorError::Internal);
};
let reply = GetTabReply {
from: self.name().into(),
tab,
};
request.reply_final(&reply)?
},
"listAddons" => {
let reply = ListAddonsReply {
from: "root".to_owned(),
addons: vec![],
};
request.reply_final(&reply)?
},
"listProcesses" => {
let process_descriptor = registry.encode::<ProcessActor, _>(&self.process_name);
let reply = ListProcessesResponse {
from: self.name().into(),
processes: vec![process_descriptor],
};
request.reply_final(&reply)?
},
"listServiceWorkerRegistrations" => {
let registrations = self
.service_workers
.borrow()
.iter()
.map(|worker_name| {
let worker_actor = registry.find::<WorkerTargetActor>(worker_name);
let url = worker_actor.url.to_string();
let scope = url.clone();
ServiceWorkerRegistrationMsg {
actor: worker_actor.name().into(),
scope,
url: url.clone(),
registration_state: "".to_string(),
last_update_time: 0,
traits: HashMap::new(),
evaluating_worker: None,
installing_worker: None,
waiting_worker: None,
active_worker: Some(ServiceWorkerInfo {
actor: worker_actor.name().into(),
url,
state: 4, state_text: "activated".to_string(),
id: worker_actor.worker_id.to_string(),
fetch: false,
traits: HashMap::new(),
}),
}
})
.collect();
let reply = ListServiceWorkerRegistrationsReply {
from: self.name().into(),
registrations,
};
request.reply_final(&reply)?
},
"listTabs" => {
let top_level_tabs: Vec<_> = self
.tabs
.borrow()
.iter()
.filter(|&tab_name| {
registry
.find::<TabDescriptorActor>(tab_name)
.is_top_level_global(registry)
})
.cloned()
.collect();
let active_tab_name = self.active_tab.borrow().clone();
let some_active_tab = active_tab_name
.as_ref()
.is_some_and(|name| top_level_tabs.contains(name));
if let Some(first_tab) = top_level_tabs.first() &&
!some_active_tab
{
*self.active_tab.borrow_mut() = Some(first_tab.clone());
}
let reply = ListTabsReply {
from: self.name().into(),
tabs: top_level_tabs
.iter()
.map(|tab_name| registry.encode::<TabDescriptorActor, _>(tab_name))
.collect(),
};
request.reply_final(&reply)?
},
"listWorkers" => {
let reply = ListWorkersReply {
from: self.name().into(),
workers: self
.workers
.borrow()
.iter()
.map(|worker_name| registry.encode::<WorkerTargetActor, _>(worker_name))
.collect(),
};
request.reply_final(&reply)?
},
"protocolDescription" => {
let msg = ProtocolDescriptionReply {
from: self.name().into(),
types: Types {
performance: PerformanceActor::description(),
device: DeviceActor::description(),
},
};
request.reply_final(&msg)?
},
"watchResources" => {
request.reply_final(&EmptyReplyMsg {
from: self.name().into(),
})?
},
"unwatchResources" => {
request.reply_final(&EmptyReplyMsg {
from: self.name().into(),
})?
},
_ => return Err(ActorError::UnrecognizedPacketType),
};
Ok(())
}
}
impl RootActor {
pub fn register(registry: &mut ActorRegistry) {
let device_actor = DeviceActor::register(registry);
let performance_actor = PerformanceActor::register(registry);
let preference_actor = PreferenceActor::register(registry);
let process_actor = ProcessActor::register(registry);
let root_actor = Self {
name: "root".into(),
global_actors: GlobalActors {
device_actor: device_actor.name().into(),
perf_actor: performance_actor.name().into(),
preference_actor: preference_actor.name().into(),
},
process_name: process_actor.name().into(),
active_tab: AtomicRefCell::new(None),
tabs: AtomicRefCell::new(vec![]),
workers: AtomicRefCell::new(vec![]),
service_workers: AtomicRefCell::new(vec![]),
};
registry.register(root_actor);
}
fn get_tab_msg_by_browser_id(
&self,
registry: &ActorRegistry,
browser_id: u32,
) -> Option<TabDescriptorActorMsg> {
let tab_descriptor_actor = self
.tabs
.borrow()
.iter()
.map(|tab_descriptor_name| registry.find::<TabDescriptorActor>(tab_descriptor_name))
.find(|tab_descriptor_actor| {
let browsing_context = registry
.find::<BrowsingContextActor>(&tab_descriptor_actor.browsing_context_name);
browsing_context.browser_id.value() == browser_id
});
if let Some(tab_descriptor_actor) = tab_descriptor_actor {
*self.active_tab.borrow_mut() = Some(tab_descriptor_actor.name().to_owned());
Some(registry.encode::<TabDescriptorActor, _>(tab_descriptor_actor.name()))
} else {
None
}
}
pub fn active_tab(&self) -> Option<String> {
self.active_tab.borrow().clone()
}
}
impl ActorEncode<RootActorMsg> for RootActor {
fn encode(&self, _: &ActorRegistry) -> RootActorMsg {
RootActorMsg {
from: self.name().into(),
application_type: "browser".to_owned(),
traits: RootTraits {
sources: false,
highlightable: true,
custom_highlighters: true,
network_monitor: true,
resources: HashMap::from([("extensions-backgroundscript-status", true)]),
},
}
}
}