IndexedDB: Properly refactor frontend/backend code by #includes
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBCursor.cpp
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 "IDBCursorBackendInterface.h"
35 #include "IDBKey.h"
36 #include "IDBObjectStore.h"
37 #include "IDBRequest.h"
38 #include "IDBTracing.h"
39 #include "IDBTransaction.h"
40 #include "ScriptCallStack.h"
41 #include "ScriptExecutionContext.h"
42 #include <limits>
43
44 namespace WebCore {
45
46 PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackendInterface> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
47 {
48     return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
49 }
50
51 const AtomicString& IDBCursor::directionNext()
52 {
53     DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral));
54     return next;
55 }
56
57 const AtomicString& IDBCursor::directionNextUnique()
58 {
59     DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral));
60     return nextunique;
61 }
62
63 const AtomicString& IDBCursor::directionPrev()
64 {
65     DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral));
66     return prev;
67 }
68
69 const AtomicString& IDBCursor::directionPrevUnique()
70 {
71     DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral));
72     return prevunique;
73 }
74
75
76 IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> 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
91 IDBCursor::~IDBCursor()
92 {
93 }
94
95 const String& IDBCursor::direction() const
96 {
97     IDB_TRACE("IDBCursor::direction");
98     return directionToString(m_direction);
99 }
100
101 const ScriptValue& IDBCursor::key() const
102 {
103     IDB_TRACE("IDBCursor::key");
104     return m_currentKeyValue;
105 }
106
107 const ScriptValue& IDBCursor::primaryKey() const
108 {
109     IDB_TRACE("IDBCursor::primaryKey");
110     return m_currentPrimaryKeyValue;
111 }
112
113 const ScriptValue& IDBCursor::value() const
114 {
115     IDB_TRACE("IDBCursor::value");
116     return m_currentValue;
117 }
118
119 IDBAny* IDBCursor::source() const
120 {
121     return m_source.get();
122 }
123
124 PassRefPtr<IDBRequest> IDBCursor::update(ScriptState* state, ScriptValue& value, ExceptionCode& ec)
125 {
126     IDB_TRACE("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(), value, keyPath);
146         if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
147             ec = IDBDatabaseException::DataError;
148             return 0;
149         }
150     }
151
152     return objectStore->put(IDBDatabaseBackendInterface::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec);
153 }
154
155 void IDBCursor::advance(long long count, ExceptionCode& ec)
156 {
157     ec = 0;
158     IDB_TRACE("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     // FIXME: This should only need to check for 0 once webkit.org/b/96798 lands.
170     if (count < 1 || count > UINT_MAX) {
171         ec = TypeError;
172         return;
173     }
174
175     m_request->setPendingCursor(this);
176     m_gotValue = false;
177     m_backend->advance(count, m_request, ec);
178     ASSERT(!ec);
179 }
180
181 void IDBCursor::continueFunction(ScriptExecutionContext* context, const ScriptValue& keyValue, ExceptionCode& ec)
182 {
183     DOMRequestState requestState(context);
184     RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
185     continueFunction(key.release(), ec);
186 }
187
188 void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
189 {
190     ec = 0;
191     IDB_TRACE("IDBCursor::continue");
192     if (key && !key->isValid()) {
193         ec = IDBDatabaseException::DataError;
194         return;
195     }
196
197     if (!m_transaction->isActive()) {
198         ec = IDBDatabaseException::TransactionInactiveError;
199         return;
200     }
201
202     if (!m_gotValue) {
203         ec = IDBDatabaseException::InvalidStateError;
204         return;
205     }
206
207     if (key) {
208         ASSERT(m_currentKey);
209         if (m_direction == IndexedDB::CursorNext || m_direction == IndexedDB::CursorNextNoDuplicate) {
210             if (!m_currentKey->isLessThan(key.get())) {
211                 ec = IDBDatabaseException::DataError;
212                 return;
213             }
214         } else {
215             if (!key->isLessThan(m_currentKey.get())) {
216                 ec = IDBDatabaseException::DataError;
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, m_request, ec);
227     ASSERT(!ec);
228 }
229
230 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
231 {
232     ec = 0;
233     IDB_TRACE("IDBCursor::delete");
234     if (!m_transaction->isActive()) {
235         ec = IDBDatabaseException::TransactionInactiveError;
236         return 0;
237     }
238     if (m_transaction->isReadOnly()) {
239         ec = IDBDatabaseException::ReadOnlyError;
240         return 0;
241     }
242
243     if (!m_gotValue || isKeyCursor()) {
244         ec = IDBDatabaseException::InvalidStateError;
245         return 0;
246     }
247     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
248     m_backend->deleteFunction(request, ec);
249     ASSERT(!ec);
250     return request.release();
251 }
252
253 void IDBCursor::postSuccessHandlerCallback()
254 {
255     m_backend->postSuccessHandlerCallback();
256 }
257
258 void IDBCursor::close()
259 {
260     m_transactionNotifier.cursorFinished();
261     if (m_request) {
262         m_request->finishCursor();
263         m_request.clear();
264     }
265 }
266
267 void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, ScriptValue& value)
268 {
269     m_currentKey = key;
270     m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey);
271
272     m_currentPrimaryKey = primaryKey;
273     m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey);
274
275     if (!isKeyCursor()) {
276         RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
277         const IDBObjectStoreMetadata metadata = objectStore->metadata();
278         if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
279 #ifndef NDEBUG
280             RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, metadata.keyPath);
281             ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get()));
282 #endif
283             bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath);
284             // 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
285             ASSERT_UNUSED(injected, injected);
286         }
287     }
288     m_currentValue = value;
289
290     m_gotValue = true;
291 }
292
293 PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore()
294 {
295     if (m_source->type() == IDBAny::IDBObjectStoreType)
296         return m_source->idbObjectStore();
297     RefPtr<IDBIndex> index = m_source->idbIndex();
298     return index->objectStore();
299 }
300
301 IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ScriptExecutionContext* context, ExceptionCode& ec)
302 {
303     if (directionString == IDBCursor::directionNext())
304         return IndexedDB::CursorNext;
305     if (directionString == IDBCursor::directionNextUnique())
306         return IndexedDB::CursorNextNoDuplicate;
307     if (directionString == IDBCursor::directionPrev())
308         return IndexedDB::CursorPrev;
309     if (directionString == IDBCursor::directionPrevUnique())
310         return IndexedDB::CursorPrevNoDuplicate;
311
312     ec = TypeError;
313     return IndexedDB::CursorNext;
314 }
315
316 const AtomicString& IDBCursor::directionToString(unsigned short direction)
317 {
318     switch (direction) {
319     case IndexedDB::CursorNext:
320         return IDBCursor::directionNext();
321
322     case IndexedDB::CursorNextNoDuplicate:
323         return IDBCursor::directionNextUnique();
324
325     case IndexedDB::CursorPrev:
326         return IDBCursor::directionPrev();
327
328     case IndexedDB::CursorPrevNoDuplicate:
329         return IDBCursor::directionPrevUnique();
330
331     default:
332         ASSERT_NOT_REACHED();
333         return IDBCursor::directionNext();
334     }
335 }
336
337 } // namespace WebCore
338
339 #endif // ENABLE(INDEXED_DATABASE)