1mod engines;
6
7use std::borrow::ToOwned;
8use std::collections::BTreeMap;
9use std::path::PathBuf;
10use std::sync::Arc;
11use std::thread;
12
13use base::generic_channel::{self, GenericReceiver, GenericSender};
14use base::id::WebViewId;
15use base::threadpool::ThreadPool;
16use malloc_size_of::MallocSizeOf;
17use malloc_size_of_derive::MallocSizeOf;
18use profile_traits::mem::{
19 ProcessReports, ProfilerChan as MemProfilerChan, Report, ReportKind, perform_memory_report,
20};
21use profile_traits::path;
22use rustc_hash::FxHashMap;
23use servo_config::pref;
24use servo_url::{ImmutableOrigin, ServoUrl};
25use storage_traits::webstorage_thread::{StorageType, WebStorageThreadMsg};
26use uuid::Uuid;
27
28use crate::webstorage::engines::WebStorageEngine;
29use crate::webstorage::engines::sqlite::SqliteEngine;
30
31const QUOTA_SIZE_LIMIT: usize = 5 * 1024 * 1024;
32
33pub trait WebStorageThreadFactory {
34 fn new(config_dir: Option<PathBuf>, mem_profiler_chan: MemProfilerChan) -> Self;
35}
36
37impl WebStorageThreadFactory for GenericSender<WebStorageThreadMsg> {
38 fn new(
40 config_dir: Option<PathBuf>,
41 mem_profiler_chan: MemProfilerChan,
42 ) -> GenericSender<WebStorageThreadMsg> {
43 let (chan, port) = generic_channel::channel().unwrap();
44 let chan2 = chan.clone();
45 thread::Builder::new()
46 .name("WebStorageManager".to_owned())
47 .spawn(move || {
48 mem_profiler_chan.run_with_memory_reporting(
49 || WebStorageManager::new(port, config_dir).start(),
50 String::from("storage-reporter"),
51 chan2,
52 WebStorageThreadMsg::CollectMemoryReport,
53 );
54 })
55 .expect("Thread spawning failed");
56 chan
57 }
58}
59
60#[derive(Clone, Default, MallocSizeOf)]
61pub struct OriginEntry {
62 tree: BTreeMap<String, String>,
63 size: usize,
64}
65
66impl OriginEntry {
67 pub fn inner(&self) -> &BTreeMap<String, String> {
68 &self.tree
69 }
70
71 pub fn insert(&mut self, key: String, value: String) -> Option<String> {
72 let old_value = self.tree.insert(key.clone(), value.clone());
73 let size_change = match &old_value {
74 Some(old) => value.len() as isize - old.len() as isize,
75 None => (key.len() + value.len()) as isize,
76 };
77 self.size = (self.size as isize + size_change) as usize;
78 old_value
79 }
80
81 pub fn remove(&mut self, key: &str) -> Option<String> {
82 let old_value = self.tree.remove(key);
83 if let Some(old) = &old_value {
84 self.size -= key.len() + old.len();
85 }
86 old_value
87 }
88
89 pub fn clear(&mut self) {
90 self.tree.clear();
91 self.size = 0;
92 }
93
94 pub fn size(&self) -> usize {
95 self.size
96 }
97}
98
99struct WebStorageEnvironment<E: WebStorageEngine> {
100 engine: E,
101 data: OriginEntry,
102}
103
104impl<E: WebStorageEngine> MallocSizeOf for WebStorageEnvironment<E> {
105 fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
106 self.data.size_of(ops)
107 }
108}
109
110impl<E: WebStorageEngine> WebStorageEnvironment<E> {
111 fn new(engine: E) -> Self {
112 WebStorageEnvironment {
113 data: engine.load().unwrap_or_default(),
114 engine,
115 }
116 }
117
118 fn clear(&mut self) {
119 self.data.clear();
120 let _ = self.engine.clear();
121 }
122
123 fn delete(&mut self, key: &str) {
124 let _ = self.engine.delete(key);
125 }
126
127 fn set(&mut self, key: &str, value: &str) {
128 let _ = self.engine.set(key, value);
129 }
130}
131
132impl<E: WebStorageEngine> Drop for WebStorageEnvironment<E> {
133 fn drop(&mut self) {
134 self.engine.save(&self.data);
135 }
136}
137
138struct WebStorageManager {
139 port: GenericReceiver<WebStorageThreadMsg>,
140 session_data: FxHashMap<WebViewId, FxHashMap<ImmutableOrigin, OriginEntry>>,
141 config_dir: Option<PathBuf>,
142 thread_pool: Arc<ThreadPool>,
143 environments: FxHashMap<ImmutableOrigin, WebStorageEnvironment<SqliteEngine>>,
144}
145
146impl WebStorageManager {
147 fn new(
148 port: GenericReceiver<WebStorageThreadMsg>,
149 config_dir: Option<PathBuf>,
150 ) -> WebStorageManager {
151 let thread_count = thread::available_parallelism()
155 .map(|i| i.get())
156 .unwrap_or(pref!(threadpools_fallback_worker_num) as usize)
157 .min(pref!(threadpools_webstorage_workers_max).max(1) as usize);
158 WebStorageManager {
159 port,
160 session_data: FxHashMap::default(),
161 config_dir,
162 thread_pool: Arc::new(ThreadPool::new(thread_count, "WebStorage".to_string())),
163 environments: FxHashMap::default(),
164 }
165 }
166}
167
168impl WebStorageManager {
169 fn start(&mut self) {
170 loop {
171 match self.port.recv().unwrap() {
172 WebStorageThreadMsg::Length(sender, storage_type, webview_id, url) => {
173 self.length(sender, storage_type, webview_id, url)
174 },
175 WebStorageThreadMsg::Key(sender, storage_type, webview_id, url, index) => {
176 self.key(sender, storage_type, webview_id, url, index)
177 },
178 WebStorageThreadMsg::Keys(sender, storage_type, webview_id, url) => {
179 self.keys(sender, storage_type, webview_id, url)
180 },
181 WebStorageThreadMsg::SetItem(
182 sender,
183 storage_type,
184 webview_id,
185 url,
186 name,
187 value,
188 ) => {
189 self.set_item(sender, storage_type, webview_id, url, name, value);
190 },
191 WebStorageThreadMsg::GetItem(sender, storage_type, webview_id, url, name) => {
192 self.request_item(sender, storage_type, webview_id, url, name)
193 },
194 WebStorageThreadMsg::RemoveItem(sender, storage_type, webview_id, url, name) => {
195 self.remove_item(sender, storage_type, webview_id, url, name);
196 },
197 WebStorageThreadMsg::Clear(sender, storage_type, webview_id, url) => {
198 self.clear(sender, storage_type, webview_id, url);
199 },
200 WebStorageThreadMsg::Clone {
201 sender,
202 src: src_webview_id,
203 dest: dest_webview_id,
204 } => {
205 self.clone(src_webview_id, dest_webview_id);
206 let _ = sender.send(());
207 },
208 WebStorageThreadMsg::CollectMemoryReport(sender) => {
209 let reports = self.collect_memory_reports();
210 sender.send(ProcessReports::new(reports));
211 },
212 WebStorageThreadMsg::Exit(sender) => {
213 let _ = sender.send(());
215 break;
216 },
217 }
218 }
219 }
220
221 fn collect_memory_reports(&self) -> Vec<Report> {
222 let mut reports = vec![];
223 perform_memory_report(|ops| {
224 reports.push(Report {
225 path: path!["storage", "local"],
226 kind: ReportKind::ExplicitJemallocHeapSize,
227 size: self.environments.size_of(ops),
228 });
229
230 reports.push(Report {
231 path: path!["storage", "session"],
232 kind: ReportKind::ExplicitJemallocHeapSize,
233 size: self.session_data.size_of(ops),
234 });
235 });
236 reports
237 }
238
239 fn get_origin_location(&self, origin: &ImmutableOrigin) -> Option<PathBuf> {
240 match &self.config_dir {
241 Some(config_dir) => {
242 const NAMESPACE_SERVO_WEBSTORAGE: &uuid::Uuid = &Uuid::from_bytes([
243 0x37, 0x9e, 0x56, 0xb0, 0x1a, 0x76, 0x44, 0xc5, 0xa4, 0xdb, 0xe2, 0x18, 0xc5,
244 0xc8, 0xa3, 0x5d,
245 ]);
246 let origin_uuid = Uuid::new_v5(
247 NAMESPACE_SERVO_WEBSTORAGE,
248 origin.ascii_serialization().as_bytes(),
249 );
250 Some(config_dir.join("webstorage").join(origin_uuid.to_string()))
251 },
252 None => None,
253 }
254 }
255
256 fn get_environment(
257 &mut self,
258 origin: &ImmutableOrigin,
259 ) -> &WebStorageEnvironment<SqliteEngine> {
260 if self.environments.contains_key(origin) {
261 return self.environments.get(origin).unwrap();
262 }
263
264 let origin_location = self.get_origin_location(origin);
265
266 let engine = SqliteEngine::new(&origin_location, self.thread_pool.clone()).unwrap();
267 let environment = WebStorageEnvironment::new(engine);
268 self.environments.insert(origin.clone(), environment);
269 self.environments.get(origin).unwrap()
270 }
271
272 fn get_environment_mut(
273 &mut self,
274 origin: &ImmutableOrigin,
275 ) -> &mut WebStorageEnvironment<SqliteEngine> {
276 if self.environments.contains_key(origin) {
277 return self.environments.get_mut(origin).unwrap();
278 }
279
280 let origin_location = self.get_origin_location(origin);
281
282 let engine = SqliteEngine::new(&origin_location, self.thread_pool.clone()).unwrap();
283 let environment = WebStorageEnvironment::new(engine);
284 self.environments.insert(origin.clone(), environment);
285 self.environments.get_mut(origin).unwrap()
286 }
287
288 fn select_data(
289 &mut self,
290 storage_type: StorageType,
291 webview_id: WebViewId,
292 origin: ImmutableOrigin,
293 ) -> Option<&OriginEntry> {
294 match storage_type {
295 StorageType::Session => self
296 .session_data
297 .get(&webview_id)
298 .and_then(|origin_map| origin_map.get(&origin)),
299 StorageType::Local => Some(&self.get_environment(&origin).data),
300 }
301 }
302
303 fn select_data_mut(
304 &mut self,
305 storage_type: StorageType,
306 webview_id: WebViewId,
307 origin: ImmutableOrigin,
308 ) -> Option<&mut OriginEntry> {
309 match storage_type {
310 StorageType::Session => self
311 .session_data
312 .get_mut(&webview_id)
313 .and_then(|origin_map| origin_map.get_mut(&origin)),
314 StorageType::Local => Some(&mut self.get_environment_mut(&origin).data),
315 }
316 }
317
318 fn ensure_data_mut(
319 &mut self,
320 storage_type: StorageType,
321 webview_id: WebViewId,
322 origin: ImmutableOrigin,
323 ) -> &mut OriginEntry {
324 match storage_type {
325 StorageType::Session => self
326 .session_data
327 .entry(webview_id)
328 .or_default()
329 .entry(origin)
330 .or_default(),
331 StorageType::Local => &mut self.get_environment_mut(&origin).data,
332 }
333 }
334
335 fn length(
336 &mut self,
337 sender: GenericSender<usize>,
338 storage_type: StorageType,
339 webview_id: WebViewId,
340 url: ServoUrl,
341 ) {
342 let data = self.select_data(storage_type, webview_id, url.origin());
343 sender
344 .send(data.map_or(0, |entry| entry.inner().len()))
345 .unwrap();
346 }
347
348 fn key(
349 &mut self,
350 sender: GenericSender<Option<String>>,
351 storage_type: StorageType,
352 webview_id: WebViewId,
353 url: ServoUrl,
354 index: u32,
355 ) {
356 let data = self.select_data(storage_type, webview_id, url.origin());
357 let key = data
358 .and_then(|entry| entry.inner().keys().nth(index as usize))
359 .cloned();
360 sender.send(key).unwrap();
361 }
362
363 fn keys(
364 &mut self,
365 sender: GenericSender<Vec<String>>,
366 storage_type: StorageType,
367 webview_id: WebViewId,
368 url: ServoUrl,
369 ) {
370 let data = self.select_data(storage_type, webview_id, url.origin());
371 let keys = data.map_or(vec![], |entry| entry.inner().keys().cloned().collect());
372
373 sender.send(keys).unwrap();
374 }
375
376 fn set_item(
381 &mut self,
382 sender: GenericSender<Result<(bool, Option<String>), ()>>,
383 storage_type: StorageType,
384 webview_id: WebViewId,
385 url: ServoUrl,
386 name: String,
387 value: String,
388 ) {
389 let (this_storage_size, other_storage_size) = {
390 let local_data = self.select_data(StorageType::Local, webview_id, url.origin());
391 let local_data_size = local_data.map_or(0, OriginEntry::size);
392 let session_data = self.select_data(StorageType::Session, webview_id, url.origin());
393 let session_data_size = session_data.map_or(0, OriginEntry::size);
394 match storage_type {
395 StorageType::Local => (local_data_size, session_data_size),
396 StorageType::Session => (session_data_size, local_data_size),
397 }
398 };
399
400 let entry = self.ensure_data_mut(storage_type, webview_id, url.origin());
401
402 let mut new_total_size = this_storage_size + value.len();
403 if let Some(old_value) = entry.inner().get(&name) {
404 new_total_size -= old_value.len();
405 } else {
406 new_total_size += name.len();
407 }
408
409 let message = if (new_total_size + other_storage_size) > QUOTA_SIZE_LIMIT {
410 Err(())
411 } else {
412 let result =
413 entry
414 .insert(name.clone(), value.clone())
415 .map_or(Ok((true, None)), |old| {
416 if old == value {
417 Ok((false, None))
418 } else {
419 Ok((true, Some(old)))
420 }
421 });
422 let env = self.get_environment_mut(&url.origin());
423 env.set(&name, &value);
424 result
425 };
426 sender.send(message).unwrap();
427 }
428
429 fn request_item(
430 &mut self,
431 sender: GenericSender<Option<String>>,
432 storage_type: StorageType,
433 webview_id: WebViewId,
434 url: ServoUrl,
435 name: String,
436 ) {
437 let data = self.select_data(storage_type, webview_id, url.origin());
438 sender
439 .send(data.and_then(|entry| entry.inner().get(&name)).cloned())
440 .unwrap();
441 }
442
443 fn remove_item(
445 &mut self,
446 sender: GenericSender<Option<String>>,
447 storage_type: StorageType,
448 webview_id: WebViewId,
449 url: ServoUrl,
450 name: String,
451 ) {
452 let data = self.select_data_mut(storage_type, webview_id, url.origin());
453 let old_value = data.and_then(|entry| entry.remove(&name));
454 sender.send(old_value).unwrap();
455 let env = self.get_environment_mut(&url.origin());
456 env.delete(&name);
457 }
458
459 fn clear(
460 &mut self,
461 sender: GenericSender<bool>,
462 storage_type: StorageType,
463 webview_id: WebViewId,
464 url: ServoUrl,
465 ) {
466 let data = self.select_data_mut(storage_type, webview_id, url.origin());
467 sender
468 .send(data.is_some_and(|entry| {
469 if !entry.inner().is_empty() {
470 entry.clear();
471 true
472 } else {
473 false
474 }
475 }))
476 .unwrap();
477 let env = self.get_environment_mut(&url.origin());
478 env.clear();
479 }
480
481 fn clone(&mut self, src_webview_id: WebViewId, dest_webview_id: WebViewId) {
482 let Some(src_origin_entries) = self.session_data.get(&src_webview_id) else {
483 return;
484 };
485
486 let dest_origin_entries = src_origin_entries.clone();
487 self.session_data
488 .insert(dest_webview_id, dest_origin_entries);
489 }
490}