Blob type cannot be stored correctly in IDB when IDBObjectStore has autoIncrement...
[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 "IDBBindingUtilities.h"
32 #include "IDBDatabase.h"
33 #include "IDBGetResult.h"
34 #include "IDBIndex.h"
35 #include "IDBIterateCursorData.h"
36 #include "IDBObjectStore.h"
37 #include "IDBRequest.h"
38 #include "IDBTransaction.h"
39 #include "Logging.h"
40 #include "ScriptExecutionContext.h"
41 #include <JavaScriptCore/HeapInlines.h>
42 #include <JavaScriptCore/JSCJSValueInlines.h>
43 #include <JavaScriptCore/StrongInlines.h>
44
45 namespace WebCore {
46 using namespace JSC;
47
48 Ref<IDBCursor> IDBCursor::create(IDBObjectStore& objectStore, const IDBCursorInfo& info)
49 {
50     return adoptRef(*new IDBCursor(objectStore, info));
51 }
52
53 Ref<IDBCursor> IDBCursor::create(IDBIndex& index, const IDBCursorInfo& info)
54 {
55     return adoptRef(*new IDBCursor(index, info));
56 }
57
58 IDBCursor::IDBCursor(IDBObjectStore& objectStore, const IDBCursorInfo& info)
59     : m_info(info)
60     , m_source(&objectStore)
61 {
62     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
63 }
64
65 IDBCursor::IDBCursor(IDBIndex& index, const IDBCursorInfo& info)
66     : m_info(info)
67     , m_source(&index)
68 {
69     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
70 }
71
72 IDBCursor::~IDBCursor()
73 {
74     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
75 }
76
77 bool IDBCursor::sourcesDeleted() const
78 {
79     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
80
81     return WTF::switchOn(m_source,
82         [] (const RefPtr<IDBObjectStore>& objectStore) { return objectStore->isDeleted(); },
83         [] (const RefPtr<IDBIndex>& index) { return index->isDeleted() || index->objectStore().isDeleted(); }
84     );
85 }
86
87 IDBObjectStore& IDBCursor::effectiveObjectStore() const
88 {
89     return WTF::switchOn(m_source,
90         [] (const RefPtr<IDBObjectStore>& objectStore) -> IDBObjectStore& { return *objectStore; },
91         [] (const RefPtr<IDBIndex>& index) -> IDBObjectStore& { return index->objectStore(); }
92     );
93 }
94
95 IDBTransaction& IDBCursor::transaction() const
96 {
97     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
98     return effectiveObjectStore().transaction();
99 }
100
101 ExceptionOr<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value)
102 {
103     LOG(IndexedDB, "IDBCursor::update");
104     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
105
106     if (sourcesDeleted())
107         return Exception { InvalidStateError, "Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted."_s };
108
109     if (!transaction().isActive())
110         return Exception { TransactionInactiveError, "Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished."_s };
111
112     if (transaction().isReadOnly())
113         return Exception { ReadonlyError, "Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction."_s };
114
115     if (!m_gotValue)
116         return Exception { InvalidStateError, "Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end."_s };
117
118     if (!isKeyCursorWithValue())
119         return Exception { InvalidStateError, "Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor."_s };
120
121     auto& objectStore = effectiveObjectStore();
122     auto& optionalKeyPath = objectStore.info().keyPath();
123     const bool usesInLineKeys = !!optionalKeyPath;
124     if (usesInLineKeys) {
125         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value());
126         IDBKeyData keyPathKeyData(keyPathKey.get());
127         if (!keyPathKey || keyPathKeyData != m_primaryKeyData)
128             return Exception { DataError, "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."_s };
129     }
130
131     auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_primaryKey.copyRef());
132     if (putResult.hasException())
133         return putResult.releaseException();
134
135     auto request = putResult.releaseReturnValue();
136     request->setSource(*this);
137
138     return request;
139 }
140
141 ExceptionOr<void> IDBCursor::advance(unsigned count)
142 {
143     LOG(IndexedDB, "IDBCursor::advance");
144     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
145
146     if (!m_request)
147         return Exception { InvalidStateError };
148
149     if (!count)
150         return Exception { TypeError, "Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0."_s };
151
152     if (!transaction().isActive())
153         return Exception { TransactionInactiveError, "Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished."_s };
154
155     if (sourcesDeleted())
156         return Exception { InvalidStateError, "Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted."_s };
157
158     if (!m_gotValue)
159         return Exception { InvalidStateError, "Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end."_s };
160
161     m_gotValue = false;
162
163     uncheckedIterateCursor(IDBKeyData(), count);
164
165     return { };
166 }
167
168 ExceptionOr<void> IDBCursor::continuePrimaryKey(ExecState& state, JSValue keyValue, JSValue primaryKeyValue)
169 {
170     if (!m_request)
171         return Exception { InvalidStateError };
172
173     if (!transaction().isActive())
174         return Exception { TransactionInactiveError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The transaction is inactive or finished."_s };
175
176     if (sourcesDeleted())
177         return Exception { InvalidStateError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source or effective object store has been deleted."_s };
178
179     if (!WTF::holds_alternative<RefPtr<IDBIndex>>(m_source))
180         return Exception { InvalidAccessError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source is not an index."_s };
181
182     auto direction = m_info.cursorDirection();
183     if (direction != IndexedDB::CursorDirection::Next && direction != IndexedDB::CursorDirection::Prev)
184         return Exception { InvalidAccessError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's direction must be either \"next\" or \"prev\"."_s };
185
186     if (!m_gotValue)
187         return Exception { InvalidStateError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor is being iterated or has iterated past its end."_s };
188
189     RefPtr<IDBKey> key = scriptValueToIDBKey(state, keyValue);
190     if (!key->isValid())
191         return Exception { DataError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is not a valid key."_s };
192
193     RefPtr<IDBKey> primaryKey = scriptValueToIDBKey(state, primaryKeyValue);
194     if (!primaryKey->isValid())
195         return Exception { DataError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The second parameter is not a valid key."_s };
196
197     IDBKeyData keyData = { key.get() };
198     IDBKeyData primaryKeyData = { primaryKey.get() };
199
200     if (keyData < m_keyData && direction == IndexedDB::CursorDirection::Next)
201         return Exception { DataError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is less than this cursor's position and this cursor's direction is \"next\"."_s };
202
203     if (keyData > m_keyData && direction == IndexedDB::CursorDirection::Prev)
204         return Exception { DataError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is greater than this cursor's position and this cursor's direction is \"prev\"."_s };
205
206     if (keyData == m_keyData) {
207         if (primaryKeyData <= m_primaryKeyData && direction == IndexedDB::CursorDirection::Next)
208             return Exception { DataError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position less-than-or-equal-to this cursor's position and this cursor's direction is \"next\"."_s };
209         if (primaryKeyData >= m_primaryKeyData && direction == IndexedDB::CursorDirection::Prev)
210             return Exception { DataError, "Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position greater-than-or-equal-to this cursor's position and this cursor's direction is \"prev\"."_s };
211     }
212
213     m_gotValue = false;
214
215     uncheckedIterateCursor(keyData, primaryKeyData);
216
217     return { };
218 }
219
220 ExceptionOr<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue)
221 {
222     RefPtr<IDBKey> key;
223     if (!keyValue.isUndefined())
224         key = scriptValueToIDBKey(execState, keyValue);
225
226     return continueFunction(key.get());
227 }
228
229 ExceptionOr<void> IDBCursor::continueFunction(const IDBKeyData& key)
230 {
231     LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data());
232     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
233
234     if (!m_request)
235         return Exception { InvalidStateError };
236
237     if (!transaction().isActive())
238         return Exception { TransactionInactiveError, "Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished."_s };
239
240     if (sourcesDeleted())
241         return Exception { InvalidStateError, "Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted."_s };
242
243     if (!m_gotValue)
244         return Exception { InvalidStateError, "Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end."_s };
245
246     if (!key.isNull() && !key.isValid())
247         return Exception { DataError, "Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key."_s };
248
249     if (m_info.isDirectionForward()) {
250         if (!key.isNull() && key.compare(m_keyData) <= 0)
251             return Exception { DataError, "Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position."_s };
252     } else {
253         if (!key.isNull() && key.compare(m_keyData) >= 0)
254             return Exception { DataError, "Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position."_s };
255     }
256
257     m_gotValue = false;
258
259     uncheckedIterateCursor(key, 0);
260
261     return { };
262 }
263
264 void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count)
265 {
266     ASSERT(m_request);
267     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
268
269     m_request->willIterateCursor(*this);
270     transaction().iterateCursor(*this, { key, { }, count });
271 }
272
273 void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, const IDBKeyData& primaryKey)
274 {
275     ASSERT(m_request);
276     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
277
278     m_request->willIterateCursor(*this);
279     transaction().iterateCursor(*this, { key, primaryKey, 0 });
280 }
281
282 ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(ExecState& state)
283 {
284     LOG(IndexedDB, "IDBCursor::deleteFunction");
285     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
286
287     if (sourcesDeleted())
288         return Exception { InvalidStateError, "Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted."_s };
289
290     if (!transaction().isActive())
291         return Exception { TransactionInactiveError, "Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished."_s };
292
293     if (transaction().isReadOnly())
294         return Exception { ReadonlyError, "Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction."_s };
295
296     if (!m_gotValue)
297         return Exception { InvalidStateError, "Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end."_s };
298
299     if (!isKeyCursorWithValue())
300         return Exception { InvalidStateError, "Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor."_s };
301
302     auto result = effectiveObjectStore().deleteFunction(state, IDBKeyRange::create(m_primaryKey.copyRef()).ptr());
303     if (result.hasException())
304         return result.releaseException();
305
306     auto request = result.releaseReturnValue();
307     request->setSource(*this);
308
309     return request;
310 }
311
312 bool IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
313 {
314     LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data());
315     ASSERT(&effectiveObjectStore().transaction().database().originThread() == &Thread::current());
316
317     auto* context = request.scriptExecutionContext();
318     if (!context)
319         return false;
320
321     VM& vm = context->vm();
322     JSLockHolder lock(vm);
323
324     m_keyWrapper = { };
325     m_primaryKeyWrapper = { };
326     m_valueWrapper = { };
327
328     if (!getResult.isDefined()) {
329         m_keyData = { };
330         m_key = nullptr;
331         m_primaryKeyData = { };
332         m_primaryKey = nullptr;
333         m_value = { };
334
335         m_gotValue = false;
336         return false;
337     }
338
339     m_keyData = getResult.keyData();
340     m_key = m_keyData.maybeCreateIDBKey();
341     m_primaryKeyData = getResult.primaryKeyData();
342     m_primaryKey = m_primaryKeyData.maybeCreateIDBKey();
343
344     if (isKeyCursorWithValue()) {
345         m_value = getResult.value();
346         m_keyPath = getResult.keyPath();
347     }
348
349     m_gotValue = true;
350     return true;
351 }
352
353 void IDBCursor::clearWrappers()
354 {
355     m_keyWrapper.clear();
356     m_primaryKeyWrapper.clear();
357     m_valueWrapper.clear();
358 }
359
360 } // namespace WebCore
361
362 #endif // ENABLE(INDEXED_DATABASE)