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/core/v8/ExceptionState.h"
     30 #include "bindings/core/v8/ScriptState.h"
     31 #include "bindings/modules/v8/IDBBindingUtilities.h"
     32 #include "core/dom/ExceptionCode.h"
     33 #include "core/dom/ExecutionContext.h"
     34 #include "core/inspector/ScriptCallStack.h"
     35 #include "modules/IndexedDBNames.h"
     36 #include "modules/indexeddb/IDBAny.h"
     37 #include "modules/indexeddb/IDBDatabase.h"
     38 #include "modules/indexeddb/IDBObjectStore.h"
     39 #include "modules/indexeddb/IDBTracing.h"
     40 #include "modules/indexeddb/IDBTransaction.h"
     41 #include "modules/indexeddb/WebIDBCallbacksImpl.h"
     42 #include "public/platform/WebBlobInfo.h"
     43 #include "public/platform/WebIDBDatabase.h"
     44 #include "public/platform/WebIDBKeyRange.h"
     45 #include <limits>
     46 
     47 using blink::WebIDBCursor;
     48 using blink::WebIDBDatabase;
     49 
     50 namespace blink {
     51 
     52 IDBCursor* IDBCursor::create(PassOwnPtr<WebIDBCursor> backend, WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
     53 {
     54     return new IDBCursor(backend, direction, request, source, transaction);
     55 }
     56 
     57 IDBCursor::IDBCursor(PassOwnPtr<WebIDBCursor> backend, WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
     58     : m_backend(backend)
     59     , m_request(request)
     60     , m_direction(direction)
     61     , m_source(source)
     62     , m_transaction(transaction)
     63     , m_gotValue(false)
     64     , m_keyDirty(true)
     65     , m_primaryKeyDirty(true)
     66     , m_valueDirty(true)
     67 {
     68     ASSERT(m_backend);
     69     ASSERT(m_request);
     70     ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
     71     ASSERT(m_transaction);
     72 }
     73 
     74 IDBCursor::~IDBCursor()
     75 {
     76     ASSERT(!m_blobInfo || m_blobInfo->size() == 0);
     77 }
     78 
     79 void IDBCursor::dispose()
     80 {
     81     handleBlobAcks();
     82 }
     83 
     84 void IDBCursor::trace(Visitor* visitor)
     85 {
     86     visitor->trace(m_request);
     87     visitor->trace(m_source);
     88     visitor->trace(m_transaction);
     89     visitor->trace(m_key);
     90     visitor->trace(m_primaryKey);
     91 }
     92 
     93 IDBRequest* IDBCursor::update(ScriptState* scriptState, const ScriptValue& value, ExceptionState& exceptionState)
     94 {
     95     IDB_TRACE("IDBCursor::update");
     96 
     97     if (!m_gotValue) {
     98         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
     99         return 0;
    100     }
    101     if (isKeyCursor()) {
    102         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
    103         return 0;
    104     }
    105     if (isDeleted()) {
    106         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    107         return 0;
    108     }
    109     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
    110         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    111         return 0;
    112     }
    113     if (!m_transaction->isActive()) {
    114         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    115         return 0;
    116     }
    117     if (m_transaction->isReadOnly()) {
    118         exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
    119         return 0;
    120     }
    121 
    122     IDBObjectStore* objectStore = effectiveObjectStore();
    123     return objectStore->put(scriptState, WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState);
    124 }
    125 
    126 void IDBCursor::advance(unsigned long count, ExceptionState& exceptionState)
    127 {
    128     IDB_TRACE("IDBCursor::advance");
    129     if (!count) {
    130         exceptionState.throwTypeError("A count argument with value 0 (zero) was supplied, must be greater than 0.");
    131         return;
    132     }
    133     if (!m_gotValue) {
    134         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    135         return;
    136     }
    137     if (isDeleted()) {
    138         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    139         return;
    140     }
    141 
    142     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
    143         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    144         return;
    145     }
    146     if (!m_transaction->isActive()) {
    147         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    148         return;
    149     }
    150 
    151     m_request->setPendingCursor(this);
    152     m_gotValue = false;
    153     m_backend->advance(count, WebIDBCallbacksImpl::create(m_request).leakPtr());
    154 }
    155 
    156 void IDBCursor::continueFunction(ScriptState* scriptState, const ScriptValue& keyValue, ExceptionState& exceptionState)
    157 {
    158     IDB_TRACE("IDBCursor::continue");
    159     IDBKey* key = keyValue.isUndefined() || keyValue.isNull() ? nullptr : scriptValueToIDBKey(scriptState->isolate(), keyValue);
    160     if (key && !key->isValid()) {
    161         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
    162         return;
    163     }
    164     continueFunction(key, 0, exceptionState);
    165 }
    166 
    167 void IDBCursor::continuePrimaryKey(ScriptState* scriptState, const ScriptValue& keyValue, const ScriptValue& primaryKeyValue, ExceptionState& exceptionState)
    168 {
    169     IDB_TRACE("IDBCursor::continuePrimaryKey");
    170     IDBKey* key = scriptValueToIDBKey(scriptState->isolate(), keyValue);
    171     IDBKey* primaryKey = scriptValueToIDBKey(scriptState->isolate(), primaryKeyValue);
    172     if (!key->isValid() || !primaryKey->isValid()) {
    173         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
    174         return;
    175     }
    176     continueFunction(key, primaryKey, exceptionState);
    177 }
    178 
    179 void IDBCursor::continueFunction(IDBKey* key, IDBKey* primaryKey, ExceptionState& exceptionState)
    180 {
    181     ASSERT(!primaryKey || (key && primaryKey));
    182 
    183     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
    184         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    185         return;
    186     }
    187     if (!m_transaction->isActive()) {
    188         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    189         return;
    190     }
    191 
    192     if (!m_gotValue) {
    193         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    194         return;
    195     }
    196 
    197     if (isDeleted()) {
    198         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    199         return;
    200     }
    201 
    202     if (key) {
    203         ASSERT(m_key);
    204         if (m_direction == WebIDBCursorDirectionNext || m_direction == WebIDBCursorDirectionNextNoDuplicate) {
    205             const bool ok = m_key->isLessThan(key)
    206                 || (primaryKey && m_key->isEqual(key) && m_primaryKey->isLessThan(primaryKey));
    207             if (!ok) {
    208                 exceptionState.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position.");
    209                 return;
    210             }
    211 
    212         } else {
    213             const bool ok = key->isLessThan(m_key.get())
    214                 || (primaryKey && key->isEqual(m_key.get()) && primaryKey->isLessThan(m_primaryKey.get()));
    215             if (!ok) {
    216                 exceptionState.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position.");
    217                 return;
    218             }
    219         }
    220     }
    221 
    222     // FIXME: We're not using the context from when continue was called, which means the callback
    223     //        will be on the original context openCursor was called on. Is this right?
    224     m_request->setPendingCursor(this);
    225     m_gotValue = false;
    226     m_backend->continueFunction(key, primaryKey, WebIDBCallbacksImpl::create(m_request).leakPtr());
    227 }
    228 
    229 IDBRequest* IDBCursor::deleteFunction(ScriptState* scriptState, ExceptionState& exceptionState)
    230 {
    231     IDB_TRACE("IDBCursor::delete");
    232     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
    233         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
    234         return 0;
    235     }
    236     if (!m_transaction->isActive()) {
    237         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
    238         return 0;
    239     }
    240     if (m_transaction->isReadOnly()) {
    241         exceptionState.throwDOMException(ReadOnlyError, "The record may not be deleted inside a read-only transaction.");
    242         return 0;
    243     }
    244 
    245     if (!m_gotValue) {
    246         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
    247         return 0;
    248     }
    249     if (isKeyCursor()) {
    250         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
    251         return 0;
    252     }
    253     if (isDeleted()) {
    254         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
    255         return 0;
    256     }
    257     if (!m_transaction->backendDB()) {
    258         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
    259         return 0;
    260     }
    261 
    262     IDBKeyRange* keyRange = IDBKeyRange::only(m_primaryKey, exceptionState);
    263     ASSERT(!exceptionState.hadException());
    264 
    265     IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
    266     m_transaction->backendDB()->deleteRange(m_transaction->id(), effectiveObjectStore()->id(), keyRange, WebIDBCallbacksImpl::create(request).leakPtr());
    267     return request;
    268 }
    269 
    270 void IDBCursor::postSuccessHandlerCallback()
    271 {
    272     if (m_backend)
    273         m_backend->postSuccessHandlerCallback();
    274 }
    275 
    276 void IDBCursor::close()
    277 {
    278     handleBlobAcks();
    279     m_request.clear();
    280     m_backend.clear();
    281 }
    282 
    283 ScriptValue IDBCursor::key(ScriptState* scriptState)
    284 {
    285     m_keyDirty = false;
    286     return idbKeyToScriptValue(scriptState, m_key);
    287 }
    288 
    289 ScriptValue IDBCursor::primaryKey(ScriptState* scriptState)
    290 {
    291     m_primaryKeyDirty = false;
    292     return idbKeyToScriptValue(scriptState, m_primaryKey);
    293 }
    294 
    295 ScriptValue IDBCursor::value(ScriptState* scriptState)
    296 {
    297     ASSERT(isCursorWithValue());
    298 
    299     IDBObjectStore* objectStore = effectiveObjectStore();
    300     const IDBObjectStoreMetadata& metadata = objectStore->metadata();
    301     IDBAny* value;
    302     if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
    303         value = IDBAny::create(m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
    304 #if ENABLE(ASSERT)
    305         assertPrimaryKeyValidOrInjectable(scriptState, m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
    306 #endif
    307     } else {
    308         value = IDBAny::create(m_value, m_blobInfo.get());
    309     }
    310 
    311     m_valueDirty = false;
    312     ScriptValue scriptValue = idbAnyToScriptValue(scriptState, value);
    313     handleBlobAcks();
    314     return scriptValue;
    315 }
    316 
    317 ScriptValue IDBCursor::source(ScriptState* scriptState) const
    318 {
    319     return idbAnyToScriptValue(scriptState, m_source);
    320 }
    321 
    322 void IDBCursor::setValueReady(IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
    323 {
    324     m_key = key;
    325     m_keyDirty = true;
    326 
    327     m_primaryKey = primaryKey;
    328     m_primaryKeyDirty = true;
    329 
    330     if (isCursorWithValue()) {
    331         m_value = value;
    332         handleBlobAcks();
    333         m_blobInfo = blobInfo;
    334         m_valueDirty = true;
    335         if (m_blobInfo && m_blobInfo->size() > 0)
    336             V8PerIsolateData::from(m_request->scriptState()->isolate())->ensureIDBPendingTransactionMonitor()->registerCursor(*this);
    337     }
    338 
    339     m_gotValue = true;
    340 }
    341 
    342 IDBObjectStore* IDBCursor::effectiveObjectStore() const
    343 {
    344     if (m_source->type() == IDBAny::IDBObjectStoreType)
    345         return m_source->idbObjectStore();
    346     return m_source->idbIndex()->objectStore();
    347 }
    348 
    349 bool IDBCursor::isDeleted() const
    350 {
    351     if (m_source->type() == IDBAny::IDBObjectStoreType)
    352         return m_source->idbObjectStore()->isDeleted();
    353     return m_source->idbIndex()->isDeleted();
    354 }
    355 
    356 void IDBCursor::handleBlobAcks()
    357 {
    358     ASSERT(m_request || !m_blobInfo || !m_blobInfo->size());
    359     if (m_blobInfo.get() && m_blobInfo->size()) {
    360         ASSERT(m_request);
    361         m_transaction->db()->ackReceivedBlobs(m_blobInfo.get());
    362         m_blobInfo.clear();
    363         V8PerIsolateData::from(m_request->scriptState()->isolate())->ensureIDBPendingTransactionMonitor()->unregisterCursor(*this);
    364     }
    365 }
    366 
    367 WebIDBCursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionState& exceptionState)
    368 {
    369     if (directionString == IndexedDBNames::next)
    370         return WebIDBCursorDirectionNext;
    371     if (directionString == IndexedDBNames::nextunique)
    372         return WebIDBCursorDirectionNextNoDuplicate;
    373     if (directionString == IndexedDBNames::prev)
    374         return WebIDBCursorDirectionPrev;
    375     if (directionString == IndexedDBNames::prevunique)
    376         return WebIDBCursorDirectionPrevNoDuplicate;
    377 
    378     exceptionState.throwTypeError("The direction provided ('" + directionString + "') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
    379     return WebIDBCursorDirectionNext;
    380 }
    381 
    382 const String& IDBCursor::direction() const
    383 {
    384     switch (m_direction) {
    385     case WebIDBCursorDirectionNext:
    386         return IndexedDBNames::next;
    387 
    388     case WebIDBCursorDirectionNextNoDuplicate:
    389         return IndexedDBNames::nextunique;
    390 
    391     case WebIDBCursorDirectionPrev:
    392         return IndexedDBNames::prev;
    393 
    394     case WebIDBCursorDirectionPrevNoDuplicate:
    395         return IndexedDBNames::prevunique;
    396 
    397     default:
    398         ASSERT_NOT_REACHED();
    399         return IndexedDBNames::next;
    400     }
    401 }
    402 
    403 } // namespace blink
    404