41a44f9784245526bb21fd35e2ed12744247a162
[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;
291
292     if (backend->valueKey())
293         value = idbKeyToScriptValue(requestState(), backend->valueKey());
294     else
295         value = deserializeIDBValueBuffer(requestState(), backend->valueBuffer());
296
297     ASSERT(!m_pendingCursor);
298     RefPtr<IDBCursor> cursor;
299     switch (m_cursorType) {
300     case IndexedDB::CursorType::KeyOnly:
301         cursor = IDBCursor::create(backend.release(), m_cursorDirection, this, m_source.get(), m_transaction.get());
302         break;
303     case IndexedDB::CursorType::KeyAndValue:
304         cursor = IDBCursorWithValue::create(backend.release(), m_cursorDirection, this, m_source.get(), m_transaction.get());
305         break;
306     default:
307         ASSERT_NOT_REACHED();
308     }
309     setResultCursor(cursor, key.release(), primaryKey.release(), value);
310
311     enqueueEvent(createSuccessEvent());
312 }
313
314 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
315 {
316     LOG(StorageAPI, "IDBRequest::onSuccess(IDBKey)");
317     if (!shouldEnqueueEvent())
318         return;
319
320     if (idbKey && idbKey->isValid()) {
321         DOMRequestState::Scope scope(m_requestState);
322         m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey));
323     } else
324         m_result = IDBAny::createInvalid();
325     enqueueEvent(createSuccessEvent());
326 }
327
328 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
329 {
330     LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer)");
331     if (!shouldEnqueueEvent())
332         return;
333
334     DOMRequestState::Scope scope(m_requestState);
335     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
336     onSuccessInternal(value);
337 }
338
339 #ifndef NDEBUG
340 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
341 {
342     if (source->type() == IDBAny::IDBObjectStoreType)
343         return source->idbObjectStore();
344     if (source->type() == IDBAny::IDBIndexType)
345         return source->idbIndex()->objectStore();
346
347     ASSERT_NOT_REACHED();
348     return 0;
349 }
350 #endif
351
352 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
353 {
354     LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
355     if (!shouldEnqueueEvent())
356         return;
357
358 #ifndef NDEBUG
359     ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath());
360 #endif
361     DOMRequestState::Scope scope(m_requestState);
362     Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
363
364     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
365 #ifndef NDEBUG
366     RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath);
367     ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get()));
368 #endif
369     bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath);
370     ASSERT_UNUSED(injected, injected);
371     onSuccessInternal(value);
372 }
373
374 void IDBRequest::onSuccess(int64_t value)
375 {
376     LOG(StorageAPI, "IDBRequest::onSuccess(int64_t)");
377     if (!shouldEnqueueEvent())
378         return;
379     return onSuccessInternal(SerializedScriptValue::numberValue(value));
380 }
381
382 void IDBRequest::onSuccess()
383 {
384     LOG(StorageAPI, "IDBRequest::onSuccess()");
385     if (!shouldEnqueueEvent())
386         return;
387     return onSuccessInternal(SerializedScriptValue::undefinedValue());
388 }
389
390 void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
391 {
392     ASSERT(!m_contextStopped);
393     DOMRequestState::Scope scope(m_requestState);
394     return onSuccessInternal(deserializeIDBValue(requestState(), value));
395 }
396
397 void IDBRequest::onSuccessInternal(const Deprecated::ScriptValue& value)
398 {
399     m_result = IDBAny::create(value);
400     if (m_pendingCursor) {
401         m_pendingCursor->close();
402         m_pendingCursor.clear();
403     }
404     enqueueEvent(createSuccessEvent());
405 }
406
407 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
408 {
409     onSuccess(key, primaryKey, buffer, nullptr);
410 }
411
412 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> valueKey)
413 {
414     LOG(StorageAPI, "IDBRequest::onSuccess(key, primaryKey, valueBuffer, valueKey)");
415     if (!shouldEnqueueEvent())
416         return;
417
418     DOMRequestState::Scope scope(m_requestState);
419
420     Deprecated::ScriptValue value;
421     if (valueKey)
422         value = idbKeyToScriptValue(requestState(), valueKey);
423     else
424         value = deserializeIDBValueBuffer(requestState(), buffer);
425
426     ASSERT(m_pendingCursor);
427     setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
428     enqueueEvent(createSuccessEvent());
429 }
430
431 bool IDBRequest::hasPendingActivity() const
432 {
433     // FIXME: In an ideal world, we should return true as long as anyone has a or can
434     //        get a handle to us and we have event listeners. This is order to handle
435     //        user generated events properly.
436     return m_hasPendingActivity && !m_contextStopped;
437 }
438
439 void IDBRequest::stop()
440 {
441     if (m_contextStopped)
442         return;
443
444     m_contextStopped = true;
445     m_requestState.clear();
446     if (m_readyState == PENDING)
447         markEarlyDeath();
448 }
449
450 EventTargetInterface IDBRequest::eventTargetInterface() const
451 {
452     return IDBRequestEventTargetInterfaceType;
453 }
454
455 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
456 {
457     LOG(StorageAPI, "IDBRequest::dispatchEvent");
458     ASSERT(m_readyState == PENDING);
459     ASSERT(!m_contextStopped);
460     ASSERT(m_hasPendingActivity);
461     ASSERT(m_enqueuedEvents.size());
462     ASSERT(scriptExecutionContext());
463     ASSERT(event->target() == this);
464     ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState);
465
466     DOMRequestState::Scope scope(m_requestState);
467
468     if (event->type() != eventNames().blockedEvent)
469         m_readyState = DONE;
470
471     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
472         if (m_enqueuedEvents[i].get() == event.get())
473             m_enqueuedEvents.remove(i);
474     }
475
476     Vector<RefPtr<EventTarget>> targets;
477     targets.append(this);
478     if (m_transaction && !m_preventPropagation) {
479         targets.append(m_transaction);
480         // If there ever are events that are associated with a database but
481         // that do not have a transaction, then this will not work and we need
482         // this object to actually hold a reference to the database (to ensure
483         // it stays alive).
484         targets.append(m_transaction->db());
485     }
486
487     // Cursor properties should not updated until the success event is being dispatched.
488     RefPtr<IDBCursor> cursorToNotify;
489     if (event->type() == eventNames().successEvent) {
490         cursorToNotify = getResultCursor();
491         if (cursorToNotify) {
492             cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue);
493             m_cursorValue.clear();
494         }
495     }
496
497     if (event->type() == eventNames().upgradeneededEvent) {
498         ASSERT(!m_didFireUpgradeNeededEvent);
499         m_didFireUpgradeNeededEvent = true;
500     }
501
502     // FIXME: When we allow custom event dispatching, this will probably need to change.
503     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());
504     const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError));
505
506     if (setTransactionActive)
507         m_transaction->setActive(true);
508
509     bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
510
511     if (m_transaction) {
512         if (m_readyState == DONE)
513             m_transaction->unregisterRequest(this);
514
515         // Possibly abort the transaction. This must occur after unregistering (so this request
516         // doesn't receive a second error) and before deactivating (which might trigger commit).
517         if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
518             m_transaction->setError(m_error, m_errorMessage);
519             m_transaction->abort(IGNORE_EXCEPTION);
520         }
521
522         // If this was the last request in the transaction's list, it may commit here.
523         if (setTransactionActive)
524             m_transaction->setActive(false);
525     }
526
527     if (cursorToNotify)
528         cursorToNotify->postSuccessHandlerCallback();
529
530     if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent)
531         m_hasPendingActivity = false;
532
533     return dontPreventDefault;
534 }
535
536 void IDBRequest::uncaughtExceptionInEventHandler()
537 {
538     if (m_transaction && !m_requestAborted) {
539         m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler.");
540         m_transaction->abort(IGNORE_EXCEPTION);
541     }
542 }
543
544 void IDBRequest::transactionDidFinishAndDispatch()
545 {
546     ASSERT(m_transaction);
547     ASSERT(m_transaction->isVersionChange());
548     ASSERT(m_readyState == DONE);
549     ASSERT(scriptExecutionContext());
550     m_transaction.clear();
551     m_readyState = PENDING;
552 }
553
554 void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
555 {
556     ASSERT(m_readyState == PENDING || m_readyState == DONE);
557
558     if (m_contextStopped || !scriptExecutionContext())
559         return;
560
561     ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState);
562
563     event->setTarget(this);
564
565     if (scriptExecutionContext()->eventQueue().enqueueEvent(event.get()))
566         m_enqueuedEvents.append(event);
567 }
568
569 } // namespace WebCore
570
571 #endif