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