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