Bug 1271506 - Support of IDBObjectStore.getKey. r=khuey
authorBevis Tseng <btseng@mozilla.com>
Thu, 21 Jul 2016 16:31:59 +0800
changeset 349326 91c0b3e808250c412e99012540e766a551e69483
parent 349325 cc4286c4da37de45da2de785bb2a314ea4fba083
child 349327 b531c8bff6e905a1255936f057cad47760c364a9
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1271506
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1271506 - Support of IDBObjectStore.getKey. r=khuey
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/PBackgroundIDBRequest.ipdl
dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
dom/webidl/IDBObjectStore.webidl
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/IndexedDB/idbobjectstore_getKey.htm
testing/web-platform/tests/IndexedDB/idbobjectstore_getKey_exception_order.htm
testing/web-platform/tests/IndexedDB/interfaces.idl
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2486,16 +2486,20 @@ BackgroundRequestChild::Recv__delete__(c
       case RequestResponse::TObjectStorePutResponse:
         HandleResponse(aResponse.get_ObjectStorePutResponse().key());
         break;
 
       case RequestResponse::TObjectStoreGetResponse:
         HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
         break;
 
+      case RequestResponse::TObjectStoreGetKeyResponse:
+        HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key());
+        break;
+
       case RequestResponse::TObjectStoreGetAllResponse:
         HandleResponse(aResponse.get_ObjectStoreGetAllResponse().cloneInfos());
         break;
 
       case RequestResponse::TObjectStoreGetAllKeysResponse:
         HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys());
         break;
 
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -8180,33 +8180,34 @@ private:
 
   virtual nsresult
   DoDatabaseWork(DatabaseConnection* aConnection) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
 
-class ObjectStoreGetAllKeysRequestOp final
+class ObjectStoreGetKeyRequestOp final
   : public NormalTransactionOp
 {
   friend class TransactionBase;
 
-  const ObjectStoreGetAllKeysParams mParams;
+  const uint32_t mObjectStoreId;
+  const OptionalKeyRange mOptionalKeyRange;
+  const uint32_t mLimit;
+  const bool mGetAll;
   FallibleTArray<Key> mResponse;
 
 private:
   // Only created by TransactionBase.
-  ObjectStoreGetAllKeysRequestOp(TransactionBase* aTransaction,
-                                 const ObjectStoreGetAllKeysParams& aParams)
-    : NormalTransactionOp(aTransaction)
-    , mParams(aParams)
-  { }
-
-  ~ObjectStoreGetAllKeysRequestOp()
+  ObjectStoreGetKeyRequestOp(TransactionBase* aTransaction,
+                             const RequestParams& aParams,
+                             bool aGetAll);
+
+  ~ObjectStoreGetKeyRequestOp()
   { }
 
   virtual nsresult
   DoDatabaseWork(DatabaseConnection* aConnection) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
@@ -14524,16 +14525,32 @@ TransactionBase::VerifyRequestParams(con
       }
       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
+    case RequestParams::TObjectStoreGetKeyParams: {
+      const ObjectStoreGetKeyParams& params =
+        aParams.get_ObjectStoreGetKeyParams();
+      const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
+        GetMetadataForObjectStoreId(params.objectStoreId());
+      if (NS_WARN_IF(!objectStoreMetadata)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+      if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+      break;
+    }
+
     case RequestParams::TObjectStoreGetAllParams: {
       const ObjectStoreGetAllParams& params =
         aParams.get_ObjectStoreGetAllParams();
       const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
         GetMetadataForObjectStoreId(params.objectStoreId());
       if (NS_WARN_IF(!objectStoreMetadata)) {
         ASSERT_UNLESS_FUZZING();
         return false;
@@ -14986,20 +15003,24 @@ TransactionBase::AllocRequest(const Requ
         new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ false);
       break;
 
     case RequestParams::TObjectStoreGetAllParams:
       actor =
         new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ true);
       break;
 
+    case RequestParams::TObjectStoreGetKeyParams:
+      actor =
+        new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ false);
+      break;
+
     case RequestParams::TObjectStoreGetAllKeysParams:
       actor =
-        new ObjectStoreGetAllKeysRequestOp(this,
-                                     aParams.get_ObjectStoreGetAllKeysParams());
+        new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ true);
       break;
 
     case RequestParams::TObjectStoreDeleteParams:
       actor =
         new ObjectStoreDeleteRequestOp(this,
                                        aParams.get_ObjectStoreDeleteParams());
       break;
 
@@ -25887,67 +25908,88 @@ ObjectStoreGetRequestOp::GetResponse(Req
 
     nsresult rv = ConvertResponse(0, serializedInfo);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aResponse = rv;
     }
   }
 }
 
-nsresult
-ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
+ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
+                                                  TransactionBase* aTransaction,
+                                                  const RequestParams& aParams,
+                                                  bool aGetAll)
+  : NormalTransactionOp(aTransaction)
+  , mObjectStoreId(aGetAll ?
+                     aParams.get_ObjectStoreGetAllKeysParams().objectStoreId() :
+                     aParams.get_ObjectStoreGetKeyParams().objectStoreId())
+  , mOptionalKeyRange(aGetAll ?
+                        aParams.get_ObjectStoreGetAllKeysParams()
+                               .optionalKeyRange() :
+                        OptionalKeyRange(aParams.get_ObjectStoreGetKeyParams()
+                                                .keyRange()))
+  , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().limit() : 1)
+  , mGetAll(aGetAll)
+{
+  MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetKeyParams ||
+             aParams.type() == RequestParams::TObjectStoreGetAllKeysParams);
+  MOZ_ASSERT(mObjectStoreId);
+  MOZ_ASSERT_IF(!aGetAll,
+                mOptionalKeyRange.type() ==
+                  OptionalKeyRange::TSerializedKeyRange);
+}
+
+nsresult
+ObjectStoreGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
 {
   MOZ_ASSERT(aConnection);
   aConnection->AssertIsOnConnectionThread();
 
   PROFILER_LABEL("IndexedDB",
-                 "ObjectStoreGetAllKeysRequestOp::DoDatabaseWork",
+                 "ObjectStoreGetKeyRequestOp::DoDatabaseWork",
                  js::ProfileEntry::Category::STORAGE);
 
   const bool hasKeyRange =
-    mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange;
+      mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
 
   nsAutoCString keyRangeClause;
   if (hasKeyRange) {
-    GetBindingClauseForKeyRange(
-      mParams.optionalKeyRange().get_SerializedKeyRange(),
-      NS_LITERAL_CSTRING("key"),
-      keyRangeClause);
+    GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
+                                NS_LITERAL_CSTRING("key"),
+                                keyRangeClause);
   }
 
   nsAutoCString limitClause;
-  if (uint32_t limit = mParams.limit()) {
+  if (mLimit) {
     limitClause.AssignLiteral(" LIMIT ");
-    limitClause.AppendInt(limit);
+    limitClause.AppendInt(mLimit);
   }
 
   nsCString query =
     NS_LITERAL_CSTRING("SELECT key "
                        "FROM object_data "
                        "WHERE object_store_id = :osid") +
     keyRangeClause +
     NS_LITERAL_CSTRING(" ORDER BY key ASC") +
     limitClause;
 
   DatabaseConnection::CachedStatement stmt;
   nsresult rv = aConnection->GetCachedStatement(query, &stmt);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
-                             mParams.objectStoreId());
+  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (hasKeyRange) {
-    rv = BindKeyRangeToStatement(
-      mParams.optionalKeyRange().get_SerializedKeyRange(),
-      stmt);
+    rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
+                                 stmt);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   bool hasResult;
   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
     Key* key = mResponse.AppendElement(fallible);
@@ -25960,28 +26002,42 @@ ObjectStoreGetAllKeysRequestOp::DoDataba
       return rv;
     }
   }
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  return NS_OK;
-}
-
-void
-ObjectStoreGetAllKeysRequestOp::GetResponse(RequestResponse& aResponse)
-{
-  aResponse = ObjectStoreGetAllKeysResponse();
+  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
+
+  return NS_OK;
+}
+
+void
+ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse& aResponse)
+{
+  MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
+
+  if (mGetAll) {
+    aResponse = ObjectStoreGetAllKeysResponse();
+
+    if (!mResponse.IsEmpty()) {
+      nsTArray<Key>& response =
+        aResponse.get_ObjectStoreGetAllKeysResponse().keys();
+      mResponse.SwapElements(response);
+    }
+
+    return;
+  }
+
+  aResponse = ObjectStoreGetKeyResponse();
 
   if (!mResponse.IsEmpty()) {
-    nsTArray<Key>& response =
-      aResponse.get_ObjectStoreGetAllKeysResponse().keys();
-    mResponse.SwapElements(response);
+    aResponse.get_ObjectStoreGetKeyResponse().key() = Move(mResponse[0]);
   }
 }
 
 ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
                                          TransactionBase* aTransaction,
                                          const ObjectStoreDeleteParams& aParams)
   : NormalTransactionOp(aTransaction)
   , mParams(aParams)
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1697,19 +1697,20 @@ IDBObjectStore::IndexNames()
       listNames.InsertElementSorted(indexes[index].name());
     }
   }
 
   return list.forget();
 }
 
 already_AddRefed<IDBRequest>
-IDBObjectStore::Get(JSContext* aCx,
-                    JS::Handle<JS::Value> aKey,
-                    ErrorResult& aRv)
+IDBObjectStore::GetInternal(bool aKeyOnly,
+                            JSContext* aCx,
+                            JS::Handle<JS::Value> aKey,
+                            ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
 
   if (mDeletedSpec) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
@@ -1725,19 +1726,27 @@ IDBObjectStore::Get(JSContext* aCx,
   }
 
   if (!keyRange) {
     // Must specify a key or keyRange for get().
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
     return nullptr;
   }
 
-  ObjectStoreGetParams params;
-  params.objectStoreId() = Id();
-  keyRange->ToSerialized(params.keyRange());
+  const int64_t id = Id();
+
+  SerializedKeyRange serializedKeyRange;
+  keyRange->ToSerialized(serializedKeyRange);
+
+  RequestParams params;
+  if (aKeyOnly) {
+    params = ObjectStoreGetKeyParams(id, serializedKeyRange);
+  } else {
+    params = ObjectStoreGetParams(id, serializedKeyRange);
+  }
 
   RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
   MOZ_ASSERT(request);
 
   IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
                  "database(%s).transaction(%s).objectStore(%s).get(%s)",
                "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.get()",
                IDB_LOG_ID_STRING(),
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -198,17 +198,34 @@ public:
          ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
 
     return DeleteInternal(aCx, aKey, /* aFromCursor */ false, aRv);
   }
 
   already_AddRefed<IDBRequest>
-  Get(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
+  Get(JSContext* aCx,
+      JS::Handle<JS::Value> aKey,
+      ErrorResult& aRv)
+  {
+    AssertIsOnOwningThread();
+
+    return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv);
+  }
+
+  already_AddRefed<IDBRequest>
+  GetKey(JSContext* aCx,
+         JS::Handle<JS::Value> aKey,
+         ErrorResult& aRv)
+  {
+    AssertIsOnOwningThread();
+
+    return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv);
+  }
 
   already_AddRefed<IDBRequest>
   Clear(JSContext* aCx, ErrorResult& aRv);
 
   already_AddRefed<IDBIndex>
   CreateIndex(const nsAString& aName,
               const StringOrStringSequence& aKeyPath,
               const IDBIndexParameters& aOptionalParameters,
@@ -329,16 +346,22 @@ private:
 
   already_AddRefed<IDBRequest>
   DeleteInternal(JSContext* aCx,
                  JS::Handle<JS::Value> aKey,
                  bool aFromCursor,
                  ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
+  GetInternal(bool aKeyOnly,
+              JSContext* aCx,
+              JS::Handle<JS::Value> aKey,
+              ErrorResult& aRv);
+
+  already_AddRefed<IDBRequest>
   GetAllInternal(bool aKeysOnly,
                  JSContext* aCx,
                  JS::Handle<JS::Value> aKey,
                  const Optional<uint32_t>& aLimit,
                  ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
   OpenCursorInternal(bool aKeysOnly,
--- a/dom/indexedDB/PBackgroundIDBRequest.ipdl
+++ b/dom/indexedDB/PBackgroundIDBRequest.ipdl
@@ -31,16 +31,21 @@ struct ObjectStorePutResponse
   Key key;
 };
 
 struct ObjectStoreGetResponse
 {
   SerializedStructuredCloneReadInfo cloneInfo;
 };
 
+struct ObjectStoreGetKeyResponse
+{
+  Key key;
+};
+
 struct ObjectStoreGetAllResponse
 {
   SerializedStructuredCloneReadInfo[] cloneInfos;
 };
 
 struct ObjectStoreGetAllKeysResponse
 {
   Key[] keys;
@@ -81,16 +86,17 @@ struct IndexCountResponse
 {
   uint64_t count;
 };
 
 union RequestResponse
 {
   nsresult;
   ObjectStoreGetResponse;
+  ObjectStoreGetKeyResponse;
   ObjectStoreAddResponse;
   ObjectStorePutResponse;
   ObjectStoreDeleteResponse;
   ObjectStoreClearResponse;
   ObjectStoreCountResponse;
   ObjectStoreGetAllResponse;
   ObjectStoreGetAllKeysResponse;
   IndexGetResponse;
--- a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
+++ b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
@@ -182,16 +182,22 @@ struct ObjectStorePutParams
 };
 
 struct ObjectStoreGetParams
 {
   int64_t objectStoreId;
   SerializedKeyRange keyRange;
 };
 
+struct ObjectStoreGetKeyParams
+{
+  int64_t objectStoreId;
+  SerializedKeyRange keyRange;
+};
+
 struct ObjectStoreGetAllParams
 {
   int64_t objectStoreId;
   OptionalKeyRange optionalKeyRange;
   uint32_t limit;
 };
 
 struct ObjectStoreGetAllKeysParams
@@ -255,16 +261,17 @@ struct IndexCountParams
   OptionalKeyRange optionalKeyRange;
 };
 
 union RequestParams
 {
   ObjectStoreAddParams;
   ObjectStorePutParams;
   ObjectStoreGetParams;
+  ObjectStoreGetKeyParams;
   ObjectStoreGetAllParams;
   ObjectStoreGetAllKeysParams;
   ObjectStoreDeleteParams;
   ObjectStoreClearParams;
   ObjectStoreCountParams;
   IndexGetParams;
   IndexGetKeyParams;
   IndexGetAllParams;
--- a/dom/webidl/IDBObjectStore.webidl
+++ b/dom/webidl/IDBObjectStore.webidl
@@ -32,16 +32,19 @@ interface IDBObjectStore {
 
     [Throws]
     IDBRequest delete (any key);
 
     [Throws]
     IDBRequest get (any key);
 
     [Throws]
+    IDBRequest getKey (any key);
+
+    [Throws]
     IDBRequest clear ();
 
     [Throws]
     IDBRequest openCursor (optional any range, optional IDBCursorDirection direction = "next");
 
     [Throws]
     IDBIndex   createIndex (DOMString name, (DOMString or sequence<DOMString>) keyPath, optional IDBIndexParameters optionalParameters);
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -12201,16 +12201,24 @@
         "path": "IndexedDB/idbobjectstore_get7.htm",
         "url": "/IndexedDB/idbobjectstore_get7.htm"
       },
       {
         "path": "IndexedDB/idbobjectstore_getAll.html",
         "url": "/IndexedDB/idbobjectstore_getAll.html"
       },
       {
+        "path": "IndexedDB/idbobjectstore_getKey.htm",
+        "url": "/IndexedDB/idbobjectstore_getKey.htm"
+      },
+      {
+        "path": "IndexedDB/idbobjectstore_getKey_exception_order.htm",
+        "url": "/IndexedDB/idbobjectstore_getKey_exception_order.htm"
+      },
+      {
         "path": "IndexedDB/idbobjectstore_getAllKeys.html",
         "url": "/IndexedDB/idbobjectstore_getAllKeys.html"
       },
       {
         "path": "IndexedDB/idbobjectstore_index.htm",
         "url": "/IndexedDB/idbobjectstore_index.htm"
       },
       {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/IndexedDB/idbobjectstore_getKey.htm
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>IDBObjectStore.getKey()</title>
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support.js"></script>
+
+<script>
+indexeddb_test(
+    function(t, db, txn) {
+        var os = db.createObjectStore("store");
+
+        for(var i = 3; i < 6; i++) {
+            os.add("data" + i, i);
+        }
+    },
+    function(t, db) {
+        var os = db.transaction("store").objectStore("store");
+        var req1 = os.getKey(IDBKeyRange.lowerBound(6));
+
+        req1.onerror = t.unreached_func('getKey request should succeed');
+        req1.onsuccess = t.step_func(function(e) {
+            assert_equals(e.target.result, undefined, "Record should not exist - lowerBound(6)");
+        });
+
+        var req2 = os.getKey(IDBKeyRange.upperBound(2));
+
+        req2.onerror = t.unreached_func('getKey request should succeed');
+        req2.onsuccess = t.step_func(function(e) {
+            assert_equals(e.target.result, undefined, "Record should not exist - upperBound(2)");
+            t.done();
+        });
+    },
+    "Attempt to retrieve a record that doesn't exist"
+);
+
+indexeddb_test(
+    function(t, db, txn) {
+        var os = db.createObjectStore("store");
+
+        for(var i = 3; i < 6; i++) {
+            os.add("data" + i, i);
+        }
+    },
+    function(t, db) {
+        var os = db.transaction("store").objectStore("store");
+        var req = os.getKey(IDBKeyRange.bound(0, 10));
+
+        req.onerror = t.unreached_func('getKey request should succeed');
+        req.onsuccess = t.step_func(function(e) {
+            assert_equals(e.target.result, 3, "getKey(0-10)");
+            t.done();
+        });
+    },
+    "Returns the first key in the range"
+);
+</script>
+
+<div id="log"></div>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/IndexedDB/idbobjectstore_getKey_exception_order.htm
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>IDBObjectStore.getKey() - Exception Orders</title>
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support.js"></script>
+
+<script>
+indexeddb_test(
+    function(t, db, txn) {
+        var deletedObjectStore = db.createObjectStore("s");
+        db.deleteObjectStore("s");
+        txn.oncomplete = function() {
+            assert_throws("InvalidStateError", function() {
+                deletedObjectStore.getKey(1);
+            }, "Deletion check should precede transaction-state check");
+            t.done();
+        };
+    },
+    null,
+    "InvalidStateError(Deleted ObjectStore) vs. TransactionInactiveError"
+);
+
+indexeddb_test(
+    function(t, db, txn) {
+        var store = db.createObjectStore("s");
+        txn.oncomplete = function() {
+            assert_throws("TransactionInactiveError", function() {
+                store.getKey(null);
+            }, "Transaction-state check should precede key range check");
+            t.done();
+        };
+    },
+    null,
+    "TransactionInactiveError vs. DataError"
+);
+
+indexeddb_test(
+    function(t, db, txn) {
+        var store = db.createObjectStore("s");
+        assert_throws("DataError", function() {
+            store.getKey(null);
+        }, "key range check");
+        t.done();
+    },
+    null,
+    "DataError"
+);
+</script>
+
+<div id="log"></div>
--- a/testing/web-platform/tests/IndexedDB/interfaces.idl
+++ b/testing/web-platform/tests/IndexedDB/interfaces.idl
@@ -93,16 +93,17 @@ interface IDBObjectStore {
     readonly    attribute any            keyPath;
     readonly    attribute DOMStringList  indexNames;
     readonly    attribute IDBTransaction transaction;
     readonly    attribute boolean        autoIncrement;
     IDBRequest put (any value, optional any key);
     IDBRequest add (any value, optional any key);
     IDBRequest delete (any key);
     IDBRequest get (any key);
+    IDBRequest getKey (any key);
     IDBRequest clear ();
     IDBRequest openCursor (optional any range, optional IDBCursorDirection direction = "next");
     IDBIndex   createIndex (DOMString name, (DOMString or sequence<DOMString>) keyPath, optional IDBIndexParameters optionalParameters);
     IDBIndex   index (DOMString name);
     void       deleteIndex (DOMString indexName);
     IDBRequest count (optional any key);
 };