use bitflags::bitflags;
use cookie::Cookie;
use log::warn;
use net_traits::pub_domains::registered_domain_name;
use net_traits::{CookieSource, ResourceThreads, SiteDescriptor};
use rustc_hash::FxHashMap;
use servo_url::ServoUrl;
use storage_traits::StorageThreads;
use storage_traits::webstorage_thread::{OriginDescriptor, WebStorageType};
use url::Url;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct StorageType: u8 {
const Cookies = 1 << 0;
const Local = 1 << 1;
const Session = 1 << 2;
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SiteData {
name: String,
storage_types: StorageType,
}
impl SiteData {
pub fn new(name: impl Into<String>, storage_types: StorageType) -> SiteData {
SiteData {
name: name.into(),
storage_types,
}
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn storage_types(&self) -> StorageType {
self.storage_types
}
}
pub struct SiteDataManager {
public_resource_threads: ResourceThreads,
private_resource_threads: ResourceThreads,
public_storage_threads: StorageThreads,
private_storage_threads: StorageThreads,
}
impl SiteDataManager {
pub(crate) fn new(
public_resource_threads: ResourceThreads,
private_resource_threads: ResourceThreads,
public_storage_threads: StorageThreads,
private_storage_threads: StorageThreads,
) -> Self {
Self {
public_resource_threads,
private_resource_threads,
public_storage_threads,
private_storage_threads,
}
}
pub fn site_data(&self, storage_types: StorageType) -> Vec<SiteData> {
let mut all_sites: FxHashMap<String, StorageType> = FxHashMap::default();
let mut add_sites = |sites: Vec<SiteDescriptor>, storage_type: StorageType| {
for site in sites {
all_sites
.entry(site.name)
.and_modify(|storage_types| *storage_types |= storage_type)
.or_insert(storage_type);
}
};
if storage_types.contains(StorageType::Cookies) {
let public_cookies = self.public_resource_threads.cookies();
add_sites(public_cookies, StorageType::Cookies);
let private_cookies = self.private_resource_threads.cookies();
add_sites(private_cookies, StorageType::Cookies);
}
let mut add_origins = |origins: Vec<OriginDescriptor>, storage_type: StorageType| {
for origin in origins {
let url =
ServoUrl::parse(&origin.name).expect("Should always be able to parse origins.");
let Some(domain) = registered_domain_name(&url) else {
warn!("Failed to get a registered domain name for: {url}.");
continue;
};
let domain = domain.to_string();
all_sites
.entry(domain)
.and_modify(|storage_types| *storage_types |= storage_type)
.or_insert(storage_type);
}
};
if storage_types.contains(StorageType::Local) {
let public_origins = self
.public_storage_threads
.webstorage_origins(WebStorageType::Local);
add_origins(public_origins, StorageType::Local);
let private_origins = self
.private_storage_threads
.webstorage_origins(WebStorageType::Local);
add_origins(private_origins, StorageType::Local);
}
if storage_types.contains(StorageType::Session) {
let public_origins = self
.public_storage_threads
.webstorage_origins(WebStorageType::Session);
add_origins(public_origins, StorageType::Session);
let private_origins = self
.private_storage_threads
.webstorage_origins(WebStorageType::Session);
add_origins(private_origins, StorageType::Session);
}
let mut result: Vec<SiteData> = all_sites
.into_iter()
.map(|(name, storage_types)| SiteData::new(name, storage_types))
.collect();
result.sort_by_key(SiteData::name);
result
}
pub fn clear_site_data(&self, sites: &[&str], storage_types: StorageType) {
if storage_types.contains(StorageType::Cookies) {
self.public_resource_threads.clear_cookies_for_sites(sites);
self.private_resource_threads.clear_cookies_for_sites(sites);
}
if storage_types.contains(StorageType::Local) {
self.public_storage_threads
.clear_webstorage_for_sites(WebStorageType::Local, sites);
self.private_storage_threads
.clear_webstorage_for_sites(WebStorageType::Local, sites);
}
if storage_types.contains(StorageType::Session) {
self.public_storage_threads
.clear_webstorage_for_sites(WebStorageType::Session, sites);
self.private_storage_threads
.clear_webstorage_for_sites(WebStorageType::Session, sites);
}
}
pub fn clear_cookies(&self) {
self.public_resource_threads.clear_cookies();
self.private_resource_threads.clear_cookies();
}
pub fn cookies_for_url(&self, url: Url, source: CookieSource) -> Vec<Cookie<'static>> {
self.public_resource_threads
.cookies_for_url(url.into(), source)
}
pub fn set_cookie_for_url(&self, url: Url, cookie: Cookie<'static>) {
self.public_resource_threads.set_cookie_for_url_sync(
url.into(),
cookie,
CookieSource::HTTP,
);
}
}