2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "modules/indexeddb/IDBRequest.h"
32 #include "bindings/core/v8/ExceptionState.h"
33 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
34 #include "bindings/modules/v8/IDBBindingUtilities.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "core/events/EventQueue.h"
37 #include "modules/IndexedDBNames.h"
38 #include "modules/indexeddb/IDBCursorWithValue.h"
39 #include "modules/indexeddb/IDBDatabase.h"
40 #include "modules/indexeddb/IDBEventDispatcher.h"
41 #include "modules/indexeddb/IDBTracing.h"
42 #include "platform/SharedBuffer.h"
43 #include "public/platform/WebBlobInfo.h"
45 using blink::WebIDBCursor;
49 IDBRequest* IDBRequest::create(ScriptState* scriptState, IDBAny* source, IDBTransaction* transaction)
51 IDBRequest* request = new IDBRequest(scriptState, source, transaction);
52 request->suspendIfNeeded();
53 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
55 transaction->registerRequest(request);
59 IDBRequest::IDBRequest(ScriptState* scriptState, IDBAny* source, IDBTransaction* transaction)
60 : ActiveDOMObject(scriptState->executionContext())
61 , m_contextStopped(false)
62 , m_transaction(transaction)
63 , m_readyState(PENDING)
64 , m_requestAborted(false)
65 , m_scriptState(scriptState)
67 , m_hasPendingActivity(true)
68 , m_cursorType(IndexedDB::CursorKeyAndValue)
69 , m_cursorDirection(WebIDBCursorDirectionNext)
70 , m_pendingCursor(nullptr)
71 , m_didFireUpgradeNeededEvent(false)
72 , m_preventPropagation(false)
77 IDBRequest::~IDBRequest()
79 ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionContext());
80 ASSERT(!m_blobInfo || m_blobInfo->size() == 0);
83 void IDBRequest::dispose()
88 void IDBRequest::trace(Visitor* visitor)
90 visitor->trace(m_transaction);
91 visitor->trace(m_source);
92 visitor->trace(m_result);
93 visitor->trace(m_error);
95 visitor->trace(m_enqueuedEvents);
97 visitor->trace(m_pendingCursor);
98 visitor->trace(m_cursorKey);
99 visitor->trace(m_cursorPrimaryKey);
100 EventTargetWithInlineData::trace(visitor);
103 ScriptValue IDBRequest::result(ExceptionState& exceptionState)
105 if (m_readyState != DONE) {
106 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
107 return ScriptValue();
109 if (m_contextStopped || !executionContext())
110 return ScriptValue();
111 m_resultDirty = false;
112 ScriptValue value = idbAnyToScriptValue(m_scriptState.get(), m_result);
117 DOMError* IDBRequest::error(ExceptionState& exceptionState) const
119 if (m_readyState != DONE) {
120 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
126 ScriptValue IDBRequest::source() const
128 if (m_contextStopped || !executionContext())
129 return ScriptValue();
131 return idbAnyToScriptValue(m_scriptState.get(), m_source);
134 const String& IDBRequest::readyState() const
136 ASSERT(m_readyState == PENDING || m_readyState == DONE);
138 if (m_readyState == PENDING)
139 return IndexedDBNames::pending;
141 return IndexedDBNames::done;
144 void IDBRequest::abort()
146 ASSERT(!m_requestAborted);
147 if (m_contextStopped || !executionContext())
149 ASSERT(m_readyState == PENDING || m_readyState == DONE);
150 if (m_readyState == DONE)
153 EventQueue* eventQueue = executionContext()->eventQueue();
154 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
155 bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
156 ASSERT_UNUSED(removed, removed);
158 m_enqueuedEvents.clear();
162 onError(DOMError::create(AbortError, "The transaction was aborted, so the request cannot be fulfilled."));
163 m_requestAborted = true;
166 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, WebIDBCursorDirection direction)
168 ASSERT(m_readyState == PENDING);
169 ASSERT(!m_pendingCursor);
170 m_cursorType = cursorType;
171 m_cursorDirection = direction;
174 void IDBRequest::setPendingCursor(IDBCursor* cursor)
176 ASSERT(m_readyState == DONE);
177 ASSERT(executionContext());
178 ASSERT(m_transaction);
179 ASSERT(!m_pendingCursor);
180 ASSERT(cursor == getResultCursor());
182 m_hasPendingActivity = true;
183 m_pendingCursor = cursor;
185 m_readyState = PENDING;
187 m_transaction->registerRequest(this);
190 IDBCursor* IDBRequest::getResultCursor() const
194 if (m_result->type() == IDBAny::IDBCursorType)
195 return m_result->idbCursor();
196 if (m_result->type() == IDBAny::IDBCursorWithValueType)
197 return m_result->idbCursorWithValue();
201 void IDBRequest::setResultCursor(IDBCursor* cursor, IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
203 ASSERT(m_readyState == PENDING);
205 m_cursorPrimaryKey = primaryKey;
206 m_cursorValue = value;
207 setBlobInfo(blobInfo);
209 onSuccessInternal(IDBAny::create(cursor));
212 void IDBRequest::setBlobInfo(PassOwnPtr<Vector<WebBlobInfo>> blobInfo)
215 m_blobInfo = blobInfo;
216 if (m_blobInfo && m_blobInfo->size() > 0)
217 ThreadState::current()->registerPreFinalizer(*this);
220 void IDBRequest::handleBlobAcks()
222 if (m_blobInfo.get() && m_blobInfo->size()) {
223 m_transaction->db()->ackReceivedBlobs(m_blobInfo.get());
225 ThreadState::current()->unregisterPreFinalizer(*this);
229 bool IDBRequest::shouldEnqueueEvent() const
231 if (m_contextStopped || !executionContext())
233 ASSERT(m_readyState == PENDING || m_readyState == DONE);
234 if (m_requestAborted)
236 ASSERT(m_readyState == PENDING);
237 ASSERT(!m_error && !m_result);
241 void IDBRequest::onError(DOMError* error)
243 IDB_TRACE("IDBRequest::onError()");
244 if (!shouldEnqueueEvent())
248 setResult(IDBAny::createUndefined());
249 m_pendingCursor.clear();
250 enqueueEvent(Event::createCancelableBubble(EventTypeNames::error));
253 void IDBRequest::onSuccess(const Vector<String>& stringList)
255 IDB_TRACE("IDBRequest::onSuccess(StringList)");
256 if (!shouldEnqueueEvent())
259 RefPtrWillBeRawPtr<DOMStringList> domStringList = DOMStringList::create();
260 for (size_t i = 0; i < stringList.size(); ++i)
261 domStringList->append(stringList[i]);
262 onSuccessInternal(IDBAny::create(domStringList.release()));
265 void IDBRequest::onSuccess(PassOwnPtr<WebIDBCursor> backend, IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
267 IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
268 if (!shouldEnqueueEvent())
271 ASSERT(!m_pendingCursor);
272 IDBCursor* cursor = 0;
273 switch (m_cursorType) {
274 case IndexedDB::CursorKeyOnly:
275 cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
277 case IndexedDB::CursorKeyAndValue:
278 cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
281 ASSERT_NOT_REACHED();
283 setResultCursor(cursor, key, primaryKey, value, blobInfo);
286 void IDBRequest::onSuccess(IDBKey* idbKey)
288 IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
289 if (!shouldEnqueueEvent())
292 if (idbKey && idbKey->isValid())
293 onSuccessInternal(IDBAny::create(idbKey));
295 onSuccessInternal(IDBAny::createUndefined());
298 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
300 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)");
301 if (!shouldEnqueueEvent())
304 if (m_pendingCursor) {
305 // Value should be null, signifying the end of the cursor's range.
306 ASSERT(!valueBuffer.get());
307 ASSERT(!blobInfo->size());
308 m_pendingCursor->close();
309 m_pendingCursor.clear();
312 setBlobInfo(blobInfo);
313 onSuccessInternal(IDBAny::create(valueBuffer, m_blobInfo.get()));
317 static IDBObjectStore* effectiveObjectStore(IDBAny* source)
319 if (source->type() == IDBAny::IDBObjectStoreType)
320 return source->idbObjectStore();
321 if (source->type() == IDBAny::IDBIndexType)
322 return source->idbIndex()->objectStore();
324 ASSERT_NOT_REACHED();
329 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassOwnPtr<Vector<WebBlobInfo> > blobInfo, IDBKey* prpPrimaryKey, const IDBKeyPath& keyPath)
331 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
332 if (!shouldEnqueueEvent())
335 ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath);
337 RefPtr<SharedBuffer> valueBuffer = prpValueBuffer;
338 IDBKey* primaryKey = prpPrimaryKey;
339 setBlobInfo(blobInfo);
342 assertPrimaryKeyValidOrInjectable(m_scriptState.get(), valueBuffer, m_blobInfo.get(), primaryKey, keyPath);
345 onSuccessInternal(IDBAny::create(valueBuffer, m_blobInfo.get(), primaryKey, keyPath));
348 void IDBRequest::onSuccess(int64_t value)
350 IDB_TRACE("IDBRequest::onSuccess(int64_t)");
351 if (!shouldEnqueueEvent())
353 onSuccessInternal(IDBAny::create(value));
356 void IDBRequest::onSuccess()
358 IDB_TRACE("IDBRequest::onSuccess()");
359 if (!shouldEnqueueEvent())
361 onSuccessInternal(IDBAny::createUndefined());
364 void IDBRequest::onSuccessInternal(IDBAny* result)
366 ASSERT(!m_contextStopped);
367 ASSERT(!m_pendingCursor);
369 enqueueEvent(Event::create(EventTypeNames::success));
372 void IDBRequest::setResult(IDBAny* result)
375 m_resultDirty = true;
378 void IDBRequest::onSuccess(IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
380 IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)");
381 if (!shouldEnqueueEvent())
384 ASSERT(m_pendingCursor);
385 setResultCursor(m_pendingCursor.release(), key, primaryKey, value, blobInfo);
388 bool IDBRequest::hasPendingActivity() const
390 // FIXME: In an ideal world, we should return true as long as anyone has a or can
391 // get a handle to us and we have event listeners. This is order to handle
392 // user generated events properly.
393 return m_hasPendingActivity && !m_contextStopped;
396 void IDBRequest::stop()
398 if (m_contextStopped)
401 m_contextStopped = true;
403 if (m_readyState == PENDING) {
404 m_readyState = EarlyDeath;
406 m_transaction->unregisterRequest(this);
407 m_transaction.clear();
411 m_enqueuedEvents.clear();
413 m_source->contextWillBeDestroyed();
415 m_result->contextWillBeDestroyed();
417 m_pendingCursor->contextWillBeDestroyed();
420 const AtomicString& IDBRequest::interfaceName() const
422 return EventTargetNames::IDBRequest;
425 ExecutionContext* IDBRequest::executionContext() const
427 return ActiveDOMObject::executionContext();
430 bool IDBRequest::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
432 IDB_TRACE("IDBRequest::dispatchEvent");
433 if (m_contextStopped || !executionContext())
435 ASSERT(m_readyState == PENDING);
436 ASSERT(m_hasPendingActivity);
437 ASSERT(m_enqueuedEvents.size());
438 ASSERT(event->target() == this);
440 ScriptState::Scope scope(m_scriptState.get());
442 if (event->type() != EventTypeNames::blocked)
444 dequeueEvent(event.get());
446 WillBeHeapVector<RefPtrWillBeMember<EventTarget> > targets;
447 targets.append(this);
448 if (m_transaction && !m_preventPropagation) {
449 targets.append(m_transaction);
450 // If there ever are events that are associated with a database but
451 // that do not have a transaction, then this will not work and we need
452 // this object to actually hold a reference to the database (to ensure
454 targets.append(m_transaction->db());
457 // Cursor properties should not be updated until the success event is being dispatched.
458 IDBCursor* cursorToNotify = 0;
459 if (event->type() == EventTypeNames::success) {
460 cursorToNotify = getResultCursor();
461 if (cursorToNotify) {
462 if (m_blobInfo && m_blobInfo->size() > 0)
463 ThreadState::current()->unregisterPreFinalizer(*this);
464 cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue.release(), m_blobInfo.release());
468 if (event->type() == EventTypeNames::upgradeneeded) {
469 ASSERT(!m_didFireUpgradeNeededEvent);
470 m_didFireUpgradeNeededEvent = true;
473 // FIXME: When we allow custom event dispatching, this will probably need to change.
474 ASSERT_WITH_MESSAGE(event->type() == EventTypeNames::success || event->type() == EventTypeNames::error || event->type() == EventTypeNames::blocked || event->type() == EventTypeNames::upgradeneeded, "event type was %s", event->type().utf8().data());
475 const bool setTransactionActive = m_transaction && (event->type() == EventTypeNames::success || event->type() == EventTypeNames::upgradeneeded || (event->type() == EventTypeNames::error && !m_requestAborted));
477 if (setTransactionActive)
478 m_transaction->setActive(true);
480 bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
483 if (m_readyState == DONE)
484 m_transaction->unregisterRequest(this);
486 // Possibly abort the transaction. This must occur after unregistering (so this request
487 // doesn't receive a second error) and before deactivating (which might trigger commit).
488 if (event->type() == EventTypeNames::error && dontPreventDefault && !m_requestAborted) {
489 m_transaction->setError(m_error);
490 m_transaction->abort(IGNORE_EXCEPTION);
493 // If this was the last request in the transaction's list, it may commit here.
494 if (setTransactionActive)
495 m_transaction->setActive(false);
499 cursorToNotify->postSuccessHandlerCallback();
501 // An upgradeneeded event will always be followed by a success or error event, so must
503 if (m_readyState == DONE && event->type() != EventTypeNames::upgradeneeded)
504 m_hasPendingActivity = false;
506 return dontPreventDefault;
509 void IDBRequest::uncaughtExceptionInEventHandler()
511 if (m_transaction && !m_requestAborted) {
512 m_transaction->setError(DOMError::create(AbortError, "Uncaught exception in event handler."));
513 m_transaction->abort(IGNORE_EXCEPTION);
517 void IDBRequest::transactionDidFinishAndDispatch()
519 ASSERT(m_transaction);
520 ASSERT(m_transaction->isVersionChange());
521 ASSERT(m_didFireUpgradeNeededEvent);
522 ASSERT(m_readyState == DONE);
523 ASSERT(executionContext());
524 m_transaction.clear();
526 if (m_contextStopped)
529 m_readyState = PENDING;
532 void IDBRequest::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event)
534 ASSERT(m_readyState == PENDING || m_readyState == DONE);
536 if (m_contextStopped || !executionContext())
539 ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().utf8().data(), m_readyState);
541 EventQueue* eventQueue = executionContext()->eventQueue();
542 event->setTarget(this);
544 // Keep track of enqueued events in case we need to abort prior to dispatch,
545 // in which case these must be cancelled. If the events not dispatched for
546 // other reasons they must be removed from this list via dequeueEvent().
547 if (eventQueue->enqueueEvent(event.get()))
548 m_enqueuedEvents.append(event);
551 void IDBRequest::dequeueEvent(Event* event)
553 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
554 if (m_enqueuedEvents[i].get() == event)
555 m_enqueuedEvents.remove(i);