// static already_AddRefed<IDBCursor> IDBCursor::Create(IDBRequest* aRequest, IDBTransaction* aTransaction, IDBIndex* aIndex, PRUint16 aDirection, const Key& aRangeKey, const nsACString& aContinueQuery, const nsACString& aContinueToQuery, const Key& aKey, const Key& aObjectKey) { NS_ASSERTION(aIndex, "Null pointer!"); NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!"); nsRefPtr<IDBCursor> cursor = IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), aDirection, aRangeKey, aContinueQuery, aContinueToQuery); NS_ASSERTION(cursor, "This shouldn't fail!"); cursor->mIndex = aIndex; cursor->mType = INDEXKEY; cursor->mKey = aKey, cursor->mObjectKey = aObjectKey; return cursor.forget(); }
// static already_AddRefed<IDBCursor> IDBCursor::Create(IDBIndex* aIndex, BackgroundCursorChild* aBackgroundActor, Direction aDirection, const Key& aKey, const Key& aPrimaryKey) { MOZ_ASSERT(aIndex); aIndex->AssertIsOnOwningThread(); MOZ_ASSERT(aBackgroundActor); MOZ_ASSERT(!aKey.IsUnset()); MOZ_ASSERT(!aPrimaryKey.IsUnset()); nsRefPtr<IDBCursor> cursor = new IDBCursor(Type_IndexKey, nullptr, aIndex, aIndex->ObjectStore()->Transaction(), aBackgroundActor, aDirection, aKey); cursor->mPrimaryKey = Move(aPrimaryKey); return cursor.forget(); }
nsresult KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, Key& aKey, ExtractOrCreateKeyCallback aCallback, void* aClosure) const { NS_ASSERTION(IsString(), "This doesn't make sense!"); JS::Rooted<JS::Value> value(aCx); aKey.Unset(); nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[0], value.address(), CreateProperties, aCallback, aClosure); if (NS_FAILED(rv)) { return rv; } if (NS_FAILED(aKey.AppendItem(aCx, false, value))) { NS_ASSERTION(aKey.IsUnset(), "Should be unset"); return value.isUndefined() ? NS_OK : NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } aKey.FinishArray(); return NS_OK; }
nsresult KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const { uint32_t len = mStrings.Length(); JS::Rooted<JS::Value> value(aCx); aKey.Unset(); for (uint32_t i = 0; i < len; ++i) { nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[i], value.address(), DoNotCreateProperties, nullptr, nullptr); if (NS_FAILED(rv)) { return rv; } if (NS_FAILED(aKey.AppendItem(aCx, IsArray() && i == 0, value))) { NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset"); return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } } aKey.FinishArray(); return NS_OK; }
NS_IMETHODIMP IDBIndex::GetKey(nsIVariant* aKey, nsIIDBRequest** _retval) { NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; } Key key; nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key); if (NS_FAILED(rv)) { return rv; } if (key.IsUnset()) { return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } nsRefPtr<IDBRequest> request = GenerateRequest(this); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr<GetKeyHelper> helper = new GetKeyHelper(transaction, request, this, key); rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); request.forget(_retval); return NS_OK; }
END_INDEXEDDB_NAMESPACE // static already_AddRefed<IDBCursor> IDBCursor::Create(IDBRequest* aRequest, IDBTransaction* aTransaction, IDBObjectStore* aObjectStore, PRUint16 aDirection, const Key& aRangeKey, const nsACString& aContinueQuery, const nsACString& aContinueToQuery, const Key& aKey, StructuredCloneReadInfo& aCloneReadInfo) { NS_ASSERTION(aObjectStore, "Null pointer!"); NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); nsRefPtr<IDBCursor> cursor = IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, aRangeKey, aContinueQuery, aContinueToQuery); NS_ASSERTION(cursor, "This shouldn't fail!"); cursor->mObjectStore = aObjectStore; cursor->mType = OBJECTSTORE; cursor->mKey = aKey; cursor->mCloneReadInfo.Swap(aCloneReadInfo); return cursor.forget(); }
// static already_AddRefed<IDBCursor> IDBCursor::Create(BackgroundCursorChild* aBackgroundActor, const Key& aKey, const Key& aPrimaryKey) { 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, aBackgroundActor, aKey); cursor->mPrimaryKey = Move(aPrimaryKey); return cursor.forget(); }
// static already_AddRefed<IDBCursor> IDBCursor::Create(BackgroundCursorChild* aBackgroundActor, const Key& aKey, const Key& aSortKey, const Key& aPrimaryKey, StructuredCloneReadInfo&& aCloneInfo) { MOZ_ASSERT(aBackgroundActor); aBackgroundActor->AssertIsOnOwningThread(); MOZ_ASSERT(aBackgroundActor->GetIndex()); MOZ_ASSERT(!aBackgroundActor->GetObjectStore()); MOZ_ASSERT(!aKey.IsUnset()); MOZ_ASSERT(!aPrimaryKey.IsUnset()); RefPtr<IDBCursor> cursor = new IDBCursor(Type_Index, aBackgroundActor, aKey); cursor->mSortKey = std::move(aSortKey); cursor->mPrimaryKey = std::move(aPrimaryKey); cursor->mCloneInfo = std::move(aCloneInfo); return cursor.forget(); }
void IDBCursor::Continue(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult &aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (!mHaveValue || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } Key key; aRv = key.SetFromJSVal(aCx, aKey); if (aRv.Failed()) { return; } if (!key.IsUnset()) { switch (mDirection) { case NEXT: case NEXT_UNIQUE: if (key <= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; case PREV: case PREV_UNIQUE: if (key >= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; default: MOZ_CRASH("Unknown direction type!"); } } const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); mRequest->SetLoggingSerialNumber(requestSerialNumber); if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).continue(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continue()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(mSourceObjectStore), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key)); } else { IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).continue(%s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continue()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()), IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key)); } mBackgroundActor->SendContinueInternal(ContinueParams(key)); mContinueCalled = true; }
void IDBCursor::ContinuePrimaryKey(JSContext* aCx, JS::Handle<JS::Value> aKey, JS::Handle<JS::Value> aPrimaryKey, ErrorResult &aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (IsSourceDeleted()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } if ((mType != Type_Index && mType != Type_IndexKey) || (mDirection != NEXT && mDirection != PREV)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (!mHaveValue || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } Key key; aRv = key.SetFromJSVal(aCx, aKey); if (aRv.Failed()) { return; } if (IsLocaleAware() && !key.IsUnset()) { Key tmp; aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale()); if (aRv.Failed()) { return; } key = tmp; } const Key& sortKey = IsLocaleAware() ? mSortKey : mKey; if (key.IsUnset()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } Key primaryKey; aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey); if (aRv.Failed()) { return; } if (primaryKey.IsUnset()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } switch (mDirection) { case NEXT: if (key < sortKey || (key == sortKey && primaryKey <= mPrimaryKey)) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; case PREV: if (key > sortKey || (key == sortKey && primaryKey >= mPrimaryKey)) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; default: MOZ_CRASH("Unknown direction type!"); } const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); mRequest->SetLoggingSerialNumber(requestSerialNumber); IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).continuePrimaryKey(%s, %s)", "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continuePrimaryKey()", IDB_LOG_ID_STRING(), mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()), IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key), IDB_LOG_STRINGIFY(primaryKey)); mBackgroundActor->SendContinueInternal(ContinuePrimaryKeyParams(key, primaryKey)); mContinueCalled = true; }
NS_IMETHODIMP ContinueRunnable::Run() { nsresult rv; if (!NS_IsMainThread()) { rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } // Remove cached stuff from last time. mCursor->mCachedKey = nsnull; mCursor->mCachedValue = JSVAL_VOID; mCursor->mHaveCachedValue = false; mCursor->mContinueCalled = false; if (mCursor->mType == IDBCursor::INDEX) { mCursor->mKeyData.RemoveElementAt(mCursor->mDataIndex); } else { mCursor->mData.RemoveElementAt(mCursor->mDataIndex); } if (mCursor->mDataIndex) { mCursor->mDataIndex--; } nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID); if (!variant) { NS_ERROR("Couldn't create variant!"); return NS_ERROR_FAILURE; } PRBool empty = mCursor->mType == IDBCursor::INDEX ? mCursor->mKeyData.IsEmpty() : mCursor->mData.IsEmpty(); if (empty) { rv = variant->SetAsEmpty(); NS_ENSURE_SUCCESS(rv, rv); } else { if (!mKey.IsUnset()) { NS_ASSERTION(!mKey.IsNull(), "Huh?!"); NS_WARNING("Using a slow O(n) search for continue(key), do something " "smarter!"); // Skip ahead to our next key match. PRInt32 index = PRInt32(mCursor->mDataIndex); if (mCursor->mType == IDBCursor::INDEX) { while (index >= 0) { const Key& key = mCursor->mKeyData[index].key; if (mKey == key) { break; } if (key < mKey) { index--; continue; } index = -1; break; } if (index >= 0) { mCursor->mDataIndex = PRUint32(index); mCursor->mKeyData.RemoveElementsAt(index + 1, mCursor->mKeyData.Length() - index - 1); } } else { while (index >= 0) { const Key& key = mCursor->mData[index].key; if (mKey == key) { break; } if (key < mKey) { index--; continue; } index = -1; break; } if (index >= 0) { mCursor->mDataIndex = PRUint32(index); mCursor->mData.RemoveElementsAt(index + 1, mCursor->mData.Length() - index - 1); } } } rv = variant->SetAsISupports(static_cast<IDBRequest::Generator*>(mCursor)); NS_ENSURE_SUCCESS(rv, rv); } rv = variant->SetWritable(PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIDOMEvent> event = IDBSuccessEvent::Create(mCursor->mRequest, variant, mCursor->mTransaction); if (!event) { NS_ERROR("Failed to create event!"); return NS_ERROR_FAILURE; } PRBool dummy; mCursor->mRequest->DispatchEvent(event, &dummy); mCursor->mTransaction->OnRequestFinished(); mCursor = nsnull; return NS_OK; }
NS_IMETHODIMP IDBCursor::Update(const jsval &aValue, JSContext* aCx, nsIIDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (mType != OBJECTSTORE) { NS_NOTYETIMPLEMENTED("Implement me!"); return NS_ERROR_NOT_IMPLEMENTED; } if (!mObjectStore->TransactionIsOpen()) { return NS_ERROR_UNEXPECTED; } if (!mObjectStore->IsWriteAllowed()) { return NS_ERROR_OBJECT_IS_IMMUTABLE; } const Key& key = mData[mDataIndex].key; NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!"); JSAutoRequest ar(aCx); js::AutoValueRooter clone(aCx); nsresult rv = nsContentUtils::CreateStructuredClone(aCx, aValue, clone.jsval_addr()); if (NS_FAILED(rv)) { return rv; } if (!mObjectStore->KeyPath().IsEmpty()) { // Make sure the object given has the correct keyPath value set on it or // we will add it. const nsString& keyPath = mObjectStore->KeyPath(); const jschar* keyPathChars = reinterpret_cast<const jschar*>(keyPath.get()); const size_t keyPathLen = keyPath.Length(); js::AutoValueRooter prop(aCx); JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(clone.jsval_value()), keyPathChars, keyPathLen, prop.jsval_addr()); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); if (JSVAL_IS_VOID(prop.jsval_value())) { rv = IDBObjectStore::GetJSValFromKey(key, aCx, prop.jsval_addr()); NS_ENSURE_SUCCESS(rv, rv); ok = JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(clone.jsval_value()), keyPathChars, keyPathLen, prop.jsval_value(), nsnull, nsnull, JSPROP_ENUMERATE); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); } else { Key newKey; rv = IDBObjectStore::GetKeyFromJSVal(prop.jsval_value(), newKey); NS_ENSURE_SUCCESS(rv, rv); if (newKey.IsUnset() || newKey.IsNull() || newKey != key) { return NS_ERROR_INVALID_ARG; } } } nsTArray<IndexUpdateInfo> indexUpdateInfo; rv = IDBObjectStore::GetIndexUpdateInfo(mObjectStore->GetObjectStoreInfo(), aCx, clone.jsval_value(), indexUpdateInfo); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIJSON> json(new nsJSON()); nsString jsonValue; rv = json->EncodeFromJSVal(clone.jsval_addr(), aCx, jsonValue); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr<IDBRequest> request = GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner()); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); nsRefPtr<UpdateHelper> helper = new UpdateHelper(mTransaction, request, mObjectStore->Id(), jsonValue, key, mObjectStore->IsAutoIncrement(), indexUpdateInfo); rv = helper->DispatchToTransactionPool(); NS_ENSURE_SUCCESS(rv, rv); request.forget(_retval); return NS_OK; }
void IDBCursor::Continue(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult &aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (!mHaveValue || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } Key key; aRv = key.SetFromJSVal(aCx, aKey); if (aRv.Failed()) { return; } if (!key.IsUnset()) { switch (mDirection) { case NEXT: case NEXT_UNIQUE: if (key <= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; case PREV: case PREV_UNIQUE: if (key >= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; default: MOZ_CRASH("Unknown direction type!"); } } mBackgroundActor->SendContinueInternal(ContinueParams(key)); mContinueCalled = true; #ifdef IDB_PROFILER_USE_MARKS if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).cursor(%s)." "continue(%s)", "IDBRequest[%llu] MT IDBCursor.continue()", Request()->GetSerialNumber(), IDB_PROFILER_STRING(Transaction()->Database()), IDB_PROFILER_STRING(Transaction()), IDB_PROFILER_STRING(mSourceObjectStore), IDB_PROFILER_STRING(mDirection), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); } else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "cursor(%s).continue(%s)", "IDBRequest[%llu] MT IDBCursor.continue()", Request()->GetSerialNumber(), IDB_PROFILER_STRING(Transaction()->Database()), IDB_PROFILER_STRING(Transaction()), IDB_PROFILER_STRING(mSourceIndex->ObjectStore()), IDB_PROFILER_STRING(mSourceIndex), IDB_PROFILER_STRING(mDirection), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); } #endif }