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 namespace {
30 
31 already_AddRefed<IDBRequest>
GenerateRequest(JSContext * aCx,IDBIndex * aIndex)32 GenerateRequest(JSContext* aCx, IDBIndex* aIndex)
33 {
34   MOZ_ASSERT(aIndex);
35   aIndex->AssertIsOnOwningThread();
36 
37   IDBTransaction* 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 
IDBIndex(IDBObjectStore * aObjectStore,const IndexMetadata * aMetadata)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 {
55   MOZ_ASSERT(aObjectStore);
56   aObjectStore->AssertIsOnOwningThread();
57   MOZ_ASSERT(aMetadata);
58 }
59 
~IDBIndex()60 IDBIndex::~IDBIndex()
61 {
62   AssertIsOnOwningThread();
63 
64   if (mRooted) {
65     mCachedKeyPath.setUndefined();
66     mozilla::DropJSObjects(this);
67   }
68 }
69 
70 already_AddRefed<IDBIndex>
Create(IDBObjectStore * aObjectStore,const IndexMetadata & aMetadata)71 IDBIndex::Create(IDBObjectStore* aObjectStore,
72                  const IndexMetadata& aMetadata)
73 {
74   MOZ_ASSERT(aObjectStore);
75   aObjectStore->AssertIsOnOwningThread();
76 
77   RefPtr<IDBIndex> index = new IDBIndex(aObjectStore, &aMetadata);
78 
79   return index.forget();
80 }
81 
82 #ifdef DEBUG
83 
84 void
AssertIsOnOwningThread() const85 IDBIndex::AssertIsOnOwningThread() const
86 {
87   MOZ_ASSERT(mObjectStore);
88   mObjectStore->AssertIsOnOwningThread();
89 }
90 
91 #endif // DEBUG
92 
93 void
RefreshMetadata(bool aMayDelete)94 IDBIndex::RefreshMetadata(bool aMayDelete)
95 {
96   AssertIsOnOwningThread();
97   MOZ_ASSERT_IF(mDeletedMetadata, mMetadata == mDeletedMetadata);
98 
99   const nsTArray<IndexMetadata>& indexes = mObjectStore->Spec().indexes();
100 
101   bool found = false;
102 
103   for (uint32_t count = indexes.Length(), index = 0;
104        index < count;
105        index++) {
106     const IndexMetadata& metadata = indexes[index];
107 
108     if (metadata.id() == Id()) {
109       mMetadata = &metadata;
110 
111       found = true;
112       break;
113     }
114   }
115 
116   MOZ_ASSERT_IF(!aMayDelete && !mDeletedMetadata, found);
117 
118   if (found) {
119     MOZ_ASSERT(mMetadata != mDeletedMetadata);
120     mDeletedMetadata = nullptr;
121   } else {
122     NoteDeletion();
123   }
124 }
125 
126 void
NoteDeletion()127 IDBIndex::NoteDeletion()
128 {
129   AssertIsOnOwningThread();
130   MOZ_ASSERT(mMetadata);
131   MOZ_ASSERT(Id() == mMetadata->id());
132 
133   if (mDeletedMetadata) {
134     MOZ_ASSERT(mMetadata == mDeletedMetadata);
135     return;
136   }
137 
138   mDeletedMetadata = new IndexMetadata(*mMetadata);
139 
140   mMetadata = mDeletedMetadata;
141 }
142 
143 const nsString&
Name() const144 IDBIndex::Name() const
145 {
146   AssertIsOnOwningThread();
147   MOZ_ASSERT(mMetadata);
148 
149   return mMetadata->name();
150 }
151 
152 void
SetName(const nsAString & aName,ErrorResult & aRv)153 IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv)
154 {
155   AssertIsOnOwningThread();
156 
157   IDBTransaction* transaction = mObjectStore->Transaction();
158 
159   if (transaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
160       mDeletedMetadata) {
161     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
162     return;
163   }
164 
165   if (!transaction->IsOpen()) {
166     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
167     return;
168   }
169 
170   if (aName == mMetadata->name()) {
171     return;
172   }
173 
174   // Cache logging string of this index before renaming.
175   const LoggingString loggingOldIndex(this);
176 
177   const int64_t indexId = Id();
178 
179   nsresult rv =
180     transaction->Database()->RenameIndex(mObjectStore->Id(),
181                                          indexId,
182                                          aName);
183 
184   if (NS_FAILED(rv)) {
185     aRv.Throw(rv);
186     return;
187   }
188 
189   // Don't do this in the macro because we always need to increment the serial
190   // number to keep in sync with the parent.
191   const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
192 
193   IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
194                  "database(%s).transaction(%s).objectStore(%s).index(%s)."
195                  "rename(%s)",
196                "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.rename()",
197                IDB_LOG_ID_STRING(),
198                transaction->LoggingSerialNumber(),
199                requestSerialNumber,
200                IDB_LOG_STRINGIFY(transaction->Database()),
201                IDB_LOG_STRINGIFY(transaction),
202                IDB_LOG_STRINGIFY(mObjectStore),
203                loggingOldIndex.get(),
204                IDB_LOG_STRINGIFY(this));
205 
206   transaction->RenameIndex(mObjectStore, indexId, aName);
207 }
208 
209 bool
Unique() const210 IDBIndex::Unique() const
211 {
212   AssertIsOnOwningThread();
213   MOZ_ASSERT(mMetadata);
214 
215   return mMetadata->unique();
216 }
217 
218 bool
MultiEntry() const219 IDBIndex::MultiEntry() const
220 {
221   AssertIsOnOwningThread();
222   MOZ_ASSERT(mMetadata);
223 
224   return mMetadata->multiEntry();
225 }
226 
227 bool
LocaleAware() const228 IDBIndex::LocaleAware() const
229 {
230   AssertIsOnOwningThread();
231   MOZ_ASSERT(mMetadata);
232 
233   return mMetadata->locale().IsEmpty();
234 }
235 
236 const indexedDB::KeyPath&
GetKeyPath() const237 IDBIndex::GetKeyPath() const
238 {
239   AssertIsOnOwningThread();
240   MOZ_ASSERT(mMetadata);
241 
242   return mMetadata->keyPath();
243 }
244 
245 void
GetLocale(nsString & aLocale) const246 IDBIndex::GetLocale(nsString& aLocale) const
247 {
248   AssertIsOnOwningThread();
249   MOZ_ASSERT(mMetadata);
250 
251   if (mMetadata->locale().IsEmpty()) {
252     SetDOMStringToNull(aLocale);
253   } else {
254     aLocale.AssignWithConversion(mMetadata->locale());
255   }
256 }
257 
258 const nsCString&
Locale() const259 IDBIndex::Locale() const
260 {
261   AssertIsOnOwningThread();
262   MOZ_ASSERT(mMetadata);
263 
264   return mMetadata->locale();
265 }
266 
267 bool
IsAutoLocale() const268 IDBIndex::IsAutoLocale() const
269 {
270   AssertIsOnOwningThread();
271   MOZ_ASSERT(mMetadata);
272 
273   return mMetadata->autoLocale();
274 }
275 
276 nsPIDOMWindowInner*
GetParentObject() const277 IDBIndex::GetParentObject() const
278 {
279   AssertIsOnOwningThread();
280 
281   return mObjectStore->GetParentObject();
282 }
283 
284 void
GetKeyPath(JSContext * aCx,JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv)285 IDBIndex::GetKeyPath(JSContext* aCx,
286                      JS::MutableHandle<JS::Value> aResult,
287                      ErrorResult& aRv)
288 {
289   AssertIsOnOwningThread();
290 
291   if (!mCachedKeyPath.isUndefined()) {
292     MOZ_ASSERT(mRooted);
293     aResult.set(mCachedKeyPath);
294     return;
295   }
296 
297   MOZ_ASSERT(!mRooted);
298 
299   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
300   if (NS_WARN_IF(aRv.Failed())) {
301     return;
302   }
303 
304   if (mCachedKeyPath.isGCThing()) {
305     mozilla::HoldJSObjects(this);
306     mRooted = true;
307   }
308 
309   aResult.set(mCachedKeyPath);
310 }
311 
312 already_AddRefed<IDBRequest>
GetInternal(bool aKeyOnly,JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)313 IDBIndex::GetInternal(bool aKeyOnly,
314                       JSContext* aCx,
315                       JS::Handle<JS::Value> aKey,
316                       ErrorResult& aRv)
317 {
318   AssertIsOnOwningThread();
319 
320   if (mDeletedMetadata) {
321     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
322     return nullptr;
323   }
324 
325   IDBTransaction* transaction = mObjectStore->Transaction();
326   if (!transaction->IsOpen()) {
327     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
328     return nullptr;
329   }
330 
331   RefPtr<IDBKeyRange> keyRange;
332   aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
333   if (NS_WARN_IF(aRv.Failed())) {
334     return nullptr;
335   }
336 
337   if (!keyRange) {
338     // Must specify a key or keyRange for get() and getKey().
339     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
340     return nullptr;
341   }
342 
343   const int64_t objectStoreId = mObjectStore->Id();
344   const int64_t indexId = Id();
345 
346   SerializedKeyRange serializedKeyRange;
347   keyRange->ToSerialized(serializedKeyRange);
348 
349   RequestParams params;
350 
351   if (aKeyOnly) {
352     params = IndexGetKeyParams(objectStoreId, indexId, serializedKeyRange);
353   } else {
354     params = IndexGetParams(objectStoreId, indexId, serializedKeyRange);
355   }
356 
357   RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
358   MOZ_ASSERT(request);
359 
360   if (aKeyOnly) {
361     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
362                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
363                    "getKey(%s)",
364                  "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getKey()",
365                  IDB_LOG_ID_STRING(),
366                  transaction->LoggingSerialNumber(),
367                  request->LoggingSerialNumber(),
368                  IDB_LOG_STRINGIFY(transaction->Database()),
369                  IDB_LOG_STRINGIFY(transaction),
370                  IDB_LOG_STRINGIFY(mObjectStore),
371                  IDB_LOG_STRINGIFY(this),
372                  IDB_LOG_STRINGIFY(keyRange));
373   } else {
374     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
375                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
376                    "get(%s)",
377                  "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.get()",
378                  IDB_LOG_ID_STRING(),
379                  transaction->LoggingSerialNumber(),
380                  request->LoggingSerialNumber(),
381                  IDB_LOG_STRINGIFY(transaction->Database()),
382                  IDB_LOG_STRINGIFY(transaction),
383                  IDB_LOG_STRINGIFY(mObjectStore),
384                  IDB_LOG_STRINGIFY(this),
385                  IDB_LOG_STRINGIFY(keyRange));
386   }
387 
388   transaction->StartRequest(request, params);
389 
390   return request.forget();
391 }
392 
393 already_AddRefed<IDBRequest>
GetAllInternal(bool aKeysOnly,JSContext * aCx,JS::Handle<JS::Value> aKey,const Optional<uint32_t> & aLimit,ErrorResult & aRv)394 IDBIndex::GetAllInternal(bool aKeysOnly,
395                          JSContext* aCx,
396                          JS::Handle<JS::Value> aKey,
397                          const Optional<uint32_t>& aLimit,
398                          ErrorResult& aRv)
399 {
400   AssertIsOnOwningThread();
401 
402   if (mDeletedMetadata) {
403     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
404     return nullptr;
405   }
406 
407   IDBTransaction* transaction = mObjectStore->Transaction();
408   if (!transaction->IsOpen()) {
409     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
410     return nullptr;
411   }
412 
413   RefPtr<IDBKeyRange> keyRange;
414   aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
415   if (NS_WARN_IF(aRv.Failed())) {
416     return nullptr;
417   }
418 
419   const int64_t objectStoreId = mObjectStore->Id();
420   const int64_t indexId = Id();
421 
422   OptionalKeyRange optionalKeyRange;
423   if (keyRange) {
424     SerializedKeyRange serializedKeyRange;
425     keyRange->ToSerialized(serializedKeyRange);
426     optionalKeyRange = serializedKeyRange;
427   } else {
428     optionalKeyRange = void_t();
429   }
430 
431   const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
432 
433   RequestParams params;
434   if (aKeysOnly) {
435     params = IndexGetAllKeysParams(objectStoreId, indexId, optionalKeyRange,
436                                    limit);
437   } else {
438     params = IndexGetAllParams(objectStoreId, indexId, optionalKeyRange, limit);
439   }
440 
441   RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
442   MOZ_ASSERT(request);
443 
444   if (aKeysOnly) {
445     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
446                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
447                    "getAllKeys(%s, %s)",
448                  "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getAllKeys()",
449                  IDB_LOG_ID_STRING(),
450                  transaction->LoggingSerialNumber(),
451                  request->LoggingSerialNumber(),
452                  IDB_LOG_STRINGIFY(transaction->Database()),
453                  IDB_LOG_STRINGIFY(transaction),
454                  IDB_LOG_STRINGIFY(mObjectStore),
455                  IDB_LOG_STRINGIFY(this),
456                  IDB_LOG_STRINGIFY(keyRange),
457                  IDB_LOG_STRINGIFY(aLimit));
458   } else {
459     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
460                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
461                    "getAll(%s, %s)",
462                  "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getAll()",
463                  IDB_LOG_ID_STRING(),
464                  transaction->LoggingSerialNumber(),
465                  request->LoggingSerialNumber(),
466                  IDB_LOG_STRINGIFY(transaction->Database()),
467                  IDB_LOG_STRINGIFY(transaction),
468                  IDB_LOG_STRINGIFY(mObjectStore),
469                  IDB_LOG_STRINGIFY(this),
470                  IDB_LOG_STRINGIFY(keyRange),
471                  IDB_LOG_STRINGIFY(aLimit));
472   }
473 
474   transaction->StartRequest(request, params);
475 
476   return request.forget();
477 }
478 
479 already_AddRefed<IDBRequest>
OpenCursorInternal(bool aKeysOnly,JSContext * aCx,JS::Handle<JS::Value> aRange,IDBCursorDirection aDirection,ErrorResult & aRv)480 IDBIndex::OpenCursorInternal(bool aKeysOnly,
481                              JSContext* aCx,
482                              JS::Handle<JS::Value> aRange,
483                              IDBCursorDirection aDirection,
484                              ErrorResult& aRv)
485 {
486   AssertIsOnOwningThread();
487 
488   if (mDeletedMetadata) {
489     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
490     return nullptr;
491   }
492 
493   IDBTransaction* transaction = mObjectStore->Transaction();
494   if (!transaction->IsOpen()) {
495     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
496     return nullptr;
497   }
498 
499   RefPtr<IDBKeyRange> keyRange;
500   aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
501   if (NS_WARN_IF(aRv.Failed())) {
502     return nullptr;
503   }
504 
505   int64_t objectStoreId = mObjectStore->Id();
506   int64_t indexId = Id();
507 
508   OptionalKeyRange optionalKeyRange;
509 
510   if (keyRange) {
511     SerializedKeyRange serializedKeyRange;
512     keyRange->ToSerialized(serializedKeyRange);
513 
514     optionalKeyRange = Move(serializedKeyRange);
515   } else {
516     optionalKeyRange = void_t();
517   }
518 
519   IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
520 
521   OpenCursorParams params;
522   if (aKeysOnly) {
523     IndexOpenKeyCursorParams openParams;
524     openParams.objectStoreId() = objectStoreId;
525     openParams.indexId() = indexId;
526     openParams.optionalKeyRange() = Move(optionalKeyRange);
527     openParams.direction() = direction;
528 
529     params = Move(openParams);
530   } else {
531     IndexOpenCursorParams openParams;
532     openParams.objectStoreId() = objectStoreId;
533     openParams.indexId() = indexId;
534     openParams.optionalKeyRange() = Move(optionalKeyRange);
535     openParams.direction() = direction;
536 
537     params = Move(openParams);
538   }
539 
540   RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
541   MOZ_ASSERT(request);
542 
543   if (aKeysOnly) {
544     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
545                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
546                    "openKeyCursor(%s, %s)",
547                  "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.openKeyCursor()",
548                  IDB_LOG_ID_STRING(),
549                  transaction->LoggingSerialNumber(),
550                  request->LoggingSerialNumber(),
551                  IDB_LOG_STRINGIFY(transaction->Database()),
552                  IDB_LOG_STRINGIFY(transaction),
553                  IDB_LOG_STRINGIFY(mObjectStore),
554                  IDB_LOG_STRINGIFY(this),
555                  IDB_LOG_STRINGIFY(keyRange),
556                  IDB_LOG_STRINGIFY(direction));
557   } else {
558     IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
559                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
560                    "openCursor(%s, %s)",
561                  "IndexedDB %s: C T[%lld] R[%llu]: "
562                    "IDBObjectStore.openKeyCursor()",
563                  IDB_LOG_ID_STRING(),
564                  transaction->LoggingSerialNumber(),
565                  request->LoggingSerialNumber(),
566                  IDB_LOG_STRINGIFY(transaction->Database()),
567                  IDB_LOG_STRINGIFY(transaction),
568                  IDB_LOG_STRINGIFY(mObjectStore),
569                  IDB_LOG_STRINGIFY(this),
570                  IDB_LOG_STRINGIFY(keyRange),
571                  IDB_LOG_STRINGIFY(direction));
572   }
573 
574   BackgroundCursorChild* actor =
575     new BackgroundCursorChild(request, this, direction);
576 
577   mObjectStore->Transaction()->OpenCursor(actor, params);
578 
579   return request.forget();
580 }
581 
582 already_AddRefed<IDBRequest>
Count(JSContext * aCx,JS::Handle<JS::Value> aKey,ErrorResult & aRv)583 IDBIndex::Count(JSContext* aCx,
584                 JS::Handle<JS::Value> aKey,
585                 ErrorResult& aRv)
586 {
587   AssertIsOnOwningThread();
588 
589   if (mDeletedMetadata) {
590     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
591     return nullptr;
592   }
593 
594   IDBTransaction* transaction = mObjectStore->Transaction();
595   if (!transaction->IsOpen()) {
596     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
597     return nullptr;
598   }
599 
600   RefPtr<IDBKeyRange> keyRange;
601   aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
602   if (aRv.Failed()) {
603     return nullptr;
604   }
605 
606   IndexCountParams params;
607   params.objectStoreId() = mObjectStore->Id();
608   params.indexId() = Id();
609 
610   if (keyRange) {
611     SerializedKeyRange serializedKeyRange;
612     keyRange->ToSerialized(serializedKeyRange);
613     params.optionalKeyRange() = serializedKeyRange;
614   } else {
615     params.optionalKeyRange() = void_t();
616   }
617 
618   RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
619   MOZ_ASSERT(request);
620 
621   IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
622                  "database(%s).transaction(%s).objectStore(%s).index(%s)."
623                  "count(%s)",
624                "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.count()",
625                IDB_LOG_ID_STRING(),
626                transaction->LoggingSerialNumber(),
627                request->LoggingSerialNumber(),
628                IDB_LOG_STRINGIFY(transaction->Database()),
629                IDB_LOG_STRINGIFY(transaction),
630                IDB_LOG_STRINGIFY(mObjectStore),
631                IDB_LOG_STRINGIFY(this),
632                IDB_LOG_STRINGIFY(keyRange));
633 
634   transaction->StartRequest(request, params);
635 
636   return request.forget();
637 }
638 
639 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
640 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
641 
642 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
643   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
644   NS_INTERFACE_MAP_ENTRY(nsISupports)
645 NS_INTERFACE_MAP_END
646 
647 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
648 
649 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex)
650   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
651   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
652 NS_IMPL_CYCLE_COLLECTION_TRACE_END
653 
654 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
655   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
656   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore)
657 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
658 
659 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
660   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
661 
662   // Don't unlink mObjectStore!
663 
664   tmp->mCachedKeyPath.setUndefined();
665 
666   if (tmp->mRooted) {
667     mozilla::DropJSObjects(tmp);
668     tmp->mRooted = false;
669   }
670 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
671 
672 JSObject*
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)673 IDBIndex::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
674 {
675   return IDBIndexBinding::Wrap(aCx, this, aGivenProto);
676 }
677 
678 } // namespace dom
679 } // namespace mozilla
680