Bug 920800 - 'Add openKeyCursor() to IDBObjectStore'. r=janv.

--HG--
extra : transplant_source : %15%9F%F7%CE%81%09%84%04%8D%B9H%A3D%B5%FD%F8myPV
This commit is contained in:
Ben Turner 2013-09-25 16:11:47 -07:00
parent e67fcd8bf1
commit 3bac3a71aa
18 changed files with 1155 additions and 97 deletions

View File

@ -36,8 +36,7 @@ using mozilla::dom::OwningIDBObjectStoreOrIDBIndex;
using mozilla::ErrorResult;
static_assert(sizeof(size_t) >= sizeof(IDBCursor::Direction),
"Relying on conversion between size_t and "
"IDBCursor::Direction");
"Relying on conversion between size_t and IDBCursor::Direction");
namespace {
@ -62,6 +61,9 @@ public:
UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0;
protected:
virtual ~CursorHelper()
{ }
nsRefPtr<IDBCursor> mCursor;
private:
@ -79,19 +81,17 @@ public:
int32_t aCount)
: CursorHelper(aCursor), mCount(aCount)
{
NS_ASSERTION(aCount > 0, "Must have a count!");
}
~ContinueHelper()
{
IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCursor);
MOZ_ASSERT(aCount > 0);
}
virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
MOZ_OVERRIDE;
virtual nsresult GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
JS::MutableHandle<JS::Value> aVal)
MOZ_OVERRIDE;
virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
@ -106,6 +106,11 @@ public:
MOZ_OVERRIDE;
protected:
virtual ~ContinueHelper()
{
IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
}
virtual nsresult
BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0;
@ -124,10 +129,10 @@ protected:
if (mKey.IsUnset()) {
mCursor->mHaveValue = false;
}
else {
NS_ASSERTION(mCursor->mType == IDBCursor::OBJECTSTORE ||
!mObjectKey.IsUnset(), "Bad key!");
} else {
MOZ_ASSERT(mCursor->mType == IDBCursor::OBJECTSTORE ||
mCursor->mType == IDBCursor::OBJECTSTOREKEY ||
!mObjectKey.IsUnset());
// Set new values.
mCursor->mKey = mKey;
@ -153,11 +158,31 @@ public:
: ContinueHelper(aCursor, aCount)
{ }
protected:
virtual ~ContinueObjectStoreHelper()
{ }
private:
nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
};
class ContinueObjectStoreKeyHelper : public ContinueObjectStoreHelper
{
public:
ContinueObjectStoreKeyHelper(IDBCursor* aCursor,
uint32_t aCount)
: ContinueObjectStoreHelper(aCursor, aCount)
{ }
private:
virtual ~ContinueObjectStoreKeyHelper()
{ }
virtual nsresult
GatherResultsFromStatement(mozIStorageStatement* aStatement) MOZ_OVERRIDE;
};
class ContinueIndexHelper : public ContinueHelper
{
public:
@ -166,6 +191,10 @@ public:
: ContinueHelper(aCursor, aCount)
{ }
protected:
virtual ~ContinueIndexHelper()
{ }
private:
nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
@ -180,6 +209,9 @@ public:
{ }
private:
virtual ~ContinueIndexObjectHelper()
{ }
nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
};
@ -213,6 +245,32 @@ IDBCursor::Create(IDBRequest* aRequest,
return cursor.forget();
}
// static
already_AddRefed<IDBCursor>
IDBCursor::Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBObjectStore* aObjectStore,
Direction aDirection,
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey)
{
MOZ_ASSERT(aObjectStore);
MOZ_ASSERT(!aKey.IsUnset());
nsRefPtr<IDBCursor> cursor =
IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
aRangeKey, aContinueQuery, aContinueToQuery);
NS_ASSERTION(cursor, "This shouldn't fail!");
cursor->mObjectStore = aObjectStore;
cursor->mType = OBJECTSTOREKEY;
cursor->mKey = aKey;
return cursor.forget();
}
// static
already_AddRefed<IDBCursor>
IDBCursor::Create(IDBRequest* aRequest,
@ -293,7 +351,7 @@ IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection)
return PREV_UNIQUE;
default:
MOZ_CRASH("Unknown direction!");
MOZ_ASSUME_UNREACHABLE("Unknown direction!");
}
}
@ -396,8 +454,8 @@ IDBCursor::DropJSObjects()
void
IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aCount > 0, "Must have a count!");
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCount > 0);
if (!mTransaction->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
@ -411,10 +469,7 @@ IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
mContinueToKey = aKey;
#ifdef DEBUG
NS_ASSERTION(mRequest->ReadyState() == IDBRequestReadyState::Done,
"Should be DONE!");
#endif
MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done);
mRequest->Reset();
@ -424,6 +479,10 @@ IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
helper = new ContinueObjectStoreHelper(this, aCount);
break;
case OBJECTSTOREKEY:
helper = new ContinueObjectStoreKeyHelper(this, aCount);
break;
case INDEXKEY:
helper = new ContinueIndexHelper(this, aCount);
break;
@ -433,7 +492,7 @@ IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
break;
default:
NS_NOTREACHED("Unknown cursor type!");
MOZ_ASSUME_UNREACHABLE("Unknown cursor type!");
}
nsresult rv = helper->DispatchToTransactionPool();
@ -489,15 +548,26 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor)
JSObject*
IDBCursor::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return mType != INDEXKEY
? IDBCursorWithValueBinding::Wrap(aCx, aScope, this)
: IDBCursorBinding::Wrap(aCx, aScope, this);
MOZ_ASSERT(NS_IsMainThread());
switch (mType) {
case OBJECTSTORE:
case INDEXOBJECT:
return IDBCursorWithValueBinding::Wrap(aCx, aScope, this);
case OBJECTSTOREKEY:
case INDEXKEY:
return IDBCursorBinding::Wrap(aCx, aScope, this);
default:
MOZ_ASSUME_UNREACHABLE("Bad type!");
}
}
mozilla::dom::IDBCursorDirection
IDBCursor::GetDirection() const
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
switch (mDirection) {
case NEXT:
@ -512,33 +582,39 @@ IDBCursor::GetDirection() const
case PREV_UNIQUE:
return mozilla::dom::IDBCursorDirection::Prevunique;
case DIRECTION_INVALID:
default:
MOZ_CRASH("Unknown direction!");
return mozilla::dom::IDBCursorDirection::Next;
MOZ_ASSUME_UNREACHABLE("Bad direction!");
}
}
void
IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
if (mType == OBJECTSTORE) {
aSource.SetAsIDBObjectStore() = mObjectStore;
}
else {
aSource.SetAsIDBIndex() = mIndex;
switch (mType) {
case OBJECTSTORE:
case OBJECTSTOREKEY:
MOZ_ASSERT(mObjectStore);
aSource.SetAsIDBObjectStore() = mObjectStore;
break;
case INDEXKEY:
case INDEXOBJECT:
MOZ_ASSERT(mIndex);
aSource.SetAsIDBIndex() = mIndex;
break;
default:
MOZ_ASSUME_UNREACHABLE("Bad type!");
}
}
JS::Value
IDBCursor::GetKey(JSContext* aCx, ErrorResult& aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue);
if (!mHaveValue) {
return JSVAL_VOID;
@ -562,7 +638,7 @@ IDBCursor::GetKey(JSContext* aCx, ErrorResult& aRv)
JS::Value
IDBCursor::GetPrimaryKey(JSContext* aCx, ErrorResult& aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
if (!mHaveValue) {
return JSVAL_VOID;
@ -574,12 +650,9 @@ IDBCursor::GetPrimaryKey(JSContext* aCx, ErrorResult& aRv)
mRooted = true;
}
JSAutoRequest ar(aCx);
NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
!mObjectKey.IsUnset(), "Bad key!");
const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
const Key& key =
(mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey;
MOZ_ASSERT(!key.IsUnset());
aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
ENSURE_SUCCESS(aRv, JSVAL_VOID);
@ -593,8 +666,8 @@ IDBCursor::GetPrimaryKey(JSContext* aCx, ErrorResult& aRv)
JS::Value
IDBCursor::GetValue(JSContext* aCx, ErrorResult& aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mType != INDEXKEY, "GetValue shouldn't exist on index keys");
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
if (!mHaveValue) {
return JSVAL_VOID;
@ -626,7 +699,7 @@ IDBCursor::Continue(JSContext* aCx,
const Optional<JS::Handle<JS::Value> >& aKey,
ErrorResult &aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
Key key;
if (aKey.WasPassed()) {
@ -653,7 +726,7 @@ IDBCursor::Continue(JSContext* aCx,
break;
default:
NS_NOTREACHED("Unknown direction type!");
MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
}
}
@ -663,7 +736,7 @@ IDBCursor::Continue(JSContext* aCx,
}
#ifdef IDB_PROFILER_USE_MARKS
if (mType == OBJECTSTORE) {
if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) {
IDB_PROFILER_MARK("IndexedDB Request %llu: "
"database(%s).transaction(%s).objectStore(%s).cursor(%s)."
"continue(%s)",
@ -695,7 +768,7 @@ already_AddRefed<IDBRequest>
IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
if (!mTransaction->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
@ -707,18 +780,17 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
return nullptr;
}
if (!mHaveValue || mType == INDEXKEY) {
if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
}
NS_ASSERTION(mObjectStore, "This cannot be null!");
NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
NS_ASSERTION(mType != INDEXOBJECT || !mObjectKey.IsUnset(), "Bad key!");
MOZ_ASSERT(mObjectStore);
MOZ_ASSERT(!mKey.IsUnset());
MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
MOZ_ASSERT_IF(mType == INDEXOBJECT, !mObjectKey.IsUnset());
JSAutoRequest ar(aCx);
Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
nsRefPtr<IDBRequest> request;
if (mObjectStore->HasValidKeyPath()) {
@ -796,7 +868,7 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
already_AddRefed<IDBRequest>
IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
if (!mTransaction->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
@ -808,24 +880,23 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
return nullptr;
}
if (!mHaveValue || mType == INDEXKEY) {
if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
}
NS_ASSERTION(mObjectStore, "This cannot be null!");
NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
MOZ_ASSERT(mObjectStore);
MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
MOZ_ASSERT(!mKey.IsUnset());
Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
JS::Rooted<JS::Value> key(aCx);
aRv = objectKey.ToJSVal(aCx, &key);
ENSURE_SUCCESS(aRv, nullptr);
nsRefPtr<IDBRequest> request = mObjectStore->Delete(aCx, key, aRv);
if (aRv.Failed()) {
return nullptr;
}
ENSURE_SUCCESS(aRv, nullptr);
#ifdef IDB_PROFILER_USE_MARKS
{
@ -866,7 +937,7 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
void
IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread());
if (aCount < 1) {
aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT);
@ -879,7 +950,7 @@ IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
#ifdef IDB_PROFILER_USE_MARKS
{
if (mType == OBJECTSTORE) {
if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) {
IDB_PROFILER_MARK("IndexedDB Request %llu: "
"database(%s).transaction(%s).objectStore(%s)."
"cursor(%s).advance(%ld)",
@ -1135,6 +1206,9 @@ nsresult
ContinueObjectStoreHelper::BindArgumentsToStatement(
mozIStorageStatement* aStatement)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aStatement);
// Bind object store id.
nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mCursor->mObjectStore->Id());
@ -1166,12 +1240,29 @@ nsresult
ContinueObjectStoreHelper::GatherResultsFromStatement(
mozIStorageStatement* aStatement)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aStatement);
// Figure out what kind of key we have next.
nsresult rv = mKey.SetFromStatement(aStatement, 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
mDatabase, mCloneReadInfo);
mDatabase,
mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
ContinueObjectStoreKeyHelper::GatherResultsFromStatement(
mozIStorageStatement* aStatement)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aStatement);
nsresult rv = mKey.SetFromStatement(aStatement, 0);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

View File

@ -55,6 +55,7 @@ public:
enum Type
{
OBJECTSTORE = 0,
OBJECTSTOREKEY,
INDEXKEY,
INDEXOBJECT
};
@ -83,6 +84,18 @@ public:
const Key& aKey,
StructuredCloneReadInfo& aCloneReadInfo);
// For OBJECTSTOREKEY cursors.
static
already_AddRefed<IDBCursor>
Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBObjectStore* aObjectStore,
Direction aDirection,
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey);
// For INDEXKEY cursors.
static
already_AddRefed<IDBCursor>

View File

@ -1991,7 +1991,7 @@ OpenKeyCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams)
PROFILER_MAIN_THREAD_LABEL("IndexedDB",
"OpenKeyCursorHelper::"
"PackArgumentsForParentProcess");
"PackArgumentsForParentProcess [IDBIndex.cpp]");
OpenKeyCursorParams params;

View File

@ -345,6 +345,57 @@ private:
SerializedStructuredCloneReadInfo mSerializedCloneReadInfo;
};
class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper
{
public:
OpenKeyCursorHelper(IDBTransaction* aTransaction,
IDBRequest* aRequest,
IDBObjectStore* aObjectStore,
IDBKeyRange* aKeyRange,
IDBCursor::Direction aDirection)
: ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
mKeyRange(aKeyRange), mDirection(aDirection)
{ }
virtual nsresult
DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
virtual nsresult
GetSuccessResult(JSContext* aCx, JS::MutableHandleValue aVal) MOZ_OVERRIDE;
virtual void
ReleaseMainThreadObjects() MOZ_OVERRIDE;
virtual nsresult
PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
virtual ChildProcessSendResult
SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
virtual nsresult
UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
MOZ_OVERRIDE;
private:
~OpenKeyCursorHelper()
{ }
nsresult EnsureCursor();
// In-params.
nsRefPtr<IDBKeyRange> mKeyRange;
const IDBCursor::Direction mDirection;
// Out-params.
Key mKey;
nsCString mContinueQuery;
nsCString mContinueToQuery;
Key mRangeKey;
// Only used in the parent process.
nsRefPtr<IDBCursor> mCursor;
};
class CreateIndexHelper : public NoRequestObjectStoreHelper
{
public:
@ -2368,6 +2419,69 @@ IDBObjectStore::OpenCursorFromChildProcess(
return NS_OK;
}
nsresult
IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest,
size_t aDirection,
const Key& aKey,
IDBCursor** _retval)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
auto direction = static_cast<IDBCursor::Direction>(aDirection);
nsRefPtr<IDBCursor> cursor =
IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
EmptyCString(), EmptyCString(), aKey);
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
cursor.forget(_retval);
return NS_OK;
}
already_AddRefed<IDBRequest>
IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mTransaction->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
return nullptr;
}
nsRefPtr<IDBRequest> request = GenerateRequest(this);
if (!request) {
NS_WARNING("Failed to generate request!");
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
}
auto direction = static_cast<IDBCursor::Direction>(aDirection);
nsRefPtr<OpenKeyCursorHelper> helper =
new OpenKeyCursorHelper(mTransaction, request, this, aKeyRange, direction);
nsresult rv = helper->DispatchToTransactionPool();
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch!");
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
}
IDB_PROFILER_MARK("IndexedDB Request %llu: "
"database(%s).transaction(%s).objectStore(%s)."
"openKeyCursor(%s, %s)",
"IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()",
request->GetSerialNumber(),
IDB_PROFILER_STRING(Transaction()->Database()),
IDB_PROFILER_STRING(Transaction()),
IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
IDB_PROFILER_STRING(direction));
return request.forget();
}
void
IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo)
{
@ -2875,6 +2989,29 @@ IDBObjectStore::GetAllKeys(JSContext* aCx,
return GetAllKeysInternal(keyRange, limit, aRv);
}
already_AddRefed<IDBRequest>
IDBObjectStore::OpenKeyCursor(JSContext* aCx,
const Optional<JS::HandleValue>& aRange,
IDBCursorDirection aDirection, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mTransaction->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
return nullptr;
}
nsRefPtr<IDBKeyRange> keyRange;
if (aRange.WasPassed()) {
aRv = IDBKeyRange::FromJSVal(aCx, aRange.Value(), getter_AddRefs(keyRange));
ENSURE_SUCCESS(aRv, nullptr);
}
IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
return OpenKeyCursorInternal(keyRange, static_cast<size_t>(direction), aRv);
}
inline nsresult
CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
{
@ -3912,7 +4049,7 @@ OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
params.requestParent() = requestActor;
params.direction() = mDirection;
params.key() = mKey;
params.cloneInfo() = mSerializedCloneReadInfo;
params.optionalCloneInfo() = mSerializedCloneReadInfo;
params.blobsParent().SwapElements(blobsParent);
if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
@ -3968,6 +4105,311 @@ OpenCursorHelper::UnpackResponseFromParentProcess(
return NS_OK;
}
nsresult
OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
PROFILER_LABEL("IndexedDB",
"OpenKeyCursorHelper::DoDatabaseWork [IDBObjectStore.cpp]");
NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
NS_NAMED_LITERAL_CSTRING(id, "id");
NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
// Don't actually allocate space for this string yet.
const nsCSubstringTuple queryStart =
NS_LITERAL_CSTRING("SELECT ") + keyValue +
NS_LITERAL_CSTRING(" FROM object_data WHERE object_store_id = :") + id;
nsAutoCString keyRangeClause;
if (mKeyRange) {
mKeyRange->GetBindingClause(keyValue, keyRangeClause);
}
nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue;
switch (mDirection) {
case IDBCursor::NEXT:
case IDBCursor::NEXT_UNIQUE:
directionClause.AppendLiteral(" ASC");
break;
case IDBCursor::PREV:
case IDBCursor::PREV_UNIQUE:
directionClause.AppendLiteral(" DESC");
break;
default:
MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
}
nsCString firstQuery = queryStart + keyRangeClause + directionClause +
openLimit + NS_LITERAL_CSTRING("1");
nsCOMPtr<mozIStorageStatement> stmt =
mTransaction->GetCachedStatement(firstQuery);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (mKeyRange) {
rv = mKeyRange->BindToStatement(stmt);
NS_ENSURE_SUCCESS(rv, rv);
}
bool hasResult;
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!hasResult) {
mKey.Unset();
return NS_OK;
}
rv = mKey.SetFromStatement(stmt, 0);
NS_ENSURE_SUCCESS(rv, rv);
// Now we need to make the query to get the next match.
keyRangeClause.Truncate();
nsAutoCString continueToKeyRangeClause;
NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
switch (mDirection) {
case IDBCursor::NEXT:
case IDBCursor::NEXT_UNIQUE:
AppendConditionClause(keyValue, currentKey, false, false,
keyRangeClause);
AppendConditionClause(keyValue, currentKey, false, true,
continueToKeyRangeClause);
if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
AppendConditionClause(keyValue, rangeKey, true,
!mKeyRange->IsUpperOpen(), keyRangeClause);
AppendConditionClause(keyValue, rangeKey, true,
!mKeyRange->IsUpperOpen(),
continueToKeyRangeClause);
mRangeKey = mKeyRange->Upper();
}
break;
case IDBCursor::PREV:
case IDBCursor::PREV_UNIQUE:
AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
AppendConditionClause(keyValue, currentKey, true, true,
continueToKeyRangeClause);
if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
AppendConditionClause(keyValue, rangeKey, false,
!mKeyRange->IsLowerOpen(), keyRangeClause);
AppendConditionClause(keyValue, rangeKey, false,
!mKeyRange->IsLowerOpen(),
continueToKeyRangeClause);
mRangeKey = mKeyRange->Lower();
}
break;
default:
MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
}
mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit;
mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
openLimit;
return NS_OK;
}
nsresult
OpenKeyCursorHelper::EnsureCursor()
{
MOZ_ASSERT(NS_IsMainThread());
PROFILER_MAIN_THREAD_LABEL("IndexedDB",
"OpenKeyCursorHelper::EnsureCursor "
"[IDBObjectStore.cpp]");
if (mCursor || mKey.IsUnset()) {
return NS_OK;
}
mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
mRangeKey, mContinueQuery, mContinueToQuery,
mKey);
NS_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
}
nsresult
OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx,
JS::MutableHandleValue aVal)
{
MOZ_ASSERT(NS_IsMainThread());
PROFILER_MAIN_THREAD_LABEL("IndexedDB",
"OpenKeyCursorHelper::GetSuccessResult "
"[IDBObjectStore.cpp]");
nsresult rv = EnsureCursor();
NS_ENSURE_SUCCESS(rv, rv);
if (mCursor) {
rv = WrapNative(aCx, mCursor, aVal);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
aVal.setUndefined();
}
return NS_OK;
}
void
OpenKeyCursorHelper::ReleaseMainThreadObjects()
{
MOZ_ASSERT(NS_IsMainThread());
mKeyRange = nullptr;
mCursor = nullptr;
ObjectStoreHelper::ReleaseMainThreadObjects();
}
nsresult
OpenKeyCursorHelper::PackArgumentsForParentProcess(
ObjectStoreRequestParams& aParams)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
PROFILER_MAIN_THREAD_LABEL("IndexedDB",
"OpenKeyCursorHelper::"
"PackArgumentsForParentProcess "
"[IDBObjectStore.cpp]");
OpenKeyCursorParams params;
if (mKeyRange) {
KeyRange keyRange;
mKeyRange->ToSerializedKeyRange(keyRange);
params.optionalKeyRange() = keyRange;
}
else {
params.optionalKeyRange() = mozilla::void_t();
}
params.direction() = mDirection;
aParams = params;
return NS_OK;
}
AsyncConnectionHelper::ChildProcessSendResult
OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(!mCursor);
PROFILER_MAIN_THREAD_LABEL("IndexedDB",
"OpenKeyCursorHelper::SendResponseToChildProcess "
"[IDBObjectStore.cpp]");
IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
MOZ_ASSERT(actor);
if (NS_SUCCEEDED(aResultCode)) {
nsresult rv = EnsureCursor();
if (NS_FAILED(rv)) {
NS_WARNING("EnsureCursor failed!");
aResultCode = rv;
}
}
ResponseValue response;
if (NS_FAILED(aResultCode)) {
response = aResultCode;
} else {
OpenCursorResponse openCursorResponse;
if (!mCursor) {
openCursorResponse = mozilla::void_t();
}
else {
IndexedDBObjectStoreParent* objectStoreActor =
mObjectStore->GetActorParent();
MOZ_ASSERT(objectStoreActor);
IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent();
MOZ_ASSERT(requestActor);
ObjectStoreCursorConstructorParams params;
params.requestParent() = requestActor;
params.direction() = mDirection;
params.key() = mKey;
params.optionalCloneInfo() = mozilla::void_t();
if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
return Error;
}
}
response = openCursorResponse;
}
if (!actor->SendResponse(response)) {
return Error;
}
return Success_Sent;
}
nsresult
OpenKeyCursorHelper::UnpackResponseFromParentProcess(
const ResponseValue& aResponseValue)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse);
MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() ==
OpenCursorResponse::Tvoid_t ||
aResponseValue.get_OpenCursorResponse().type() ==
OpenCursorResponse::TPIndexedDBCursorChild);
MOZ_ASSERT(!mCursor);
PROFILER_MAIN_THREAD_LABEL("IndexedDB",
"OpenKeyCursorHelper::"
"UnpackResponseFromParentProcess "
"[IDBObjectStore.cpp]");
const OpenCursorResponse& response =
aResponseValue.get_OpenCursorResponse();
switch (response.type()) {
case OpenCursorResponse::Tvoid_t:
break;
case OpenCursorResponse::TPIndexedDBCursorChild: {
IndexedDBCursorChild* actor =
static_cast<IndexedDBCursorChild*>(
response.get_PIndexedDBCursorChild());
mCursor = actor->ForgetStrongCursor();
NS_ASSERTION(mCursor, "This should never be null!");
} break;
default:
MOZ_CRASH("Unknown response union type!");
}
return NS_OK;
}
nsresult
CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{

View File

@ -239,6 +239,11 @@ public:
size_t aDirection,
ErrorResult& aRv);
already_AddRefed<IDBRequest>
OpenKeyCursorInternal(IDBKeyRange* aKeyRange,
size_t aDirection,
ErrorResult& aRv);
nsresult
OpenCursorFromChildProcess(
IDBRequest* aRequest,
@ -248,6 +253,12 @@ public:
nsTArray<StructuredCloneFile>& aBlobs,
IDBCursor** _retval);
nsresult
OpenCursorFromChildProcess(IDBRequest* aRequest,
size_t aDirection,
const Key& aKey,
IDBCursor** _retval);
void
SetInfo(ObjectStoreInfo* aInfo);
@ -347,6 +358,10 @@ public:
GetAllKeys(JSContext* aCx, const Optional<JS::HandleValue>& aKey,
const Optional<uint32_t>& aLimit, ErrorResult& aRv);
already_AddRefed<IDBRequest>
OpenKeyCursor(JSContext* aCx, const Optional<JS::HandleValue>& aRange,
IDBCursorDirection aDirection, ErrorResult& aRv);
protected:
IDBObjectStore();
~IDBObjectStore();

View File

@ -742,17 +742,40 @@ IndexedDBObjectStoreChild::RecvPIndexedDBCursorConstructor(
size_t direction = static_cast<size_t>(aParams.direction());
nsTArray<StructuredCloneFile> blobs;
IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
nsRefPtr<IDBCursor> cursor;
nsresult rv =
mObjectStore->OpenCursorFromChildProcess(request, direction, aParams.key(),
aParams.cloneInfo(), blobs,
getter_AddRefs(cursor));
NS_ENSURE_SUCCESS(rv, false);
nsresult rv;
MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!");
typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType;
switch (aParams.optionalCloneInfo().type()) {
case CursorUnionType::TSerializedStructuredCloneReadInfo: {
nsTArray<StructuredCloneFile> blobs;
IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
const SerializedStructuredCloneReadInfo& cloneInfo =
aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo();
rv = mObjectStore->OpenCursorFromChildProcess(request, direction,
aParams.key(), cloneInfo,
blobs,
getter_AddRefs(cursor));
NS_ENSURE_SUCCESS(rv, false);
MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!");
} break;
case CursorUnionType::Tvoid_t:
MOZ_ASSERT(aParams.blobsChild().IsEmpty());
rv = mObjectStore->OpenCursorFromChildProcess(request, direction,
aParams.key(),
getter_AddRefs(cursor));
NS_ENSURE_SUCCESS(rv, false);
break;
default:
MOZ_CRASH("Unknown union type!");
}
actor->SetCursor(cursor);
return true;
@ -1088,7 +1111,8 @@ IndexedDBObjectStoreRequestChild::Recv__delete__(const ResponseValue& aResponse)
MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams);
break;
case ResponseValue::TOpenCursorResponse:
MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams);
MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams ||
mRequestType == ParamsUnionType::TOpenKeyCursorParams);
break;
default:

View File

@ -6,6 +6,7 @@ include "mozilla/dom/indexedDB/SerializationHelpers.h";
using mozilla::dom::indexedDB::Key;
using mozilla::dom::indexedDB::IDBCursor::Direction;
using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
using mozilla::void_t;
@ -57,6 +58,18 @@ struct OpenCursorParams
Direction direction;
};
struct OpenKeyCursorParams
{
OptionalKeyRange optionalKeyRange;
Direction direction;
};
union OptionalStructuredCloneReadInfo
{
SerializedStructuredCloneReadInfo;
void_t;
};
} // namespace ipc
} // namespace indexedDB
} // namespace dom

View File

@ -1118,6 +1118,9 @@ IndexedDBObjectStoreParent::RecvPIndexedDBRequestConstructor(
case ObjectStoreRequestParams::TOpenCursorParams:
return actor->OpenCursor(aParams.get_OpenCursorParams());
case ObjectStoreRequestParams::TOpenKeyCursorParams:
return actor->OpenKeyCursor(aParams.get_OpenKeyCursorParams());
default:
MOZ_CRASH("Unknown type!");
}
@ -1775,6 +1778,47 @@ IndexedDBObjectStoreRequestParent::OpenCursor(const OpenCursorParams& aParams)
return true;
}
bool
IndexedDBObjectStoreRequestParent::OpenKeyCursor(
const OpenKeyCursorParams& aParams)
{
MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams);
MOZ_ASSERT(mObjectStore);
const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
nsRefPtr<IDBKeyRange> keyRange;
switch (keyRangeUnion.type()) {
case ipc::OptionalKeyRange::TKeyRange:
keyRange =
IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
break;
case ipc::OptionalKeyRange::Tvoid_t:
break;
default:
MOZ_CRASH("Unknown param type!");
}
size_t direction = static_cast<size_t>(aParams.direction());
nsRefPtr<IDBRequest> request;
{
AutoSetCurrentTransaction asct(mObjectStore->Transaction());
ErrorResult rv;
request = mObjectStore->OpenKeyCursorInternal(keyRange, direction, rv);
ENSURE_SUCCESS(rv, false);
}
request->SetActor(this);
mRequest.swap(request);
return true;
}
/*******************************************************************************
* IndexedDBIndexRequestParent
******************************************************************************/

View File

@ -710,6 +710,7 @@ class IndexedDBObjectStoreRequestParent : public IndexedDBRequestParentBase
typedef ipc::GetAllKeysParams GetAllKeysParams;
typedef ipc::CountParams CountParams;
typedef ipc::OpenCursorParams OpenCursorParams;
typedef ipc::OpenKeyCursorParams OpenKeyCursorParams;
public:
IndexedDBObjectStoreRequestParent(IDBObjectStore* aObjectStore,
@ -743,6 +744,9 @@ public:
bool
OpenCursor(const OpenCursorParams& aParams);
bool
OpenKeyCursor(const OpenKeyCursorParams& aParams);
protected:
void
ConvertBlobActors(const InfallibleTArray<PBlobParent*>& aActors,

View File

@ -9,8 +9,6 @@ include protocol PIndexedDBRequest;
include IndexedDBParams;
using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
namespace mozilla {
namespace dom {
namespace indexedDB {
@ -22,12 +20,6 @@ struct GetKeyParams
KeyRange keyRange;
};
struct OpenKeyCursorParams
{
OptionalKeyRange optionalKeyRange;
Direction direction;
};
union IndexRequestParams
{
GetParams;
@ -39,12 +31,6 @@ union IndexRequestParams
OpenKeyCursorParams;
};
union OptionalStructuredCloneReadInfo
{
SerializedStructuredCloneReadInfo;
void_t;
};
struct IndexCursorConstructorParams
{
PIndexedDBRequest request;

View File

@ -12,7 +12,6 @@ include IndexedDBParams;
using mozilla::dom::indexedDB::IndexInfo;
using mozilla::dom::indexedDB::IndexUpdateInfo;
using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
using mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo;
namespace mozilla {
@ -59,6 +58,7 @@ union ObjectStoreRequestParams
ClearParams;
CountParams;
OpenCursorParams;
OpenKeyCursorParams;
};
struct CreateIndexParams
@ -82,7 +82,7 @@ struct ObjectStoreCursorConstructorParams
PIndexedDBRequest request;
Direction direction;
Key key;
SerializedStructuredCloneReadInfo cloneInfo;
OptionalStructuredCloneReadInfo optionalCloneInfo;
PBlob[] blobs;
};

View File

@ -38,6 +38,7 @@ tail =
[test_objectCursors.js]
[test_objectStore_getAllKeys.js]
[test_objectStore_inline_autoincrement_key_added_on_put.js]
[test_objectStore_openKeyCursor.js]
[test_objectStore_remove_values.js]
[test_odd_result_order.js]
[test_open_empty_db.js]

View File

@ -72,6 +72,7 @@ MOCHITEST_FILES = \
test_objectCursors.html \
test_objectStore_getAllKeys.html \
test_objectStore_inline_autoincrement_key_added_on_put.html \
test_objectStore_openKeyCursor.html \
test_objectStore_remove_values.html \
test_object_identity.html \
test_odd_result_order.html \

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property 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_objectStore_openKeyCursor.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -40,6 +40,7 @@ MOCHITEST_FILES = \
test_objectCursors.js \
test_objectStore_getAllKeys.js \
test_objectStore_inline_autoincrement_key_added_on_put.js \
test_objectStore_openKeyCursor.js \
test_objectStore_remove_values.js \
test_odd_result_order.js \
test_open_empty_db.js \

View File

@ -0,0 +1,400 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
let testGenerator = testSteps();
function testSteps() {
const dbName = this.window ?
window.location.pathname :
"test_objectStore_openKeyCursor";
const dbVersion = 1;
const objectStoreName = "foo";
const keyCount = 100;
let request = indexedDB.open(dbName, dbVersion);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield undefined;
info("Creating database");
let db = event.target.result;
let objectStore = db.createObjectStore(objectStoreName);
for (let i = 0; i < keyCount; i++) {
objectStore.add(true, i);
}
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
db = event.target.result;
objectStore = db.transaction(objectStoreName, "readwrite")
.objectStore(objectStoreName);
info("Getting all keys");
objectStore.getAllKeys().onsuccess = grabEventAndContinueHandler;
event = yield undefined;
const allKeys = event.target.result;
ok(Array.isArray(allKeys), "Got an array result");
is(allKeys.length, keyCount, "Got correct array length");
info("Opening normal key cursor");
let seenKeys = [];
objectStore.openKeyCursor().onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
is(cursor.source, objectStore, "Correct source");
is(cursor.direction, "next", "Correct direction");
let exception = null;
try {
cursor.update(10);
} catch(e) {
exception = e;
}
ok(!!exception, "update() throws for key cursor");
exception = null;
try {
cursor.delete();
} catch(e) {
exception = e;
}
ok(!!exception, "delete() throws for key cursor");
is(cursor.key, cursor.primaryKey, "key and primaryKey match");
ok(!("value" in cursor), "No 'value' property on key cursor");
seenKeys.push(cursor.key);
cursor.continue();
};
yield undefined;
is(seenKeys.length, allKeys.length, "Saw the right number of keys");
let match = true;
for (let i = 0; i < seenKeys.length; i++) {
if (seenKeys[i] !== allKeys[i]) {
match = false;
break;
}
}
ok(match, "All keys matched");
info("Opening key cursor with keyRange");
let keyRange = IDBKeyRange.bound(10, 20, false, true);
seenKeys = [];
objectStore.openKeyCursor(keyRange).onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
is(cursor.source, objectStore, "Correct source");
is(cursor.direction, "next", "Correct direction");
let exception = null;
try {
cursor.update(10);
} catch(e) {
exception = e;
}
ok(!!exception, "update() throws for key cursor");
exception = null;
try {
cursor.delete();
} catch(e) {
exception = e;
}
ok(!!exception, "delete() throws for key cursor");
is(cursor.key, cursor.primaryKey, "key and primaryKey match");
ok(!("value" in cursor), "No 'value' property on key cursor");
seenKeys.push(cursor.key);
cursor.continue();
};
yield undefined;
is(seenKeys.length, 10, "Saw the right number of keys");
match = true;
for (let i = 0; i < seenKeys.length; i++) {
if (seenKeys[i] !== allKeys[i + 10]) {
match = false;
break;
}
}
ok(match, "All keys matched");
info("Opening key cursor with unmatched keyRange");
keyRange = IDBKeyRange.bound(10000, 200000);
seenKeys = [];
objectStore.openKeyCursor(keyRange).onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
ok(false, "Shouldn't have any keys here");
cursor.continue();
};
yield undefined;
is(seenKeys.length, 0, "Saw the right number of keys");
info("Opening reverse key cursor");
seenKeys = [];
objectStore.openKeyCursor(null, "prev").onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
is(cursor.source, objectStore, "Correct source");
is(cursor.direction, "prev", "Correct direction");
let exception = null;
try {
cursor.update(10);
} catch(e) {
exception = e;
}
ok(!!exception, "update() throws for key cursor");
exception = null;
try {
cursor.delete();
} catch(e) {
exception = e;
}
ok(!!exception, "delete() throws for key cursor");
is(cursor.key, cursor.primaryKey, "key and primaryKey match");
ok(!("value" in cursor), "No 'value' property on key cursor");
seenKeys.push(cursor.key);
cursor.continue();
};
yield undefined;
is(seenKeys.length, allKeys.length, "Saw the right number of keys");
seenKeys.reverse();
match = true;
for (let i = 0; i < seenKeys.length; i++) {
if (seenKeys[i] !== allKeys[i]) {
match = false;
break;
}
}
ok(match, "All keys matched");
info("Opening reverse key cursor with key range");
keyRange = IDBKeyRange.bound(10, 20, false, true);
seenKeys = [];
objectStore.openKeyCursor(keyRange, "prev").onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
is(cursor.source, objectStore, "Correct source");
is(cursor.direction, "prev", "Correct direction");
let exception = null;
try {
cursor.update(10);
} catch(e) {
exception = e;
}
ok(!!exception, "update() throws for key cursor");
exception = null;
try {
cursor.delete();
} catch(e) {
exception = e;
}
ok(!!exception, "delete() throws for key cursor");
is(cursor.key, cursor.primaryKey, "key and primaryKey match");
ok(!("value" in cursor), "No 'value' property on key cursor");
seenKeys.push(cursor.key);
cursor.continue();
};
yield undefined;
is(seenKeys.length, 10, "Saw the right number of keys");
seenKeys.reverse();
match = true;
for (let i = 0; i < 10; i++) {
if (seenKeys[i] !== allKeys[i + 10]) {
match = false;
break;
}
}
ok(match, "All keys matched");
info("Opening reverse key cursor with unmatched key range");
keyRange = IDBKeyRange.bound(10000, 200000);
seenKeys = [];
objectStore.openKeyCursor(keyRange, "prev").onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
ok(false, "Shouldn't have any keys here");
cursor.continue();
};
yield undefined;
is(seenKeys.length, 0, "Saw the right number of keys");
info("Opening key cursor with advance");
seenKeys = [];
objectStore.openKeyCursor().onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
is(cursor.source, objectStore, "Correct source");
is(cursor.direction, "next", "Correct direction");
let exception = null;
try {
cursor.update(10);
} catch(e) {
exception = e;
}
ok(!!exception, "update() throws for key cursor");
exception = null;
try {
cursor.delete();
} catch(e) {
exception = e;
}
ok(!!exception, "delete() throws for key cursor");
is(cursor.key, cursor.primaryKey, "key and primaryKey match");
ok(!("value" in cursor), "No 'value' property on key cursor");
seenKeys.push(cursor.key);
if (seenKeys.length == 1) {
cursor.advance(10);
} else {
cursor.continue();
}
};
yield undefined;
is(seenKeys.length, allKeys.length - 9, "Saw the right number of keys");
let match = true;
for (let i = 0, j = 0; i < seenKeys.length; i++) {
if (seenKeys[i] !== allKeys[i + j]) {
match = false;
break;
}
if (i == 0) {
j = 9;
}
}
ok(match, "All keys matched");
info("Opening key cursor with continue-to-key");
seenKeys = [];
objectStore.openKeyCursor().onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
continueToNextStepSync();
return;
}
is(cursor.source, objectStore, "Correct source");
is(cursor.direction, "next", "Correct direction");
let exception = null;
try {
cursor.update(10);
} catch(e) {
exception = e;
}
ok(!!exception, "update() throws for key cursor");
exception = null;
try {
cursor.delete();
} catch(e) {
exception = e;
}
ok(!!exception, "delete() throws for key cursor");
is(cursor.key, cursor.primaryKey, "key and primaryKey match");
ok(!("value" in cursor), "No 'value' property on key cursor");
seenKeys.push(cursor.key);
if (seenKeys.length == 1) {
cursor.continue(10);
} else {
cursor.continue();
}
};
yield undefined;
is(seenKeys.length, allKeys.length - 9, "Saw the right number of keys");
let match = true;
for (let i = 0, j = 0; i < seenKeys.length; i++) {
if (seenKeys[i] !== allKeys[i + j]) {
match = false;
break;
}
if (i == 0) {
j = 9;
}
}
ok(match, "All keys matched");
finishTest();
yield undefined;
}

View File

@ -41,6 +41,7 @@ tail =
[test_objectCursors.js]
[test_objectStore_getAllKeys.js]
[test_objectStore_inline_autoincrement_key_added_on_put.js]
[test_objectStore_openKeyCursor.js]
[test_objectStore_remove_values.js]
[test_odd_result_order.js]
[test_open_empty_db.js]

View File

@ -71,4 +71,7 @@ partial interface IDBObjectStore {
[Pref="dom.indexedDB.experimental", Throws]
IDBRequest getAllKeys (optional any key, optional unsigned long limit);
[Pref="dom.indexedDB.experimental", Throws]
IDBRequest openKeyCursor (optional any range, optional IDBCursorDirection direction = "next");
};