IDB: Index cursor complete advance() and iterate() support
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBRequest.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  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "IDBRequest.h"
31
32 #if ENABLE(INDEXED_DATABASE)
33
34 #include "EventException.h"
35 #include "EventListener.h"
36 #include "EventNames.h"
37 #include "EventQueue.h"
38 #include "ExceptionCodePlaceholder.h"
39 #include "IDBBindingUtilities.h"
40 #include "IDBCursorBackend.h"
41 #include "IDBCursorWithValue.h"
42 #include "IDBDatabase.h"
43 #include "IDBEventDispatcher.h"
44 #include "IDBTransaction.h"
45 #include "Logging.h"
46 #include "ScriptExecutionContext.h"
47
48 namespace WebCore {
49
50 PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
51 {
52     RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, IDBDatabaseBackend::NormalTask, transaction)));
53     request->suspendIfNeeded();
54     // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
55     if (transaction)
56         transaction->registerRequest(request.get());
57     return request.release();
58 }
59
60 PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
61 {
62     RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, taskType, transaction)));
63     request->suspendIfNeeded();
64     // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
65     if (transaction)
66         transaction->registerRequest(request.get());
67     return request.release();
68 }
69
70 IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
71     : ActiveDOMObject(context)
72     , m_result(0)
73     , m_errorCode(0)
74     , m_contextStopped(false)
75     , m_transaction(transaction)
76     , m_readyState(PENDING)
77     , m_requestAborted(false)
78     , m_source(source)
79     , m_taskType(taskType)
80     , m_hasPendingActivity(true)
81     , m_cursorType(IndexedDB::CursorType::KeyAndValue)
82     , m_cursorDirection(IndexedDB::CursorDirection::Next)
83     , m_cursorFinished(false)
84     , m_pendingCursor(0)
85     , m_didFireUpgradeNeededEvent(false)
86     , m_preventPropagation(false)
87     , m_requestState(context)
88 {
89 }
90
91 IDBRequest::~IDBRequest()
92 {
93     ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !scriptExecutionContext());
94 }
95
96 PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const
97 {
98     if (m_readyState != DONE) {
99         ec = IDBDatabaseException::InvalidStateError;
100         return 0;
101     }
102     return m_result;
103 }
104
105 PassRefPtr<DOMError> IDBRequest::error(ExceptionCode& ec) const
106 {
107     if (m_readyState != DONE) {
108         ec = IDBDatabaseException::InvalidStateError;
109         return 0;
110     }
111     return m_error;
112 }
113
114 unsigned short IDBRequest::errorCode(ExceptionCode& ec) const
115 {
116     if (m_readyState != DONE) {
117         ec = IDBDatabaseException::InvalidStateError;
118         return 0;
119     }
120     return m_errorCode;
121 }
122
123 PassRefPtr<IDBAny> IDBRequest::source() const
124 {
125     return m_source;
126 }
127
128 PassRefPtr<IDBTransaction> IDBRequest::transaction() const
129 {
130     return m_transaction;
131 }
132
133 const String& IDBRequest::readyState() const
134 {
135     ASSERT(m_readyState == PENDING || m_readyState == DONE);
136     DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
137     DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
138
139     if (m_readyState == PENDING)
140         return pending;
141
142     return done;
143 }
144
145 void IDBRequest::markEarlyDeath()
146 {
147     ASSERT(m_readyState == PENDING);
148     m_readyState = EarlyDeath;
149     if (m_transaction)
150         m_transaction->unregisterRequest(this);
151 }
152
153 void IDBRequest::abort()
154 {
155     ASSERT(!m_requestAborted);
156     if (m_contextStopped || !scriptExecutionContext())
157         return;
158     ASSERT(m_readyState == PENDING || m_readyState == DONE);
159     if (m_readyState == DONE)
160         return;
161
162     // Enqueued events may be the only reference to this object.
163     RefPtr<IDBRequest> self(this);
164
165     EventQueue& eventQueue = scriptExecutionContext()->eventQueue();
166     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
167         bool removed = eventQueue.cancelEvent(*m_enqueuedEvents[i]);
168         ASSERT_UNUSED(removed, removed);
169     }
170     m_enqueuedEvents.clear();
171
172     m_errorCode = 0;
173     m_error.clear();
174     m_errorMessage = String();
175     m_result.clear();
176     onError(IDBDatabaseError::create(IDBDatabaseException::AbortError));
177     m_requestAborted = true;
178 }
179
180 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction)
181 {
182     ASSERT(m_readyState == PENDING);
183     ASSERT(!m_pendingCursor);
184     m_cursorType = cursorType;
185     m_cursorDirection = direction;
186 }
187
188 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
189 {
190     ASSERT(m_readyState == DONE);
191     ASSERT(scriptExecutionContext());
192     ASSERT(m_transaction);
193     ASSERT(!m_pendingCursor);
194     ASSERT(cursor == getResultCursor());
195
196     m_pendingCursor = cursor;
197     m_result.clear();
198     m_readyState = PENDING;
199     m_errorCode = 0;
200     m_error.clear();
201     m_errorMessage = String();
202     m_transaction->registerRequest(this);
203 }
204
205 PassRefPtr<IDBCursor> IDBRequest::getResultCursor()
206 {
207     if (!m_result)
208         return 0;
209     if (m_result->type() == IDBAny::IDBCursorType)
210         return m_result->idbCursor();
211     if (m_result->type() == IDBAny::IDBCursorWithValueType)
212         return m_result->idbCursorWithValue();
213     return 0;
214 }
215
216 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, const Deprecated::ScriptValue& value)
217 {
218     ASSERT(m_readyState == PENDING);
219     m_cursorKey = key;
220     m_cursorPrimaryKey = primaryKey;
221     m_cursorValue = value;
222
223     if (m_cursorType == IndexedDB::CursorType::KeyOnly) {
224         m_result = IDBAny::create(cursor);
225         return;
226     }
227
228     m_result = IDBAny::create(IDBCursorWithValue::fromCursor(cursor));
229 }
230
231 void IDBRequest::finishCursor()
232 {
233     m_cursorFinished = true;
234     if (m_readyState != PENDING)
235         m_hasPendingActivity = false;
236 }
237
238 bool IDBRequest::shouldEnqueueEvent() const
239 {
240     if (m_contextStopped || !scriptExecutionContext())
241         return false;
242     ASSERT(m_readyState == PENDING || m_readyState == DONE);
243     if (m_requestAborted)
244         return false;
245     ASSERT(m_readyState == PENDING);
246     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
247     return true;
248 }
249
250 void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
251 {
252     LOG(StorageAPI, "IDBRequest::onError()");
253     if (!shouldEnqueueEvent())
254         return;
255
256     m_errorCode = error->code();
257     m_errorMessage = error->message();
258     m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode()));
259     m_pendingCursor.clear();
260     enqueueEvent(Event::create(eventNames().errorEvent, true, true));
261 }
262
263 static PassRefPtr<Event> createSuccessEvent()
264 {
265     return Event::create(eventNames().successEvent, false, false);
266 }
267
268 void IDBRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
269 {
270     LOG(StorageAPI, "IDBRequest::onSuccess(DOMStringList)");
271     if (!shouldEnqueueEvent())
272         return;
273
274     m_result = IDBAny::create(domStringList);
275     enqueueEvent(createSuccessEvent());
276 }
277
278 void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackend> prpBackend)
279 {
280     LOG(StorageAPI, "IDBRequest::onSuccess(IDBCursor)");
281     if (!shouldEnqueueEvent())
282         return;
283
284     DOMRequestState::Scope scope(m_requestState);
285
286     RefPtr<IDBCursorBackend> backend = prpBackend;
287     RefPtr<IDBKey> key = backend->key();
288     RefPtr<IDBKey> primaryKey = backend->primaryKey();
289
290     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), backend->valueBuffer());
291
292     ASSERT(!m_pendingCursor);
293     RefPtr<IDBCursor> cursor;
294     switch (m_cursorType) {
295     case IndexedDB::CursorType::KeyOnly:
296         cursor = IDBCursor::create(backend.release(), m_cursorDirection, this, m_source.get(), m_transaction.get());
297         break;
298     case IndexedDB::CursorType::KeyAndValue:
299         cursor = IDBCursorWithValue::create(backend.release(), m_cursorDirection, this, m_source.get(), m_transaction.get());
300         break;
301     default:
302         ASSERT_NOT_REACHED();
303     }
304     setResultCursor(cursor, key.release(), primaryKey.release(), value);
305
306     enqueueEvent(createSuccessEvent());
307 }
308
309 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
310 {
311     LOG(StorageAPI, "IDBRequest::onSuccess(IDBKey)");
312     if (!shouldEnqueueEvent())
313         return;
314
315     if (idbKey && idbKey->isValid()) {
316         DOMRequestState::Scope scope(m_requestState);
317         m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey));
318     } else
319         m_result = IDBAny::createInvalid();
320     enqueueEvent(createSuccessEvent());
321 }
322
323 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
324 {
325     LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer)");
326     if (!shouldEnqueueEvent())
327         return;
328
329     DOMRequestState::Scope scope(m_requestState);
330     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
331     onSuccessInternal(value);
332 }
333
334 #ifndef NDEBUG
335 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
336 {
337     if (source->type() == IDBAny::IDBObjectStoreType)
338         return source->idbObjectStore();
339     if (source->type() == IDBAny::IDBIndexType)
340         return source->idbIndex()->objectStore();
341
342     ASSERT_NOT_REACHED();
343     return 0;
344 }
345 #endif
346
347 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
348 {
349     LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
350     if (!shouldEnqueueEvent())
351         return;
352
353 #ifndef NDEBUG
354     ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath());
355 #endif
356     DOMRequestState::Scope scope(m_requestState);
357     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
358
359     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
360 #ifndef NDEBUG
361     RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath);
362     ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get()));
363 #endif
364     bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath);
365     ASSERT_UNUSED(injected, injected);
366     onSuccessInternal(value);
367 }
368
369 void IDBRequest::onSuccess(int64_t value)
370 {
371     LOG(StorageAPI, "IDBRequest::onSuccess(int64_t)");
372     if (!shouldEnqueueEvent())
373         return;
374     return onSuccessInternal(SerializedScriptValue::numberValue(value));
375 }
376
377 void IDBRequest::onSuccess()
378 {
379     LOG(StorageAPI, "IDBRequest::onSuccess()");
380     if (!shouldEnqueueEvent())
381         return;
382     return onSuccessInternal(SerializedScriptValue::undefinedValue());
383 }
384
385 void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
386 {
387     ASSERT(!m_contextStopped);
388     DOMRequestState::Scope scope(m_requestState);
389     return onSuccessInternal(deserializeIDBValue(requestState(), value));
390 }
391
392 void IDBRequest::onSuccessInternal(const Deprecated::ScriptValue& value)
393 {
394     m_result = IDBAny::create(value);
395     if (m_pendingCursor) {
396         m_pendingCursor->close();
397         m_pendingCursor.clear();
398     }
399     enqueueEvent(createSuccessEvent());
400 }
401
402 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
403 {
404     onSuccess(key, primaryKey, buffer, nullptr);
405 }
406
407 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey>)
408 {
409     LOG(StorageAPI, "IDBRequest::onSuccess(key, primaryKey, valueBuffer, valueKey)");
410     if (!shouldEnqueueEvent())
411         return;
412
413     DOMRequestState::Scope scope(m_requestState);
414
415     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
416
417     ASSERT(m_pendingCursor);
418     setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
419     enqueueEvent(createSuccessEvent());
420 }
421
422 bool IDBRequest::hasPendingActivity() const
423 {
424     // FIXME: In an ideal world, we should return true as long as anyone has a or can
425     //        get a handle to us and we have event listeners. This is order to handle
426     //        user generated events properly.
427     return m_hasPendingActivity && !m_contextStopped;
428 }
429
430 void IDBRequest::stop()
431 {
432     if (m_contextStopped)
433         return;
434
435     m_contextStopped = true;
436     m_requestState.clear();
437     if (m_readyState == PENDING)
438         markEarlyDeath();
439 }
440
441 EventTargetInterface IDBRequest::eventTargetInterface() const
442 {
443     return IDBRequestEventTargetInterfaceType;
444 }
445
446 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
447 {
448     LOG(StorageAPI, "IDBRequest::dispatchEvent");
449     ASSERT(m_readyState == PENDING);
450     ASSERT(!m_contextStopped);
451     ASSERT(m_hasPendingActivity);
452     ASSERT(m_enqueuedEvents.size());
453     ASSERT(scriptExecutionContext());
454     ASSERT(event->target() == this);
455     ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState);
456
457     DOMRequestState::Scope scope(m_requestState);
458
459     if (event->type() != eventNames().blockedEvent)
460         m_readyState = DONE;
461
462     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
463         if (m_enqueuedEvents[i].get() == event.get())
464             m_enqueuedEvents.remove(i);
465     }
466
467     Vector<RefPtr<EventTarget>> targets;
468     targets.append(this);
469     if (m_transaction && !m_preventPropagation) {
470         targets.append(m_transaction);
471         // If there ever are events that are associated with a database but
472         // that do not have a transaction, then this will not work and we need
473         // this object to actually hold a reference to the database (to ensure
474         // it stays alive).
475         targets.append(m_transaction->db());
476     }
477
478     // Cursor properties should not updated until the success event is being dispatched.
479     RefPtr<IDBCursor> cursorToNotify;
480     if (event->type() == eventNames().successEvent) {
481         cursorToNotify = getResultCursor();
482         if (cursorToNotify) {
483             cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue);
484             m_cursorValue.clear();
485         }
486     }
487
488     if (event->type() == eventNames().upgradeneededEvent) {
489         ASSERT(!m_didFireUpgradeNeededEvent);
490         m_didFireUpgradeNeededEvent = true;
491     }
492
493     // FIXME: When we allow custom event dispatching, this will probably need to change.
494     ASSERT_WITH_MESSAGE(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent || event->type() == eventNames().upgradeneededEvent, "event type was %s", event->type().string().utf8().data());
495     const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError));
496
497     if (setTransactionActive)
498         m_transaction->setActive(true);
499
500     bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
501
502     if (m_transaction) {
503         if (m_readyState == DONE)
504             m_transaction->unregisterRequest(this);
505
506         // Possibly abort the transaction. This must occur after unregistering (so this request
507         // doesn't receive a second error) and before deactivating (which might trigger commit).
508         if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
509             m_transaction->setError(m_error, m_errorMessage);
510             m_transaction->abort(IGNORE_EXCEPTION);
511         }
512
513         // If this was the last request in the transaction's list, it may commit here.
514         if (setTransactionActive)
515             m_transaction->setActive(false);
516     }
517
518     if (cursorToNotify)
519         cursorToNotify->postSuccessHandlerCallback();
520
521     if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent)
522         m_hasPendingActivity = false;
523
524     return dontPreventDefault;
525 }
526
527 void IDBRequest::uncaughtExceptionInEventHandler()
528 {
529     if (m_transaction && !m_requestAborted) {
530         m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler.");
531         m_transaction->abort(IGNORE_EXCEPTION);
532     }
533 }
534
535 void IDBRequest::transactionDidFinishAndDispatch()
536 {
537     ASSERT(m_transaction);
538     ASSERT(m_transaction->isVersionChange());
539     ASSERT(m_readyState == DONE);
540     ASSERT(scriptExecutionContext());
541     m_transaction.clear();
542     m_readyState = PENDING;
543 }
544
545 void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
546 {
547     ASSERT(m_readyState == PENDING || m_readyState == DONE);
548
549     if (m_contextStopped || !scriptExecutionContext())
550         return;
551
552     ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState);
553
554     event->setTarget(this);
555
556     if (scriptExecutionContext()->eventQueue().enqueueEvent(event.get()))
557         m_enqueuedEvents.append(event);
558 }
559
560 } // namespace WebCore
561
562 #endif