Home | History | Annotate | Download | only in indexeddb
      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 "modules/indexeddb/IDBRequest.h"
     31 
     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"
     42 
     43 namespace WebCore {
     44 
     45 PassRefPtr<IDBRequest> IDBRequest::create(ExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
     46 {
     47     RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, transaction)));
     48     request->suspendIfNeeded();
     49     // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
     50     if (transaction)
     51         transaction->registerRequest(request.get());
     52     return request.release();
     53 }
     54 
     55 IDBRequest::IDBRequest(ExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
     56     : ActiveDOMObject(context)
     57     , m_contextStopped(false)
     58     , m_transaction(transaction)
     59     , m_readyState(PENDING)
     60     , m_requestAborted(false)
     61     , m_source(source)
     62     , m_hasPendingActivity(true)
     63     , m_cursorType(IndexedDB::CursorKeyAndValue)
     64     , m_cursorDirection(IndexedDB::CursorNext)
     65     , m_pendingCursor(0)
     66     , m_didFireUpgradeNeededEvent(false)
     67     , m_preventPropagation(false)
     68     , m_resultDirty(true)
     69     , m_requestState(context)
     70 {
     71     ScriptWrappable::init(this);
     72 }
     73 
     74 IDBRequest::~IDBRequest()
     75 {
     76     ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionContext());
     77 }
     78 
     79 ScriptValue IDBRequest::result(ExceptionState& exceptionState)
     80 {
     81     if (m_readyState != DONE) {
     82         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
     83         return ScriptValue();
     84     }
     85     m_resultDirty = false;
     86     return idbAnyToScriptValue(&m_requestState, m_result);
     87 }
     88 
     89 PassRefPtr<DOMError> IDBRequest::error(ExceptionState& exceptionState) const
     90 {
     91     if (m_readyState != DONE) {
     92         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
     93         return 0;
     94     }
     95     return m_error;
     96 }
     97 
     98 ScriptValue IDBRequest::source(ExecutionContext* context) const
     99 {
    100     DOMRequestState requestState(context);
    101     return idbAnyToScriptValue(&requestState, m_source);
    102 }
    103 
    104 const String& IDBRequest::readyState() const
    105 {
    106     ASSERT(m_readyState == PENDING || m_readyState == DONE);
    107     DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
    108     DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
    109 
    110     if (m_readyState == PENDING)
    111         return pending;
    112 
    113     return done;
    114 }
    115 
    116 void IDBRequest::abort()
    117 {
    118     ASSERT(!m_requestAborted);
    119     if (m_contextStopped || !executionContext())
    120         return;
    121     ASSERT(m_readyState == PENDING || m_readyState == DONE);
    122     if (m_readyState == DONE)
    123         return;
    124 
    125     // Enqueued events may be the only reference to this object.
    126     RefPtr<IDBRequest> self(this);
    127 
    128     EventQueue* eventQueue = executionContext()->eventQueue();
    129     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
    130         bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
    131         ASSERT_UNUSED(removed, removed);
    132     }
    133     m_enqueuedEvents.clear();
    134 
    135     m_error.clear();
    136     m_result.clear();
    137     onError(DOMError::create(AbortError, "The transaction was aborted, so the request cannot be fulfilled."));
    138     m_requestAborted = true;
    139 }
    140 
    141 void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction)
    142 {
    143     ASSERT(m_readyState == PENDING);
    144     ASSERT(!m_pendingCursor);
    145     m_cursorType = cursorType;
    146     m_cursorDirection = direction;
    147 }
    148 
    149 void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
    150 {
    151     ASSERT(m_readyState == DONE);
    152     ASSERT(executionContext());
    153     ASSERT(m_transaction);
    154     ASSERT(!m_pendingCursor);
    155     ASSERT(cursor == getResultCursor());
    156 
    157     m_hasPendingActivity = true;
    158     m_pendingCursor = cursor;
    159     m_result.clear();
    160     m_readyState = PENDING;
    161     m_error.clear();
    162     m_transaction->registerRequest(this);
    163 }
    164 
    165 IDBCursor* IDBRequest::getResultCursor() const
    166 {
    167     if (!m_result)
    168         return 0;
    169     if (m_result->type() == IDBAny::IDBCursorType)
    170         return m_result->idbCursor();
    171     if (m_result->type() == IDBAny::IDBCursorWithValueType)
    172         return m_result->idbCursorWithValue();
    173     return 0;
    174 }
    175 
    176 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
    177 {
    178     ASSERT(m_readyState == PENDING);
    179     m_cursorKey = key;
    180     m_cursorPrimaryKey = primaryKey;
    181     m_cursorValue = value;
    182 
    183     onSuccessInternal(IDBAny::create(cursor));
    184 }
    185 
    186 void IDBRequest::checkForReferenceCycle()
    187 {
    188     // If this request and its cursor have the only references
    189     // to each other, then explicitly break the cycle.
    190     IDBCursor* cursor = getResultCursor();
    191     if (!cursor || cursor->request() != this)
    192         return;
    193 
    194     if (!hasOneRef() || !cursor->hasOneRef())
    195         return;
    196 
    197     m_result.clear();
    198 }
    199 
    200 bool IDBRequest::shouldEnqueueEvent() const
    201 {
    202     if (m_contextStopped || !executionContext())
    203         return false;
    204     ASSERT(m_readyState == PENDING || m_readyState == DONE);
    205     if (m_requestAborted)
    206         return false;
    207     ASSERT(m_readyState == PENDING);
    208     ASSERT(!m_error && !m_result);
    209     return true;
    210 }
    211 
    212 void IDBRequest::onError(PassRefPtr<DOMError> error)
    213 {
    214     IDB_TRACE("IDBRequest::onError()");
    215     if (!shouldEnqueueEvent())
    216         return;
    217 
    218     m_error = error;
    219     m_pendingCursor.clear();
    220     enqueueEvent(Event::createCancelableBubble(EventTypeNames::error));
    221 }
    222 
    223 void IDBRequest::onSuccess(const Vector<String>& stringList)
    224 {
    225     IDB_TRACE("IDBRequest::onSuccess(StringList)");
    226     if (!shouldEnqueueEvent())
    227         return;
    228 
    229     RefPtr<DOMStringList> domStringList = DOMStringList::create();
    230     for (size_t i = 0; i < stringList.size(); ++i)
    231         domStringList->append(stringList[i]);
    232     onSuccessInternal(IDBAny::create(domStringList.release()));
    233 }
    234 
    235 void IDBRequest::onSuccess(PassOwnPtr<blink::WebIDBCursor> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
    236 {
    237     IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
    238     if (!shouldEnqueueEvent())
    239         return;
    240 
    241     ASSERT(!m_pendingCursor);
    242     RefPtr<IDBCursor> cursor;
    243     switch (m_cursorType) {
    244     case IndexedDB::CursorKeyOnly:
    245         cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
    246         break;
    247     case IndexedDB::CursorKeyAndValue:
    248         cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
    249         break;
    250     default:
    251         ASSERT_NOT_REACHED();
    252     }
    253     setResultCursor(cursor, key, primaryKey, value);
    254 }
    255 
    256 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
    257 {
    258     IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
    259     if (!shouldEnqueueEvent())
    260         return;
    261 
    262     if (idbKey && idbKey->isValid())
    263         onSuccessInternal(IDBAny::create(idbKey));
    264     else
    265         onSuccessInternal(IDBAny::createUndefined());
    266 }
    267 
    268 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
    269 {
    270     IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)");
    271     if (!shouldEnqueueEvent())
    272         return;
    273 
    274     if (m_pendingCursor) {
    275         // Value should be null, signifying the end of the cursor's range.
    276         ASSERT(!valueBuffer.get());
    277         m_pendingCursor->close();
    278         m_pendingCursor.clear();
    279     }
    280 
    281     onSuccessInternal(IDBAny::create(valueBuffer));
    282 }
    283 
    284 #ifndef NDEBUG
    285 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
    286 {
    287     if (source->type() == IDBAny::IDBObjectStoreType)
    288         return source->idbObjectStore();
    289     if (source->type() == IDBAny::IDBIndexType)
    290         return source->idbIndex()->objectStore();
    291 
    292     ASSERT_NOT_REACHED();
    293     return 0;
    294 }
    295 #endif
    296 
    297 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
    298 {
    299     IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
    300     if (!shouldEnqueueEvent())
    301         return;
    302 
    303 #ifndef NDEBUG
    304     ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath);
    305 #endif
    306 
    307     RefPtr<SharedBuffer> valueBuffer = prpValueBuffer;
    308     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
    309 
    310 #ifndef NDEBUG
    311     assertPrimaryKeyValidOrInjectable(&m_requestState, valueBuffer, primaryKey, keyPath);
    312 #endif
    313 
    314     onSuccessInternal(IDBAny::create(valueBuffer, primaryKey, keyPath));
    315 }
    316 
    317 void IDBRequest::onSuccess(int64_t value)
    318 {
    319     IDB_TRACE("IDBRequest::onSuccess(int64_t)");
    320     if (!shouldEnqueueEvent())
    321         return;
    322     onSuccessInternal(IDBAny::create(value));
    323 }
    324 
    325 void IDBRequest::onSuccess()
    326 {
    327     IDB_TRACE("IDBRequest::onSuccess()");
    328     if (!shouldEnqueueEvent())
    329         return;
    330     onSuccessInternal(IDBAny::createUndefined());
    331 }
    332 
    333 void IDBRequest::onSuccessInternal(PassRefPtr<IDBAny> result)
    334 {
    335     ASSERT(!m_contextStopped);
    336     ASSERT(!m_pendingCursor);
    337     setResult(result);
    338     enqueueEvent(Event::create(EventTypeNames::success));
    339 }
    340 
    341 void IDBRequest::setResult(PassRefPtr<IDBAny> result)
    342 {
    343     m_result = result;
    344     m_resultDirty = true;
    345 }
    346 
    347 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
    348 {
    349     IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)");
    350     if (!shouldEnqueueEvent())
    351         return;
    352 
    353     ASSERT(m_pendingCursor);
    354     setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
    355 }
    356 
    357 bool IDBRequest::hasPendingActivity() const
    358 {
    359     // FIXME: In an ideal world, we should return true as long as anyone has a or can
    360     //        get a handle to us and we have event listeners. This is order to handle
    361     //        user generated events properly.
    362     return m_hasPendingActivity && !m_contextStopped;
    363 }
    364 
    365 void IDBRequest::stop()
    366 {
    367     if (m_contextStopped)
    368         return;
    369 
    370     m_contextStopped = true;
    371     m_requestState.clear();
    372 
    373     RefPtr<IDBRequest> protect(this);
    374 
    375     if (m_readyState == PENDING) {
    376         m_readyState = EarlyDeath;
    377         if (m_transaction) {
    378             m_transaction->unregisterRequest(this);
    379             m_transaction.clear();
    380         }
    381     }
    382 
    383     m_enqueuedEvents.clear();
    384 }
    385 
    386 const AtomicString& IDBRequest::interfaceName() const
    387 {
    388     return EventTargetNames::IDBRequest;
    389 }
    390 
    391 ExecutionContext* IDBRequest::executionContext() const
    392 {
    393     return ActiveDOMObject::executionContext();
    394 }
    395 
    396 bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
    397 {
    398     IDB_TRACE("IDBRequest::dispatchEvent");
    399     if (m_contextStopped || !executionContext())
    400         return false;
    401     ASSERT(m_requestState.isValid());
    402     ASSERT(m_readyState == PENDING);
    403     ASSERT(m_hasPendingActivity);
    404     ASSERT(m_enqueuedEvents.size());
    405     ASSERT(event->target() == this);
    406 
    407     DOMRequestState::Scope scope(m_requestState);
    408 
    409     if (event->type() != EventTypeNames::blocked)
    410         m_readyState = DONE;
    411     dequeueEvent(event.get());
    412 
    413     Vector<RefPtr<EventTarget> > targets;
    414     targets.append(this);
    415     if (m_transaction && !m_preventPropagation) {
    416         targets.append(m_transaction);
    417         // If there ever are events that are associated with a database but
    418         // that do not have a transaction, then this will not work and we need
    419         // this object to actually hold a reference to the database (to ensure
    420         // it stays alive).
    421         targets.append(m_transaction->db());
    422     }
    423 
    424     // Cursor properties should not be updated until the success event is being dispatched.
    425     RefPtr<IDBCursor> cursorToNotify;
    426     if (event->type() == EventTypeNames::success) {
    427         cursorToNotify = getResultCursor();
    428         if (cursorToNotify)
    429             cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue.release());
    430     }
    431 
    432     if (event->type() == EventTypeNames::upgradeneeded) {
    433         ASSERT(!m_didFireUpgradeNeededEvent);
    434         m_didFireUpgradeNeededEvent = true;
    435     }
    436 
    437     // FIXME: When we allow custom event dispatching, this will probably need to change.
    438     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().string().utf8().data());
    439     const bool setTransactionActive = m_transaction && (event->type() == EventTypeNames::success || event->type() == EventTypeNames::upgradeneeded || (event->type() == EventTypeNames::error && !m_requestAborted));
    440 
    441     if (setTransactionActive)
    442         m_transaction->setActive(true);
    443 
    444     bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
    445 
    446     if (m_transaction) {
    447         if (m_readyState == DONE)
    448             m_transaction->unregisterRequest(this);
    449 
    450         // Possibly abort the transaction. This must occur after unregistering (so this request
    451         // doesn't receive a second error) and before deactivating (which might trigger commit).
    452         if (event->type() == EventTypeNames::error && dontPreventDefault && !m_requestAborted) {
    453             m_transaction->setError(m_error);
    454             m_transaction->abort(IGNORE_EXCEPTION);
    455         }
    456 
    457         // If this was the last request in the transaction's list, it may commit here.
    458         if (setTransactionActive)
    459             m_transaction->setActive(false);
    460     }
    461 
    462     if (cursorToNotify)
    463         cursorToNotify->postSuccessHandlerCallback();
    464 
    465     // An upgradeneeded event will always be followed by a success or error event, so must
    466     // be kept alive.
    467     if (m_readyState == DONE && event->type() != EventTypeNames::upgradeneeded)
    468         m_hasPendingActivity = false;
    469 
    470     return dontPreventDefault;
    471 }
    472 
    473 void IDBRequest::uncaughtExceptionInEventHandler()
    474 {
    475     if (m_transaction && !m_requestAborted) {
    476         m_transaction->setError(DOMError::create(AbortError, "Uncaught exception in event handler."));
    477         m_transaction->abort(IGNORE_EXCEPTION);
    478     }
    479 }
    480 
    481 void IDBRequest::transactionDidFinishAndDispatch()
    482 {
    483     ASSERT(m_transaction);
    484     ASSERT(m_transaction->isVersionChange());
    485     ASSERT(m_didFireUpgradeNeededEvent);
    486     ASSERT(m_readyState == DONE);
    487     ASSERT(executionContext());
    488     m_transaction.clear();
    489 
    490     if (m_contextStopped)
    491         return;
    492 
    493     m_readyState = PENDING;
    494 }
    495 
    496 void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
    497 {
    498     ASSERT(m_readyState == PENDING || m_readyState == DONE);
    499 
    500     if (m_contextStopped || !executionContext())
    501         return;
    502 
    503     ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState);
    504 
    505     EventQueue* eventQueue = executionContext()->eventQueue();
    506     event->setTarget(this);
    507 
    508     // Keep track of enqueued events in case we need to abort prior to dispatch,
    509     // in which case these must be cancelled. If the events not dispatched for
    510     // other reasons they must be removed from this list via dequeueEvent().
    511     if (eventQueue->enqueueEvent(event.get()))
    512         m_enqueuedEvents.append(event);
    513 }
    514 
    515 void IDBRequest::dequeueEvent(Event* event)
    516 {
    517     for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
    518         if (m_enqueuedEvents[i].get() == event)
    519             m_enqueuedEvents.remove(i);
    520     }
    521 }
    522 
    523 } // namespace WebCore
    524