IndexedDB: Introduce putWithIndexKeys and calculate them in the renderer
[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 "ScriptExecutionContext.h"
41 #include "SerializedScriptValue.h"
42
43 namespace WebCore {
44
45 PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackendInterface> backend, Direction direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
46 {
47     return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
48 }
49
50 const AtomicString& IDBCursor::directionNext()
51 {
52     DEFINE_STATIC_LOCAL(AtomicString, next, ("next"));
53     return next;
54 }
55
56 const AtomicString& IDBCursor::directionNextUnique()
57 {
58     DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique"));
59     return nextunique;
60 }
61
62 const AtomicString& IDBCursor::directionPrev()
63 {
64     DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev"));
65     return prev;
66 }
67
68 const AtomicString& IDBCursor::directionPrevUnique()
69 {
70     DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique"));
71     return prevunique;
72 }
73
74
75 IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, Direction direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
76     : m_backend(backend)
77     , m_request(request)
78     , m_direction(direction)
79     , m_source(source)
80     , m_transaction(transaction)
81     , m_transactionNotifier(transaction, this)
82     , m_gotValue(false)
83     , m_valueIsDirty(true)
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     ExceptionCode ec = 0;
99     const AtomicString& direction = directionToString(m_direction, ec);
100     ASSERT(!ec);
101     return direction;
102 }
103
104 PassRefPtr<IDBKey> IDBCursor::key() const
105 {
106     IDB_TRACE("IDBCursor::key");
107     return m_currentKey;
108 }
109
110 PassRefPtr<IDBKey> IDBCursor::primaryKey() const
111 {
112     IDB_TRACE("IDBCursor::primaryKey");
113     return m_currentPrimaryKey;
114 }
115
116 PassRefPtr<IDBAny> IDBCursor::value()
117 {
118     IDB_TRACE("IDBCursor::value");
119     m_valueIsDirty = false;
120     return m_currentValue;
121 }
122
123 IDBAny* IDBCursor::source() const
124 {
125     return m_source.get();
126 }
127
128 PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> prpValue, ExceptionCode& ec)
129 {
130     IDB_TRACE("IDBCursor::update");
131     RefPtr<SerializedScriptValue> value = prpValue;
132
133     if (!m_gotValue || isKeyCursor()) {
134         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
135         return 0;
136     }
137     if (!m_transaction->isActive()) {
138         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
139         return 0;
140     }
141     if (m_transaction->isReadOnly()) {
142         ec = IDBDatabaseException::READ_ONLY_ERR;
143         return 0;
144     }
145     if (value->blobURLs().size() > 0) {
146         // FIXME: Add Blob/File/FileList support
147         ec = IDBDatabaseException::IDB_DATA_CLONE_ERR;
148         return 0;
149     }
150
151     RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
152     const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
153     const bool usesInLineKeys = !keyPath.isNull();
154     if (usesInLineKeys) {
155         RefPtr<IDBKey> keyPathKey = createIDBKeyFromSerializedValueAndKeyPath(value, keyPath);
156         if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
157             ec = IDBDatabaseException::DATA_ERR;
158             return 0;
159         }
160     }
161
162     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
163     m_backend->update(value, request, ec);
164     if (ec) {
165         request->markEarlyDeath();
166         return 0;
167     }
168     return request.release();
169 }
170
171 void IDBCursor::advance(unsigned long count, ExceptionCode& ec)
172 {
173     IDB_TRACE("IDBCursor::advance");
174     if (!m_gotValue) {
175         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
176         return;
177     }
178
179     if (!m_transaction->isActive()) {
180         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
181         return;
182     }
183
184     if (!count) {
185         ec = IDBDatabaseException::IDB_TYPE_ERR;
186         return;
187     }
188
189     if (!m_request->resetReadyState(m_transaction.get())) {
190         ASSERT_NOT_REACHED();
191         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
192         return;
193     }
194     m_request->setCursor(this);
195     m_gotValue = false;
196     m_backend->advance(count, m_request, ec);
197 }
198
199 void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
200 {
201     IDB_TRACE("IDBCursor::continue");
202     if (key && !key->isValid()) {
203         ec = IDBDatabaseException::DATA_ERR;
204         return;
205     }
206
207     if (!m_transaction->isActive()) {
208         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
209         return;
210     }
211
212     if (!m_gotValue) {
213         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
214         return;
215     }
216
217     // FIXME: We're not using the context from when continue was called, which means the callback
218     //        will be on the original context openCursor was called on. Is this right?
219     if (m_request->resetReadyState(m_transaction.get())) {
220         m_request->setCursor(this);
221         m_gotValue = false;
222         m_backend->continueFunction(key, m_request, ec);
223     } else {
224         ASSERT_NOT_REACHED();
225         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
226     }
227 }
228
229 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
230 {
231     IDB_TRACE("IDBCursor::delete");
232     if (!m_transaction->isActive()) {
233         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
234         return 0;
235     }
236     if (m_transaction->isReadOnly()) {
237         ec = IDBDatabaseException::READ_ONLY_ERR;
238         return 0;
239     }
240
241     if (!m_gotValue) {
242         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
243         return 0;
244     }
245     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
246     m_backend->deleteFunction(request, ec);
247     if (ec) {
248         request->markEarlyDeath();
249         return 0;
250     }
251     return request.release();
252 }
253
254 void IDBCursor::postSuccessHandlerCallback()
255 {
256     m_backend->postSuccessHandlerCallback();
257 }
258
259 void IDBCursor::close()
260 {
261     ASSERT(m_request);
262     m_request->finishCursor();
263     m_request.clear();
264 }
265
266 void IDBCursor::setValueReady()
267 {
268     m_currentKey = m_backend->key();
269     m_currentPrimaryKey = m_backend->primaryKey();
270
271     RefPtr<SerializedScriptValue> value = m_backend->value();
272 #ifndef NDEBUG
273     if (!isKeyCursor()) {
274         // FIXME: Actually inject the primaryKey at the keyPath.
275         RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
276         if (objectStore->autoIncrement() && !objectStore->metadata().keyPath.isNull()) {
277             const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
278             RefPtr<IDBKey> expectedKey = createIDBKeyFromSerializedValueAndKeyPath(value, keyPath);
279             ASSERT(m_currentPrimaryKey);
280             ASSERT(expectedKey->isEqual(m_currentPrimaryKey.get()));
281         }
282     }
283 #endif
284     m_currentValue = IDBAny::create(value.release());
285
286     m_gotValue = true;
287     m_valueIsDirty = true;
288 }
289
290 PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore()
291 {
292     if (m_source->type() == IDBAny::IDBObjectStoreType)
293         return m_source->idbObjectStore();
294     RefPtr<IDBIndex> index = m_source->idbIndex();
295     return index->objectStore();
296 }
297
298 IDBCursor::Direction IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec)
299 {
300     if (directionString == IDBCursor::directionNext())
301         return IDBCursor::NEXT;
302     if (directionString == IDBCursor::directionNextUnique())
303         return IDBCursor::NEXT_NO_DUPLICATE;
304     if (directionString == IDBCursor::directionPrev())
305         return IDBCursor::PREV;
306     if (directionString == IDBCursor::directionPrevUnique())
307         return IDBCursor::PREV_NO_DUPLICATE;
308
309     ec = IDBDatabaseException::IDB_TYPE_ERR;
310     return IDBCursor::NEXT;
311 }
312
313 const AtomicString& IDBCursor::directionToString(unsigned short direction, ExceptionCode& ec)
314 {
315     switch (direction) {
316     case IDBCursor::NEXT:
317         return IDBCursor::directionNext();
318
319     case IDBCursor::NEXT_NO_DUPLICATE:
320         return IDBCursor::directionNextUnique();
321
322     case IDBCursor::PREV:
323         return IDBCursor::directionPrev();
324
325     case IDBCursor::PREV_NO_DUPLICATE:
326         return IDBCursor::directionPrevUnique();
327
328     default:
329         ec = IDBDatabaseException::IDB_TYPE_ERR;
330         return IDBCursor::directionNext();
331     }
332 }
333
334 } // namespace WebCore
335
336 #endif // ENABLE(INDEXED_DATABASE)