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