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