Bug 1081703. r=khuey, a=dveditz
authorBen Turner <bent.mozilla@gmail.com>
Fri, 07 Nov 2014 16:42:53 -0800
changeset 233824 18b9b7d704223b1b3792104288f7e6f742548574
parent 233823 bff65964c8a6b0f953710783f040e738401142f9
child 233825 ff9dd6530ea1aad2776f76cb72b4a65b6199bab0
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, dveditz
bugs1081703
milestone35.0a2
Bug 1081703. r=khuey, a=dveditz
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsChild.h
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBCursor.h
dom/indexedDB/test/mochitest.ini
dom/indexedDB/test/test_invalid_cursor.html
dom/indexedDB/test/unit/test_invalid_cursor.js
dom/indexedDB/test/unit/xpcshell-shared.ini
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2022,17 +2022,16 @@ BackgroundCursorChild::SendContinueInter
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
   MOZ_ASSERT(mTransaction);
   MOZ_ASSERT(mCursor);
   MOZ_ASSERT(!mStrongRequest);
   MOZ_ASSERT(!mStrongCursor);
 
   // Make sure all our DOM objects stay alive.
-  mStrongRequest = mRequest;
   mStrongCursor = mCursor;
 
   MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done);
   mRequest->Reset();
 
   mTransaction->OnNewRequest();
 
   MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams));
@@ -2114,19 +2113,17 @@ BackgroundCursorChild::HandleResponse(
                        response.cloneInfo(),
                        cloneReadInfo.mFiles);
 
   nsRefPtr<IDBCursor> newCursor;
 
   if (mCursor) {
     mCursor->Reset(Move(response.key()), Move(cloneReadInfo));
   } else {
-    newCursor = IDBCursor::Create(mObjectStore,
-                                  this,
-                                  mDirection,
+    newCursor = IDBCursor::Create(this,
                                   Move(response.key()),
                                   Move(cloneReadInfo));
     mCursor = newCursor;
   }
 
   ResultHelper helper(mRequest, mTransaction, mCursor);
   DispatchSuccessEvent(&helper);
 }
@@ -2145,20 +2142,17 @@ BackgroundCursorChild::HandleResponse(
   // XXX Fix this somehow...
   auto& response = const_cast<ObjectStoreKeyCursorResponse&>(aResponse);
 
   nsRefPtr<IDBCursor> newCursor;
 
   if (mCursor) {
     mCursor->Reset(Move(response.key()));
   } else {
-    newCursor = IDBCursor::Create(mObjectStore,
-                                  this,
-                                  mDirection,
-                                  Move(response.key()));
+    newCursor = IDBCursor::Create(this, Move(response.key()));
     mCursor = newCursor;
   }
 
   ResultHelper helper(mRequest, mTransaction, mCursor);
   DispatchSuccessEvent(&helper);
 }
 
 void
@@ -2182,19 +2176,17 @@ BackgroundCursorChild::HandleResponse(co
 
   nsRefPtr<IDBCursor> newCursor;
 
   if (mCursor) {
     mCursor->Reset(Move(response.key()),
                    Move(response.objectKey()),
                    Move(cloneReadInfo));
   } else {
-    newCursor = IDBCursor::Create(mIndex,
-                                  this,
-                                  mDirection,
+    newCursor = IDBCursor::Create(this,
                                   Move(response.key()),
                                   Move(response.objectKey()),
                                   Move(cloneReadInfo));
     mCursor = newCursor;
   }
 
   ResultHelper helper(mRequest, mTransaction, mCursor);
   DispatchSuccessEvent(&helper);
@@ -2213,19 +2205,17 @@ BackgroundCursorChild::HandleResponse(co
   // XXX Fix this somehow...
   auto& response = const_cast<IndexKeyCursorResponse&>(aResponse);
 
   nsRefPtr<IDBCursor> newCursor;
 
   if (mCursor) {
     mCursor->Reset(Move(response.key()), Move(response.objectKey()));
   } else {
-    newCursor = IDBCursor::Create(mIndex,
-                                  this,
-                                  mDirection,
+    newCursor = IDBCursor::Create(this,
                                   Move(response.key()),
                                   Move(response.objectKey()));
     mCursor = newCursor;
   }
 
   ResultHelper helper(mRequest, mTransaction, mCursor);
   DispatchSuccessEvent(&helper);
 }
@@ -2260,18 +2250,18 @@ BackgroundCursorChild::ActorDestroy(Acto
 
 bool
 BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
   MOZ_ASSERT(mRequest);
   MOZ_ASSERT(mTransaction);
-  MOZ_ASSERT(mStrongRequest);
   MOZ_ASSERT_IF(mCursor, mStrongCursor);
+  MOZ_ASSERT_IF(!mCursor, mStrongRequest);
 
   MaybeCollectGarbageOnIPCMessage();
 
   nsRefPtr<IDBRequest> request;
   mStrongRequest.swap(request);
 
   nsRefPtr<IDBCursor> cursor;
   mStrongCursor.swap(cursor);
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -563,16 +563,48 @@ public:
 #endif
 
   void
   SendContinueInternal(const CursorRequestParams& aParams);
 
   void
   SendDeleteMeInternal();
 
+  IDBRequest*
+  GetRequest() const
+  {
+    AssertIsOnOwningThread();
+
+    return mRequest;
+  }
+
+  IDBObjectStore*
+  GetObjectStore() const
+  {
+    AssertIsOnOwningThread();
+
+    return mObjectStore;
+  }
+
+  IDBIndex*
+  GetIndex() const
+  {
+    AssertIsOnOwningThread();
+
+    return mIndex;
+  }
+
+  Direction
+  GetDirection() const
+  {
+    AssertIsOnOwningThread();
+
+    return mDirection;
+  }
+
 private:
   // Only destroyed by BackgroundTransactionChild or
   // BackgroundVersionChangeTransactionChild.
   ~BackgroundCursorChild();
 
   void
   HandleResponse(nsresult aResponse);
 
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -22,46 +22,44 @@
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 IDBCursor::IDBCursor(Type aType,
-                     IDBObjectStore* aSourceObjectStore,
-                     IDBIndex* aSourceIndex,
-                     IDBTransaction* aTransaction,
                      BackgroundCursorChild* aBackgroundActor,
-                     Direction aDirection,
                      const Key& aKey)
-  : mSourceObjectStore(aSourceObjectStore)
-  , mSourceIndex(aSourceIndex)
-  , mTransaction(aTransaction)
-  , mBackgroundActor(aBackgroundActor)
-  , mScriptOwner(aTransaction->Database()->GetScriptOwner())
+  : mBackgroundActor(aBackgroundActor)
+  , mRequest(aBackgroundActor->GetRequest())
+  , mSourceObjectStore(aBackgroundActor->GetObjectStore())
+  , mSourceIndex(aBackgroundActor->GetIndex())
+  , mTransaction(mRequest->GetTransaction())
+  , mScriptOwner(mTransaction->Database()->GetScriptOwner())
   , mCachedKey(JSVAL_VOID)
   , mCachedPrimaryKey(JSVAL_VOID)
   , mCachedValue(JSVAL_VOID)
   , mKey(aKey)
   , mType(aType)
-  , mDirection(aDirection)
+  , mDirection(aBackgroundActor->GetDirection())
   , mHaveCachedKey(false)
   , mHaveCachedPrimaryKey(false)
   , mHaveCachedValue(false)
   , mRooted(false)
   , mContinueCalled(false)
   , mHaveValue(true)
 {
+  MOZ_ASSERT(aBackgroundActor);
+  aBackgroundActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
   MOZ_ASSERT_IF(aType == Type_ObjectStore || aType == Type_ObjectStoreKey,
-                aSourceObjectStore);
-  MOZ_ASSERT_IF(aType == Type_Index || aType == Type_IndexKey, aSourceIndex);
-  MOZ_ASSERT(aTransaction);
-  aTransaction->AssertIsOnOwningThread();
-  MOZ_ASSERT(aBackgroundActor);
+                mSourceObjectStore);
+  MOZ_ASSERT_IF(aType == Type_Index || aType == Type_IndexKey, mSourceIndex);
+  MOZ_ASSERT(mTransaction);
   MOZ_ASSERT(!aKey.IsUnset());
   MOZ_ASSERT(mScriptOwner);
 
   if (mScriptOwner) {
     mozilla::HoldJSObjects(this);
     mRooted = true;
   }
 }
@@ -75,117 +73,89 @@ IDBCursor::~IDBCursor()
   if (mBackgroundActor) {
     mBackgroundActor->SendDeleteMeInternal();
     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
   }
 }
 
 // static
 already_AddRefed<IDBCursor>
-IDBCursor::Create(IDBObjectStore* aObjectStore,
-                  BackgroundCursorChild* aBackgroundActor,
-                  Direction aDirection,
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
                   const Key& aKey,
                   StructuredCloneReadInfo&& aCloneInfo)
 {
-  MOZ_ASSERT(aObjectStore);
-  aObjectStore->AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
+  aBackgroundActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(aBackgroundActor->GetObjectStore());
+  MOZ_ASSERT(!aBackgroundActor->GetIndex());
   MOZ_ASSERT(!aKey.IsUnset());
 
   nsRefPtr<IDBCursor> cursor =
-    new IDBCursor(Type_ObjectStore,
-                  aObjectStore,
-                  nullptr,
-                  aObjectStore->Transaction(),
-                  aBackgroundActor,
-                  aDirection,
-                  aKey);
+    new IDBCursor(Type_ObjectStore, aBackgroundActor, aKey);
 
   cursor->mCloneInfo = Move(aCloneInfo);
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
-IDBCursor::Create(IDBObjectStore* aObjectStore,
-                  BackgroundCursorChild* aBackgroundActor,
-                  Direction aDirection,
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
                   const Key& aKey)
 {
-  MOZ_ASSERT(aObjectStore);
-  aObjectStore->AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
+  aBackgroundActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(aBackgroundActor->GetObjectStore());
+  MOZ_ASSERT(!aBackgroundActor->GetIndex());
   MOZ_ASSERT(!aKey.IsUnset());
 
   nsRefPtr<IDBCursor> cursor =
-    new IDBCursor(Type_ObjectStoreKey,
-                  aObjectStore,
-                  nullptr,
-                  aObjectStore->Transaction(),
-                  aBackgroundActor,
-                  aDirection,
-                  aKey);
+    new IDBCursor(Type_ObjectStoreKey, aBackgroundActor, aKey);
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
-IDBCursor::Create(IDBIndex* aIndex,
-                  BackgroundCursorChild* aBackgroundActor,
-                  Direction aDirection,
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
                   const Key& aKey,
                   const Key& aPrimaryKey,
                   StructuredCloneReadInfo&& aCloneInfo)
 {
-  MOZ_ASSERT(aIndex);
-  aIndex->AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
+  aBackgroundActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(aBackgroundActor->GetIndex());
+  MOZ_ASSERT(!aBackgroundActor->GetObjectStore());
   MOZ_ASSERT(!aKey.IsUnset());
   MOZ_ASSERT(!aPrimaryKey.IsUnset());
 
   nsRefPtr<IDBCursor> cursor =
-    new IDBCursor(Type_Index,
-                  nullptr,
-                  aIndex,
-                  aIndex->ObjectStore()->Transaction(),
-                  aBackgroundActor,
-                  aDirection,
-                  aKey);
+    new IDBCursor(Type_Index, aBackgroundActor, aKey);
 
   cursor->mPrimaryKey = Move(aPrimaryKey);
   cursor->mCloneInfo = Move(aCloneInfo);
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
-IDBCursor::Create(IDBIndex* aIndex,
-                  BackgroundCursorChild* aBackgroundActor,
-                  Direction aDirection,
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
                   const Key& aKey,
                   const Key& aPrimaryKey)
 {
-  MOZ_ASSERT(aIndex);
-  aIndex->AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
+  aBackgroundActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(aBackgroundActor->GetIndex());
+  MOZ_ASSERT(!aBackgroundActor->GetObjectStore());
   MOZ_ASSERT(!aKey.IsUnset());
   MOZ_ASSERT(!aPrimaryKey.IsUnset());
 
   nsRefPtr<IDBCursor> cursor =
-    new IDBCursor(Type_IndexKey,
-                  nullptr,
-                  aIndex,
-                  aIndex->ObjectStore()->Transaction(),
-                  aBackgroundActor,
-                  aDirection,
-                  aKey);
+    new IDBCursor(Type_IndexKey, aBackgroundActor, aKey);
 
   cursor->mPrimaryKey = Move(aPrimaryKey);
 
   return cursor.forget();
 }
 
 // static
 auto
@@ -795,36 +765,36 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceIndex)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
   MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined());
   MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey,
                 tmp->mCachedPrimaryKey.isUndefined());
   MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined());
 
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
-  // Don't unlink mSourceObjectStore or mSourceIndex or mTransaction!
+  // Don't unlink mRequest, mSourceObjectStore, or mSourceIndex!
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->DropJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 JSObject*
 IDBCursor::WrapObject(JSContext* aCx)
 {
   AssertIsOnOwningThread();
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -54,21 +54,24 @@ private:
   enum Type
   {
     Type_ObjectStore,
     Type_ObjectStoreKey,
     Type_Index,
     Type_IndexKey,
   };
 
+  BackgroundCursorChild* mBackgroundActor;
+
+  nsRefPtr<IDBRequest> mRequest;
   nsRefPtr<IDBObjectStore> mSourceObjectStore;
   nsRefPtr<IDBIndex> mSourceIndex;
-  nsRefPtr<IDBTransaction> mTransaction;
 
-  BackgroundCursorChild* mBackgroundActor;
+  // mSourceObjectStore or mSourceIndex will hold this alive.
+  IDBTransaction* mTransaction;
 
   JS::Heap<JSObject*> mScriptOwner;
 
   // These are cycle-collected!
   JS::Heap<JS::Value> mCachedKey;
   JS::Heap<JS::Value> mCachedPrimaryKey;
   JS::Heap<JS::Value> mCachedValue;
 
@@ -83,40 +86,32 @@ private:
   bool mHaveCachedPrimaryKey : 1;
   bool mHaveCachedValue : 1;
   bool mRooted : 1;
   bool mContinueCalled : 1;
   bool mHaveValue : 1;
 
 public:
   static already_AddRefed<IDBCursor>
-  Create(IDBObjectStore* aObjectStore,
-         BackgroundCursorChild* aBackgroundActor,
-         Direction aDirection,
+  Create(BackgroundCursorChild* aBackgroundActor,
          const Key& aKey,
          StructuredCloneReadInfo&& aCloneInfo);
 
   static already_AddRefed<IDBCursor>
-  Create(IDBObjectStore* aObjectStore,
-         BackgroundCursorChild* aBackgroundActor,
-         Direction aDirection,
+  Create(BackgroundCursorChild* aBackgroundActor,
          const Key& aKey);
 
   static already_AddRefed<IDBCursor>
-  Create(IDBIndex* aIndex,
-         BackgroundCursorChild* aBackgroundActor,
-         Direction aDirection,
+  Create(BackgroundCursorChild* aBackgroundActor,
          const Key& aKey,
          const Key& aPrimaryKey,
          StructuredCloneReadInfo&& aCloneInfo);
 
   static already_AddRefed<IDBCursor>
-  Create(IDBIndex* aIndex,
-         BackgroundCursorChild* aBackgroundActor,
-         Direction aDirection,
+  Create(BackgroundCursorChild* aBackgroundActor,
          const Key& aKey,
          const Key& aPrimaryKey);
 
   static Direction
   ConvertDirection(IDBCursorDirection aDirection);
 
   void
   AssertIsOnOwningThread() const
@@ -189,21 +184,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor)
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
 private:
   IDBCursor(Type aType,
-            IDBObjectStore* aSourceObjectStore,
-            IDBIndex* aSourceIndex,
-            IDBTransaction* aTransaction,
             BackgroundCursorChild* aBackgroundActor,
-            Direction aDirection,
             const Key& aKey);
 
   ~IDBCursor();
 
   void
   DropJSObjects();
 };
 
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -40,16 +40,17 @@ support-files =
   unit/test_index_empty_keyPath.js
   unit/test_index_getAll.js
   unit/test_index_getAllObjects.js
   unit/test_index_object_cursors.js
   unit/test_index_update_delete.js
   unit/test_indexes.js
   unit/test_indexes_bad_values.js
   unit/test_indexes_funny_things.js
+  unit/test_invalid_cursor.js
   unit/test_invalid_version.js
   unit/test_invalidate.js
   unit/test_key_requirements.js
   unit/test_keys.js
   unit/test_lowDiskSpace.js
   unit/test_multientry.js
   unit/test_names_sorted.js
   unit/test_objectCursors.js
@@ -256,16 +257,18 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_index_update_delete.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_indexes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_indexes_bad_values.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_indexes_funny_things.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
+[test_invalid_cursor.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_invalid_version.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_invalidate.html]
 # disabled for the moment
 skip-if = true
 # skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_key_requirements.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_invalid_cursor.html
@@ -0,0 +1,18 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>IndexedDB Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_invalid_cursor.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_invalid_cursor.js
@@ -0,0 +1,62 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+let testGenerator = testSteps();
+
+function testSteps()
+{
+  const dbName = ("window" in this) ? window.location.pathname : "test";
+  const dbVersion = 1;
+  const objectStoreName = "foo";
+  const data = 0;
+
+  let req = indexedDB.open(dbName, dbVersion);
+  req.onerror = errorHandler;
+  req.onupgradeneeded = grabEventAndContinueHandler;
+  req.onsuccess = grabEventAndContinueHandler;
+
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Got upgradeneeded event");
+
+  let db = event.target.result;
+
+  let objectStore =
+    db.createObjectStore(objectStoreName, { autoIncrement: true });
+  objectStore.add(data);
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got success event for open");
+
+  objectStore = db.transaction(objectStoreName).objectStore(objectStoreName);
+
+  objectStore.openCursor().onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "success", "Got success event for openCursor");
+
+  let cursor = event.target.result;
+  is(cursor.value, data, "Got correct cursor value");
+
+  objectStore.get(cursor.key).onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, data, "Got correct get value");
+
+  info("Collecting garbage");
+
+  gc();
+
+  info("Done collecting garbage");
+
+  cursor.continue();
+  event = yield undefined;
+
+  is(event.target.result, null, "No more entries");
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-shared.ini
+++ b/dom/indexedDB/test/unit/xpcshell-shared.ini
@@ -31,16 +31,17 @@ skip-if = toolkit == 'android' # bug 107
 [test_index_empty_keyPath.js]
 [test_index_getAll.js]
 [test_index_getAllObjects.js]
 [test_index_object_cursors.js]
 [test_index_update_delete.js]
 [test_indexes.js]
 [test_indexes_bad_values.js]
 [test_indexes_funny_things.js]
+[test_invalid_cursor.js]
 [test_invalid_version.js]
 [test_key_requirements.js]
 [test_keys.js]
 [test_multientry.js]
 [test_names_sorted.js]
 [test_object_identity.js]
 [test_objectCursors.js]
 [test_objectStore_getAllKeys.js]