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