195a1820f1a3520fc64f5df2f2ea962b433f1f86
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBCursor.cpp
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "IDBCursor.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "ExceptionCode.h"
32 #include "IDBBindingUtilities.h"
33 #include "IDBDatabase.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBGetResult.h"
36 #include "IDBIndex.h"
37 #include "IDBObjectStore.h"
38 #include "IDBRequest.h"
39 #include "IDBTransaction.h"
40 #include "Logging.h"
41 #include "ScriptExecutionContext.h"
42 #include <heap/StrongInlines.h>
43 #include <runtime/JSCJSValueInlines.h>
44 #include <wtf/NeverDestroyed.h>
45
46 using namespace JSC;
47
48 namespace WebCore {
49
50 const AtomicString& IDBCursor::directionNext()
51 {
52     static NeverDestroyed<AtomicString> next("next", AtomicString::ConstructFromLiteral);
53     return next;
54 }
55
56 const AtomicString& IDBCursor::directionNextUnique()
57 {
58     static NeverDestroyed<AtomicString> nextunique("nextunique", AtomicString::ConstructFromLiteral);
59     return nextunique;
60 }
61
62 const AtomicString& IDBCursor::directionPrev()
63 {
64     static NeverDestroyed<AtomicString> prev("prev", AtomicString::ConstructFromLiteral);
65     return prev;
66 }
67
68 const AtomicString& IDBCursor::directionPrevUnique()
69 {
70     static NeverDestroyed<AtomicString> prevunique("prevunique", AtomicString::ConstructFromLiteral);
71     return prevunique;
72 }
73
74 Optional<IndexedDB::CursorDirection> IDBCursor::stringToDirection(const String& directionString)
75 {
76     if (directionString == directionNext())
77         return IndexedDB::CursorDirection::Next;
78     if (directionString == directionNextUnique())
79         return IndexedDB::CursorDirection::NextNoDuplicate;
80     if (directionString == directionPrev())
81         return IndexedDB::CursorDirection::Prev;
82     if (directionString == directionPrevUnique())
83         return IndexedDB::CursorDirection::PrevNoDuplicate;
84
85     return Nullopt;
86 }
87
88 const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction)
89 {
90     switch (direction) {
91     case IndexedDB::CursorDirection::Next:
92         return directionNext();
93
94     case IndexedDB::CursorDirection::NextNoDuplicate:
95         return directionNextUnique();
96
97     case IndexedDB::CursorDirection::Prev:
98         return directionPrev();
99
100     case IndexedDB::CursorDirection::PrevNoDuplicate:
101         return directionPrevUnique();
102
103     default:
104         ASSERT_NOT_REACHED();
105         return directionNext();
106     }
107 }
108
109 Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
110 {
111     return adoptRef(*new IDBCursor(transaction, objectStore, info));
112 }
113
114 Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
115 {
116     return adoptRef(*new IDBCursor(transaction, index, info));
117 }
118
119 IDBCursor::IDBCursor(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
120     : ActiveDOMObject(transaction.scriptExecutionContext())
121     , m_info(info)
122     , m_objectStore(&objectStore)
123 {
124     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
125
126     suspendIfNeeded();
127 }
128
129 IDBCursor::IDBCursor(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
130     : ActiveDOMObject(transaction.scriptExecutionContext())
131     , m_info(info)
132     , m_index(&index)
133 {
134     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
135
136     suspendIfNeeded();
137 }
138
139 IDBCursor::~IDBCursor()
140 {
141     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
142 }
143
144 bool IDBCursor::sourcesDeleted() const
145 {
146     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
147
148     if (m_objectStore)
149         return m_objectStore->isDeleted();
150
151     ASSERT(m_index);
152     return m_index->isDeleted() || m_index->objectStore().isDeleted();
153 }
154
155 IDBObjectStore& IDBCursor::effectiveObjectStore() const
156 {
157     if (m_objectStore)
158         return *m_objectStore;
159
160     ASSERT(m_index);
161     return m_index->objectStore();
162 }
163
164 IDBTransaction& IDBCursor::transaction() const
165 {
166     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
167     return effectiveObjectStore().transaction();
168 }
169
170 const String& IDBCursor::direction() const
171 {
172     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
173     return directionToString(m_info.cursorDirection());
174 }
175
176 ExceptionOr<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value)
177 {
178     LOG(IndexedDB, "IDBCursor::update");
179     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
180
181     if (sourcesDeleted())
182         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
183
184     if (!transaction().isActive())
185         return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.") };
186
187     if (transaction().isReadOnly())
188         return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction.") };
189
190     if (!m_gotValue)
191         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
192
193     if (!isKeyCursorWithValue())
194         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.") };
195
196     auto& objectStore = effectiveObjectStore();
197     auto& optionalKeyPath = objectStore.info().keyPath();
198     const bool usesInLineKeys = !!optionalKeyPath;
199     if (usesInLineKeys) {
200         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value());
201         IDBKeyData keyPathKeyData(keyPathKey.get());
202         if (!keyPathKey || keyPathKeyData != m_currentPrimaryKeyData)
203             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.") };
204     }
205
206     auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_currentPrimaryKey.get());
207     if (putResult.hasException())
208         return putResult.releaseException();
209
210     auto request = putResult.releaseReturnValue();
211     request->setSource(*this);
212     ++m_outstandingRequestCount;
213
214     return WTFMove(request);
215 }
216
217 ExceptionOr<void> IDBCursor::advance(unsigned count)
218 {
219     LOG(IndexedDB, "IDBCursor::advance");
220     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
221
222     if (!m_request)
223         return Exception { IDBDatabaseException::InvalidStateError };
224
225     if (!count)
226         return Exception { TypeError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0.") };
227
228     if (sourcesDeleted())
229         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
230
231     if (!transaction().isActive())
232         return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.") };
233
234     if (!m_gotValue)
235         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
236
237     m_gotValue = false;
238
239     uncheckedIterateCursor(IDBKeyData(), count);
240
241     return { };
242 }
243
244 ExceptionOr<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue)
245 {
246     RefPtr<IDBKey> key;
247     if (!keyValue.isUndefined())
248         key = scriptValueToIDBKey(execState, keyValue);
249
250     return continueFunction(key.get());
251 }
252
253 ExceptionOr<void> IDBCursor::continueFunction(const IDBKeyData& key)
254 {
255     LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data());
256     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
257
258     if (!m_request)
259         return Exception { IDBDatabaseException::InvalidStateError };
260
261     if (sourcesDeleted())
262         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
263
264     if (!transaction().isActive())
265         return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.") };
266
267     if (!m_gotValue)
268         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
269
270     if (!key.isNull() && !key.isValid())
271         return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.") };
272
273     if (m_info.isDirectionForward()) {
274         if (!key.isNull() && key.compare(m_currentKeyData) <= 0)
275             return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position.") };
276     } else {
277         if (!key.isNull() && key.compare(m_currentKeyData) >= 0)
278             return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position.") };
279     }
280
281     m_gotValue = false;
282
283     uncheckedIterateCursor(key, 0);
284
285     return { };
286 }
287
288 void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count)
289 {
290     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
291
292     ++m_outstandingRequestCount;
293
294     m_request->willIterateCursor(*this);
295     transaction().iterateCursor(*this, key, count);
296 }
297
298 ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(ExecState& state)
299 {
300     LOG(IndexedDB, "IDBCursor::deleteFunction");
301     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
302
303     if (sourcesDeleted())
304         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
305
306     if (!transaction().isActive())
307         return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.") };
308
309     if (transaction().isReadOnly())
310         return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction.") };
311
312     if (!m_gotValue)
313         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
314
315     if (!isKeyCursorWithValue())
316         return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.") };
317
318     auto result = effectiveObjectStore().deleteFunction(state, m_currentPrimaryKey.get());
319     if (result.hasException())
320         return result.releaseException();
321
322     auto request = result.releaseReturnValue();
323     request->setSource(*this);
324     ++m_outstandingRequestCount;
325
326     return WTFMove(request);
327 }
328
329 void IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
330 {
331     LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data());
332     ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
333
334     auto* context = request.scriptExecutionContext();
335     if (!context)
336         return;
337
338     auto* exec = context->execState();
339     if (!exec)
340         return;
341
342     if (!getResult.isDefined()) {
343         m_currentKey = { };
344         m_currentKeyData = { };
345         m_currentPrimaryKey = { };
346         m_currentPrimaryKeyData = { };
347         m_currentValue = { };
348
349         m_gotValue = false;
350         return;
351     }
352
353     auto& vm = context->vm();
354
355     m_currentKey = { vm, idbKeyDataToScriptValue(*exec, getResult.keyData()) };
356     m_currentKeyData = getResult.keyData();
357     m_currentPrimaryKey = { vm, idbKeyDataToScriptValue(*exec, getResult.primaryKeyData()) };
358     m_currentPrimaryKeyData = getResult.primaryKeyData();
359
360     if (isKeyCursorWithValue())
361         m_currentValue = { vm, deserializeIDBValueToJSValue(*exec, getResult.value()) };
362     else
363         m_currentValue = { };
364
365     m_gotValue = true;
366 }
367
368 const char* IDBCursor::activeDOMObjectName() const
369 {
370     return "IDBCursor";
371 }
372
373 bool IDBCursor::canSuspendForDocumentSuspension() const
374 {
375     return false;
376 }
377
378 bool IDBCursor::hasPendingActivity() const
379 {
380     return m_outstandingRequestCount;
381 }
382
383 void IDBCursor::decrementOutstandingRequestCount()
384 {
385     ASSERT(m_outstandingRequestCount);
386     --m_outstandingRequestCount;
387 }
388
389 } // namespace WebCore
390
391 #endif // ENABLE(INDEXED_DATABASE)