servo/
site_data_manager.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 bitflags::bitflags;
6use log::warn;
7use net_traits::pub_domains::registered_domain_name;
8use net_traits::{ResourceThreads, SiteDescriptor};
9use rustc_hash::FxHashMap;
10use servo_url::ServoUrl;
11use storage_traits::StorageThreads;
12use storage_traits::webstorage_thread::{OriginDescriptor, WebStorageType};
13
14bitflags! {
15    /// Identifies categories of site data associated with a site.
16    ///
17    /// This type is used by `SiteDataManager` to query, describe, and manage
18    /// different kinds of data stored by the user agent for a given site.
19    ///
20    /// Additional storage categories (e.g. IndexedDB) may be added in the
21    /// future.
22    #[derive(Clone, Copy, Debug, PartialEq)]
23    pub struct StorageType: u8 {
24        /// Corresponds to the HTTP cookies:
25        /// <https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies>
26        const Cookies = 1 << 0;
27
28        /// Corresponds to the `localStorage` Web API:
29        /// <https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage>
30        const Local   = 1 << 1;
31
32        /// Corresponds to the `sessionStorage` Web API:
33        /// <https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage>
34        const Session = 1 << 2;
35    }
36}
37
38#[derive(Clone, Debug, PartialEq)]
39pub struct SiteData {
40    name: String,
41    storage_types: StorageType,
42}
43
44impl SiteData {
45    pub fn new(name: impl Into<String>, storage_types: StorageType) -> SiteData {
46        SiteData {
47            name: name.into(),
48            storage_types,
49        }
50    }
51
52    pub fn name(&self) -> String {
53        self.name.clone()
54    }
55
56    pub fn storage_types(&self) -> StorageType {
57        self.storage_types
58    }
59}
60
61/// Provides APIs for inspecting and managing site data.
62///
63/// `SiteDataManager` exposes information about data that is conceptually
64/// associated with a site (equivalent to an eTLD+1), such as web exposed
65/// storage mechanisms like `localStorage` and `sessionStorage`.
66///
67/// The manager can be used by embedders to list sites with stored data.
68/// Support for site scoped management operations (e.g. clearing data for a
69/// specific site) will be added in the future.
70///
71/// Note: Network layer state (such as the HTTP cache) is intentionally not
72/// handled by `SiteDataManager`. That functionality lives in `NetworkManager`.
73pub struct SiteDataManager {
74    public_resource_threads: ResourceThreads,
75    private_resource_threads: ResourceThreads,
76    public_storage_threads: StorageThreads,
77    private_storage_threads: StorageThreads,
78}
79
80impl SiteDataManager {
81    pub(crate) fn new(
82        public_resource_threads: ResourceThreads,
83        private_resource_threads: ResourceThreads,
84        public_storage_threads: StorageThreads,
85        private_storage_threads: StorageThreads,
86    ) -> Self {
87        Self {
88            public_resource_threads,
89            private_resource_threads,
90            public_storage_threads,
91            private_storage_threads,
92        }
93    }
94
95    /// Return a list of sites that have associated site data.
96    ///
97    /// The returned list is filtered by the provided `storage_types` bitflags.
98    /// Each [`SiteData`] entry represents a site (equivalent to an eTLD+1)
99    /// and indicates which kinds of storage data are present for it (e.g.
100    /// localStorage, sessionStorage).
101    ///
102    /// The returned list is sorted by site name.
103    ///
104    /// Both public and private storage are included in the result.
105    pub fn site_data(&self, storage_types: StorageType) -> Vec<SiteData> {
106        let mut all_sites: FxHashMap<String, StorageType> = FxHashMap::default();
107
108        let mut add_sites = |sites: Vec<SiteDescriptor>, storage_type: StorageType| {
109            for site in sites {
110                all_sites
111                    .entry(site.name)
112                    .and_modify(|storage_types| *storage_types |= storage_type)
113                    .or_insert(storage_type);
114            }
115        };
116
117        if storage_types.contains(StorageType::Cookies) {
118            let public_cookies = self.public_resource_threads.cookies();
119            add_sites(public_cookies, StorageType::Cookies);
120
121            let private_cookies = self.private_resource_threads.cookies();
122            add_sites(private_cookies, StorageType::Cookies);
123        }
124
125        let mut add_origins = |origins: Vec<OriginDescriptor>, storage_type: StorageType| {
126            for origin in origins {
127                let url =
128                    ServoUrl::parse(&origin.name).expect("Should always be able to parse origins.");
129
130                let Some(domain) = registered_domain_name(&url) else {
131                    warn!("Failed to get a registered domain name for: {url}.");
132                    continue;
133                };
134                let domain = domain.to_string();
135
136                all_sites
137                    .entry(domain)
138                    .and_modify(|storage_types| *storage_types |= storage_type)
139                    .or_insert(storage_type);
140            }
141        };
142
143        if storage_types.contains(StorageType::Local) {
144            let public_origins = self
145                .public_storage_threads
146                .webstorage_origins(WebStorageType::Local);
147            add_origins(public_origins, StorageType::Local);
148
149            let private_origins = self
150                .private_storage_threads
151                .webstorage_origins(WebStorageType::Local);
152            add_origins(private_origins, StorageType::Local);
153        }
154
155        if storage_types.contains(StorageType::Session) {
156            let public_origins = self
157                .public_storage_threads
158                .webstorage_origins(WebStorageType::Session);
159            add_origins(public_origins, StorageType::Session);
160
161            let private_origins = self
162                .private_storage_threads
163                .webstorage_origins(WebStorageType::Session);
164            add_origins(private_origins, StorageType::Session);
165        }
166
167        let mut result: Vec<SiteData> = all_sites
168            .into_iter()
169            .map(|(name, storage_types)| SiteData::new(name, storage_types))
170            .collect();
171
172        result.sort_by_key(SiteData::name);
173
174        result
175    }
176
177    /// Clear site data for the given sites.
178    ///
179    /// The clearing is restricted to the provided `storage_types` bitflags.
180    /// Both public and private browsing data are affected.
181    pub fn clear_site_data(&self, sites: &[&str], storage_types: StorageType) {
182        if storage_types.contains(StorageType::Cookies) {
183            self.public_resource_threads.clear_cookies_for_sites(sites);
184            self.private_resource_threads.clear_cookies_for_sites(sites);
185        }
186
187        if storage_types.contains(StorageType::Local) {
188            self.public_storage_threads
189                .clear_webstorage_for_sites(WebStorageType::Local, sites);
190            self.private_storage_threads
191                .clear_webstorage_for_sites(WebStorageType::Local, sites);
192        }
193
194        if storage_types.contains(StorageType::Session) {
195            self.public_storage_threads
196                .clear_webstorage_for_sites(WebStorageType::Session, sites);
197            self.private_storage_threads
198                .clear_webstorage_for_sites(WebStorageType::Session, sites);
199        }
200    }
201
202    pub fn clear_cookies(&self) {
203        self.public_resource_threads.clear_cookies();
204        self.private_resource_threads.clear_cookies();
205    }
206}