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