Source code

Revision control

Other Tools

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 "IDBIndex.h"
8
9
#include "FileInfo.h"
10
#include "IDBCursor.h"
11
#include "IDBEvents.h"
12
#include "IDBKeyRange.h"
13
#include "IDBObjectStore.h"
14
#include "IDBRequest.h"
15
#include "IDBTransaction.h"
16
#include "IndexedDatabase.h"
17
#include "IndexedDatabaseInlines.h"
18
#include "mozilla/ErrorResult.h"
19
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
20
#include "ProfilerHelpers.h"
21
#include "ReportInternalError.h"
22
23
// Include this last to avoid path problems on Windows.
24
#include "ActorsChild.h"
25
26
namespace mozilla {
27
namespace dom {
28
29
using namespace mozilla::dom::indexedDB;
30
31
namespace {
32
33
already_AddRefed<IDBRequest> GenerateRequest(JSContext* aCx, IDBIndex* aIndex) {
34
MOZ_ASSERT(aIndex);
35
aIndex->AssertIsOnOwningThread();
36
37
IDBTransaction* const transaction = aIndex->ObjectStore()->Transaction();
38
39
RefPtr<IDBRequest> request =
40
IDBRequest::Create(aCx, aIndex, transaction->Database(), transaction);
41
MOZ_ASSERT(request);
42
43
return request.forget();
44
}
45
46
} // namespace
47
48
IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata)
49
: mObjectStore(aObjectStore),
50
mCachedKeyPath(JS::UndefinedValue()),
51
mMetadata(aMetadata),
52
mId(aMetadata->id()),
53
mRooted(false) {
54
MOZ_ASSERT(aObjectStore);
55
aObjectStore->AssertIsOnOwningThread();
56
MOZ_ASSERT(aMetadata);
57
}
58
59
IDBIndex::~IDBIndex() {
60
AssertIsOnOwningThread();
61
62
if (mRooted) {
63
mCachedKeyPath.setUndefined();
64
mozilla::DropJSObjects(this);
65
}
66
}
67
68
already_AddRefed<IDBIndex> IDBIndex::Create(IDBObjectStore* aObjectStore,
69
const IndexMetadata& aMetadata) {
70
MOZ_ASSERT(aObjectStore);
71
aObjectStore->AssertIsOnOwningThread();
72
73
RefPtr<IDBIndex> index = new IDBIndex(aObjectStore, &aMetadata);
74
75
return index.forget();
76
}
77
78
#ifdef DEBUG
79
80
void IDBIndex::AssertIsOnOwningThread() const {
81
MOZ_ASSERT(mObjectStore);
82
mObjectStore->AssertIsOnOwningThread();
83
}
84
85
#endif // DEBUG
86
87
void IDBIndex::RefreshMetadata(bool aMayDelete) {
88
AssertIsOnOwningThread();
89
MOZ_ASSERT_IF(mDeletedMetadata, mMetadata == mDeletedMetadata);
90
91
const auto& indexes = mObjectStore->Spec().indexes();
92
const auto foundIt = std::find_if(
93
indexes.cbegin(), indexes.cend(),
94
[id = Id()](const auto& metadata) { return metadata.id() == id; });
95
const bool found = foundIt != indexes.cend();
96
97
MOZ_ASSERT_IF(!aMayDelete && !mDeletedMetadata, found);
98
99
if (found) {
100
mMetadata = &*foundIt;
101
MOZ_ASSERT(mMetadata != mDeletedMetadata);
102
mDeletedMetadata = nullptr;
103
} else {
104
NoteDeletion();
105
}
106
}
107
108
void IDBIndex::NoteDeletion() {
109
AssertIsOnOwningThread();
110
MOZ_ASSERT(mMetadata);
111
MOZ_ASSERT(Id() == mMetadata->id());
112
113
if (mDeletedMetadata) {
114
MOZ_ASSERT(mMetadata == mDeletedMetadata);
115
return;
116
}
117
118
mDeletedMetadata = new IndexMetadata(*mMetadata);
119
120
mMetadata = mDeletedMetadata;
121
}
122
123
const nsString& IDBIndex::Name() const {
124
AssertIsOnOwningThread();
125
MOZ_ASSERT(mMetadata);
126
127
return mMetadata->name();
128
}
129
130
void IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv) {
131
AssertIsOnOwningThread();
132
133
IDBTransaction* const transaction = mObjectStore->Transaction();
134
135
if (transaction->GetMode() != IDBTransaction::Mode::VersionChange ||
136
mDeletedMetadata) {
137
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
138
return;
139
}
140
141
if (!transaction->CanAcceptRequests()) {
142
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
143
return;
144
}
145
146
if (aName == mMetadata->name()) {
147
return;
148
}
149
150
// Cache logging string of this index before renaming.
151
const LoggingString loggingOldIndex(this);
152
153
const int64_t indexId = Id();
154
155
nsresult rv =
156
transaction->Database()->RenameIndex(mObjectStore->Id(), indexId, aName);
157
158
if (NS_FAILED(rv)) {
159
aRv.Throw(rv);
160
return;
161
}
162
163
// Don't do this in the macro because we always need to increment the serial
164
// number to keep in sync with the parent.
165
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
166
167
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
168
"database(%s).transaction(%s).objectStore(%s).index(%s)."
169
"rename(%s)",
170
"IDBIndex.rename()", transaction->LoggingSerialNumber(),
171
requestSerialNumber, IDB_LOG_STRINGIFY(transaction->Database()),
172
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
173
loggingOldIndex.get(), IDB_LOG_STRINGIFY(this));
174
175
transaction->RenameIndex(mObjectStore, indexId, aName);
176
}
177
178
bool IDBIndex::Unique() const {
179
AssertIsOnOwningThread();
180
MOZ_ASSERT(mMetadata);
181
182
return mMetadata->unique();
183
}
184
185
bool IDBIndex::MultiEntry() const {
186
AssertIsOnOwningThread();
187
MOZ_ASSERT(mMetadata);
188
189
return mMetadata->multiEntry();
190
}
191
192
bool IDBIndex::LocaleAware() const {
193
AssertIsOnOwningThread();
194
MOZ_ASSERT(mMetadata);
195
196
return mMetadata->locale().IsEmpty();
197
}
198
199
const indexedDB::KeyPath& IDBIndex::GetKeyPath() const {
200
AssertIsOnOwningThread();
201
MOZ_ASSERT(mMetadata);
202
203
return mMetadata->keyPath();
204
}
205
206
void IDBIndex::GetLocale(nsString& aLocale) const {
207
AssertIsOnOwningThread();
208
MOZ_ASSERT(mMetadata);
209
210
if (mMetadata->locale().IsEmpty()) {
211
SetDOMStringToNull(aLocale);
212
} else {
213
CopyASCIItoUTF16(mMetadata->locale(), aLocale);
214
}
215
}
216
217
const nsCString& IDBIndex::Locale() const {
218
AssertIsOnOwningThread();
219
MOZ_ASSERT(mMetadata);
220
221
return mMetadata->locale();
222
}
223
224
bool IDBIndex::IsAutoLocale() const {
225
AssertIsOnOwningThread();
226
MOZ_ASSERT(mMetadata);
227
228
return mMetadata->autoLocale();
229
}
230
231
nsIGlobalObject* IDBIndex::GetParentObject() const {
232
AssertIsOnOwningThread();
233
234
return mObjectStore->GetParentObject();
235
}
236
237
void IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
238
ErrorResult& aRv) {
239
AssertIsOnOwningThread();
240
241
if (!mCachedKeyPath.isUndefined()) {
242
MOZ_ASSERT(mRooted);
243
aResult.set(mCachedKeyPath);
244
return;
245
}
246
247
MOZ_ASSERT(!mRooted);
248
249
aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
250
if (NS_WARN_IF(aRv.Failed())) {
251
return;
252
}
253
254
if (mCachedKeyPath.isGCThing()) {
255
mozilla::HoldJSObjects(this);
256
mRooted = true;
257
}
258
259
aResult.set(mCachedKeyPath);
260
}
261
262
already_AddRefed<IDBRequest> IDBIndex::GetInternal(bool aKeyOnly,
263
JSContext* aCx,
264
JS::Handle<JS::Value> aKey,
265
ErrorResult& aRv) {
266
AssertIsOnOwningThread();
267
268
if (mDeletedMetadata) {
269
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
270
return nullptr;
271
}
272
273
IDBTransaction* transaction = mObjectStore->Transaction();
274
if (!transaction->CanAcceptRequests()) {
275
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
276
return nullptr;
277
}
278
279
RefPtr<IDBKeyRange> keyRange;
280
IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange), aRv);
281
if (NS_WARN_IF(aRv.Failed())) {
282
return nullptr;
283
}
284
285
if (!keyRange) {
286
// Must specify a key or keyRange for get() and getKey().
287
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
288
return nullptr;
289
}
290
291
const int64_t objectStoreId = mObjectStore->Id();
292
const int64_t indexId = Id();
293
294
SerializedKeyRange serializedKeyRange;
295
keyRange->ToSerialized(serializedKeyRange);
296
297
RequestParams params;
298
299
if (aKeyOnly) {
300
params = IndexGetKeyParams(objectStoreId, indexId, serializedKeyRange);
301
} else {
302
params = IndexGetParams(objectStoreId, indexId, serializedKeyRange);
303
}
304
305
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
306
MOZ_ASSERT(request);
307
308
if (aKeyOnly) {
309
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
310
"database(%s).transaction(%s).objectStore(%s).index(%s)."
311
"getKey(%s)",
312
"IDBIndex.getKey()", transaction->LoggingSerialNumber(),
313
request->LoggingSerialNumber(),
314
IDB_LOG_STRINGIFY(transaction->Database()),
315
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
316
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange));
317
} else {
318
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
319
"database(%s).transaction(%s).objectStore(%s).index(%s)."
320
"get(%s)",
321
"IDBIndex.get()", transaction->LoggingSerialNumber(),
322
request->LoggingSerialNumber(),
323
IDB_LOG_STRINGIFY(transaction->Database()),
324
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
325
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange));
326
}
327
328
// TODO: This is necessary to preserve request ordering only. Proper
329
// sequencing of requests should be done in a more sophisticated manner that
330
// doesn't require invalidating cursor caches (Bug 1580499).
331
transaction->InvalidateCursorCaches();
332
333
transaction->StartRequest(request, params);
334
335
return request.forget();
336
}
337
338
already_AddRefed<IDBRequest> IDBIndex::GetAllInternal(
339
bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aKey,
340
const Optional<uint32_t>& aLimit, ErrorResult& aRv) {
341
AssertIsOnOwningThread();
342
343
if (mDeletedMetadata) {
344
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
345
return nullptr;
346
}
347
348
IDBTransaction* transaction = mObjectStore->Transaction();
349
if (!transaction->CanAcceptRequests()) {
350
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
351
return nullptr;
352
}
353
354
RefPtr<IDBKeyRange> keyRange;
355
IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange), aRv);
356
if (NS_WARN_IF(aRv.Failed())) {
357
return nullptr;
358
}
359
360
const int64_t objectStoreId = mObjectStore->Id();
361
const int64_t indexId = Id();
362
363
Maybe<SerializedKeyRange> optionalKeyRange;
364
if (keyRange) {
365
SerializedKeyRange serializedKeyRange;
366
keyRange->ToSerialized(serializedKeyRange);
367
optionalKeyRange.emplace(serializedKeyRange);
368
}
369
370
const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
371
372
const auto& params =
373
aKeysOnly ? RequestParams{IndexGetAllKeysParams(objectStoreId, indexId,
374
optionalKeyRange, limit)}
375
: RequestParams{IndexGetAllParams(objectStoreId, indexId,
376
optionalKeyRange, limit)};
377
378
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
379
MOZ_ASSERT(request);
380
381
if (aKeysOnly) {
382
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
383
"database(%s).transaction(%s).objectStore(%s).index(%s)."
384
"getAllKeys(%s, %s)",
385
"IDBIndex.getAllKeys()", transaction->LoggingSerialNumber(),
386
request->LoggingSerialNumber(),
387
IDB_LOG_STRINGIFY(transaction->Database()),
388
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
389
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
390
IDB_LOG_STRINGIFY(aLimit));
391
} else {
392
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
393
"database(%s).transaction(%s).objectStore(%s).index(%s)."
394
"getAll(%s, %s)",
395
"IDBIndex.getAll()", transaction->LoggingSerialNumber(),
396
request->LoggingSerialNumber(),
397
IDB_LOG_STRINGIFY(transaction->Database()),
398
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
399
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
400
IDB_LOG_STRINGIFY(aLimit));
401
}
402
403
// TODO: This is necessary to preserve request ordering only. Proper
404
// sequencing of requests should be done in a more sophisticated manner that
405
// doesn't require invalidating cursor caches (Bug 1580499).
406
transaction->InvalidateCursorCaches();
407
408
transaction->StartRequest(request, params);
409
410
return request.forget();
411
}
412
413
already_AddRefed<IDBRequest> IDBIndex::OpenCursorInternal(
414
bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aRange,
415
IDBCursorDirection aDirection, ErrorResult& aRv) {
416
AssertIsOnOwningThread();
417
418
if (mDeletedMetadata) {
419
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
420
return nullptr;
421
}
422
423
IDBTransaction* transaction = mObjectStore->Transaction();
424
if (!transaction->CanAcceptRequests()) {
425
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
426
return nullptr;
427
}
428
429
RefPtr<IDBKeyRange> keyRange;
430
IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange), aRv);
431
if (NS_WARN_IF(aRv.Failed())) {
432
return nullptr;
433
}
434
435
const int64_t objectStoreId = mObjectStore->Id();
436
const int64_t indexId = Id();
437
438
Maybe<SerializedKeyRange> optionalKeyRange;
439
440
if (keyRange) {
441
SerializedKeyRange serializedKeyRange;
442
keyRange->ToSerialized(serializedKeyRange);
443
444
optionalKeyRange.emplace(std::move(serializedKeyRange));
445
}
446
447
const IDBCursor::Direction direction =
448
IDBCursor::ConvertDirection(aDirection);
449
450
const CommonIndexOpenCursorParams commonIndexParams = {
451
{objectStoreId, std::move(optionalKeyRange), direction}, indexId};
452
453
const auto params =
454
aKeysOnly ? OpenCursorParams{IndexOpenKeyCursorParams{commonIndexParams}}
455
: OpenCursorParams{IndexOpenCursorParams{commonIndexParams}};
456
457
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
458
MOZ_ASSERT(request);
459
460
if (aKeysOnly) {
461
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
462
"database(%s).transaction(%s).objectStore(%s).index(%s)."
463
"openKeyCursor(%s, %s)",
464
"IDBIndex.openKeyCursor()", transaction->LoggingSerialNumber(),
465
request->LoggingSerialNumber(),
466
IDB_LOG_STRINGIFY(transaction->Database()),
467
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
468
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
469
IDB_LOG_STRINGIFY(direction));
470
} else {
471
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
472
"database(%s).transaction(%s).objectStore(%s).index(%s)."
473
"openCursor(%s, %s)",
474
"IDBIndex.openCursor()", transaction->LoggingSerialNumber(),
475
request->LoggingSerialNumber(),
476
IDB_LOG_STRINGIFY(transaction->Database()),
477
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
478
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
479
IDB_LOG_STRINGIFY(direction));
480
}
481
482
BackgroundCursorChild* const actor =
483
new BackgroundCursorChild(request, this, direction);
484
485
// TODO: This is necessary to preserve request ordering only. Proper
486
// sequencing of requests should be done in a more sophisticated manner that
487
// doesn't require invalidating cursor caches (Bug 1580499).
488
transaction->InvalidateCursorCaches();
489
490
mObjectStore->Transaction()->OpenCursor(actor, params);
491
492
return request.forget();
493
}
494
495
already_AddRefed<IDBRequest> IDBIndex::Count(JSContext* aCx,
496
JS::Handle<JS::Value> aKey,
497
ErrorResult& aRv) {
498
AssertIsOnOwningThread();
499
500
if (mDeletedMetadata) {
501
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
502
return nullptr;
503
}
504
505
IDBTransaction* const transaction = mObjectStore->Transaction();
506
if (!transaction->CanAcceptRequests()) {
507
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
508
return nullptr;
509
}
510
511
RefPtr<IDBKeyRange> keyRange;
512
IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange), aRv);
513
if (aRv.Failed()) {
514
return nullptr;
515
}
516
517
IndexCountParams params;
518
params.objectStoreId() = mObjectStore->Id();
519
params.indexId() = Id();
520
521
if (keyRange) {
522
SerializedKeyRange serializedKeyRange;
523
keyRange->ToSerialized(serializedKeyRange);
524
params.optionalKeyRange().emplace(serializedKeyRange);
525
}
526
527
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
528
MOZ_ASSERT(request);
529
530
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
531
"database(%s).transaction(%s).objectStore(%s).index(%s)."
532
"count(%s)",
533
"IDBIndex.count()", transaction->LoggingSerialNumber(),
534
request->LoggingSerialNumber(),
535
IDB_LOG_STRINGIFY(transaction->Database()),
536
IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
537
IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange));
538
539
// TODO: This is necessary to preserve request ordering only. Proper
540
// sequencing of requests should be done in a more sophisticated manner that
541
// doesn't require invalidating cursor caches (Bug 1580499).
542
transaction->InvalidateCursorCaches();
543
544
transaction->StartRequest(request, params);
545
546
return request.forget();
547
}
548
549
NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
550
NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
551
552
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
553
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
554
NS_INTERFACE_MAP_ENTRY(nsISupports)
555
NS_INTERFACE_MAP_END
556
557
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
558
559
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex)
560
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
561
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
562
NS_IMPL_CYCLE_COLLECTION_TRACE_END
563
564
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
565
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore)
566
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
567
568
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
569
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
570
571
// Don't unlink mObjectStore!
572
573
tmp->mCachedKeyPath.setUndefined();
574
575
if (tmp->mRooted) {
576
mozilla::DropJSObjects(tmp);
577
tmp->mRooted = false;
578
}
579
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
580
581
JSObject* IDBIndex::WrapObject(JSContext* aCx,
582
JS::Handle<JSObject*> aGivenProto) {
583
return IDBIndex_Binding::Wrap(aCx, this, aGivenProto);
584
}
585
586
} // namespace dom
587
} // namespace mozilla