IndexedDB: IDBTransaction::abort() should throw DOMException
[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 "IDBBindingUtilities.h"
39 #include "IDBCursorWithValue.h"
40 #include "IDBDatabase.h"
41 #include "IDBEventDispatcher.h"
42 #include "IDBTracing.h"
43 #include "IDBTransaction.h"
44
45 namespace WebCore {
46
47 PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
48 {
49     RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, transaction)));
50     request->suspendIfNeeded();
51     return request.release();
52 }
53
54 IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
55     : ActiveDOMObject(context, this)
56     , m_errorCode(0)
57     , m_source(source)
58     , m_transaction(transaction)
59     , m_readyState(PENDING)
60     , m_requestAborted(false)
61     , m_cursorFinished(false)
62     , m_contextStopped(false)
63     , m_cursorType(IDBCursorBackendInterface::InvalidCursorType)
64     , m_cursorDirection(IDBCursor::NEXT)
65     , m_pendingCursor(0)
66 {
67     if (m_transaction) {
68         m_transaction->registerRequest(this);
69     }
70 }
71
72 IDBRequest::~IDBRequest()
73 {
74     ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !scriptExecutionContext());
75 }
76
77 PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const
78 {
79     if (m_readyState != DONE) {
80         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
81         return 0;
82     }
83     return m_result;
84 }
85
86 PassRefPtr<DOMError> IDBRequest::error(ExceptionCode& ec) const
87 {
88     if (m_readyState != DONE) {
89         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
90         return 0;
91     }
92     return m_error;
93 }
94
95 unsigned short IDBRequest::errorCode(ExceptionCode& ec) const
96 {
97     if (m_readyState != DONE) {
98         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
99         return 0;
100     }
101     return m_errorCode;
102 }
103
104 String IDBRequest::webkitErrorMessage(ExceptionCode& ec) const
105 {
106     if (m_readyState != DONE) {
107         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
108         return String();
109     }
110     return m_errorMessage;
111 }
112
113 PassRefPtr<IDBAny> IDBRequest::source() const
114 {
115     return m_source;
116 }
117
118 PassRefPtr<IDBTransaction> IDBRequest::transaction() const
119 {
120     return m_transaction;
121 }
122
123 const String& IDBRequest::readyState() const
124 {
125     ASSERT(m_readyState == PENDING || m_readyState == DONE);
126     DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending"));
127     DEFINE_STATIC_LOCAL(AtomicString, done, ("done"));
128
129     if (m_readyState == PENDING)
130         return pending;
131
132     return done;
133 }
134
135 void IDBRequest::markEarlyDeath()
136 {
137     ASSERT(m_readyState == PENDING);
138     m_readyState = EarlyDeath;
139     if (m_transaction)
140         m_transaction->unregisterRequest(this);
141 }
142
143 void IDBRequest::abort()
144 {
145     ASSERT(m_readyState == PENDING || m_readyState == DONE);
146     ASSERT(!m_requestAborted);
147     if (m_contextStopped || !scriptExecutionContext())
148         return;
149     if (m_readyState == DONE)
150         return;
151
152     EventQueue* eventQueue = scriptExecutionContext()->eventQueue();
153     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
154         bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
155         ASSERT_UNUSED(removed, removed);
156     }
157     m_enqueuedEvents.clear();
158
159     m_errorCode = 0;
160     m_error.clear();
161     m_errorMessage = String();
162     m_result.clear();
163     onError(IDBDatabaseError::create(IDBDatabaseException::IDB_ABORT_ERR, "The transaction was aborted, so the request cannot be fulfilled."));
164     m_requestAborted = true;
165 }
166
167 void IDBRequest::setCursorDetails(IDBCursorBackendInterface::CursorType cursorType, IDBCursor::Direction direction)
168 {
169     ASSERT(m_readyState == PENDING);
170     ASSERT(m_cursorType == IDBCursorBackendInterface::InvalidCursorType);
171     m_cursorType = cursorType;
172     m_cursorDirection = direction;
173 }
174
175 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
176 {
177     ASSERT(m_readyState == DONE);
178     ASSERT(scriptExecutionContext());
179     ASSERT(m_transaction);
180     ASSERT(!m_pendingCursor);
181     ASSERT(cursor == getResultCursor());
182
183     m_pendingCursor = cursor;
184     m_result.clear();
185     m_readyState = PENDING;
186     m_errorCode = 0;
187     m_error.clear();
188     m_errorMessage = String();
189     m_transaction->registerRequest(this);
190 }
191
192 PassRefPtr<IDBCursor> IDBRequest::getResultCursor()
193 {
194     if (!m_result)
195         return 0;
196     if (m_result->type() == IDBAny::IDBCursorType)
197         return m_result->idbCursor();
198     if (m_result->type() == IDBAny::IDBCursorWithValueType)
199         return m_result->idbCursorWithValue();
200     return 0;
201 }
202
203 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> prpCursor)
204 {
205     ASSERT(m_readyState == PENDING);
206     if (m_cursorType == IDBCursorBackendInterface::IndexKeyCursor) {
207         m_result = IDBAny::create(prpCursor);
208         return;
209     }
210
211     m_result = IDBAny::create(IDBCursorWithValue::fromCursor(prpCursor));
212 }
213
214 void IDBRequest::finishCursor()
215 {
216     ASSERT(m_readyState == PENDING || m_readyState == DONE);
217     m_cursorFinished = true;
218 }
219
220 void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
221 {
222     ASSERT(m_readyState == PENDING || m_readyState == DONE);
223     if (m_requestAborted)
224         return;
225     ASSERT(m_readyState == PENDING);
226     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
227     m_errorCode = error->code();
228     m_errorMessage = error->message();
229     m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode()));
230     m_pendingCursor.clear();
231     enqueueEvent(Event::create(eventNames().errorEvent, true, true));
232 }
233
234 static PassRefPtr<Event> createSuccessEvent()
235 {
236     return Event::create(eventNames().successEvent, false, false);
237 }
238
239 void IDBRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
240 {
241     IDB_TRACE("IDBRequest::onSuccess(DOMStringList)");
242     ASSERT(m_readyState == PENDING || m_readyState == DONE);
243     if (m_requestAborted)
244         return;
245     ASSERT(m_readyState == PENDING);
246     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
247     m_result = IDBAny::create(domStringList);
248     enqueueEvent(createSuccessEvent());
249 }
250
251 void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend)
252 {
253     IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
254     ASSERT(m_readyState == PENDING || m_readyState == DONE);
255     if (m_requestAborted)
256         return;
257     ASSERT(m_readyState == PENDING);
258     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
259     ASSERT(m_cursorType != IDBCursorBackendInterface::InvalidCursorType);
260     RefPtr<IDBCursor> cursor;
261     if (m_cursorType == IDBCursorBackendInterface::IndexKeyCursor)
262         cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
263     else
264         cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
265     setResultCursor(cursor);
266
267     enqueueEvent(createSuccessEvent());
268 }
269
270 void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend)
271 {
272     IDB_TRACE("IDBRequest::onSuccess(IDBDatabase)");
273     ASSERT(m_readyState == PENDING || m_readyState == DONE);
274     if (m_requestAborted)
275         return;
276     ASSERT(m_readyState == PENDING);
277     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
278     if (m_contextStopped || !scriptExecutionContext())
279         return;
280
281     RefPtr<IDBDatabase> idbDatabase = IDBDatabase::create(scriptExecutionContext(), backend);
282     idbDatabase->registerFrontendCallbacks();
283
284     m_result = IDBAny::create(idbDatabase.release());
285     enqueueEvent(createSuccessEvent());
286 }
287
288 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
289 {
290     IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
291     ASSERT(m_readyState == PENDING || m_readyState == DONE);
292     if (m_requestAborted)
293         return;
294     ASSERT(m_readyState == PENDING);
295     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
296     if (idbKey && idbKey->isValid())
297         m_result = IDBAny::create(idbKey);
298     else
299         m_result = IDBAny::create(SerializedScriptValue::undefinedValue());
300     enqueueEvent(createSuccessEvent());
301 }
302
303 void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend)
304 {
305     IDB_TRACE("IDBRequest::onSuccess(IDBTransaction)");
306     ASSERT(m_readyState == PENDING || m_readyState == DONE);
307     if (m_requestAborted)
308         return;
309     ASSERT(m_readyState == PENDING);
310     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
311     RefPtr<IDBTransactionBackendInterface> backend = prpBackend;
312
313     if (m_contextStopped || !scriptExecutionContext()) {
314         backend->abort();
315         return;
316     }
317
318     RefPtr<IDBTransaction> frontend = IDBTransaction::create(scriptExecutionContext(), backend, IDBTransaction::VERSION_CHANGE, m_source->idbDatabase().get());
319     backend->setCallbacks(frontend.get());
320     m_transaction = frontend;
321
322     ASSERT(m_source->type() == IDBAny::IDBDatabaseType);
323     ASSERT(m_transaction->isVersionChange());
324
325     m_result = IDBAny::create(frontend.release());
326     enqueueEvent(createSuccessEvent());
327 }
328
329 void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue)
330 {
331     IDB_TRACE("IDBRequest::onSuccess(SerializedScriptValue)");
332     ASSERT(m_readyState == PENDING || m_readyState == DONE);
333     if (m_requestAborted)
334         return;
335     ASSERT(m_readyState == PENDING);
336     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
337     m_result = IDBAny::create(serializedScriptValue);
338     m_pendingCursor.clear();
339     enqueueEvent(createSuccessEvent());
340 }
341
342
343 void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> prpSerializedScriptValue, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
344 {
345     LOG_ERROR("CHECKING: onSuccess(value, key, keypath)");
346     ASSERT(m_readyState == PENDING || m_readyState == DONE);
347     if (m_requestAborted)
348         return;
349     ASSERT(m_readyState == PENDING);
350     RefPtr<SerializedScriptValue> serializedScriptValue = prpSerializedScriptValue;
351 #ifndef NDEBUG
352     // FIXME: Assert until we can actually inject the right value.
353     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
354     RefPtr<IDBKey> expectedKey =
355               createIDBKeyFromSerializedValueAndKeyPath(serializedScriptValue, keyPath);
356     ASSERT(primaryKey);
357     ASSERT(expectedKey->isEqual(primaryKey.get()));
358 #endif
359     onSuccess(serializedScriptValue.release());
360 }
361
362 void IDBRequest::onSuccessWithContinuation()
363 {
364     IDB_TRACE("IDBRequest::onSuccessWithContinuation");
365     ASSERT(m_readyState == PENDING || m_readyState == DONE);
366     if (m_requestAborted)
367         return;
368     ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
369     ASSERT(m_pendingCursor);
370     setResultCursor(m_pendingCursor.release());
371     enqueueEvent(createSuccessEvent());
372 }
373
374 bool IDBRequest::hasPendingActivity() const
375 {
376     // FIXME: In an ideal world, we should return true as long as anyone has a or can
377     //        get a handle to us and we have event listeners. This is order to handle
378     //        user generated events properly.
379     return m_readyState == PENDING || ActiveDOMObject::hasPendingActivity();
380 }
381
382 void IDBRequest::stop()
383 {
384     ActiveDOMObject::stop();
385     if (m_contextStopped)
386         return;
387
388     m_contextStopped = true;
389     if (m_readyState == PENDING)
390         markEarlyDeath();
391 }
392
393 void IDBRequest::onBlocked()
394 {
395     ASSERT_NOT_REACHED();
396 }
397
398 const AtomicString& IDBRequest::interfaceName() const
399 {
400     return eventNames().interfaceForIDBRequest;
401 }
402
403 ScriptExecutionContext* IDBRequest::scriptExecutionContext() const
404 {
405     return ActiveDOMObject::scriptExecutionContext();
406 }
407
408 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
409 {
410     IDB_TRACE("IDBRequest::dispatchEvent");
411     ASSERT(m_readyState == PENDING);
412     ASSERT(!m_contextStopped);
413     ASSERT(m_enqueuedEvents.size());
414     ASSERT(scriptExecutionContext());
415     ASSERT(event->target() == this);
416     ASSERT_WITH_MESSAGE(m_readyState < DONE, "m_readyState < DONE(%d), was %d", DONE, m_readyState);
417
418     if (event->type() != eventNames().blockedEvent)
419         m_readyState = DONE;
420
421     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
422         if (m_enqueuedEvents[i].get() == event.get())
423             m_enqueuedEvents.remove(i);
424     }
425
426     Vector<RefPtr<EventTarget> > targets;
427     targets.append(this);
428     if (m_transaction) {
429         targets.append(m_transaction);
430         // If there ever are events that are associated with a database but
431         // that do not have a transaction, then this will not work and we need
432         // this object to actually hold a reference to the database (to ensure
433         // it stays alive).
434         targets.append(m_transaction->db());
435     }
436
437     // Cursor properties should not updated until the success event is being dispatched.
438     RefPtr<IDBCursor> cursorToNotify;
439     if (event->type() == eventNames().successEvent) {
440         cursorToNotify = getResultCursor();
441         if (cursorToNotify)
442             cursorToNotify->setValueReady();
443     }
444
445     // FIXME: When we allow custom event dispatching, this will probably need to change.
446     ASSERT(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent);
447     const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::IDB_ABORT_ERR));
448
449     if (setTransactionActive)
450         m_transaction->setActive(true);
451     bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
452     if (setTransactionActive)
453         m_transaction->setActive(false);
454
455     if (cursorToNotify)
456         cursorToNotify->postSuccessHandlerCallback();
457
458     if (m_transaction) {
459         if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
460             m_transaction->setError(m_error);
461             ExceptionCode unused;
462             m_transaction->abort(unused);
463         }
464
465         if (event->type() != eventNames().blockedEvent)
466             m_transaction->backend()->didCompleteTaskEvents();
467
468         if (m_readyState == DONE)
469             m_transaction->unregisterRequest(this);
470     }
471
472     return dontPreventDefault;
473 }
474
475 void IDBRequest::uncaughtExceptionInEventHandler()
476 {
477     if (m_transaction && !m_requestAborted) {
478         m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::IDB_ABORT_ERR)));
479         ExceptionCode unused; 
480         m_transaction->abort(unused);
481     }
482 }
483
484 void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
485 {
486     ASSERT(m_readyState == PENDING || m_readyState == DONE);
487
488     if (m_contextStopped || !scriptExecutionContext())
489         return;
490
491     ASSERT(m_readyState == PENDING);
492
493     EventQueue* eventQueue = scriptExecutionContext()->eventQueue();
494     event->setTarget(this);
495
496     if (eventQueue->enqueueEvent(event.get()))
497         m_enqueuedEvents.append(event);
498 }
499
500 EventTargetData* IDBRequest::eventTargetData()
501 {
502     return &m_eventTargetData;
503 }
504
505 EventTargetData* IDBRequest::ensureEventTargetData()
506 {
507     return &m_eventTargetData;
508 }
509
510 } // namespace WebCore
511
512 #endif