|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "base/basictypes.h" |
|
8 |
|
9 #include "IDBKeyRange.h" |
|
10 |
|
11 #include "nsIXPConnect.h" |
|
12 |
|
13 #include "nsJSUtils.h" |
|
14 #include "nsThreadUtils.h" |
|
15 #include "nsContentUtils.h" |
|
16 #include "nsDOMClassInfoID.h" |
|
17 #include "Key.h" |
|
18 |
|
19 #include "mozilla/dom/IDBKeyRangeBinding.h" |
|
20 #include "mozilla/dom/indexedDB/PIndexedDBIndex.h" |
|
21 #include "mozilla/dom/indexedDB/PIndexedDBObjectStore.h" |
|
22 |
|
23 using namespace mozilla; |
|
24 using namespace mozilla::dom; |
|
25 USING_INDEXEDDB_NAMESPACE |
|
26 using namespace mozilla::dom::indexedDB::ipc; |
|
27 |
|
28 namespace { |
|
29 |
|
30 inline nsresult |
|
31 GetKeyFromJSVal(JSContext* aCx, |
|
32 JS::Handle<JS::Value> aVal, |
|
33 Key& aKey, |
|
34 bool aAllowUnset = false) |
|
35 { |
|
36 nsresult rv = aKey.SetFromJSVal(aCx, aVal); |
|
37 if (NS_FAILED(rv)) { |
|
38 NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB, |
|
39 "Bad error code!"); |
|
40 return rv; |
|
41 } |
|
42 |
|
43 if (aKey.IsUnset() && !aAllowUnset) { |
|
44 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
|
45 } |
|
46 |
|
47 return NS_OK; |
|
48 } |
|
49 |
|
50 } // anonymous namespace |
|
51 |
|
52 // static |
|
53 nsresult |
|
54 IDBKeyRange::FromJSVal(JSContext* aCx, |
|
55 JS::Handle<JS::Value> aVal, |
|
56 IDBKeyRange** aKeyRange) |
|
57 { |
|
58 nsRefPtr<IDBKeyRange> keyRange; |
|
59 |
|
60 if (aVal.isNullOrUndefined()) { |
|
61 // undefined and null returns no IDBKeyRange. |
|
62 keyRange.forget(aKeyRange); |
|
63 return NS_OK; |
|
64 } |
|
65 |
|
66 JS::Rooted<JSObject*> obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr); |
|
67 if (aVal.isPrimitive() || JS_IsArrayObject(aCx, obj) || |
|
68 JS_ObjectIsDate(aCx, obj)) { |
|
69 // A valid key returns an 'only' IDBKeyRange. |
|
70 keyRange = new IDBKeyRange(nullptr, false, false, true); |
|
71 |
|
72 nsresult rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower()); |
|
73 if (NS_FAILED(rv)) { |
|
74 return rv; |
|
75 } |
|
76 } |
|
77 else { |
|
78 MOZ_ASSERT(aVal.isObject()); |
|
79 // An object is not permitted unless it's another IDBKeyRange. |
|
80 if (NS_FAILED(UNWRAP_OBJECT(IDBKeyRange, obj, keyRange))) { |
|
81 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
|
82 } |
|
83 } |
|
84 |
|
85 keyRange.forget(aKeyRange); |
|
86 return NS_OK; |
|
87 } |
|
88 |
|
89 // static |
|
90 template <class T> |
|
91 already_AddRefed<IDBKeyRange> |
|
92 IDBKeyRange::FromSerializedKeyRange(const T& aKeyRange) |
|
93 { |
|
94 nsRefPtr<IDBKeyRange> keyRange = |
|
95 new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(), |
|
96 aKeyRange.isOnly()); |
|
97 keyRange->Lower() = aKeyRange.lower(); |
|
98 if (!keyRange->IsOnly()) { |
|
99 keyRange->Upper() = aKeyRange.upper(); |
|
100 } |
|
101 return keyRange.forget(); |
|
102 } |
|
103 |
|
104 template <class T> |
|
105 void |
|
106 IDBKeyRange::ToSerializedKeyRange(T& aKeyRange) |
|
107 { |
|
108 aKeyRange.lowerOpen() = IsLowerOpen(); |
|
109 aKeyRange.upperOpen() = IsUpperOpen(); |
|
110 aKeyRange.isOnly() = IsOnly(); |
|
111 |
|
112 aKeyRange.lower() = Lower(); |
|
113 if (!IsOnly()) { |
|
114 aKeyRange.upper() = Upper(); |
|
115 } |
|
116 } |
|
117 |
|
118 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange) |
|
119 |
|
120 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange) |
|
121 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) |
|
122 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
123 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
124 |
|
125 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange) |
|
126 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedLowerVal) |
|
127 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedUpperVal) |
|
128 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
129 |
|
130 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange) |
|
131 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) |
|
132 tmp->DropJSObjects(); |
|
133 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
134 |
|
135 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange) |
|
136 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
137 NS_INTERFACE_MAP_END |
|
138 |
|
139 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange) |
|
140 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange) |
|
141 |
|
142 void |
|
143 IDBKeyRange::DropJSObjects() |
|
144 { |
|
145 if (!mRooted) { |
|
146 return; |
|
147 } |
|
148 mCachedLowerVal = JS::UndefinedValue(); |
|
149 mCachedUpperVal = JS::UndefinedValue(); |
|
150 mHaveCachedLowerVal = false; |
|
151 mHaveCachedUpperVal = false; |
|
152 mRooted = false; |
|
153 mozilla::DropJSObjects(this); |
|
154 } |
|
155 |
|
156 IDBKeyRange::~IDBKeyRange() |
|
157 { |
|
158 DropJSObjects(); |
|
159 } |
|
160 |
|
161 JSObject* |
|
162 IDBKeyRange::WrapObject(JSContext* aCx) |
|
163 { |
|
164 return IDBKeyRangeBinding::Wrap(aCx, this); |
|
165 } |
|
166 |
|
167 void |
|
168 IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, |
|
169 ErrorResult& aRv) |
|
170 { |
|
171 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
172 |
|
173 if (!mHaveCachedLowerVal) { |
|
174 if (!mRooted) { |
|
175 mozilla::HoldJSObjects(this); |
|
176 mRooted = true; |
|
177 } |
|
178 |
|
179 aRv = Lower().ToJSVal(aCx, mCachedLowerVal); |
|
180 if (aRv.Failed()) { |
|
181 return; |
|
182 } |
|
183 |
|
184 mHaveCachedLowerVal = true; |
|
185 } |
|
186 |
|
187 JS::ExposeValueToActiveJS(mCachedLowerVal); |
|
188 aResult.set(mCachedLowerVal); |
|
189 } |
|
190 |
|
191 void |
|
192 IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, |
|
193 ErrorResult& aRv) |
|
194 { |
|
195 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
196 |
|
197 if (!mHaveCachedUpperVal) { |
|
198 if (!mRooted) { |
|
199 mozilla::HoldJSObjects(this); |
|
200 mRooted = true; |
|
201 } |
|
202 |
|
203 aRv = Upper().ToJSVal(aCx, mCachedUpperVal); |
|
204 if (aRv.Failed()) { |
|
205 return; |
|
206 } |
|
207 |
|
208 mHaveCachedUpperVal = true; |
|
209 } |
|
210 |
|
211 JS::ExposeValueToActiveJS(mCachedUpperVal); |
|
212 aResult.set(mCachedUpperVal); |
|
213 } |
|
214 |
|
215 // static |
|
216 already_AddRefed<IDBKeyRange> |
|
217 IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx, |
|
218 JS::Handle<JS::Value> aValue, ErrorResult& aRv) |
|
219 { |
|
220 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
221 |
|
222 nsRefPtr<IDBKeyRange> keyRange = |
|
223 new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true); |
|
224 |
|
225 aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower()); |
|
226 if (aRv.Failed()) { |
|
227 return nullptr; |
|
228 } |
|
229 |
|
230 return keyRange.forget(); |
|
231 } |
|
232 |
|
233 // static |
|
234 already_AddRefed<IDBKeyRange> |
|
235 IDBKeyRange::LowerBound(const GlobalObject& aGlobal, JSContext* aCx, |
|
236 JS::Handle<JS::Value> aValue, bool aOpen, |
|
237 ErrorResult& aRv) |
|
238 { |
|
239 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
240 |
|
241 nsRefPtr<IDBKeyRange> keyRange = |
|
242 new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false); |
|
243 |
|
244 aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower()); |
|
245 if (aRv.Failed()) { |
|
246 return nullptr; |
|
247 } |
|
248 |
|
249 return keyRange.forget(); |
|
250 } |
|
251 |
|
252 // static |
|
253 already_AddRefed<IDBKeyRange> |
|
254 IDBKeyRange::UpperBound(const GlobalObject& aGlobal, JSContext* aCx, |
|
255 JS::Handle<JS::Value> aValue, bool aOpen, |
|
256 ErrorResult& aRv) |
|
257 { |
|
258 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
259 |
|
260 nsRefPtr<IDBKeyRange> keyRange = |
|
261 new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false); |
|
262 |
|
263 aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Upper()); |
|
264 if (aRv.Failed()) { |
|
265 return nullptr; |
|
266 } |
|
267 |
|
268 return keyRange.forget(); |
|
269 } |
|
270 |
|
271 // static |
|
272 already_AddRefed<IDBKeyRange> |
|
273 IDBKeyRange::Bound(const GlobalObject& aGlobal, JSContext* aCx, |
|
274 JS::Handle<JS::Value> aLower, JS::Handle<JS::Value> aUpper, |
|
275 bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv) |
|
276 { |
|
277 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
278 |
|
279 nsRefPtr<IDBKeyRange> keyRange = |
|
280 new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false); |
|
281 |
|
282 aRv = GetKeyFromJSVal(aCx, aLower, keyRange->Lower()); |
|
283 if (aRv.Failed()) { |
|
284 return nullptr; |
|
285 } |
|
286 |
|
287 aRv = GetKeyFromJSVal(aCx, aUpper, keyRange->Upper()); |
|
288 if (aRv.Failed()) { |
|
289 return nullptr; |
|
290 } |
|
291 |
|
292 if (keyRange->Lower() > keyRange->Upper() || |
|
293 (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen))) { |
|
294 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); |
|
295 return nullptr; |
|
296 } |
|
297 |
|
298 return keyRange.forget(); |
|
299 } |
|
300 |
|
301 // Explicitly instantiate for all our key range types... Grumble. |
|
302 template already_AddRefed<IDBKeyRange> |
|
303 IDBKeyRange::FromSerializedKeyRange<KeyRange> (const KeyRange& aKeyRange); |
|
304 |
|
305 template void |
|
306 IDBKeyRange::ToSerializedKeyRange<KeyRange> (KeyRange& aKeyRange); |