2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "IDBCursor.h"
29 #if ENABLE(INDEXED_DATABASE)
31 #include "ExceptionCode.h"
32 #include "IDBBindingUtilities.h"
33 #include "IDBDatabase.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBGetResult.h"
37 #include "IDBIterateCursorData.h"
38 #include "IDBObjectStore.h"
39 #include "IDBRequest.h"
40 #include "IDBTransaction.h"
42 #include "ScriptExecutionContext.h"
43 #include <heap/StrongInlines.h>
44 #include <runtime/JSCJSValueInlines.h>
45 #include <wtf/NeverDestroyed.h>
51 const AtomicString& IDBCursor::directionNext()
53 static NeverDestroyed<AtomicString> next("next", AtomicString::ConstructFromLiteral);
57 const AtomicString& IDBCursor::directionNextUnique()
59 static NeverDestroyed<AtomicString> nextunique("nextunique", AtomicString::ConstructFromLiteral);
63 const AtomicString& IDBCursor::directionPrev()
65 static NeverDestroyed<AtomicString> prev("prev", AtomicString::ConstructFromLiteral);
69 const AtomicString& IDBCursor::directionPrevUnique()
71 static NeverDestroyed<AtomicString> prevunique("prevunique", AtomicString::ConstructFromLiteral);
75 Optional<IndexedDB::CursorDirection> IDBCursor::stringToDirection(const String& directionString)
77 if (directionString == directionNext())
78 return IndexedDB::CursorDirection::Next;
79 if (directionString == directionNextUnique())
80 return IndexedDB::CursorDirection::NextNoDuplicate;
81 if (directionString == directionPrev())
82 return IndexedDB::CursorDirection::Prev;
83 if (directionString == directionPrevUnique())
84 return IndexedDB::CursorDirection::PrevNoDuplicate;
89 const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction)
92 case IndexedDB::CursorDirection::Next:
93 return directionNext();
95 case IndexedDB::CursorDirection::NextNoDuplicate:
96 return directionNextUnique();
98 case IndexedDB::CursorDirection::Prev:
99 return directionPrev();
101 case IndexedDB::CursorDirection::PrevNoDuplicate:
102 return directionPrevUnique();
105 ASSERT_NOT_REACHED();
106 return directionNext();
110 Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
112 return adoptRef(*new IDBCursor(transaction, objectStore, info));
115 Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
117 return adoptRef(*new IDBCursor(transaction, index, info));
120 IDBCursor::IDBCursor(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
121 : ActiveDOMObject(transaction.scriptExecutionContext())
123 , m_objectStore(&objectStore)
125 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
130 IDBCursor::IDBCursor(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
131 : ActiveDOMObject(transaction.scriptExecutionContext())
135 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
140 IDBCursor::~IDBCursor()
142 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
145 bool IDBCursor::sourcesDeleted() const
147 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
150 return m_objectStore->isDeleted();
153 return m_index->isDeleted() || m_index->objectStore().isDeleted();
156 IDBObjectStore& IDBCursor::effectiveObjectStore() const
159 return *m_objectStore;
162 return m_index->objectStore();
165 IDBTransaction& IDBCursor::transaction() const
167 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
168 return effectiveObjectStore().transaction();
171 const String& IDBCursor::direction() const
173 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
174 return directionToString(m_info.cursorDirection());
177 ExceptionOr<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value)
179 LOG(IndexedDB, "IDBCursor::update");
180 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
182 if (sourcesDeleted())
183 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
185 if (!transaction().isActive())
186 return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.") };
188 if (transaction().isReadOnly())
189 return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction.") };
192 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
194 if (!isKeyCursorWithValue())
195 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.") };
197 auto& objectStore = effectiveObjectStore();
198 auto& optionalKeyPath = objectStore.info().keyPath();
199 const bool usesInLineKeys = !!optionalKeyPath;
200 if (usesInLineKeys) {
201 RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value());
202 IDBKeyData keyPathKeyData(keyPathKey.get());
203 if (!keyPathKey || keyPathKeyData != m_currentPrimaryKeyData)
204 return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.") };
207 auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_currentPrimaryKey.get());
208 if (putResult.hasException())
209 return putResult.releaseException();
211 auto request = putResult.releaseReturnValue();
212 request->setSource(*this);
213 ++m_outstandingRequestCount;
215 return WTFMove(request);
218 ExceptionOr<void> IDBCursor::advance(unsigned count)
220 LOG(IndexedDB, "IDBCursor::advance");
221 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
224 return Exception { IDBDatabaseException::InvalidStateError };
227 return Exception { TypeError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0.") };
229 if (sourcesDeleted())
230 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
232 if (!transaction().isActive())
233 return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.") };
236 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
240 uncheckedIterateCursor(IDBKeyData(), count);
245 ExceptionOr<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue)
248 if (!keyValue.isUndefined())
249 key = scriptValueToIDBKey(execState, keyValue);
251 return continueFunction(key.get());
254 ExceptionOr<void> IDBCursor::continueFunction(const IDBKeyData& key)
256 LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data());
257 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
260 return Exception { IDBDatabaseException::InvalidStateError };
262 if (sourcesDeleted())
263 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
265 if (!transaction().isActive())
266 return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.") };
269 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
271 if (!key.isNull() && !key.isValid())
272 return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.") };
274 if (m_info.isDirectionForward()) {
275 if (!key.isNull() && key.compare(m_currentKeyData) <= 0)
276 return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position.") };
278 if (!key.isNull() && key.compare(m_currentKeyData) >= 0)
279 return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position.") };
284 uncheckedIterateCursor(key, 0);
289 void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count)
291 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
293 ++m_outstandingRequestCount;
295 m_request->willIterateCursor(*this);
296 transaction().iterateCursor(*this, { key, count });
299 ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(ExecState& state)
301 LOG(IndexedDB, "IDBCursor::deleteFunction");
302 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
304 if (sourcesDeleted())
305 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
307 if (!transaction().isActive())
308 return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.") };
310 if (transaction().isReadOnly())
311 return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction.") };
314 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
316 if (!isKeyCursorWithValue())
317 return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.") };
319 auto result = effectiveObjectStore().deleteFunction(state, m_currentPrimaryKey.get());
320 if (result.hasException())
321 return result.releaseException();
323 auto request = result.releaseReturnValue();
324 request->setSource(*this);
325 ++m_outstandingRequestCount;
327 return WTFMove(request);
330 void IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
332 LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data());
333 ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
335 auto* context = request.scriptExecutionContext();
339 auto* exec = context->execState();
343 if (!getResult.isDefined()) {
345 m_currentKeyData = { };
346 m_currentPrimaryKey = { };
347 m_currentPrimaryKeyData = { };
348 m_currentValue = { };
354 auto& vm = context->vm();
356 m_currentKey = { vm, idbKeyDataToScriptValue(*exec, getResult.keyData()) };
357 m_currentKeyData = getResult.keyData();
358 m_currentPrimaryKey = { vm, idbKeyDataToScriptValue(*exec, getResult.primaryKeyData()) };
359 m_currentPrimaryKeyData = getResult.primaryKeyData();
361 if (isKeyCursorWithValue())
362 m_currentValue = { vm, deserializeIDBValueToJSValue(*exec, getResult.value()) };
364 m_currentValue = { };
369 const char* IDBCursor::activeDOMObjectName() const
374 bool IDBCursor::canSuspendForDocumentSuspension() const
379 bool IDBCursor::hasPendingActivity() const
381 return m_outstandingRequestCount;
384 void IDBCursor::decrementOutstandingRequestCount()
386 ASSERT(m_outstandingRequestCount);
387 --m_outstandingRequestCount;
390 } // namespace WebCore
392 #endif // ENABLE(INDEXED_DATABASE)