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 "IDBCursor.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "IDBAny.h"
32#include "IDBBindingUtilities.h"
33#include "IDBCallbacks.h"
34#include "IDBCursorBackend.h"
35#include "IDBKey.h"
36#include "IDBObjectStore.h"
37#include "IDBRequest.h"
38#include "IDBTransaction.h"
39#include "Logging.h"
40#include "ScriptExecutionContext.h"
41#include <inspector/ScriptCallStack.h>
42#include <limits>
43
44namespace WebCore {
45
46PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
47{
48    return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
49}
50
51const AtomicString& IDBCursor::directionNext()
52{
53    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral));
54    return next;
55}
56
57const AtomicString& IDBCursor::directionNextUnique()
58{
59    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral));
60    return nextunique;
61}
62
63const AtomicString& IDBCursor::directionPrev()
64{
65    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral));
66    return prev;
67}
68
69const AtomicString& IDBCursor::directionPrevUnique()
70{
71    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral));
72    return prevunique;
73}
74
75
76IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
77    : m_backend(backend)
78    , m_request(request)
79    , m_direction(direction)
80    , m_source(source)
81    , m_transaction(transaction)
82    , m_transactionNotifier(transaction, this)
83    , m_gotValue(false)
84{
85    ASSERT(m_backend);
86    ASSERT(m_request);
87    ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
88    ASSERT(m_transaction);
89}
90
91IDBCursor::~IDBCursor()
92{
93}
94
95const String& IDBCursor::direction() const
96{
97    LOG(StorageAPI, "IDBCursor::direction");
98    return directionToString(m_direction);
99}
100
101const Deprecated::ScriptValue& IDBCursor::key() const
102{
103    LOG(StorageAPI, "IDBCursor::key");
104    return m_currentKeyValue;
105}
106
107const Deprecated::ScriptValue& IDBCursor::primaryKey() const
108{
109    LOG(StorageAPI, "IDBCursor::primaryKey");
110    return m_currentPrimaryKeyValue;
111}
112
113const Deprecated::ScriptValue& IDBCursor::value() const
114{
115    LOG(StorageAPI, "IDBCursor::value");
116    return m_currentValue;
117}
118
119IDBAny* IDBCursor::source() const
120{
121    return m_source.get();
122}
123
124PassRefPtr<IDBRequest> IDBCursor::update(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec)
125{
126    LOG(StorageAPI, "IDBCursor::update");
127
128    if (!m_gotValue || isKeyCursor()) {
129        ec = IDBDatabaseException::InvalidStateError;
130        return 0;
131    }
132    if (!m_transaction->isActive()) {
133        ec = IDBDatabaseException::TransactionInactiveError;
134        return 0;
135    }
136    if (m_transaction->isReadOnly()) {
137        ec = IDBDatabaseException::ReadOnlyError;
138        return 0;
139    }
140
141    RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
142    const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
143    const bool usesInLineKeys = !keyPath.isNull();
144    if (usesInLineKeys) {
145        RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState()->exec(), value, keyPath);
146        if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
147            ec = IDBDatabaseException::DataError;
148            return 0;
149        }
150    }
151
152    return objectStore->put(IDBDatabaseBackend::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec);
153}
154
155void IDBCursor::advance(unsigned long count, ExceptionCode& ec)
156{
157    ec = 0;
158    LOG(StorageAPI, "IDBCursor::advance");
159    if (!m_gotValue) {
160        ec = IDBDatabaseException::InvalidStateError;
161        return;
162    }
163
164    if (!m_transaction->isActive()) {
165        ec = IDBDatabaseException::TransactionInactiveError;
166        return;
167    }
168
169    if (!count) {
170        ec = TypeError;
171        return;
172    }
173
174    m_request->setPendingCursor(this);
175    m_gotValue = false;
176    m_backend->advance(count, m_request, ec);
177    ASSERT(!ec);
178}
179
180void IDBCursor::continueFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec)
181{
182    DOMRequestState requestState(context);
183    RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
184    continueFunction(key.release(), ec);
185}
186
187void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
188{
189    ec = 0;
190    LOG(StorageAPI, "IDBCursor::continue");
191    if (key && !key->isValid()) {
192        ec = IDBDatabaseException::DataError;
193        return;
194    }
195
196    if (!m_transaction->isActive()) {
197        ec = IDBDatabaseException::TransactionInactiveError;
198        return;
199    }
200
201    if (!m_gotValue) {
202        ec = IDBDatabaseException::InvalidStateError;
203        return;
204    }
205
206    if (key) {
207        ASSERT(m_currentKey);
208        if (m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::NextNoDuplicate) {
209            if (!m_currentKey->isLessThan(key.get())) {
210                ec = IDBDatabaseException::DataError;
211                return;
212            }
213        } else {
214            if (!key->isLessThan(m_currentKey.get())) {
215                ec = IDBDatabaseException::DataError;
216                return;
217            }
218        }
219    }
220
221    // FIXME: We're not using the context from when continue was called, which means the callback
222    //        will be on the original context openCursor was called on. Is this right?
223    m_request->setPendingCursor(this);
224    m_gotValue = false;
225    m_backend->continueFunction(key, m_request, ec);
226    ASSERT(!ec);
227}
228
229PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
230{
231    ec = 0;
232    LOG(StorageAPI, "IDBCursor::delete");
233    if (!m_transaction->isActive()) {
234        ec = IDBDatabaseException::TransactionInactiveError;
235        return 0;
236    }
237    if (m_transaction->isReadOnly()) {
238        ec = IDBDatabaseException::ReadOnlyError;
239        return 0;
240    }
241
242    if (!m_gotValue || isKeyCursor()) {
243        ec = IDBDatabaseException::InvalidStateError;
244        return 0;
245    }
246    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
247    m_backend->deleteFunction(request, ec);
248    ASSERT(!ec);
249    return request.release();
250}
251
252void IDBCursor::postSuccessHandlerCallback()
253{
254    m_backend->postSuccessHandlerCallback();
255}
256
257void IDBCursor::close()
258{
259    m_transactionNotifier.cursorFinished();
260    if (m_request) {
261        m_request->finishCursor();
262        m_request.clear();
263    }
264}
265
266void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, Deprecated::ScriptValue& value)
267{
268    m_currentKey = key;
269    m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey);
270
271    m_currentPrimaryKey = primaryKey;
272    m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey);
273
274    if (!isKeyCursor()) {
275        RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
276        const IDBObjectStoreMetadata metadata = objectStore->metadata();
277        if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
278#ifndef NDEBUG
279            RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState()->exec(), value, metadata.keyPath);
280            ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get()));
281#endif
282            bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath);
283            // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278
284            ASSERT_UNUSED(injected, injected);
285        }
286    }
287    m_currentValue = value;
288
289    m_gotValue = true;
290}
291
292PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore()
293{
294    if (m_source->type() == IDBAny::IDBObjectStoreType)
295        return m_source->idbObjectStore();
296    RefPtr<IDBIndex> index = m_source->idbIndex();
297    return index->objectStore();
298}
299
300IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec)
301{
302    if (directionString == IDBCursor::directionNext())
303        return IndexedDB::CursorDirection::Next;
304    if (directionString == IDBCursor::directionNextUnique())
305        return IndexedDB::CursorDirection::NextNoDuplicate;
306    if (directionString == IDBCursor::directionPrev())
307        return IndexedDB::CursorDirection::Prev;
308    if (directionString == IDBCursor::directionPrevUnique())
309        return IndexedDB::CursorDirection::PrevNoDuplicate;
310
311    ec = TypeError;
312    return IndexedDB::CursorDirection::Next;
313}
314
315const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction)
316{
317    switch (direction) {
318    case IndexedDB::CursorDirection::Next:
319        return IDBCursor::directionNext();
320
321    case IndexedDB::CursorDirection::NextNoDuplicate:
322        return IDBCursor::directionNextUnique();
323
324    case IndexedDB::CursorDirection::Prev:
325        return IDBCursor::directionPrev();
326
327    case IndexedDB::CursorDirection::PrevNoDuplicate:
328        return IDBCursor::directionPrevUnique();
329
330    default:
331        ASSERT_NOT_REACHED();
332        return IDBCursor::directionNext();
333    }
334}
335
336} // namespace WebCore
337
338#endif // ENABLE(INDEXED_DATABASE)
339