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