Coverage Report

Created: 2018-09-25 14:53

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