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  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "modules/indexeddb/IDBCursor.h"
     28 
     29 #include "bindings/v8/ExceptionState.h"
     30 #include "bindings/v8/IDBBindingUtilities.h"
     31 #include "core/dom/ExceptionCode.h"
     32 #include "core/dom/ExecutionContext.h"
     33 #include "core/inspector/ScriptCallStack.h"
     34 #include "modules/indexeddb/IDBAny.h"
     35 #include "modules/indexeddb/IDBDatabase.h"
     36 #include "modules/indexeddb/IDBObjectStore.h"
     37 #include "modules/indexeddb/IDBTracing.h"
     38 #include "modules/indexeddb/IDBTransaction.h"
     39 #include "modules/indexeddb/WebIDBCallbacksImpl.h"
     40 #include "public/platform/WebIDBDatabase.h"
     41 #include "public/platform/WebIDBKeyRange.h"
     42 #include <limits>
     43 
     44 using blink::WebIDBDatabase;
     45 
     46 namespace WebCore {
     47 
     48 PassRefPtr<IDBCursor> IDBCursor::create(PassOwnPtr<blink::WebIDBCursor> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
     49 {
     50     return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
     51 }
     52 
     53 const AtomicString& IDBCursor::directionNext()
     54 {
     55     DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral));
     56     return next;
     57 }
     58 
     59 const AtomicString& IDBCursor::directionNextUnique()
     60 {
     61     DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral));
     62     return nextunique;
     63 }
     64 
     65 const AtomicString& IDBCursor::directionPrev()
     66 {
     67     DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral));
     68     return prev;
     69 }
     70 
     71 const AtomicString& IDBCursor::directionPrevUnique()
     72 {
     73     DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral));
     74     return prevunique;
     75 }
     76 
     77 IDBCursor::IDBCursor(PassOwnPtr<blink::WebIDBCursor> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
     78     : m_backend(backend)
     79     , m_request(request)
     80     , m_direction(direction)
     81     , m_source(source)
     82     , m_transaction(transaction)
     83     , m_gotValue(false)
     84     , m_keyDirty(true)
     85     , m_primaryKeyDirty(true)
     86     , m_valueDirty(true)
     87 {
     88     ASSERT(m_backend);
     89     ASSERT(m_request);
     90     ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
     91     ASSERT(m_transaction);
     92     ScriptWrappable::init(this);
     93 }
     94 
     95 IDBCursor::~IDBCursor()
     96 {
     97 }
     98 
     99 PassRefPtr<IDBRequest> IDBCursor::update(ScriptState* state, ScriptValue& value, ExceptionState& exceptionState)
    100 {
    101     IDB_TRACE("IDBCursor::update");
    102 
    103     if (!m_gotValue) {
    104         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    105         return 0;
    106     }
    107     if (isKeyCursor()) {
    108         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
    109         return 0;
    110     }
    111     if (isDeleted()) {
    112         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    113         return 0;
    114     }
    115     if (m_transaction->isFinished()) {
    116         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    117         return 0;
    118     }
    119     if (!m_transaction->isActive()) {
    120         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    121         return 0;
    122     }
    123     if (m_transaction->isReadOnly()) {
    124         exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
    125         return 0;
    126     }
    127 
    128     RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
    129     const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
    130     const bool usesInLineKeys = !keyPath.isNull();
    131     if (usesInLineKeys) {
    132         RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath);
    133         if (!keyPathKey || !keyPathKey->isEqual(m_primaryKey.get())) {
    134             exceptionState.throwDOMException(DataError, "The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.");
    135             return 0;
    136         }
    137     }
    138 
    139     return objectStore->put(WebIDBDatabase::CursorUpdate, IDBAny::create(this), state, value, m_primaryKey, exceptionState);
    140 }
    141 
    142 void IDBCursor::advance(unsigned long count, ExceptionState& exceptionState)
    143 {
    144     IDB_TRACE("IDBCursor::advance");
    145     if (!m_gotValue) {
    146         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    147         return;
    148     }
    149     if (isDeleted()) {
    150         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    151         return;
    152     }
    153 
    154     if (m_transaction->isFinished()) {
    155         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    156         return;
    157     }
    158     if (!m_transaction->isActive()) {
    159         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    160         return;
    161     }
    162 
    163     if (!count) {
    164         exceptionState.throwUninformativeAndGenericTypeError();
    165         return;
    166     }
    167 
    168     m_request->setPendingCursor(this);
    169     m_gotValue = false;
    170     m_backend->advance(count, WebIDBCallbacksImpl::create(m_request).leakPtr());
    171 }
    172 
    173 void IDBCursor::continueFunction(ExecutionContext* context, const ScriptValue& keyValue, ExceptionState& exceptionState)
    174 {
    175     IDB_TRACE("IDBCursor::continue");
    176     DOMRequestState requestState(context);
    177     RefPtr<IDBKey> key = keyValue.isUndefined() || keyValue.isNull() ? 0 : scriptValueToIDBKey(&requestState, keyValue);
    178     if (key && !key->isValid()) {
    179         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
    180         return;
    181     }
    182     continueFunction(key.release(), 0, exceptionState);
    183 }
    184 
    185 void IDBCursor::continuePrimaryKey(ExecutionContext* context, const ScriptValue& keyValue, const ScriptValue& primaryKeyValue, ExceptionState& exceptionState)
    186 {
    187     IDB_TRACE("IDBCursor::continuePrimaryKey");
    188     DOMRequestState requestState(context);
    189     RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
    190     RefPtr<IDBKey> primaryKey = scriptValueToIDBKey(&requestState, primaryKeyValue);
    191     if (!key->isValid() || !primaryKey->isValid()) {
    192         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
    193         return;
    194     }
    195     continueFunction(key.release(), primaryKey.release(), exceptionState);
    196 }
    197 
    198 void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, ExceptionState& exceptionState)
    199 {
    200     ASSERT(!primaryKey || (key && primaryKey));
    201 
    202     if (m_transaction->isFinished()) {
    203         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    204         return;
    205     }
    206     if (!m_transaction->isActive()) {
    207         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    208         return;
    209     }
    210 
    211     if (!m_gotValue) {
    212         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    213         return;
    214     }
    215 
    216     if (isDeleted()) {
    217         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    218         return;
    219     }
    220 
    221     if (key) {
    222         ASSERT(m_key);
    223         if (m_direction == IndexedDB::CursorNext || m_direction == IndexedDB::CursorNextNoDuplicate) {
    224             const bool ok = m_key->isLessThan(key.get())
    225                 || (primaryKey && m_key->isEqual(key.get()) && m_primaryKey->isLessThan(primaryKey.get()));
    226             if (!ok) {
    227                 exceptionState.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position.");
    228                 return;
    229             }
    230 
    231         } else {
    232             const bool ok = key->isLessThan(m_key.get())
    233                 || (primaryKey && key->isEqual(m_key.get()) && primaryKey->isLessThan(m_primaryKey.get()));
    234             if (!ok) {
    235                 exceptionState.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position.");
    236                 return;
    237             }
    238         }
    239     }
    240 
    241     // FIXME: We're not using the context from when continue was called, which means the callback
    242     //        will be on the original context openCursor was called on. Is this right?
    243     m_request->setPendingCursor(this);
    244     m_gotValue = false;
    245     m_backend->continueFunction(key, primaryKey, WebIDBCallbacksImpl::create(m_request).leakPtr());
    246 }
    247 
    248 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ExecutionContext* context, ExceptionState& exceptionState)
    249 {
    250     IDB_TRACE("IDBCursor::delete");
    251     if (m_transaction->isFinished()) {
    252         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    253         return 0;
    254     }
    255     if (!m_transaction->isActive()) {
    256         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    257         return 0;
    258     }
    259     if (m_transaction->isReadOnly()) {
    260         exceptionState.throwDOMException(ReadOnlyError, "The record may not be deleted inside a read-only transaction.");
    261         return 0;
    262     }
    263 
    264     if (!m_gotValue) {
    265         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    266         return 0;
    267     }
    268     if (isKeyCursor()) {
    269         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
    270         return 0;
    271     }
    272     if (isDeleted()) {
    273         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    274         return 0;
    275     }
    276 
    277     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(m_primaryKey, exceptionState);
    278     ASSERT(!exceptionState.hadException());
    279 
    280     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
    281     m_transaction->backendDB()->deleteRange(m_transaction->id(), effectiveObjectStore()->id(), keyRange.release(), WebIDBCallbacksImpl::create(request).leakPtr());
    282     return request.release();
    283 }
    284 
    285 void IDBCursor::postSuccessHandlerCallback()
    286 {
    287     if (m_backend)
    288         m_backend->postSuccessHandlerCallback();
    289 }
    290 
    291 void IDBCursor::close()
    292 {
    293     // The notifier may be the last reference to this cursor.
    294     RefPtr<IDBCursor> protect(this);
    295     m_request.clear();
    296     m_backend.clear();
    297 }
    298 
    299 void IDBCursor::checkForReferenceCycle()
    300 {
    301     // If this cursor and its request have the only references
    302     // to each other, then explicitly break the cycle.
    303     if (!m_request || m_request->getResultCursor() != this)
    304         return;
    305 
    306     if (!hasOneRef() || !m_request->hasOneRef())
    307         return;
    308 
    309     m_request.clear();
    310 }
    311 
    312 ScriptValue IDBCursor::key(ExecutionContext* context)
    313 {
    314     m_keyDirty = false;
    315     DOMRequestState requestState(context);
    316     return idbKeyToScriptValue(&requestState, m_key);
    317 }
    318 
    319 ScriptValue IDBCursor::primaryKey(ExecutionContext* context)
    320 {
    321     m_primaryKeyDirty = false;
    322     DOMRequestState requestState(context);
    323     return idbKeyToScriptValue(&requestState, m_primaryKey);
    324 }
    325 
    326 ScriptValue IDBCursor::value(ExecutionContext* context)
    327 {
    328     ASSERT(isCursorWithValue());
    329 
    330     DOMRequestState requestState(context);
    331     RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
    332     const IDBObjectStoreMetadata& metadata = objectStore->metadata();
    333     RefPtr<IDBAny> value;
    334     if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
    335         value = IDBAny::create(m_value, m_primaryKey, metadata.keyPath);
    336 #ifndef NDEBUG
    337         assertPrimaryKeyValidOrInjectable(&requestState, m_value, m_primaryKey, metadata.keyPath);
    338 #endif
    339     } else {
    340         value = IDBAny::create(m_value);
    341     }
    342 
    343     m_valueDirty = false;
    344     return idbAnyToScriptValue(&requestState, value);
    345 }
    346 
    347 ScriptValue IDBCursor::source(ExecutionContext* context) const
    348 {
    349     DOMRequestState requestState(context);
    350     return idbAnyToScriptValue(&requestState, m_source);
    351 }
    352 
    353 void IDBCursor::setValueReady(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
    354 {
    355     m_key = key;
    356     m_keyDirty = true;
    357 
    358     m_primaryKey = primaryKey;
    359     m_primaryKeyDirty = true;
    360 
    361     if (isCursorWithValue()) {
    362         m_value = value;
    363         m_valueDirty = true;
    364     }
    365 
    366     m_gotValue = true;
    367 }
    368 
    369 PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore() const
    370 {
    371     if (m_source->type() == IDBAny::IDBObjectStoreType)
    372         return m_source->idbObjectStore();
    373     RefPtr<IDBIndex> index = m_source->idbIndex();
    374     return index->objectStore();
    375 }
    376 
    377 bool IDBCursor::isDeleted() const
    378 {
    379     if (m_source->type() == IDBAny::IDBObjectStoreType)
    380         return m_source->idbObjectStore()->isDeleted();
    381     return m_source->idbIndex()->isDeleted();
    382 }
    383 
    384 IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionState& exceptionState)
    385 {
    386     if (directionString.isNull() || directionString == IDBCursor::directionNext())
    387         return IndexedDB::CursorNext;
    388     if (directionString == IDBCursor::directionNextUnique())
    389         return IndexedDB::CursorNextNoDuplicate;
    390     if (directionString == IDBCursor::directionPrev())
    391         return IndexedDB::CursorPrev;
    392     if (directionString == IDBCursor::directionPrevUnique())
    393         return IndexedDB::CursorPrevNoDuplicate;
    394 
    395     exceptionState.throwUninformativeAndGenericTypeError();
    396     return IndexedDB::CursorNext;
    397 }
    398 
    399 const AtomicString& IDBCursor::directionToString(unsigned short direction)
    400 {
    401     switch (direction) {
    402     case IndexedDB::CursorNext:
    403         return IDBCursor::directionNext();
    404 
    405     case IndexedDB::CursorNextNoDuplicate:
    406         return IDBCursor::directionNextUnique();
    407 
    408     case IndexedDB::CursorPrev:
    409         return IDBCursor::directionPrev();
    410 
    411     case IndexedDB::CursorPrevNoDuplicate:
    412         return IDBCursor::directionPrevUnique();
    413 
    414     default:
    415         ASSERT_NOT_REACHED();
    416         return IDBCursor::directionNext();
    417     }
    418 }
    419 
    420 } // namespace WebCore
    421