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/v8/ExceptionState.h"
33 #include "bindings/v8/ExceptionStatePlaceholder.h"
34 #include "bindings/v8/IDBBindingUtilities.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "core/events/EventQueue.h"
37 #include "modules/indexeddb/IDBCursorWithValue.h"
38 #include "modules/indexeddb/IDBDatabase.h"
39 #include "modules/indexeddb/IDBEventDispatcher.h"
40 #include "modules/indexeddb/IDBTracing.h"
41 #include "platform/SharedBuffer.h"
43 using blink::WebIDBCursor;
47 PassRefPtr<IDBRequest> IDBRequest::create(ExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
49 RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, transaction)));
50 request->suspendIfNeeded();
51 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
53 transaction->registerRequest(request.get());
54 return request.release();
57 IDBRequest::IDBRequest(ExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
58 : ActiveDOMObject(context)
59 , m_contextStopped(false)
60 , m_transaction(transaction)
61 , m_readyState(PENDING)
62 , m_requestAborted(false)
64 , m_hasPendingActivity(true)
65 , m_cursorType(IndexedDB::CursorKeyAndValue)
66 , m_cursorDirection(blink::WebIDBCursor::Next)
68 , m_didFireUpgradeNeededEvent(false)
69 , m_preventPropagation(false)
71 , m_requestState(context)
73 ScriptWrappable::init(this);
76 IDBRequest::~IDBRequest()
78 ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionContext());
81 ScriptValue IDBRequest::result(ExceptionState& exceptionState)
83 if (m_readyState != DONE) {
84 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
87 m_resultDirty = false;
88 return idbAnyToScriptValue(&m_requestState, m_result);
91 PassRefPtr<DOMError> IDBRequest::error(ExceptionState& exceptionState) const
93 if (m_readyState != DONE) {
94 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
100 ScriptValue IDBRequest::source(ExecutionContext* context) const
102 DOMRequestState requestState(context);
103 return idbAnyToScriptValue(&requestState, m_source);
106 const String& IDBRequest::readyState() const
108 ASSERT(m_readyState == PENDING || m_readyState == DONE);
109 DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
110 DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
112 if (m_readyState == PENDING)
118 void IDBRequest::abort()
120 ASSERT(!m_requestAborted);
121 if (m_contextStopped || !executionContext())
123 ASSERT(m_readyState == PENDING || m_readyState == DONE);
124 if (m_readyState == DONE)
127 // Enqueued events may be the only reference to this object.
128 RefPtr<IDBRequest> self(this);
130 EventQueue* eventQueue = executionContext()->eventQueue();
131 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
132 bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
133 ASSERT_UNUSED(removed, removed);
135 m_enqueuedEvents.clear();
139 onError(DOMError::create(AbortError, "The transaction was aborted, so the request cannot be fulfilled."));
140 m_requestAborted = true;
143 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, WebIDBCursor::Direction direction)
145 ASSERT(m_readyState == PENDING);
146 ASSERT(!m_pendingCursor);
147 m_cursorType = cursorType;
148 m_cursorDirection = direction;
151 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
153 ASSERT(m_readyState == DONE);
154 ASSERT(executionContext());
155 ASSERT(m_transaction);
156 ASSERT(!m_pendingCursor);
157 ASSERT(cursor == getResultCursor());
159 m_hasPendingActivity = true;
160 m_pendingCursor = cursor;
161 setResult(PassRefPtr<IDBAny>(0));
162 m_readyState = PENDING;
164 m_transaction->registerRequest(this);
167 IDBCursor* IDBRequest::getResultCursor() const
171 if (m_result->type() == IDBAny::IDBCursorType)
172 return m_result->idbCursor();
173 if (m_result->type() == IDBAny::IDBCursorWithValueType)
174 return m_result->idbCursorWithValue();
178 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
180 ASSERT(m_readyState == PENDING);
182 m_cursorPrimaryKey = primaryKey;
183 m_cursorValue = value;
185 onSuccessInternal(IDBAny::create(cursor));
188 void IDBRequest::checkForReferenceCycle()
190 // If this request and its cursor have the only references
191 // to each other, then explicitly break the cycle.
192 IDBCursor* cursor = getResultCursor();
193 if (!cursor || cursor->request() != this)
196 if (!hasOneRef() || !cursor->hasOneRef())
202 bool IDBRequest::shouldEnqueueEvent() const
204 if (m_contextStopped || !executionContext())
206 ASSERT(m_readyState == PENDING || m_readyState == DONE);
207 if (m_requestAborted)
209 ASSERT(m_readyState == PENDING);
210 ASSERT(!m_error && !m_result);
214 void IDBRequest::onError(PassRefPtr<DOMError> error)
216 IDB_TRACE("IDBRequest::onError()");
217 if (!shouldEnqueueEvent())
221 m_pendingCursor.clear();
222 enqueueEvent(Event::createCancelableBubble(EventTypeNames::error));
225 void IDBRequest::onSuccess(const Vector<String>& stringList)
227 IDB_TRACE("IDBRequest::onSuccess(StringList)");
228 if (!shouldEnqueueEvent())
231 RefPtr<DOMStringList> domStringList = DOMStringList::create();
232 for (size_t i = 0; i < stringList.size(); ++i)
233 domStringList->append(stringList[i]);
234 onSuccessInternal(IDBAny::create(domStringList.release()));
237 void IDBRequest::onSuccess(PassOwnPtr<blink::WebIDBCursor> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
239 IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
240 if (!shouldEnqueueEvent())
243 ASSERT(!m_pendingCursor);
244 RefPtr<IDBCursor> cursor;
245 switch (m_cursorType) {
246 case IndexedDB::CursorKeyOnly:
247 cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
249 case IndexedDB::CursorKeyAndValue:
250 cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
253 ASSERT_NOT_REACHED();
255 setResultCursor(cursor, key, primaryKey, value);
258 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
260 IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
261 if (!shouldEnqueueEvent())
264 if (idbKey && idbKey->isValid())
265 onSuccessInternal(IDBAny::create(idbKey));
267 onSuccessInternal(IDBAny::createUndefined());
270 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
272 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)");
273 if (!shouldEnqueueEvent())
276 if (m_pendingCursor) {
277 // Value should be null, signifying the end of the cursor's range.
278 ASSERT(!valueBuffer.get());
279 m_pendingCursor->close();
280 m_pendingCursor.clear();
283 onSuccessInternal(IDBAny::create(valueBuffer));
287 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
289 if (source->type() == IDBAny::IDBObjectStoreType)
290 return source->idbObjectStore();
291 if (source->type() == IDBAny::IDBIndexType)
292 return source->idbIndex()->objectStore();
294 ASSERT_NOT_REACHED();
299 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
301 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
302 if (!shouldEnqueueEvent())
306 ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath);
309 RefPtr<SharedBuffer> valueBuffer = prpValueBuffer;
310 RefPtr<IDBKey> primaryKey = prpPrimaryKey;
313 assertPrimaryKeyValidOrInjectable(&m_requestState, valueBuffer, primaryKey, keyPath);
316 onSuccessInternal(IDBAny::create(valueBuffer, primaryKey, keyPath));
319 void IDBRequest::onSuccess(int64_t value)
321 IDB_TRACE("IDBRequest::onSuccess(int64_t)");
322 if (!shouldEnqueueEvent())
324 onSuccessInternal(IDBAny::create(value));
327 void IDBRequest::onSuccess()
329 IDB_TRACE("IDBRequest::onSuccess()");
330 if (!shouldEnqueueEvent())
332 onSuccessInternal(IDBAny::createUndefined());
335 void IDBRequest::onSuccessInternal(PassRefPtr<IDBAny> result)
337 ASSERT(!m_contextStopped);
338 ASSERT(!m_pendingCursor);
340 enqueueEvent(Event::create(EventTypeNames::success));
343 void IDBRequest::setResult(PassRefPtr<IDBAny> result)
346 m_resultDirty = true;
349 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
351 IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)");
352 if (!shouldEnqueueEvent())
355 ASSERT(m_pendingCursor);
356 setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
359 bool IDBRequest::hasPendingActivity() const
361 // FIXME: In an ideal world, we should return true as long as anyone has a or can
362 // get a handle to us and we have event listeners. This is order to handle
363 // user generated events properly.
364 return m_hasPendingActivity && !m_contextStopped;
367 void IDBRequest::stop()
369 if (m_contextStopped)
372 m_contextStopped = true;
373 m_requestState.clear();
375 RefPtr<IDBRequest> protect(this);
377 if (m_readyState == PENDING) {
378 m_readyState = EarlyDeath;
380 m_transaction->unregisterRequest(this);
381 m_transaction.clear();
385 m_enqueuedEvents.clear();
388 const AtomicString& IDBRequest::interfaceName() const
390 return EventTargetNames::IDBRequest;
393 ExecutionContext* IDBRequest::executionContext() const
395 return ActiveDOMObject::executionContext();
398 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
400 IDB_TRACE("IDBRequest::dispatchEvent");
401 if (m_contextStopped || !executionContext())
403 ASSERT(m_requestState.isValid());
404 ASSERT(m_readyState == PENDING);
405 ASSERT(m_hasPendingActivity);
406 ASSERT(m_enqueuedEvents.size());
407 ASSERT(event->target() == this);
409 DOMRequestState::Scope scope(m_requestState);
411 if (event->type() != EventTypeNames::blocked)
413 dequeueEvent(event.get());
415 Vector<RefPtr<EventTarget> > targets;
416 targets.append(this);
417 if (m_transaction && !m_preventPropagation) {
418 targets.append(m_transaction);
419 // If there ever are events that are associated with a database but
420 // that do not have a transaction, then this will not work and we need
421 // this object to actually hold a reference to the database (to ensure
423 targets.append(m_transaction->db());
426 // Cursor properties should not be updated until the success event is being dispatched.
427 RefPtr<IDBCursor> cursorToNotify;
428 if (event->type() == EventTypeNames::success) {
429 cursorToNotify = getResultCursor();
431 cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue.release());
434 if (event->type() == EventTypeNames::upgradeneeded) {
435 ASSERT(!m_didFireUpgradeNeededEvent);
436 m_didFireUpgradeNeededEvent = true;
439 // FIXME: When we allow custom event dispatching, this will probably need to change.
440 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());
441 const bool setTransactionActive = m_transaction && (event->type() == EventTypeNames::success || event->type() == EventTypeNames::upgradeneeded || (event->type() == EventTypeNames::error && !m_requestAborted));
443 if (setTransactionActive)
444 m_transaction->setActive(true);
446 bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
449 if (m_readyState == DONE)
450 m_transaction->unregisterRequest(this);
452 // Possibly abort the transaction. This must occur after unregistering (so this request
453 // doesn't receive a second error) and before deactivating (which might trigger commit).
454 if (event->type() == EventTypeNames::error && dontPreventDefault && !m_requestAborted) {
455 m_transaction->setError(m_error);
456 m_transaction->abort(IGNORE_EXCEPTION);
459 // If this was the last request in the transaction's list, it may commit here.
460 if (setTransactionActive)
461 m_transaction->setActive(false);
465 cursorToNotify->postSuccessHandlerCallback();
467 // An upgradeneeded event will always be followed by a success or error event, so must
469 if (m_readyState == DONE && event->type() != EventTypeNames::upgradeneeded)
470 m_hasPendingActivity = false;
472 return dontPreventDefault;
475 void IDBRequest::uncaughtExceptionInEventHandler()
477 if (m_transaction && !m_requestAborted) {
478 m_transaction->setError(DOMError::create(AbortError, "Uncaught exception in event handler."));
479 m_transaction->abort(IGNORE_EXCEPTION);
483 void IDBRequest::transactionDidFinishAndDispatch()
485 ASSERT(m_transaction);
486 ASSERT(m_transaction->isVersionChange());
487 ASSERT(m_didFireUpgradeNeededEvent);
488 ASSERT(m_readyState == DONE);
489 ASSERT(executionContext());
490 m_transaction.clear();
492 if (m_contextStopped)
495 m_readyState = PENDING;
498 void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
500 ASSERT(m_readyState == PENDING || m_readyState == DONE);
502 if (m_contextStopped || !executionContext())
505 ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().utf8().data(), m_readyState);
507 EventQueue* eventQueue = executionContext()->eventQueue();
508 event->setTarget(this);
510 // Keep track of enqueued events in case we need to abort prior to dispatch,
511 // in which case these must be cancelled. If the events not dispatched for
512 // other reasons they must be removed from this list via dequeueEvent().
513 if (eventQueue->enqueueEvent(event.get()))
514 m_enqueuedEvents.append(event);
517 void IDBRequest::dequeueEvent(Event* event)
519 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
520 if (m_enqueuedEvents[i].get() == event)
521 m_enqueuedEvents.remove(i);
525 } // namespace WebCore