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 DoneAfterResponses {
81 remaining_responses: u8,
82 callback: Box<dyn FnOnce()>,
83 },
84}
85
86pub struct SiteDataManager {
99 public_resource_threads: ResourceThreads,
100 private_resource_threads: ResourceThreads,
101 public_storage_threads: StorageThreads,
102 private_storage_threads: StorageThreads,
103 next_cookie_op_id: Cell<u64>,
104 pending_cookie_callbacks: RefCell<FxHashMap<CookieOperationId, CookieOperationCallback>>,
105}
106
107impl SiteDataManager {
108 pub(crate) fn new(
109 public_resource_threads: ResourceThreads,
110 private_resource_threads: ResourceThreads,
111 public_storage_threads: StorageThreads,
112 private_storage_threads: StorageThreads,
113 ) -> Self {
114 Self {
115 public_resource_threads,
116 private_resource_threads,
117 public_storage_threads,
118 private_storage_threads,
119 next_cookie_op_id: Cell::new(0),
120 pending_cookie_callbacks: RefCell::new(FxHashMap::default()),
121 }
122 }
123
124 pub fn site_data(&self, storage_types: StorageType) -> Vec<SiteData> {
135 let mut all_sites: FxHashMap<String, StorageType> = FxHashMap::default();
136
137 let mut add_sites = |sites: Vec<SiteDescriptor>, storage_type: StorageType| {
138 for site in sites {
139 all_sites
140 .entry(site.name)
141 .and_modify(|storage_types| *storage_types |= storage_type)
142 .or_insert(storage_type);
143 }
144 };
145
146 if storage_types.contains(StorageType::Cookies) {
147 let public_cookies = self.public_resource_threads.cookies();
148 add_sites(public_cookies, StorageType::Cookies);
149
150 let private_cookies = self.private_resource_threads.cookies();
151 add_sites(private_cookies, StorageType::Cookies);
152 }
153
154 let mut add_origins = |origins: Vec<OriginDescriptor>, storage_type: StorageType| {
155 for origin in origins {
156 let url =
157 ServoUrl::parse(&origin.name).expect("Should always be able to parse origins.");
158
159 let Some(domain) = registered_domain_name(&url) else {
160 warn!("Failed to get a registered domain name for: {url}.");
161 continue;
162 };
163 let domain = domain.to_string();
164
165 all_sites
166 .entry(domain)
167 .and_modify(|storage_types| *storage_types |= storage_type)
168 .or_insert(storage_type);
169 }
170 };
171
172 if storage_types.contains(StorageType::Local) {
173 let public_origins = self
174 .public_storage_threads
175 .webstorage_origins(WebStorageType::Local);
176 add_origins(public_origins, StorageType::Local);
177
178 let private_origins = self
179 .private_storage_threads
180 .webstorage_origins(WebStorageType::Local);
181 add_origins(private_origins, StorageType::Local);
182 }
183
184 if storage_types.contains(StorageType::Session) {
185 let public_origins = self
186 .public_storage_threads
187 .webstorage_origins(WebStorageType::Session);
188 add_origins(public_origins, StorageType::Session);
189
190 let private_origins = self
191 .private_storage_threads
192 .webstorage_origins(WebStorageType::Session);
193 add_origins(private_origins, StorageType::Session);
194 }
195
196 let mut result: Vec<SiteData> = all_sites
197 .into_iter()
198 .map(|(name, storage_types)| SiteData::new(name, storage_types))
199 .collect();
200
201 result.sort_by_key(SiteData::name);
202
203 result
204 }
205
206 pub fn clear_site_data(&self, sites: &[&str], storage_types: StorageType) {
211 if storage_types.contains(StorageType::Cookies) {
212 self.public_resource_threads.clear_cookies_for_sites(sites);
213 self.private_resource_threads.clear_cookies_for_sites(sites);
214 }
215
216 if storage_types.contains(StorageType::Local) {
217 self.public_storage_threads
218 .clear_webstorage_for_sites(WebStorageType::Local, sites);
219 self.private_storage_threads
220 .clear_webstorage_for_sites(WebStorageType::Local, sites);
221 }
222
223 if storage_types.contains(StorageType::Session) {
224 self.public_storage_threads
225 .clear_webstorage_for_sites(WebStorageType::Session, sites);
226 self.private_storage_threads
227 .clear_webstorage_for_sites(WebStorageType::Session, sites);
228 }
229 }
230
231 pub fn clear_cookies(&self, callback: Option<Box<dyn FnOnce()>>) {
235 match callback {
236 None => {
237 self.public_resource_threads.clear_cookies();
238 self.private_resource_threads.clear_cookies();
239 },
240 Some(callback) => {
241 let id = self.next_operation_id();
242 self.pending_cookie_callbacks.borrow_mut().insert(
243 id,
244 CookieOperationCallback::DoneAfterResponses {
245 remaining_responses: 2,
246 callback,
247 },
248 );
249 self.public_resource_threads.clear_cookies_async(id);
250 self.private_resource_threads.clear_cookies_async(id);
251 },
252 }
253 }
254
255 pub fn clear_session_cookies(&self, callback: Option<Box<dyn FnOnce()>>) {
260 match callback {
261 None => {
262 self.public_resource_threads.clear_session_cookies();
263 self.private_resource_threads.clear_session_cookies();
264 },
265 Some(callback) => {
266 let id = self.next_operation_id();
267 self.pending_cookie_callbacks.borrow_mut().insert(
268 id,
269 CookieOperationCallback::DoneAfterResponses {
270 remaining_responses: 2,
271 callback,
272 },
273 );
274 self.public_resource_threads.clear_session_cookies_async(id);
275 self.private_resource_threads
276 .clear_session_cookies_async(id);
277 },
278 }
279 }
280
281 pub fn cookies_for_url(&self, url: Url, source: CookieSource) -> Vec<Cookie<'static>> {
283 self.public_resource_threads
284 .cookies_for_url(url.into(), source)
285 }
286
287 pub fn cookies_for_url_async(
289 &self,
290 url: Url,
291 source: CookieSource,
292 callback: impl FnOnce(Vec<Cookie<'static>>) + 'static,
293 ) {
294 let id = self.next_operation_id();
295 self.pending_cookie_callbacks
296 .borrow_mut()
297 .insert(id, CookieOperationCallback::Cookies(Box::new(callback)));
298 self.public_resource_threads
299 .cookies_for_url_async(id, url.into(), source);
300 }
301
302 pub fn set_cookie_for_url(
306 &self,
307 url: Url,
308 cookie: Cookie<'static>,
309 callback: Option<Box<dyn FnOnce()>>,
310 ) {
311 match callback {
312 None => {
313 self.public_resource_threads.set_cookie_for_url_sync(
314 url.into(),
315 cookie,
316 CookieSource::HTTP,
317 );
318 },
319 Some(callback) => {
320 let id = self.next_operation_id();
321 self.pending_cookie_callbacks
322 .borrow_mut()
323 .insert(id, CookieOperationCallback::Done(callback));
324 self.public_resource_threads.set_cookie_for_url_async(
325 id,
326 url.into(),
327 cookie,
328 CookieSource::HTTP,
329 );
330 },
331 }
332 }
333
334 pub(crate) fn handle_cookie_response(
338 &self,
339 id: CookieOperationId,
340 response: CookieOperationResponse,
341 ) {
342 let Some(callback) = self.pending_cookie_callbacks.borrow_mut().remove(&id) else {
343 warn!("Received cookie response for unknown operation {id:?}");
344 return;
345 };
346 match (response, callback) {
347 (CookieOperationResponse::Cookies(cookies), CookieOperationCallback::Cookies(cb)) => {
348 cb(cookies);
349 },
350 (CookieOperationResponse::Done, CookieOperationCallback::Done(cb)) => {
351 cb();
352 },
353 (
354 CookieOperationResponse::Done,
355 CookieOperationCallback::DoneAfterResponses {
356 remaining_responses,
357 callback,
358 },
359 ) => {
360 if remaining_responses > 1 {
361 self.pending_cookie_callbacks.borrow_mut().insert(
362 id,
363 CookieOperationCallback::DoneAfterResponses {
364 remaining_responses: remaining_responses - 1,
365 callback,
366 },
367 );
368 } else {
369 callback();
370 }
371 },
372 _ => {
373 warn!("Cookie response type mismatch for operation {id:?}");
374 },
375 }
376 }
377
378 fn next_operation_id(&self) -> CookieOperationId {
379 let id = CookieOperationId(self.next_cookie_op_id.get());
380 self.next_cookie_op_id.set(id.0 + 1);
381 id
382 }
383}