2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "modules/indexeddb/IDBCursor.h"
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"
47 using blink::WebIDBCursor;
48 using blink::WebIDBDatabase;
52 IDBCursor* IDBCursor::create(PassOwnPtr<WebIDBCursor> backend, WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
54 return new IDBCursor(backend, direction, request, source, transaction);
57 IDBCursor::IDBCursor(PassOwnPtr<WebIDBCursor> backend, WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
60 , m_direction(direction)
62 , m_transaction(transaction)
65 , m_primaryKeyDirty(true)
70 ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
71 ASSERT(m_transaction);
72 ScriptWrappable::init(this);
75 IDBCursor::~IDBCursor()
80 void IDBCursor::trace(Visitor* visitor)
82 visitor->trace(m_request);
83 visitor->trace(m_source);
84 visitor->trace(m_transaction);
85 visitor->trace(m_key);
86 visitor->trace(m_primaryKey);
89 IDBRequest* IDBCursor::update(ScriptState* scriptState, ScriptValue& value, ExceptionState& exceptionState)
91 IDB_TRACE("IDBCursor::update");
94 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
98 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
102 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
105 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
106 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
109 if (!m_transaction->isActive()) {
110 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
113 if (m_transaction->isReadOnly()) {
114 exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
118 IDBObjectStore* objectStore = effectiveObjectStore();
119 return objectStore->put(scriptState, WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState);
122 void IDBCursor::advance(unsigned long count, ExceptionState& exceptionState)
124 IDB_TRACE("IDBCursor::advance");
126 exceptionState.throwTypeError("A count argument with value 0 (zero) was supplied, must be greater than 0.");
130 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
134 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
138 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
139 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
142 if (!m_transaction->isActive()) {
143 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
147 m_request->setPendingCursor(this);
149 m_backend->advance(count, WebIDBCallbacksImpl::create(m_request).leakPtr());
152 void IDBCursor::continueFunction(ScriptState* scriptState, const ScriptValue& keyValue, ExceptionState& exceptionState)
154 IDB_TRACE("IDBCursor::continue");
155 IDBKey* key = keyValue.isUndefined() || keyValue.isNull() ? nullptr : scriptValueToIDBKey(scriptState->isolate(), keyValue);
156 if (key && !key->isValid()) {
157 exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
160 continueFunction(key, 0, exceptionState);
163 void IDBCursor::continuePrimaryKey(ScriptState* scriptState, const ScriptValue& keyValue, const ScriptValue& primaryKeyValue, ExceptionState& exceptionState)
165 IDB_TRACE("IDBCursor::continuePrimaryKey");
166 IDBKey* key = scriptValueToIDBKey(scriptState->isolate(), keyValue);
167 IDBKey* primaryKey = scriptValueToIDBKey(scriptState->isolate(), primaryKeyValue);
168 if (!key->isValid() || !primaryKey->isValid()) {
169 exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
172 continueFunction(key, primaryKey, exceptionState);
175 void IDBCursor::continueFunction(IDBKey* key, IDBKey* primaryKey, ExceptionState& exceptionState)
177 ASSERT(!primaryKey || (key && primaryKey));
179 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
180 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
183 if (!m_transaction->isActive()) {
184 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
189 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
194 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
200 if (m_direction == WebIDBCursorDirectionNext || m_direction == WebIDBCursorDirectionNextNoDuplicate) {
201 const bool ok = m_key->isLessThan(key)
202 || (primaryKey && m_key->isEqual(key) && m_primaryKey->isLessThan(primaryKey));
204 exceptionState.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position.");
209 const bool ok = key->isLessThan(m_key.get())
210 || (primaryKey && key->isEqual(m_key.get()) && primaryKey->isLessThan(m_primaryKey.get()));
212 exceptionState.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position.");
218 // FIXME: We're not using the context from when continue was called, which means the callback
219 // will be on the original context openCursor was called on. Is this right?
220 m_request->setPendingCursor(this);
222 m_backend->continueFunction(key, primaryKey, WebIDBCallbacksImpl::create(m_request).leakPtr());
225 IDBRequest* IDBCursor::deleteFunction(ScriptState* scriptState, ExceptionState& exceptionState)
227 IDB_TRACE("IDBCursor::delete");
228 if (m_transaction->isFinished() || m_transaction->isFinishing()) {
229 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
232 if (!m_transaction->isActive()) {
233 exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
236 if (m_transaction->isReadOnly()) {
237 exceptionState.throwDOMException(ReadOnlyError, "The record may not be deleted inside a read-only transaction.");
242 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
246 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
250 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
253 if (!m_transaction->backendDB()) {
254 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
258 IDBKeyRange* keyRange = IDBKeyRange::only(m_primaryKey, exceptionState);
259 ASSERT(!exceptionState.hadException());
261 IDBRequest* request = IDBRequest::create(scriptState, IDBAny::create(this), m_transaction.get());
262 m_transaction->backendDB()->deleteRange(m_transaction->id(), effectiveObjectStore()->id(), keyRange, WebIDBCallbacksImpl::create(request).leakPtr());
266 void IDBCursor::postSuccessHandlerCallback()
269 m_backend->postSuccessHandlerCallback();
272 void IDBCursor::close()
279 ScriptValue IDBCursor::key(ScriptState* scriptState)
282 return idbKeyToScriptValue(scriptState, m_key);
285 ScriptValue IDBCursor::primaryKey(ScriptState* scriptState)
287 m_primaryKeyDirty = false;
288 return idbKeyToScriptValue(scriptState, m_primaryKey);
291 ScriptValue IDBCursor::value(ScriptState* scriptState)
293 ASSERT(isCursorWithValue());
295 IDBObjectStore* objectStore = effectiveObjectStore();
296 const IDBObjectStoreMetadata& metadata = objectStore->metadata();
298 if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
299 value = IDBAny::create(m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
301 assertPrimaryKeyValidOrInjectable(scriptState, m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
304 value = IDBAny::create(m_value, m_blobInfo.get());
307 m_valueDirty = false;
308 ScriptValue scriptValue = idbAnyToScriptValue(scriptState, value);
313 ScriptValue IDBCursor::source(ScriptState* scriptState) const
315 return idbAnyToScriptValue(scriptState, m_source);
318 void IDBCursor::setValueReady(IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
323 m_primaryKey = primaryKey;
324 m_primaryKeyDirty = true;
326 if (isCursorWithValue()) {
329 m_blobInfo = blobInfo;
336 IDBObjectStore* IDBCursor::effectiveObjectStore() const
338 if (m_source->type() == IDBAny::IDBObjectStoreType)
339 return m_source->idbObjectStore();
340 return m_source->idbIndex()->objectStore();
343 bool IDBCursor::isDeleted() const
345 if (m_source->type() == IDBAny::IDBObjectStoreType)
346 return m_source->idbObjectStore()->isDeleted();
347 return m_source->idbIndex()->isDeleted();
350 void IDBCursor::handleBlobAcks()
352 ASSERT(m_request || !m_blobInfo || !m_blobInfo->size());
353 if (m_blobInfo.get() && m_blobInfo->size()) {
355 m_transaction->db()->ackReceivedBlobs(m_blobInfo.get());
360 WebIDBCursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionState& exceptionState)
362 if (directionString == IndexedDBNames::next)
363 return WebIDBCursorDirectionNext;
364 if (directionString == IndexedDBNames::nextunique)
365 return WebIDBCursorDirectionNextNoDuplicate;
366 if (directionString == IndexedDBNames::prev)
367 return WebIDBCursorDirectionPrev;
368 if (directionString == IndexedDBNames::prevunique)
369 return WebIDBCursorDirectionPrevNoDuplicate;
371 exceptionState.throwTypeError("The direction provided ('" + directionString + "') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
372 return WebIDBCursorDirectionNext;
375 const String& IDBCursor::direction() const
377 switch (m_direction) {
378 case WebIDBCursorDirectionNext:
379 return IndexedDBNames::next;
381 case WebIDBCursorDirectionNextNoDuplicate:
382 return IndexedDBNames::nextunique;
384 case WebIDBCursorDirectionPrev:
385 return IndexedDBNames::prev;
387 case WebIDBCursorDirectionPrevNoDuplicate:
388 return IndexedDBNames::prevunique;
391 ASSERT_NOT_REACHED();
392 return IndexedDBNames::next;