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