1use std::cell::{Cell, RefCell};
6
7use bitflags::bitflags;
8use cookie::Cookie;
9use log::warn;
10use net_traits::pub_domains::registered_domain_name;
11use net_traits::{CookieOperationId, ResourceThreads, SiteDescriptor};
12use rustc_hash::FxHashMap;
13use servo_url::ServoUrl;
14use storage_traits::StorageThreads;
15use storage_traits::webstorage_thread::{OriginDescriptor, WebStorageType};
16use url::Url;
17
18use crate::CookieSource;
19
20bitflags! {
21 #[derive(Clone, Copy, Debug, PartialEq)]
29 pub struct StorageType: u8 {
30 const Cookies = 1 << 0;
33
34 const Local = 1 << 1;
37
38 const Session = 1 << 2;
41 }
42}
43
44#[derive(Clone, Debug, PartialEq)]
45pub struct SiteData {
46 name: String,
47 storage_types: StorageType,
48}
49
50impl SiteData {
51 pub fn new(name: impl Into<String>, storage_types: StorageType) -> SiteData {
52 SiteData {
53 name: name.into(),
54 storage_types,
55 }
56 }
57
58 pub fn name(&self) -> String {
59 self.name.clone()
60 }
61
62 pub fn storage_types(&self) -> StorageType {
63 self.storage_types
64 }
65}
66
67pub(crate) enum CookieOperationResponse {
69 Cookies(Vec<Cookie<'static>>),
71 Done,
73}
74
75enum CookieOperationCallback {
78 Cookies(Box<dyn FnOnce(Vec<Cookie<'static>>)>),
79 Done(Box<dyn FnOnce()>),
80}
81
82pub struct SiteDataManager {
95 public_resource_threads: ResourceThreads,
96 private_resource_threads: ResourceThreads,
97 public_storage_threads: StorageThreads,
98 private_storage_threads: StorageThreads,
99 next_cookie_op_id: Cell<u64>,
100 pending_cookie_callbacks: RefCell<FxHashMap<CookieOperationId, CookieOperationCallback>>,
101}
102
103impl SiteDataManager {
104 pub(crate) fn new(
105 public_resource_threads: ResourceThreads,
106 private_resource_threads: ResourceThreads,
107 public_storage_threads: StorageThreads,
108 private_storage_threads: StorageThreads,
109 ) -> Self {
110 Self {
111 public_resource_threads,
112 private_resource_threads,
113 public_storage_threads,
114 private_storage_threads,
115 next_cookie_op_id: Cell::new(0),
116 pending_cookie_callbacks: RefCell::new(FxHashMap::default()),
117 }
118 }
119
120 pub fn site_data(&self, storage_types: StorageType) -> Vec<SiteData> {
131 let mut all_sites: FxHashMap<String, StorageType> = FxHashMap::default();
132
133 let mut add_sites = |sites: Vec<SiteDescriptor>, storage_type: StorageType| {
134 for site in sites {
135 all_sites
136 .entry(site.name)
137 .and_modify(|storage_types| *storage_types |= storage_type)
138 .or_insert(storage_type);
139 }
140 };
141
142 if storage_types.contains(StorageType::Cookies) {
143 let public_cookies = self.public_resource_threads.cookies();
144 add_sites(public_cookies, StorageType::Cookies);
145
146 let private_cookies = self.private_resource_threads.cookies();
147 add_sites(private_cookies, StorageType::Cookies);
148 }
149
150 let mut add_origins = |origins: Vec<OriginDescriptor>, storage_type: StorageType| {
151 for origin in origins {
152 let url =
153 ServoUrl::parse(&origin.name).expect("Should always be able to parse origins.");
154
155 let Some(domain) = registered_domain_name(&url) else {
156 warn!("Failed to get a registered domain name for: {url}.");
157 continue;
158 };
159 let domain = domain.to_string();
160
161 all_sites
162 .entry(domain)
163 .and_modify(|storage_types| *storage_types |= storage_type)
164 .or_insert(storage_type);
165 }
166 };
167
168 if storage_types.contains(StorageType::Local) {
169 let public_origins = self
170 .public_storage_threads
171 .webstorage_origins(WebStorageType::Local);
172 add_origins(public_origins, StorageType::Local);
173
174 let private_origins = self
175 .private_storage_threads
176 .webstorage_origins(WebStorageType::Local);
177 add_origins(private_origins, StorageType::Local);
178 }
179
180 if storage_types.contains(StorageType::Session) {
181 let public_origins = self
182 .public_storage_threads
183 .webstorage_origins(WebStorageType::Session);
184 add_origins(public_origins, StorageType::Session);
185
186 let private_origins = self
187 .private_storage_threads
188 .webstorage_origins(WebStorageType::Session);
189 add_origins(private_origins, StorageType::Session);
190 }
191
192 let mut result: Vec<SiteData> = all_sites
193 .into_iter()
194 .map(|(name, storage_types)| SiteData::new(name, storage_types))
195 .collect();
196
197 result.sort_by_key(SiteData::name);
198
199 result
200 }
201
202 pub fn clear_site_data(&self, sites: &[&str], storage_types: StorageType) {
207 if storage_types.contains(StorageType::Cookies) {
208 self.public_resource_threads.clear_cookies_for_sites(sites);
209 self.private_resource_threads.clear_cookies_for_sites(sites);
210 }
211
212 if storage_types.contains(StorageType::Local) {
213 self.public_storage_threads
214 .clear_webstorage_for_sites(WebStorageType::Local, sites);
215 self.private_storage_threads
216 .clear_webstorage_for_sites(WebStorageType::Local, sites);
217 }
218
219 if storage_types.contains(StorageType::Session) {
220 self.public_storage_threads
221 .clear_webstorage_for_sites(WebStorageType::Session, sites);
222 self.private_storage_threads
223 .clear_webstorage_for_sites(WebStorageType::Session, sites);
224 }
225 }
226
227 pub fn clear_cookies(&self) {
228 self.public_resource_threads.clear_cookies();
229 self.private_resource_threads.clear_cookies();
230 }
231
232 pub fn clear_session_cookies(&self) {
235 self.public_resource_threads.clear_session_cookies();
236 self.private_resource_threads.clear_session_cookies();
237 }
238
239 pub fn cookies_for_url(&self, url: Url, source: CookieSource) -> Vec<Cookie<'static>> {
241 self.public_resource_threads
242 .cookies_for_url(url.into(), source)
243 }
244
245 pub fn set_cookie_for_url(&self, url: Url, cookie: Cookie<'static>) {
251 self.public_resource_threads.set_cookie_for_url_sync(
252 url.into(),
253 cookie,
254 CookieSource::HTTP,
255 );
256 }
257
258 pub fn cookies_for_url_async(
260 &self,
261 url: Url,
262 source: CookieSource,
263 callback: impl FnOnce(Vec<Cookie<'static>>) + 'static,
264 ) {
265 let id = self.next_operation_id();
266 self.pending_cookie_callbacks
267 .borrow_mut()
268 .insert(id, CookieOperationCallback::Cookies(Box::new(callback)));
269 self.public_resource_threads
270 .cookies_for_url_async(id, url.into(), source);
271 }
272
273 pub fn set_cookie_for_url_async(
275 &self,
276 url: Url,
277 cookie: Cookie<'static>,
278 callback: impl FnOnce() + 'static,
279 ) {
280 let id = self.next_operation_id();
281 self.pending_cookie_callbacks
282 .borrow_mut()
283 .insert(id, CookieOperationCallback::Done(Box::new(callback)));
284 self.public_resource_threads.set_cookie_for_url_async(
285 id,
286 url.into(),
287 cookie,
288 CookieSource::HTTP,
289 );
290 }
291
292 pub(crate) fn handle_cookie_response(
296 &self,
297 id: CookieOperationId,
298 response: CookieOperationResponse,
299 ) {
300 let Some(callback) = self.pending_cookie_callbacks.borrow_mut().remove(&id) else {
301 warn!("Received cookie response for unknown operation {id:?}");
302 return;
303 };
304 match (callback, response) {
305 (CookieOperationCallback::Cookies(cb), CookieOperationResponse::Cookies(cookies)) => {
306 cb(cookies);
307 },
308 (CookieOperationCallback::Done(cb), CookieOperationResponse::Done) => {
309 cb();
310 },
311 _ => {
312 warn!("Cookie response type mismatch for operation {id:?}");
313 },
314 }
315 }
316
317 fn next_operation_id(&self) -> CookieOperationId {
318 let id = CookieOperationId(self.next_cookie_op_id.get());
319 self.next_cookie_op_id.set(id.0 + 1);
320 id
321 }
322}