IndexedDB: IDBRequest can be GCd during event dispatch
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBCursor.cpp
1 /*
2  * Copyright (C) 2010 Google 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBCursor.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBAny.h"
32 #include "IDBBindingUtilities.h"
33 #include "IDBCallbacks.h"
34 #include "IDBCursorBackendInterface.h"
35 #include "IDBKey.h"
36 #include "IDBObjectStore.h"
37 #include "IDBRequest.h"
38 #include "IDBTracing.h"
39 #include "IDBTransaction.h"
40 #include "ScriptExecutionContext.h"
41 #include "SerializedScriptValue.h"
42
43 namespace WebCore {
44
45 PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackendInterface> backend, Direction direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
46 {
47     return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
48 }
49
50 const AtomicString& IDBCursor::directionNext()
51 {
52     DEFINE_STATIC_LOCAL(AtomicString, next, ("next"));
53     return next;
54 }
55
56 const AtomicString& IDBCursor::directionNextUnique()
57 {
58     DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique"));
59     return nextunique;
60 }
61
62 const AtomicString& IDBCursor::directionPrev()
63 {
64     DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev"));
65     return prev;
66 }
67
68 const AtomicString& IDBCursor::directionPrevUnique()
69 {
70     DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique"));
71     return prevunique;
72 }
73
74
75 IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, Direction direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
76     : m_backend(backend)
77     , m_request(request)
78     , m_direction(direction)
79     , m_source(source)
80     , m_transaction(transaction)
81     , m_transactionNotifier(transaction, this)
82     , m_gotValue(false)
83     , m_valueIsDirty(true)
84 {
85     ASSERT(m_backend);
86     ASSERT(m_request);
87     ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
88     ASSERT(m_transaction);
89 }
90
91 IDBCursor::~IDBCursor()
92 {
93 }
94
95 const String& IDBCursor::direction() const
96 {
97     IDB_TRACE("IDBCursor::direction");
98     ExceptionCode ec = 0;
99     const AtomicString& direction = directionToString(m_direction, ec);
100     ASSERT(!ec);
101     return direction;
102 }
103
104 PassRefPtr<IDBKey> IDBCursor::key() const
105 {
106     IDB_TRACE("IDBCursor::key");
107     return m_currentKey;
108 }
109
110 PassRefPtr<IDBKey> IDBCursor::primaryKey() const
111 {
112     IDB_TRACE("IDBCursor::primaryKey");
113     return m_currentPrimaryKey;
114 }
115
116 PassRefPtr<IDBAny> IDBCursor::value()
117 {
118     IDB_TRACE("IDBCursor::value");
119     m_valueIsDirty = false;
120     return m_currentValue;
121 }
122
123 IDBAny* IDBCursor::source() const
124 {
125     return m_source.get();
126 }
127
128 PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, ExceptionCode& ec)
129 {
130     IDB_TRACE("IDBCursor::update");
131     RefPtr<SerializedScriptValue> value = prpValue;
132
133     if (!m_gotValue || isKeyCursor()) {
134         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
135         return 0;
136     }
137     if (!m_transaction->isActive()) {
138         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
139         return 0;
140     }
141     if (m_transaction->isReadOnly()) {
142         ec = IDBDatabaseException::READ_ONLY_ERR;
143         return 0;
144     }
145     if (value->blobURLs().size() > 0) {
146         // FIXME: Add Blob/File/FileList support
147         ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
148         return 0;
149     }
150
151     RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
152     const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
153     const bool usesInLineKeys = !keyPath.isNull();
154     if (usesInLineKeys) {
155         RefPtr<IDBKey> keyPathKey = createIDBKeyFromSerializedValueAndKeyPath(value, keyPath);
156         if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
157             ec = IDBDatabaseException::DATA_ERR;
158             return 0;
159         }
160     }
161
162     return objectStore->put(IDBObjectStoreBackendInterface::CursorUpdate, IDBAny::create(this), context, value, m_currentPrimaryKey, ec);
163 }
164
165 void IDBCursor::advance(unsigned long count, ExceptionCode& ec)
166 {
167     IDB_TRACE("IDBCursor::advance");
168     if (!m_gotValue) {
169         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
170         return;
171     }
172
173     if (!m_transaction->isActive()) {
174         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
175         return;
176     }
177
178     if (!count) {
179         ec = NATIVE_TYPE_ERR;
180         return;
181     }
182
183     m_request->setPendingCursor(this);
184     m_gotValue = false;
185     m_backend->advance(count, m_request, ec);
186     if (ec)
187         m_request->markEarlyDeath();
188 }
189
190 void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
191 {
192     IDB_TRACE("IDBCursor::continue");
193     if (key && !key->isValid()) {
194         ec = IDBDatabaseException::DATA_ERR;
195         return;
196     }
197
198     if (!m_transaction->isActive()) {
199         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
200         return;
201     }
202
203     if (!m_gotValue) {
204         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
205         return;
206     }
207
208     if (key) {
209         ASSERT(m_currentKey);
210         if (m_direction == IDBCursor::NEXT || m_direction == IDBCursor::NEXT_NO_DUPLICATE) {
211             if (!m_currentKey->isLessThan(key.get())) {
212                 ec = IDBDatabaseException::DATA_ERR;
213                 return;
214             }
215         } else {
216             if (!key->isLessThan(m_currentKey.get())) {
217                 ec = IDBDatabaseException::DATA_ERR;
218                 return;
219             }
220         }
221     }
222
223     // FIXME: We're not using the context from when continue was called, which means the callback
224     //        will be on the original context openCursor was called on. Is this right?
225     m_request->setPendingCursor(this);
226     m_gotValue = false;
227     m_backend->continueFunction(key, m_request, ec);
228     if (ec)
229         m_request->markEarlyDeath();
230 }
231
232 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
233 {
234     IDB_TRACE("IDBCursor::delete");
235     if (!m_transaction->isActive()) {
236         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
237         return 0;
238     }
239     if (m_transaction->isReadOnly()) {
240         ec = IDBDatabaseException::READ_ONLY_ERR;
241         return 0;
242     }
243
244     if (!m_gotValue) {
245         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
246         return 0;
247     }
248     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
249     m_backend->deleteFunction(request, ec);
250     if (ec) {
251         request->markEarlyDeath();
252         return 0;
253     }
254     return request.release();
255 }
256
257 void IDBCursor::postSuccessHandlerCallback()
258 {
259     m_backend->postSuccessHandlerCallback();
260 }
261
262 void IDBCursor::close()
263 {
264     ASSERT(m_request);
265     m_request->finishCursor();
266     m_request.clear();
267 }
268
269 void IDBCursor::setValueReady(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SerializedScriptValue> prpValue)
270 {
271     m_currentKey = key;
272     m_currentPrimaryKey = primaryKey;
273
274     RefPtr<SerializedScriptValue> value = prpValue;
275     if (!isKeyCursor()) {
276         RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
277         const IDBObjectStoreMetadata metadata = objectStore->metadata();
278         if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
279 #ifndef NDEBUG
280             RefPtr<IDBKey> expectedKey = createIDBKeyFromSerializedValueAndKeyPath(value, metadata.keyPath);
281             ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get()));
282 #endif
283             RefPtr<SerializedScriptValue> valueAfterInjection = injectIDBKeyIntoSerializedValue(m_currentPrimaryKey, value, metadata.keyPath);
284             ASSERT(valueAfterInjection);
285             // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278
286             if (valueAfterInjection)
287                 value = valueAfterInjection;
288         }
289     }
290     m_currentValue = IDBAny::create(value.release());
291
292     m_gotValue = true;
293     m_valueIsDirty = true;
294 }
295
296 PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore()
297 {
298     if (m_source->type() == IDBAny::IDBObjectStoreType)
299         return m_source->idbObjectStore();
300     RefPtr<IDBIndex> index = m_source->idbIndex();
301     return index->objectStore();
302 }
303
304 IDBCursor::Direction IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec)
305 {
306     if (directionString == IDBCursor::directionNext())
307         return IDBCursor::NEXT;
308     if (directionString == IDBCursor::directionNextUnique())
309         return IDBCursor::NEXT_NO_DUPLICATE;
310     if (directionString == IDBCursor::directionPrev())
311         return IDBCursor::PREV;
312     if (directionString == IDBCursor::directionPrevUnique())
313         return IDBCursor::PREV_NO_DUPLICATE;
314
315     ec = NATIVE_TYPE_ERR;
316     return IDBCursor::NEXT;
317 }
318
319 const AtomicString& IDBCursor::directionToString(unsigned short direction, ExceptionCode& ec)
320 {
321     switch (direction) {
322     case IDBCursor::NEXT:
323         return IDBCursor::directionNext();
324
325     case IDBCursor::NEXT_NO_DUPLICATE:
326         return IDBCursor::directionNextUnique();
327
328     case IDBCursor::PREV:
329         return IDBCursor::directionPrev();
330
331     case IDBCursor::PREV_NO_DUPLICATE:
332         return IDBCursor::directionPrevUnique();
333
334     default:
335         ec = NATIVE_TYPE_ERR;
336         return IDBCursor::directionNext();
337     }
338 }
339
340 } // namespace WebCore
341
342 #endif // ENABLE(INDEXED_DATABASE)