Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / 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 "modules/indexeddb/IDBCursor.h"
28
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"
45 #include <limits>
46
47 using blink::WebIDBCursor;
48 using blink::WebIDBDatabase;
49
50 namespace blink {
51
52 IDBCursor* IDBCursor::create(PassOwnPtr<WebIDBCursor> backend, WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
53 {
54     return new IDBCursor(backend, direction, request, source, transaction);
55 }
56
57 IDBCursor::IDBCursor(PassOwnPtr<WebIDBCursor> backend, WebIDBCursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
58     : m_backend(backend)
59     , m_request(request)
60     , m_direction(direction)
61     , m_source(source)
62     , m_transaction(transaction)
63     , m_gotValue(false)
64     , m_keyDirty(true)
65     , m_primaryKeyDirty(true)
66     , m_valueDirty(true)
67 {
68     ASSERT(m_backend);
69     ASSERT(m_request);
70     ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
71     ASSERT(m_transaction);
72     ScriptWrappable::init(this);
73 }
74
75 IDBCursor::~IDBCursor()
76 {
77     handleBlobAcks();
78 }
79
80 void IDBCursor::trace(Visitor* visitor)
81 {
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);
87 }
88
89 IDBRequest* IDBCursor::update(ScriptState* scriptState, ScriptValue& value, ExceptionState& exceptionState)
90 {
91     IDB_TRACE("IDBCursor::update");
92
93     if (!m_gotValue) {
94         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
95         return 0;
96     }
97     if (isKeyCursor()) {
98         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
99         return 0;
100     }
101     if (isDeleted()) {
102         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
103         return 0;
104     }
105     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
106         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
107         return 0;
108     }
109     if (!m_transaction->isActive()) {
110         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
111         return 0;
112     }
113     if (m_transaction->isReadOnly()) {
114         exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
115         return 0;
116     }
117
118     IDBObjectStore* objectStore = effectiveObjectStore();
119     return objectStore->put(scriptState, WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState);
120 }
121
122 void IDBCursor::advance(unsigned long count, ExceptionState& exceptionState)
123 {
124     IDB_TRACE("IDBCursor::advance");
125     if (!count) {
126         exceptionState.throwTypeError("A count argument with value 0 (zero) was supplied, must be greater than 0.");
127         return;
128     }
129     if (!m_gotValue) {
130         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
131         return;
132     }
133     if (isDeleted()) {
134         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
135         return;
136     }
137
138     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
139         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
140         return;
141     }
142     if (!m_transaction->isActive()) {
143         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
144         return;
145     }
146
147     m_request->setPendingCursor(this);
148     m_gotValue = false;
149     m_backend->advance(count, WebIDBCallbacksImpl::create(m_request).leakPtr());
150 }
151
152 void IDBCursor::continueFunction(ScriptState* scriptState, const ScriptValue& keyValue, ExceptionState& exceptionState)
153 {
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);
158         return;
159     }
160     continueFunction(key, 0, exceptionState);
161 }
162
163 void IDBCursor::continuePrimaryKey(ScriptState* scriptState, const ScriptValue& keyValue, const ScriptValue& primaryKeyValue, ExceptionState& exceptionState)
164 {
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);
170         return;
171     }
172     continueFunction(key, primaryKey, exceptionState);
173 }
174
175 void IDBCursor::continueFunction(IDBKey* key, IDBKey* primaryKey, ExceptionState& exceptionState)
176 {
177     ASSERT(!primaryKey || (key && primaryKey));
178
179     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
180         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
181         return;
182     }
183     if (!m_transaction->isActive()) {
184         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
185         return;
186     }
187
188     if (!m_gotValue) {
189         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
190         return;
191     }
192
193     if (isDeleted()) {
194         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
195         return;
196     }
197
198     if (key) {
199         ASSERT(m_key);
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));
203             if (!ok) {
204                 exceptionState.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position.");
205                 return;
206             }
207
208         } else {
209             const bool ok = key->isLessThan(m_key.get())
210                 || (primaryKey && key->isEqual(m_key.get()) && primaryKey->isLessThan(m_primaryKey.get()));
211             if (!ok) {
212                 exceptionState.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position.");
213                 return;
214             }
215         }
216     }
217
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);
221     m_gotValue = false;
222     m_backend->continueFunction(key, primaryKey, WebIDBCallbacksImpl::create(m_request).leakPtr());
223 }
224
225 IDBRequest* IDBCursor::deleteFunction(ScriptState* scriptState, ExceptionState& exceptionState)
226 {
227     IDB_TRACE("IDBCursor::delete");
228     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
229         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
230         return 0;
231     }
232     if (!m_transaction->isActive()) {
233         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
234         return 0;
235     }
236     if (m_transaction->isReadOnly()) {
237         exceptionState.throwDOMException(ReadOnlyError, "The record may not be deleted inside a read-only transaction.");
238         return 0;
239     }
240
241     if (!m_gotValue) {
242         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
243         return 0;
244     }
245     if (isKeyCursor()) {
246         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
247         return 0;
248     }
249     if (isDeleted()) {
250         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
251         return 0;
252     }
253     if (!m_transaction->backendDB()) {
254         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
255         return 0;
256     }
257
258     IDBKeyRange* keyRange = IDBKeyRange::only(m_primaryKey, exceptionState);
259     ASSERT(!exceptionState.hadException());
260
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());
263     return request;
264 }
265
266 void IDBCursor::postSuccessHandlerCallback()
267 {
268     if (m_backend)
269         m_backend->postSuccessHandlerCallback();
270 }
271
272 void IDBCursor::close()
273 {
274     handleBlobAcks();
275     m_request.clear();
276     m_backend.clear();
277 }
278
279 ScriptValue IDBCursor::key(ScriptState* scriptState)
280 {
281     m_keyDirty = false;
282     return idbKeyToScriptValue(scriptState, m_key);
283 }
284
285 ScriptValue IDBCursor::primaryKey(ScriptState* scriptState)
286 {
287     m_primaryKeyDirty = false;
288     return idbKeyToScriptValue(scriptState, m_primaryKey);
289 }
290
291 ScriptValue IDBCursor::value(ScriptState* scriptState)
292 {
293     ASSERT(isCursorWithValue());
294
295     IDBObjectStore* objectStore = effectiveObjectStore();
296     const IDBObjectStoreMetadata& metadata = objectStore->metadata();
297     IDBAny* value;
298     if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
299         value = IDBAny::create(m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
300 #if ENABLE(ASSERT)
301         assertPrimaryKeyValidOrInjectable(scriptState, m_value, m_blobInfo.get(), m_primaryKey, metadata.keyPath);
302 #endif
303     } else {
304         value = IDBAny::create(m_value, m_blobInfo.get());
305     }
306
307     m_valueDirty = false;
308     ScriptValue scriptValue = idbAnyToScriptValue(scriptState, value);
309     handleBlobAcks();
310     return scriptValue;
311 }
312
313 ScriptValue IDBCursor::source(ScriptState* scriptState) const
314 {
315     return idbAnyToScriptValue(scriptState, m_source);
316 }
317
318 void IDBCursor::setValueReady(IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
319 {
320     m_key = key;
321     m_keyDirty = true;
322
323     m_primaryKey = primaryKey;
324     m_primaryKeyDirty = true;
325
326     if (isCursorWithValue()) {
327         m_value = value;
328         handleBlobAcks();
329         m_blobInfo = blobInfo;
330         m_valueDirty = true;
331     }
332
333     m_gotValue = true;
334 }
335
336 IDBObjectStore* IDBCursor::effectiveObjectStore() const
337 {
338     if (m_source->type() == IDBAny::IDBObjectStoreType)
339         return m_source->idbObjectStore();
340     return m_source->idbIndex()->objectStore();
341 }
342
343 bool IDBCursor::isDeleted() const
344 {
345     if (m_source->type() == IDBAny::IDBObjectStoreType)
346         return m_source->idbObjectStore()->isDeleted();
347     return m_source->idbIndex()->isDeleted();
348 }
349
350 void IDBCursor::handleBlobAcks()
351 {
352     ASSERT(m_request || !m_blobInfo || !m_blobInfo->size());
353     if (m_blobInfo.get() && m_blobInfo->size()) {
354         ASSERT(m_request);
355         m_transaction->db()->ackReceivedBlobs(m_blobInfo.get());
356         m_blobInfo.clear();
357     }
358 }
359
360 WebIDBCursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionState& exceptionState)
361 {
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;
370
371     exceptionState.throwTypeError("The direction provided ('" + directionString + "') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
372     return WebIDBCursorDirectionNext;
373 }
374
375 const String& IDBCursor::direction() const
376 {
377     switch (m_direction) {
378     case WebIDBCursorDirectionNext:
379         return IndexedDBNames::next;
380
381     case WebIDBCursorDirectionNextNoDuplicate:
382         return IndexedDBNames::nextunique;
383
384     case WebIDBCursorDirectionPrev:
385         return IndexedDBNames::prev;
386
387     case WebIDBCursorDirectionPrevNoDuplicate:
388         return IndexedDBNames::prevunique;
389
390     default:
391         ASSERT_NOT_REACHED();
392         return IndexedDBNames::next;
393     }
394 }
395
396 } // namespace blink