IndexedDB: Properly refactor frontend/backend code by #includes
[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 "IDBCursorBackendInterface.h"
41 #include "IDBCursorWithValue.h"
42 #include "IDBDatabase.h"
43 #include "IDBEventDispatcher.h"
44 #include "IDBTracing.h"
45 #include "IDBTransaction.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, IDBDatabaseBackendInterface::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, IDBDatabaseBackendInterface::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, IDBDatabaseBackendInterface::TaskType taskType, IDBTransaction* transaction)
71     : ActiveDOMObject(context, this)
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::CursorKeyAndValue)
82     , m_cursorDirection(IndexedDB::CursorNext)
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 String IDBRequest::webkitErrorMessage(ExceptionCode& ec) const
124 {
125     if (m_readyState != DONE) {
126         ec = IDBDatabaseException::InvalidStateError;
127         return String();
128     }
129     return m_errorMessage;
130 }
131
132 PassRefPtr<IDBAny> IDBRequest::source() const
133 {
134     return m_source;
135 }
136
137 PassRefPtr<IDBTransaction> IDBRequest::transaction() const
138 {
139     return m_transaction;
140 }
141
142 const String& IDBRequest::readyState() const
143 {
144     ASSERT(m_readyState == PENDING || m_readyState == DONE);
145     DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
146     DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
147
148     if (m_readyState == PENDING)
149         return pending;
150
151     return done;
152 }
153
154 void IDBRequest::markEarlyDeath()
155 {
156     ASSERT(m_readyState == PENDING);
157     m_readyState = EarlyDeath;
158     if (m_transaction)
159         m_transaction->unregisterRequest(this);
160 }
161
162 void IDBRequest::abort()
163 {
164     ASSERT(!m_requestAborted);
165     if (m_contextStopped || !scriptExecutionContext())
166         return;
167     ASSERT(m_readyState == PENDING || m_readyState == DONE);
168     if (m_readyState == DONE)
169         return;
170
171     // Enqueued events may be the only reference to this object.
172     RefPtr<IDBRequest> self(this);
173
174     EventQueue* eventQueue = scriptExecutionContext()->eventQueue();
175     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
176         bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
177         ASSERT_UNUSED(removed, removed);
178     }
179     m_enqueuedEvents.clear();
180
181     m_errorCode = 0;
182     m_error.clear();
183     m_errorMessage = String();
184     m_result.clear();
185     onError(IDBDatabaseError::create(IDBDatabaseException::AbortError));
186     m_requestAborted = true;
187 }
188
189 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction)
190 {
191     ASSERT(m_readyState == PENDING);
192     ASSERT(!m_pendingCursor);
193     m_cursorType = cursorType;
194     m_cursorDirection = direction;
195 }
196
197 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
198 {
199     ASSERT(m_readyState == DONE);
200     ASSERT(scriptExecutionContext());
201     ASSERT(m_transaction);
202     ASSERT(!m_pendingCursor);
203     ASSERT(cursor == getResultCursor());
204
205     m_pendingCursor = cursor;
206     m_result.clear();
207     m_readyState = PENDING;
208     m_errorCode = 0;
209     m_error.clear();
210     m_errorMessage = String();
211     m_transaction->registerRequest(this);
212 }
213
214 PassRefPtr<IDBCursor> IDBRequest::getResultCursor()
215 {
216     if (!m_result)
217         return 0;
218     if (m_result->type() == IDBAny::IDBCursorType)
219         return m_result->idbCursor();
220     if (m_result->type() == IDBAny::IDBCursorWithValueType)
221         return m_result->idbCursorWithValue();
222     return 0;
223 }
224
225 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, const ScriptValue& value)
226 {
227     ASSERT(m_readyState == PENDING);
228     m_cursorKey = key;
229     m_cursorPrimaryKey = primaryKey;
230     m_cursorValue = value;
231
232     if (m_cursorType == IndexedDB::CursorKeyOnly) {
233         m_result = IDBAny::create(cursor);
234         return;
235     }
236
237     m_result = IDBAny::create(IDBCursorWithValue::fromCursor(cursor));
238 }
239
240 void IDBRequest::finishCursor()
241 {
242     m_cursorFinished = true;
243     if (m_readyState != PENDING)
244         m_hasPendingActivity = false;
245 }
246
247 bool IDBRequest::shouldEnqueueEvent() const
248 {
249     if (m_contextStopped || !scriptExecutionContext())
250         return false;
251     ASSERT(m_readyState == PENDING || m_readyState == DONE);
252     if (m_requestAborted)
253         return false;
254     ASSERT(m_readyState == PENDING);
255     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
256     return true;
257 }
258
259 void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
260 {
261     IDB_TRACE("IDBRequest::onError()");
262     if (!shouldEnqueueEvent())
263         return;
264
265     m_errorCode = error->code();
266     m_errorMessage = error->message();
267     m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode()));
268     m_pendingCursor.clear();
269     enqueueEvent(Event::create(eventNames().errorEvent, true, true));
270 }
271
272 static PassRefPtr<Event> createSuccessEvent()
273 {
274     return Event::create(eventNames().successEvent, false, false);
275 }
276
277 void IDBRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
278 {
279     IDB_TRACE("IDBRequest::onSuccess(DOMStringList)");
280     if (!shouldEnqueueEvent())
281         return;
282
283     m_result = IDBAny::create(domStringList);
284     enqueueEvent(createSuccessEvent());
285 }
286
287 void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
288 {
289     IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
290     if (!shouldEnqueueEvent())
291         return;
292
293     DOMRequestState::Scope scope(m_requestState);
294     ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
295     ASSERT(!m_pendingCursor);
296     RefPtr<IDBCursor> cursor;
297     switch (m_cursorType) {
298     case IndexedDB::CursorKeyOnly:
299         cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
300         break;
301     case IndexedDB::CursorKeyAndValue:
302         cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
303         break;
304     default:
305         ASSERT_NOT_REACHED();
306     }
307     setResultCursor(cursor, key, primaryKey, value);
308
309     enqueueEvent(createSuccessEvent());
310 }
311
312 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
313 {
314     IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
315     if (!shouldEnqueueEvent())
316         return;
317
318     if (idbKey && idbKey->isValid()) {
319         DOMRequestState::Scope scope(m_requestState);
320         m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey));
321     } else
322         m_result = IDBAny::createInvalid();
323     enqueueEvent(createSuccessEvent());
324 }
325
326 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
327 {
328     IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)");
329     if (!shouldEnqueueEvent())
330         return;
331
332     DOMRequestState::Scope scope(m_requestState);
333     ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
334     onSuccessInternal(value);
335 }
336
337 #ifndef NDEBUG
338 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
339 {
340     if (source->type() == IDBAny::IDBObjectStoreType)
341         return source->idbObjectStore();
342     if (source->type() == IDBAny::IDBIndexType)
343         return source->idbIndex()->objectStore();
344
345     ASSERT_NOT_REACHED();
346     return 0;
347 }
348 #endif
349
350 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
351 {
352     IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
353     if (!shouldEnqueueEvent())
354         return;
355
356 #ifndef NDEBUG
357     ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath());
358 #endif
359     DOMRequestState::Scope scope(m_requestState);
360     ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
361
362     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
363 #ifndef NDEBUG
364     RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath);
365     ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get()));
366 #endif
367     bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath);
368     ASSERT_UNUSED(injected, injected);
369     onSuccessInternal(value);
370 }
371
372 void IDBRequest::onSuccess(int64_t value)
373 {
374     return onSuccessInternal(SerializedScriptValue::numberValue(value));
375 }
376
377 void IDBRequest::onSuccess()
378 {
379     return onSuccessInternal(SerializedScriptValue::undefinedValue());
380 }
381
382 void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
383 {
384     DOMRequestState::Scope scope(m_requestState);
385     return onSuccessInternal(deserializeIDBValue(requestState(), value));
386 }
387
388 void IDBRequest::onSuccessInternal(const ScriptValue& value)
389 {
390     m_result = IDBAny::create(value);
391     if (m_pendingCursor) {
392         m_pendingCursor->close();
393         m_pendingCursor.clear();
394     }
395     enqueueEvent(createSuccessEvent());
396 }
397
398 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
399 {
400     IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)");
401     if (!shouldEnqueueEvent())
402         return;
403
404     DOMRequestState::Scope scope(m_requestState);
405     ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
406     ASSERT(m_pendingCursor);
407     setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
408     enqueueEvent(createSuccessEvent());
409 }
410
411 bool IDBRequest::hasPendingActivity() const
412 {
413     // FIXME: In an ideal world, we should return true as long as anyone has a or can
414     //        get a handle to us and we have event listeners. This is order to handle
415     //        user generated events properly.
416     return m_hasPendingActivity;
417 }
418
419 void IDBRequest::stop()
420 {
421     ActiveDOMObject::stop();
422     if (m_contextStopped)
423         return;
424
425     m_contextStopped = true;
426     m_requestState.clear();
427     if (m_readyState == PENDING)
428         markEarlyDeath();
429 }
430
431 const AtomicString& IDBRequest::interfaceName() const
432 {
433     return eventNames().interfaceForIDBRequest;
434 }
435
436 ScriptExecutionContext* IDBRequest::scriptExecutionContext() const
437 {
438     return ActiveDOMObject::scriptExecutionContext();
439 }
440
441 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
442 {
443     IDB_TRACE("IDBRequest::dispatchEvent");
444     ASSERT(m_readyState == PENDING);
445     ASSERT(!m_contextStopped);
446     ASSERT(m_hasPendingActivity);
447     ASSERT(m_enqueuedEvents.size());
448     ASSERT(scriptExecutionContext());
449     ASSERT(event->target() == this);
450     ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState);
451
452     DOMRequestState::Scope scope(m_requestState);
453
454     if (event->type() != eventNames().blockedEvent)
455         m_readyState = DONE;
456
457     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
458         if (m_enqueuedEvents[i].get() == event.get())
459             m_enqueuedEvents.remove(i);
460     }
461
462     Vector<RefPtr<EventTarget> > targets;
463     targets.append(this);
464     if (m_transaction && !m_preventPropagation) {
465         targets.append(m_transaction);
466         // If there ever are events that are associated with a database but
467         // that do not have a transaction, then this will not work and we need
468         // this object to actually hold a reference to the database (to ensure
469         // it stays alive).
470         targets.append(m_transaction->db());
471     }
472
473     // Cursor properties should not updated until the success event is being dispatched.
474     RefPtr<IDBCursor> cursorToNotify;
475     if (event->type() == eventNames().successEvent) {
476         cursorToNotify = getResultCursor();
477         if (cursorToNotify) {
478             cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue);
479             m_cursorValue.clear();
480         }
481     }
482
483     if (event->type() == eventNames().upgradeneededEvent) {
484         ASSERT(!m_didFireUpgradeNeededEvent);
485         m_didFireUpgradeNeededEvent = true;
486     }
487
488     // FIXME: When we allow custom event dispatching, this will probably need to change.
489     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());
490     const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError));
491
492     if (setTransactionActive)
493         m_transaction->setActive(true);
494
495     bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
496
497     if (m_transaction) {
498         if (m_readyState == DONE)
499             m_transaction->unregisterRequest(this);
500
501         // Possibly abort the transaction. This must occur after unregistering (so this request
502         // doesn't receive a second error) and before deactivating (which might trigger commit).
503         if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
504             m_transaction->setError(m_error, m_errorMessage);
505             m_transaction->abort(IGNORE_EXCEPTION);
506         }
507
508         // If this was the last request in the transaction's list, it may commit here.
509         if (setTransactionActive)
510             m_transaction->setActive(false);
511     }
512
513     if (cursorToNotify)
514         cursorToNotify->postSuccessHandlerCallback();
515
516     if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent)
517         m_hasPendingActivity = false;
518
519     return dontPreventDefault;
520 }
521
522 void IDBRequest::uncaughtExceptionInEventHandler()
523 {
524     if (m_transaction && !m_requestAborted) {
525         m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler.");
526         m_transaction->abort(IGNORE_EXCEPTION);
527     }
528 }
529
530 void IDBRequest::transactionDidFinishAndDispatch()
531 {
532     ASSERT(m_transaction);
533     ASSERT(m_transaction->isVersionChange());
534     ASSERT(m_readyState == DONE);
535     ASSERT(scriptExecutionContext());
536     m_transaction.clear();
537     m_readyState = PENDING;
538 }
539
540 void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
541 {
542     ASSERT(m_readyState == PENDING || m_readyState == DONE);
543
544     if (m_contextStopped || !scriptExecutionContext())
545         return;
546
547     ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState);
548
549     EventQueue* eventQueue = scriptExecutionContext()->eventQueue();
550     event->setTarget(this);
551
552     if (eventQueue->enqueueEvent(event.get()))
553         m_enqueuedEvents.append(event);
554 }
555
556 EventTargetData* IDBRequest::eventTargetData()
557 {
558     return &m_eventTargetData;
559 }
560
561 EventTargetData* IDBRequest::ensureEventTargetData()
562 {
563     return &m_eventTargetData;
564 }
565
566 } // namespace WebCore
567
568 #endif