Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=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 "IDBKeyRange.h"
8 :
9 : #include "Key.h"
10 : #include "mozilla/ErrorResult.h"
11 : #include "mozilla/dom/BindingUtils.h"
12 : #include "mozilla/dom/IDBKeyRangeBinding.h"
13 : #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
14 :
15 : namespace mozilla {
16 : namespace dom {
17 :
18 : using namespace mozilla::dom::indexedDB;
19 :
20 : namespace {
21 :
22 : nsresult
23 0 : GetKeyFromJSVal(JSContext* aCx,
24 : JS::Handle<JS::Value> aVal,
25 : Key& aKey)
26 : {
27 0 : nsresult rv = aKey.SetFromJSVal(aCx, aVal);
28 0 : if (NS_FAILED(rv)) {
29 0 : MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
30 0 : return rv;
31 : }
32 :
33 0 : if (aKey.IsUnset()) {
34 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
35 : }
36 :
37 0 : return NS_OK;
38 : }
39 :
40 : } // namespace
41 :
42 0 : IDBKeyRange::IDBKeyRange(nsISupports* aGlobal,
43 : bool aLowerOpen,
44 : bool aUpperOpen,
45 0 : bool aIsOnly)
46 : : mGlobal(aGlobal)
47 0 : , mCachedLowerVal(JS::UndefinedValue())
48 0 : , mCachedUpperVal(JS::UndefinedValue())
49 : , mLowerOpen(aLowerOpen)
50 : , mUpperOpen(aUpperOpen)
51 : , mIsOnly(aIsOnly)
52 : , mHaveCachedLowerVal(false)
53 : , mHaveCachedUpperVal(false)
54 0 : , mRooted(false)
55 : {
56 0 : AssertIsOnOwningThread();
57 0 : }
58 :
59 0 : IDBKeyRange::~IDBKeyRange()
60 : {
61 0 : DropJSObjects();
62 0 : }
63 :
64 0 : IDBLocaleAwareKeyRange::IDBLocaleAwareKeyRange(nsISupports* aGlobal,
65 : bool aLowerOpen,
66 : bool aUpperOpen,
67 0 : bool aIsOnly)
68 0 : : IDBKeyRange(aGlobal, aLowerOpen, aUpperOpen, aIsOnly)
69 : {
70 0 : AssertIsOnOwningThread();
71 0 : }
72 :
73 0 : IDBLocaleAwareKeyRange::~IDBLocaleAwareKeyRange()
74 : {
75 0 : DropJSObjects();
76 0 : }
77 :
78 : // static
79 : nsresult
80 0 : IDBKeyRange::FromJSVal(JSContext* aCx,
81 : JS::Handle<JS::Value> aVal,
82 : IDBKeyRange** aKeyRange)
83 : {
84 0 : MOZ_ASSERT_IF(!aCx, aVal.isUndefined());
85 :
86 0 : RefPtr<IDBKeyRange> keyRange;
87 :
88 0 : if (aVal.isNullOrUndefined()) {
89 : // undefined and null returns no IDBKeyRange.
90 0 : keyRange.forget(aKeyRange);
91 0 : return NS_OK;
92 : }
93 :
94 0 : JS::Rooted<JSObject*> obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr);
95 0 : bool isValidKey = aVal.isPrimitive();
96 0 : if (!isValidKey) {
97 : js::ESClass cls;
98 0 : if (!js::GetBuiltinClass(aCx, obj, &cls)) {
99 0 : return NS_ERROR_UNEXPECTED;
100 : }
101 0 : isValidKey = cls == js::ESClass::Array || cls == js::ESClass::Date;
102 : }
103 0 : if (isValidKey) {
104 : // A valid key returns an 'only' IDBKeyRange.
105 0 : keyRange = new IDBKeyRange(nullptr, false, false, true);
106 :
107 0 : nsresult rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
108 0 : if (NS_FAILED(rv)) {
109 0 : return rv;
110 : }
111 : }
112 : else {
113 0 : MOZ_ASSERT(aVal.isObject());
114 : // An object is not permitted unless it's another IDBKeyRange.
115 0 : if (NS_FAILED(UNWRAP_OBJECT(IDBKeyRange, obj, keyRange))) {
116 0 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
117 : }
118 : }
119 :
120 0 : keyRange.forget(aKeyRange);
121 0 : return NS_OK;
122 : }
123 :
124 : // static
125 : already_AddRefed<IDBKeyRange>
126 0 : IDBKeyRange::FromSerialized(const SerializedKeyRange& aKeyRange)
127 : {
128 : RefPtr<IDBKeyRange> keyRange =
129 0 : new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(),
130 0 : aKeyRange.isOnly());
131 0 : keyRange->Lower() = aKeyRange.lower();
132 0 : if (!keyRange->IsOnly()) {
133 0 : keyRange->Upper() = aKeyRange.upper();
134 : }
135 0 : return keyRange.forget();
136 : }
137 :
138 : void
139 0 : IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const
140 : {
141 0 : aKeyRange.lowerOpen() = LowerOpen();
142 0 : aKeyRange.upperOpen() = UpperOpen();
143 0 : aKeyRange.isOnly() = IsOnly();
144 :
145 0 : aKeyRange.lower() = Lower();
146 0 : if (!IsOnly()) {
147 0 : aKeyRange.upper() = Upper();
148 : }
149 0 : }
150 :
151 : void
152 0 : IDBKeyRange::GetBindingClause(const nsACString& aKeyColumnName,
153 : nsACString& _retval) const
154 : {
155 0 : NS_NAMED_LITERAL_CSTRING(andStr, " AND ");
156 0 : NS_NAMED_LITERAL_CSTRING(spacecolon, " :");
157 0 : NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
158 :
159 0 : if (IsOnly()) {
160 : // Both keys are set and they're equal.
161 0 : _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") +
162 0 : spacecolon + lowerKey;
163 0 : return;
164 : }
165 :
166 0 : nsAutoCString clause;
167 :
168 0 : if (!Lower().IsUnset()) {
169 : // Lower key is set.
170 0 : clause.Append(andStr + aKeyColumnName);
171 0 : clause.AppendLiteral(" >");
172 0 : if (!LowerOpen()) {
173 0 : clause.Append('=');
174 : }
175 0 : clause.Append(spacecolon + lowerKey);
176 : }
177 :
178 0 : if (!Upper().IsUnset()) {
179 : // Upper key is set.
180 0 : clause.Append(andStr + aKeyColumnName);
181 0 : clause.AppendLiteral(" <");
182 0 : if (!UpperOpen()) {
183 0 : clause.Append('=');
184 : }
185 0 : clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key"));
186 : }
187 :
188 0 : _retval = clause;
189 : }
190 :
191 : nsresult
192 0 : IDBKeyRange::BindToStatement(mozIStorageStatement* aStatement) const
193 : {
194 0 : MOZ_ASSERT(aStatement);
195 :
196 0 : NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
197 :
198 0 : if (IsOnly()) {
199 0 : return Lower().BindToStatement(aStatement, lowerKey);
200 : }
201 :
202 : nsresult rv;
203 :
204 0 : if (!Lower().IsUnset()) {
205 0 : rv = Lower().BindToStatement(aStatement, lowerKey);
206 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
207 0 : return rv;
208 : }
209 : }
210 :
211 0 : if (!Upper().IsUnset()) {
212 0 : rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
213 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
214 0 : return rv;
215 : }
216 : }
217 :
218 0 : return NS_OK;
219 : }
220 :
221 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange)
222 :
223 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange)
224 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
225 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
226 :
227 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
228 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedLowerVal)
229 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedUpperVal)
230 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
231 :
232 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
233 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
234 0 : tmp->DropJSObjects();
235 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
236 :
237 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
238 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
239 0 : NS_INTERFACE_MAP_END
240 :
241 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange)
242 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
243 :
244 0 : NS_IMPL_ISUPPORTS_INHERITED0(IDBLocaleAwareKeyRange, IDBKeyRange)
245 :
246 : void
247 0 : IDBKeyRange::DropJSObjects()
248 : {
249 0 : if (!mRooted) {
250 0 : return;
251 : }
252 0 : mCachedLowerVal.setUndefined();
253 0 : mCachedUpperVal.setUndefined();
254 0 : mHaveCachedLowerVal = false;
255 0 : mHaveCachedUpperVal = false;
256 0 : mRooted = false;
257 0 : mozilla::DropJSObjects(this);
258 : }
259 :
260 : bool
261 0 : IDBKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
262 : {
263 0 : return IDBKeyRangeBinding::Wrap(aCx, this, aGivenProto, aReflector);
264 : }
265 :
266 : bool
267 0 : IDBLocaleAwareKeyRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
268 : {
269 0 : return IDBLocaleAwareKeyRangeBinding::Wrap(aCx, this, aGivenProto, aReflector);
270 : }
271 :
272 : void
273 0 : IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
274 : ErrorResult& aRv)
275 : {
276 0 : AssertIsOnOwningThread();
277 :
278 0 : if (!mHaveCachedLowerVal) {
279 0 : if (!mRooted) {
280 0 : mozilla::HoldJSObjects(this);
281 0 : mRooted = true;
282 : }
283 :
284 0 : aRv = Lower().ToJSVal(aCx, mCachedLowerVal);
285 0 : if (aRv.Failed()) {
286 0 : return;
287 : }
288 :
289 0 : mHaveCachedLowerVal = true;
290 : }
291 :
292 0 : aResult.set(mCachedLowerVal);
293 : }
294 :
295 : void
296 0 : IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
297 : ErrorResult& aRv)
298 : {
299 0 : AssertIsOnOwningThread();
300 :
301 0 : if (!mHaveCachedUpperVal) {
302 0 : if (!mRooted) {
303 0 : mozilla::HoldJSObjects(this);
304 0 : mRooted = true;
305 : }
306 :
307 0 : aRv = Upper().ToJSVal(aCx, mCachedUpperVal);
308 0 : if (aRv.Failed()) {
309 0 : return;
310 : }
311 :
312 0 : mHaveCachedUpperVal = true;
313 : }
314 :
315 0 : aResult.set(mCachedUpperVal);
316 : }
317 :
318 : bool
319 0 : IDBKeyRange::Includes(JSContext* aCx,
320 : JS::Handle<JS::Value> aValue,
321 : ErrorResult& aRv) const
322 : {
323 0 : Key key;
324 0 : aRv = GetKeyFromJSVal(aCx, aValue, key);
325 0 : if (aRv.Failed()) {
326 0 : return false;
327 : }
328 :
329 0 : MOZ_ASSERT(!(Lower().IsUnset() && Upper().IsUnset()));
330 0 : MOZ_ASSERT_IF(IsOnly(),
331 : !Lower().IsUnset() && !LowerOpen() &&
332 : Lower() == Upper() && LowerOpen() == UpperOpen());
333 :
334 0 : if (!Lower().IsUnset()) {
335 0 : switch (Key::CompareKeys(Lower(), key)) {
336 : case 1:
337 0 : return false;
338 : case 0:
339 : // Identical keys.
340 0 : return !LowerOpen();
341 : case -1:
342 0 : if (IsOnly()) {
343 0 : return false;
344 : }
345 0 : break;
346 : default:
347 0 : MOZ_CRASH();
348 : }
349 : }
350 :
351 0 : if (!Upper().IsUnset()) {
352 0 : switch (Key::CompareKeys(key, Upper())) {
353 : case 1:
354 0 : return false;
355 : case 0:
356 : // Identical keys.
357 0 : return !UpperOpen();
358 : case -1:
359 0 : break;
360 : }
361 : }
362 :
363 0 : return true;
364 : }
365 :
366 : // static
367 : already_AddRefed<IDBKeyRange>
368 0 : IDBKeyRange::Only(const GlobalObject& aGlobal,
369 : JS::Handle<JS::Value> aValue,
370 : ErrorResult& aRv)
371 : {
372 : RefPtr<IDBKeyRange> keyRange =
373 0 : new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true);
374 :
375 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
376 0 : if (aRv.Failed()) {
377 0 : return nullptr;
378 : }
379 :
380 0 : return keyRange.forget();
381 : }
382 :
383 : // static
384 : already_AddRefed<IDBKeyRange>
385 0 : IDBKeyRange::LowerBound(const GlobalObject& aGlobal,
386 : JS::Handle<JS::Value> aValue,
387 : bool aOpen,
388 : ErrorResult& aRv)
389 : {
390 : RefPtr<IDBKeyRange> keyRange =
391 0 : new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false);
392 :
393 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Lower());
394 0 : if (aRv.Failed()) {
395 0 : return nullptr;
396 : }
397 :
398 0 : return keyRange.forget();
399 : }
400 :
401 : // static
402 : already_AddRefed<IDBKeyRange>
403 0 : IDBKeyRange::UpperBound(const GlobalObject& aGlobal,
404 : JS::Handle<JS::Value> aValue,
405 : bool aOpen,
406 : ErrorResult& aRv)
407 : {
408 : RefPtr<IDBKeyRange> keyRange =
409 0 : new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false);
410 :
411 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aValue, keyRange->Upper());
412 0 : if (aRv.Failed()) {
413 0 : return nullptr;
414 : }
415 :
416 0 : return keyRange.forget();
417 : }
418 :
419 : // static
420 : already_AddRefed<IDBKeyRange>
421 0 : IDBKeyRange::Bound(const GlobalObject& aGlobal,
422 : JS::Handle<JS::Value> aLower,
423 : JS::Handle<JS::Value> aUpper,
424 : bool aLowerOpen,
425 : bool aUpperOpen,
426 : ErrorResult& aRv)
427 : {
428 : RefPtr<IDBKeyRange> keyRange =
429 0 : new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
430 :
431 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
432 0 : if (aRv.Failed()) {
433 0 : return nullptr;
434 : }
435 :
436 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper());
437 0 : if (aRv.Failed()) {
438 0 : return nullptr;
439 : }
440 :
441 0 : if (keyRange->Lower() > keyRange->Upper() ||
442 0 : (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen))) {
443 0 : aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
444 0 : return nullptr;
445 : }
446 :
447 0 : return keyRange.forget();
448 : }
449 :
450 : // static
451 : already_AddRefed<IDBLocaleAwareKeyRange>
452 0 : IDBLocaleAwareKeyRange::Bound(const GlobalObject& aGlobal,
453 : JS::Handle<JS::Value> aLower,
454 : JS::Handle<JS::Value> aUpper,
455 : bool aLowerOpen,
456 : bool aUpperOpen,
457 : ErrorResult& aRv)
458 : {
459 : RefPtr<IDBLocaleAwareKeyRange> keyRange =
460 0 : new IDBLocaleAwareKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
461 :
462 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aLower, keyRange->Lower());
463 0 : if (aRv.Failed()) {
464 0 : return nullptr;
465 : }
466 :
467 0 : aRv = GetKeyFromJSVal(aGlobal.Context(), aUpper, keyRange->Upper());
468 0 : if (aRv.Failed()) {
469 0 : return nullptr;
470 : }
471 :
472 0 : if (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen)) {
473 0 : aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
474 0 : return nullptr;
475 : }
476 :
477 0 : return keyRange.forget();
478 : }
479 :
480 : } // namespace dom
481 : } // namespace mozilla
|