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