net/
storage_thread.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::ToOwned;
6use std::collections::{BTreeMap, HashMap};
7use std::path::PathBuf;
8use std::thread;
9
10use base::generic_channel::{self, GenericReceiver, GenericSender};
11use base::id::WebViewId;
12use malloc_size_of::MallocSizeOf;
13use net_traits::storage_thread::{StorageThreadMsg, StorageType};
14use profile_traits::mem::{
15    ProcessReports, ProfilerChan as MemProfilerChan, Report, ReportKind, perform_memory_report,
16};
17use profile_traits::path;
18use servo_url::ServoUrl;
19
20use crate::resource_thread;
21
22const QUOTA_SIZE_LIMIT: usize = 5 * 1024 * 1024;
23
24pub trait StorageThreadFactory {
25    fn new(config_dir: Option<PathBuf>, mem_profiler_chan: MemProfilerChan) -> Self;
26}
27
28impl StorageThreadFactory for GenericSender<StorageThreadMsg> {
29    /// Create a storage thread
30    fn new(
31        config_dir: Option<PathBuf>,
32        mem_profiler_chan: MemProfilerChan,
33    ) -> GenericSender<StorageThreadMsg> {
34        let (chan, port) = generic_channel::channel().unwrap();
35        let chan2 = chan.clone();
36        thread::Builder::new()
37            .name("StorageManager".to_owned())
38            .spawn(move || {
39                mem_profiler_chan.run_with_memory_reporting(
40                    || StorageManager::new(port, config_dir).start(),
41                    String::from("storage-reporter"),
42                    chan2,
43                    StorageThreadMsg::CollectMemoryReport,
44                );
45            })
46            .expect("Thread spawning failed");
47        chan
48    }
49}
50
51type OriginEntry = (usize, BTreeMap<String, String>);
52
53struct StorageManager {
54    port: GenericReceiver<StorageThreadMsg>,
55    session_data: HashMap<WebViewId, HashMap<String, OriginEntry>>,
56    local_data: HashMap<String, OriginEntry>,
57    config_dir: Option<PathBuf>,
58}
59
60impl StorageManager {
61    fn new(port: GenericReceiver<StorageThreadMsg>, config_dir: Option<PathBuf>) -> StorageManager {
62        let mut local_data = HashMap::new();
63        if let Some(ref config_dir) = config_dir {
64            resource_thread::read_json_from_file(&mut local_data, config_dir, "local_data.json");
65        }
66        StorageManager {
67            port,
68            session_data: HashMap::new(),
69            local_data,
70            config_dir,
71        }
72    }
73}
74
75impl StorageManager {
76    fn start(&mut self) {
77        loop {
78            match self.port.recv().unwrap() {
79                StorageThreadMsg::Length(sender, storage_type, webview_id, url) => {
80                    self.length(sender, storage_type, webview_id, url)
81                },
82                StorageThreadMsg::Key(sender, storage_type, webview_id, url, index) => {
83                    self.key(sender, storage_type, webview_id, url, index)
84                },
85                StorageThreadMsg::Keys(sender, storage_type, webview_id, url) => {
86                    self.keys(sender, storage_type, webview_id, url)
87                },
88                StorageThreadMsg::SetItem(sender, storage_type, webview_id, url, name, value) => {
89                    self.set_item(sender, storage_type, webview_id, url, name, value);
90                    self.save_state()
91                },
92                StorageThreadMsg::GetItem(sender, storage_type, webview_id, url, name) => {
93                    self.request_item(sender, storage_type, webview_id, url, name)
94                },
95                StorageThreadMsg::RemoveItem(sender, storage_type, webview_id, url, name) => {
96                    self.remove_item(sender, storage_type, webview_id, url, name);
97                    self.save_state()
98                },
99                StorageThreadMsg::Clear(sender, storage_type, webview_id, url) => {
100                    self.clear(sender, storage_type, webview_id, url);
101                    self.save_state()
102                },
103                StorageThreadMsg::Clone {
104                    sender,
105                    src: src_webview_id,
106                    dest: dest_webview_id,
107                } => {
108                    self.clone(src_webview_id, dest_webview_id);
109                    let _ = sender.send(());
110                },
111                StorageThreadMsg::CollectMemoryReport(sender) => {
112                    let reports = self.collect_memory_reports();
113                    sender.send(ProcessReports::new(reports));
114                },
115                StorageThreadMsg::Exit(sender) => {
116                    // Nothing to do since we save localstorage set eagerly.
117                    let _ = sender.send(());
118                    break;
119                },
120            }
121        }
122    }
123
124    fn collect_memory_reports(&self) -> Vec<Report> {
125        let mut reports = vec![];
126        perform_memory_report(|ops| {
127            reports.push(Report {
128                path: path!["storage", "local"],
129                kind: ReportKind::ExplicitJemallocHeapSize,
130                size: self.local_data.size_of(ops),
131            });
132
133            reports.push(Report {
134                path: path!["storage", "session"],
135                kind: ReportKind::ExplicitJemallocHeapSize,
136                size: self.session_data.size_of(ops),
137            });
138        });
139        reports
140    }
141
142    fn save_state(&self) {
143        if let Some(ref config_dir) = self.config_dir {
144            resource_thread::write_json_to_file(&self.local_data, config_dir, "local_data.json");
145        }
146    }
147
148    fn select_data(
149        &self,
150        storage_type: StorageType,
151        webview_id: WebViewId,
152        origin: &str,
153    ) -> Option<&OriginEntry> {
154        match storage_type {
155            StorageType::Session => self
156                .session_data
157                .get(&webview_id)
158                .and_then(|origin_map| origin_map.get(origin)),
159            StorageType::Local => self.local_data.get(origin),
160        }
161    }
162
163    fn select_data_mut(
164        &mut self,
165        storage_type: StorageType,
166        webview_id: WebViewId,
167        origin: &str,
168    ) -> Option<&mut OriginEntry> {
169        match storage_type {
170            StorageType::Session => self
171                .session_data
172                .get_mut(&webview_id)
173                .and_then(|origin_map| origin_map.get_mut(origin)),
174            StorageType::Local => self.local_data.get_mut(origin),
175        }
176    }
177
178    fn ensure_data_mut(
179        &mut self,
180        storage_type: StorageType,
181        webview_id: WebViewId,
182        origin: &str,
183    ) -> &mut OriginEntry {
184        match storage_type {
185            StorageType::Session => self
186                .session_data
187                .entry(webview_id)
188                .or_default()
189                .entry(origin.to_string())
190                .or_default(),
191            StorageType::Local => self.local_data.entry(origin.to_string()).or_default(),
192        }
193    }
194
195    fn length(
196        &self,
197        sender: GenericSender<usize>,
198        storage_type: StorageType,
199        webview_id: WebViewId,
200        url: ServoUrl,
201    ) {
202        let origin = self.origin_as_string(url);
203        let data = self.select_data(storage_type, webview_id, &origin);
204        sender
205            .send(data.map_or(0, |(_, entry)| entry.len()))
206            .unwrap();
207    }
208
209    fn key(
210        &self,
211        sender: GenericSender<Option<String>>,
212        storage_type: StorageType,
213        webview_id: WebViewId,
214        url: ServoUrl,
215        index: u32,
216    ) {
217        let origin = self.origin_as_string(url);
218        let data = self.select_data(storage_type, webview_id, &origin);
219        let key = data
220            .and_then(|(_, entry)| entry.keys().nth(index as usize))
221            .cloned();
222        sender.send(key).unwrap();
223    }
224
225    fn keys(
226        &self,
227        sender: GenericSender<Vec<String>>,
228        storage_type: StorageType,
229        webview_id: WebViewId,
230        url: ServoUrl,
231    ) {
232        let origin = self.origin_as_string(url);
233        let data = self.select_data(storage_type, webview_id, &origin);
234        let keys = data.map_or(vec![], |(_, entry)| entry.keys().cloned().collect());
235
236        sender.send(keys).unwrap();
237    }
238
239    /// Sends Ok(changed, Some(old_value)) in case there was a previous
240    /// value with the same key name but with different value name
241    /// otherwise sends Err(()) to indicate that the operation would result in
242    /// exceeding the quota limit
243    fn set_item(
244        &mut self,
245        sender: GenericSender<Result<(bool, Option<String>), ()>>,
246        storage_type: StorageType,
247        webview_id: WebViewId,
248        url: ServoUrl,
249        name: String,
250        value: String,
251    ) {
252        let origin = self.origin_as_string(url);
253
254        let (this_storage_size, other_storage_size) = {
255            let local_data = self.select_data(StorageType::Local, webview_id, &origin);
256            let session_data = self.select_data(StorageType::Session, webview_id, &origin);
257            let local_data_size = local_data.map_or(0, |&(total, _)| total);
258            let session_data_size = session_data.map_or(0, |&(total, _)| total);
259            match storage_type {
260                StorageType::Local => (local_data_size, session_data_size),
261                StorageType::Session => (session_data_size, local_data_size),
262            }
263        };
264
265        let &mut (ref mut total, ref mut entry) =
266            self.ensure_data_mut(storage_type, webview_id, &origin);
267
268        let mut new_total_size = this_storage_size + value.len();
269        if let Some(old_value) = entry.get(&name) {
270            new_total_size -= old_value.len();
271        } else {
272            new_total_size += name.len();
273        }
274
275        let message = if (new_total_size + other_storage_size) > QUOTA_SIZE_LIMIT {
276            Err(())
277        } else {
278            *total = new_total_size;
279            entry
280                .insert(name.clone(), value.clone())
281                .map_or(Ok((true, None)), |old| {
282                    if old == value {
283                        Ok((false, None))
284                    } else {
285                        Ok((true, Some(old)))
286                    }
287                })
288        };
289        sender.send(message).unwrap();
290    }
291
292    fn request_item(
293        &self,
294        sender: GenericSender<Option<String>>,
295        storage_type: StorageType,
296        webview_id: WebViewId,
297        url: ServoUrl,
298        name: String,
299    ) {
300        let origin = self.origin_as_string(url);
301        let data = self.select_data(storage_type, webview_id, &origin);
302        sender
303            .send(data.and_then(|(_, entry)| entry.get(&name)).cloned())
304            .unwrap();
305    }
306
307    /// Sends Some(old_value) in case there was a previous value with the key name, otherwise sends None
308    fn remove_item(
309        &mut self,
310        sender: GenericSender<Option<String>>,
311        storage_type: StorageType,
312        webview_id: WebViewId,
313        url: ServoUrl,
314        name: String,
315    ) {
316        let origin = self.origin_as_string(url);
317        let data = self.select_data_mut(storage_type, webview_id, &origin);
318        let old_value = data.and_then(|&mut (ref mut total, ref mut entry)| {
319            entry.remove(&name).inspect(|old| {
320                *total -= name.len() + old.len();
321            })
322        });
323        sender.send(old_value).unwrap();
324    }
325
326    fn clear(
327        &mut self,
328        sender: GenericSender<bool>,
329        storage_type: StorageType,
330        webview_id: WebViewId,
331        url: ServoUrl,
332    ) {
333        let origin = self.origin_as_string(url);
334        let data = self.select_data_mut(storage_type, webview_id, &origin);
335        sender
336            .send(data.is_some_and(|&mut (ref mut total, ref mut entry)| {
337                if !entry.is_empty() {
338                    entry.clear();
339                    *total = 0;
340                    true
341                } else {
342                    false
343                }
344            }))
345            .unwrap();
346    }
347
348    fn clone(&mut self, src_webview_id: WebViewId, dest_webview_id: WebViewId) {
349        let Some(src_origin_entries) = self.session_data.get(&src_webview_id) else {
350            return;
351        };
352
353        let dest_origin_entries = src_origin_entries.clone();
354        self.session_data
355            .insert(dest_webview_id, dest_origin_entries);
356    }
357
358    fn origin_as_string(&self, url: ServoUrl) -> String {
359        url.origin().ascii_serialization()
360    }
361}