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 http://mozilla.org/MPL/2.0/. */
4 
5 extern crate crossbeam_utils;
6 #[macro_use]
7 extern crate cstr;
8 extern crate libc;
9 #[macro_use]
10 extern crate log;
11 extern crate moz_task;
12 extern crate nserror;
13 extern crate nsstring;
14 extern crate once_cell;
15 extern crate rkv;
16 extern crate serde_json;
17 extern crate tempfile;
18 extern crate thiserror;
19 #[macro_use]
20 extern crate xpcom;
21 
22 mod error;
23 mod ffi;
24 mod iter;
25 mod persist;
26 mod statics;
27 
28 use crate::{
29     error::{XULStoreError, XULStoreResult},
30     iter::XULStoreIterator,
31     persist::{flush_writes, persist},
32     statics::DATA_CACHE,
33 };
34 use nsstring::nsAString;
35 use std::collections::btree_map::Entry;
36 use std::fmt::Display;
37 
38 const SEPARATOR: char = '\u{0009}';
39 
make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String40 pub(crate) fn make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String {
41     format!("{}{}{}{}{}", doc, SEPARATOR, id, SEPARATOR, attr)
42 }
43 
set_value( doc: &nsAString, id: &nsAString, attr: &nsAString, value: &nsAString, ) -> XULStoreResult<()>44 pub(crate) fn set_value(
45     doc: &nsAString,
46     id: &nsAString,
47     attr: &nsAString,
48     value: &nsAString,
49 ) -> XULStoreResult<()> {
50     debug!("XULStore set value: {} {} {} {}", doc, id, attr, value);
51 
52     // bug 319846 -- don't save really long attributes or values.
53     if id.len() > 512 || attr.len() > 512 {
54         return Err(XULStoreError::IdAttrNameTooLong);
55     }
56 
57     let value = if value.len() > 4096 {
58         warn!("XULStore: truncating long attribute value");
59         String::from_utf16(&value[0..4096])?
60     } else {
61         String::from_utf16(value)?
62     };
63 
64     let mut cache_guard = DATA_CACHE.lock()?;
65     let data = match cache_guard.as_mut() {
66         Some(data) => data,
67         None => return Ok(()),
68     };
69     data.entry(doc.to_string())
70         .or_default()
71         .entry(id.to_string())
72         .or_default()
73         .insert(attr.to_string(), value.clone());
74 
75     persist(make_key(doc, id, attr), Some(value))?;
76 
77     Ok(())
78 }
79 
has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool>80 pub(crate) fn has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool> {
81     debug!("XULStore has value: {} {} {}", doc, id, attr);
82 
83     let cache_guard = DATA_CACHE.lock()?;
84     let data = match cache_guard.as_ref() {
85         Some(data) => data,
86         None => return Ok(false),
87     };
88 
89     match data.get(&doc.to_string()) {
90         Some(ids) => match ids.get(&id.to_string()) {
91             Some(attrs) => Ok(attrs.contains_key(&attr.to_string())),
92             None => Ok(false),
93         },
94         None => Ok(false),
95     }
96 }
97 
get_value( doc: &nsAString, id: &nsAString, attr: &nsAString, ) -> XULStoreResult<String>98 pub(crate) fn get_value(
99     doc: &nsAString,
100     id: &nsAString,
101     attr: &nsAString,
102 ) -> XULStoreResult<String> {
103     debug!("XULStore get value {} {} {}", doc, id, attr);
104 
105     let cache_guard = DATA_CACHE.lock()?;
106     let data = match cache_guard.as_ref() {
107         Some(data) => data,
108         None => return Ok(String::new()),
109     };
110 
111     match data.get(&doc.to_string()) {
112         Some(ids) => match ids.get(&id.to_string()) {
113             Some(attrs) => match attrs.get(&attr.to_string()) {
114                 Some(value) => Ok(value.clone()),
115                 None => Ok(String::new()),
116             },
117             None => Ok(String::new()),
118         },
119         None => Ok(String::new()),
120     }
121 }
122 
remove_value( doc: &nsAString, id: &nsAString, attr: &nsAString, ) -> XULStoreResult<()>123 pub(crate) fn remove_value(
124     doc: &nsAString,
125     id: &nsAString,
126     attr: &nsAString,
127 ) -> XULStoreResult<()> {
128     debug!("XULStore remove value {} {} {}", doc, id, attr);
129 
130     let mut cache_guard = DATA_CACHE.lock()?;
131     let data = match cache_guard.as_mut() {
132         Some(data) => data,
133         None => return Ok(()),
134     };
135 
136     let mut ids_empty = false;
137     if let Some(ids) = data.get_mut(&doc.to_string()) {
138         let mut attrs_empty = false;
139         if let Some(attrs) = ids.get_mut(&id.to_string()) {
140             attrs.remove(&attr.to_string());
141             if attrs.is_empty() {
142                 attrs_empty = true;
143             }
144         }
145         if attrs_empty {
146             ids.remove(&id.to_string());
147             if ids.is_empty() {
148                 ids_empty = true;
149             }
150         }
151     };
152     if ids_empty {
153         data.remove(&doc.to_string());
154     }
155 
156     persist(make_key(doc, id, attr), None)?;
157 
158     Ok(())
159 }
160 
remove_document(doc: &nsAString) -> XULStoreResult<()>161 pub(crate) fn remove_document(doc: &nsAString) -> XULStoreResult<()> {
162     debug!("XULStore remove document {}", doc);
163 
164     let mut cache_guard = DATA_CACHE.lock()?;
165     let data = match cache_guard.as_mut() {
166         Some(data) => data,
167         None => return Ok(()),
168     };
169 
170     if let Entry::Occupied(entry) = data.entry(doc.to_string()) {
171         for (id, attrs) in entry.get() {
172             for attr in attrs.keys() {
173                 persist(make_key(entry.key(), id, attr), None)?;
174             }
175         }
176         entry.remove_entry();
177     }
178 
179     Ok(())
180 }
181 
get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator>182 pub(crate) fn get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator> {
183     debug!("XULStore get IDs for {}", doc);
184 
185     let cache_guard = DATA_CACHE.lock()?;
186     let data = match cache_guard.as_ref() {
187         Some(data) => data,
188         None => return Ok(XULStoreIterator::new(vec![].into_iter())),
189     };
190 
191     match data.get(&doc.to_string()) {
192         Some(ids) => {
193             let ids: Vec<String> = ids.keys().cloned().collect();
194             Ok(XULStoreIterator::new(ids.into_iter()))
195         }
196         None => Ok(XULStoreIterator::new(vec![].into_iter())),
197     }
198 }
199 
get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator>200 pub(crate) fn get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator> {
201     debug!("XULStore get attrs for doc, ID: {} {}", doc, id);
202 
203     let cache_guard = DATA_CACHE.lock()?;
204     let data = match cache_guard.as_ref() {
205         Some(data) => data,
206         None => return Ok(XULStoreIterator::new(vec![].into_iter())),
207     };
208 
209     match data.get(&doc.to_string()) {
210         Some(ids) => match ids.get(&id.to_string()) {
211             Some(attrs) => {
212                 let attrs: Vec<String> = attrs.keys().cloned().collect();
213                 Ok(XULStoreIterator::new(attrs.into_iter()))
214             }
215             None => Ok(XULStoreIterator::new(vec![].into_iter())),
216         },
217         None => Ok(XULStoreIterator::new(vec![].into_iter())),
218     }
219 }
220 
shutdown() -> XULStoreResult<()>221 pub(crate) fn shutdown() -> XULStoreResult<()> {
222     flush_writes()
223 }
224