Upstream version 5.34.104.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/v8/ExceptionState.h"
30 #include "bindings/v8/IDBBindingUtilities.h"
31 #include "core/dom/ExceptionCode.h"
32 #include "core/dom/ExecutionContext.h"
33 #include "core/inspector/ScriptCallStack.h"
34 #include "modules/indexeddb/IDBAny.h"
35 #include "modules/indexeddb/IDBDatabase.h"
36 #include "modules/indexeddb/IDBObjectStore.h"
37 #include "modules/indexeddb/IDBTracing.h"
38 #include "modules/indexeddb/IDBTransaction.h"
39 #include "modules/indexeddb/WebIDBCallbacksImpl.h"
40 #include "public/platform/WebIDBDatabase.h"
41 #include "public/platform/WebIDBKeyRange.h"
42 #include <limits>
43
44 using blink::WebIDBCursor;
45 using blink::WebIDBDatabase;
46
47 namespace WebCore {
48
49 PassRefPtr<IDBCursor> IDBCursor::create(PassOwnPtr<blink::WebIDBCursor> backend, WebIDBCursor::Direction direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
50 {
51     return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
52 }
53
54 const AtomicString& IDBCursor::directionNext()
55 {
56     DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral));
57     return next;
58 }
59
60 const AtomicString& IDBCursor::directionNextUnique()
61 {
62     DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral));
63     return nextunique;
64 }
65
66 const AtomicString& IDBCursor::directionPrev()
67 {
68     DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral));
69     return prev;
70 }
71
72 const AtomicString& IDBCursor::directionPrevUnique()
73 {
74     DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral));
75     return prevunique;
76 }
77
78 IDBCursor::IDBCursor(PassOwnPtr<blink::WebIDBCursor> backend, WebIDBCursor::Direction direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
79     : m_backend(backend)
80     , m_request(request)
81     , m_direction(direction)
82     , m_source(source)
83     , m_transaction(transaction)
84     , m_gotValue(false)
85     , m_keyDirty(true)
86     , m_primaryKeyDirty(true)
87     , m_valueDirty(true)
88 {
89     ASSERT(m_backend);
90     ASSERT(m_request);
91     ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
92     ASSERT(m_transaction);
93     ScriptWrappable::init(this);
94 }
95
96 IDBCursor::~IDBCursor()
97 {
98 }
99
100 PassRefPtr<IDBRequest> IDBCursor::update(ScriptState* state, ScriptValue& value, ExceptionState& exceptionState)
101 {
102     IDB_TRACE("IDBCursor::update");
103
104     if (!m_gotValue) {
105         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
106         return 0;
107     }
108     if (isKeyCursor()) {
109         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
110         return 0;
111     }
112     if (isDeleted()) {
113         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
114         return 0;
115     }
116     if (m_transaction->isFinished()) {
117         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
118         return 0;
119     }
120     if (!m_transaction->isActive()) {
121         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
122         return 0;
123     }
124     if (m_transaction->isReadOnly()) {
125         exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
126         return 0;
127     }
128
129     RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
130     const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
131     const bool usesInLineKeys = !keyPath.isNull();
132     if (usesInLineKeys) {
133         RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath);
134         if (!keyPathKey || !keyPathKey->isEqual(m_primaryKey.get())) {
135             exceptionState.throwDOMException(DataError, "The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.");
136             return 0;
137         }
138     }
139
140     return objectStore->put(WebIDBDatabase::CursorUpdate, IDBAny::create(this), state, value, m_primaryKey, exceptionState);
141 }
142
143 void IDBCursor::advance(unsigned long count, ExceptionState& exceptionState)
144 {
145     IDB_TRACE("IDBCursor::advance");
146     if (!count) {
147         exceptionState.throwTypeError("A count argument with value 0 (zero) was supplied, must be greater than 0.");
148         return;
149     }
150     if (!m_gotValue) {
151         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
152         return;
153     }
154     if (isDeleted()) {
155         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
156         return;
157     }
158
159     if (m_transaction->isFinished()) {
160         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
161         return;
162     }
163     if (!m_transaction->isActive()) {
164         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
165         return;
166     }
167
168     m_request->setPendingCursor(this);
169     m_gotValue = false;
170     m_backend->advance(count, WebIDBCallbacksImpl::create(m_request).leakPtr());
171 }
172
173 void IDBCursor::continueFunction(ExecutionContext* context, const ScriptValue& keyValue, ExceptionState& exceptionState)
174 {
175     IDB_TRACE("IDBCursor::continue");
176     DOMRequestState requestState(context);
177     RefPtr<IDBKey> key = keyValue.isUndefined() || keyValue.isNull() ? 0 : scriptValueToIDBKey(&requestState, keyValue);
178     if (key && !key->isValid()) {
179         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
180         return;
181     }
182     continueFunction(key.release(), 0, exceptionState);
183 }
184
185 void IDBCursor::continuePrimaryKey(ExecutionContext* context, const ScriptValue& keyValue, const ScriptValue& primaryKeyValue, ExceptionState& exceptionState)
186 {
187     IDB_TRACE("IDBCursor::continuePrimaryKey");
188     DOMRequestState requestState(context);
189     RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
190     RefPtr<IDBKey> primaryKey = scriptValueToIDBKey(&requestState, primaryKeyValue);
191     if (!key->isValid() || !primaryKey->isValid()) {
192         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
193         return;
194     }
195     continueFunction(key.release(), primaryKey.release(), exceptionState);
196 }
197
198 void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, ExceptionState& exceptionState)
199 {
200     ASSERT(!primaryKey || (key && primaryKey));
201
202     if (m_transaction->isFinished()) {
203         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
204         return;
205     }
206     if (!m_transaction->isActive()) {
207         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
208         return;
209     }
210
211     if (!m_gotValue) {
212         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
213         return;
214     }
215
216     if (isDeleted()) {
217         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
218         return;
219     }
220
221     if (key) {
222         ASSERT(m_key);
223         if (m_direction == WebIDBCursor::Next || m_direction == WebIDBCursor::NextNoDuplicate) {
224             const bool ok = m_key->isLessThan(key.get())
225                 || (primaryKey && m_key->isEqual(key.get()) && m_primaryKey->isLessThan(primaryKey.get()));
226             if (!ok) {
227                 exceptionState.throwDOMException(DataError, "The parameter is less than or equal to this cursor's position.");
228                 return;
229             }
230
231         } else {
232             const bool ok = key->isLessThan(m_key.get())
233                 || (primaryKey && key->isEqual(m_key.get()) && primaryKey->isLessThan(m_primaryKey.get()));
234             if (!ok) {
235                 exceptionState.throwDOMException(DataError, "The parameter is greater than or equal to this cursor's position.");
236                 return;
237             }
238         }
239     }
240
241     // FIXME: We're not using the context from when continue was called, which means the callback
242     //        will be on the original context openCursor was called on. Is this right?
243     m_request->setPendingCursor(this);
244     m_gotValue = false;
245     m_backend->continueFunction(key, primaryKey, WebIDBCallbacksImpl::create(m_request).leakPtr());
246 }
247
248 PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ExecutionContext* context, ExceptionState& exceptionState)
249 {
250     IDB_TRACE("IDBCursor::delete");
251     if (m_transaction->isFinished()) {
252         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
253         return 0;
254     }
255     if (!m_transaction->isActive()) {
256         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
257         return 0;
258     }
259     if (m_transaction->isReadOnly()) {
260         exceptionState.throwDOMException(ReadOnlyError, "The record may not be deleted inside a read-only transaction.");
261         return 0;
262     }
263
264     if (!m_gotValue) {
265         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
266         return 0;
267     }
268     if (isKeyCursor()) {
269         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
270         return 0;
271     }
272     if (isDeleted()) {
273         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
274         return 0;
275     }
276
277     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(m_primaryKey, exceptionState);
278     ASSERT(!exceptionState.hadException());
279
280     RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
281     m_transaction->backendDB()->deleteRange(m_transaction->id(), effectiveObjectStore()->id(), keyRange.release(), WebIDBCallbacksImpl::create(request).leakPtr());
282     return request.release();
283 }
284
285 void IDBCursor::postSuccessHandlerCallback()
286 {
287     if (m_backend)
288         m_backend->postSuccessHandlerCallback();
289 }
290
291 void IDBCursor::close()
292 {
293     // The notifier may be the last reference to this cursor.
294     RefPtr<IDBCursor> protect(this);
295     m_request.clear();
296     m_backend.clear();
297 }
298
299 void IDBCursor::checkForReferenceCycle()
300 {
301     // If this cursor and its request have the only references
302     // to each other, then explicitly break the cycle.
303     if (!m_request || m_request->getResultCursor() != this)
304         return;
305
306     if (!hasOneRef() || !m_request->hasOneRef())
307         return;
308
309     m_request.clear();
310 }
311
312 ScriptValue IDBCursor::key(ExecutionContext* context)
313 {
314     m_keyDirty = false;
315     DOMRequestState requestState(context);
316     return idbKeyToScriptValue(&requestState, m_key);
317 }
318
319 ScriptValue IDBCursor::primaryKey(ExecutionContext* context)
320 {
321     m_primaryKeyDirty = false;
322     DOMRequestState requestState(context);
323     return idbKeyToScriptValue(&requestState, m_primaryKey);
324 }
325
326 ScriptValue IDBCursor::value(ExecutionContext* context)
327 {
328     ASSERT(isCursorWithValue());
329
330     DOMRequestState requestState(context);
331     RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
332     const IDBObjectStoreMetadata& metadata = objectStore->metadata();
333     RefPtr<IDBAny> value;
334     if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
335         value = IDBAny::create(m_value, m_primaryKey, metadata.keyPath);
336 #ifndef NDEBUG
337         assertPrimaryKeyValidOrInjectable(&requestState, m_value, m_primaryKey, metadata.keyPath);
338 #endif
339     } else {
340         value = IDBAny::create(m_value);
341     }
342
343     m_valueDirty = false;
344     return idbAnyToScriptValue(&requestState, value);
345 }
346
347 ScriptValue IDBCursor::source(ExecutionContext* context) const
348 {
349     DOMRequestState requestState(context);
350     return idbAnyToScriptValue(&requestState, m_source);
351 }
352
353 void IDBCursor::setValueReady(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value)
354 {
355     m_key = key;
356     m_keyDirty = true;
357
358     m_primaryKey = primaryKey;
359     m_primaryKeyDirty = true;
360
361     if (isCursorWithValue()) {
362         m_value = value;
363         m_valueDirty = true;
364     }
365
366     m_gotValue = true;
367 }
368
369 PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore() const
370 {
371     if (m_source->type() == IDBAny::IDBObjectStoreType)
372         return m_source->idbObjectStore();
373     RefPtr<IDBIndex> index = m_source->idbIndex();
374     return index->objectStore();
375 }
376
377 bool IDBCursor::isDeleted() const
378 {
379     if (m_source->type() == IDBAny::IDBObjectStoreType)
380         return m_source->idbObjectStore()->isDeleted();
381     return m_source->idbIndex()->isDeleted();
382 }
383
384 WebIDBCursor::Direction IDBCursor::stringToDirection(const String& directionString, ExceptionState& exceptionState)
385 {
386     if (directionString.isNull() || directionString == IDBCursor::directionNext())
387         return WebIDBCursor::Next;
388     if (directionString == IDBCursor::directionNextUnique())
389         return WebIDBCursor::NextNoDuplicate;
390     if (directionString == IDBCursor::directionPrev())
391         return WebIDBCursor::Prev;
392     if (directionString == IDBCursor::directionPrevUnique())
393         return WebIDBCursor::PrevNoDuplicate;
394
395     exceptionState.throwTypeError("The direction provided ('" + directionString + "') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
396     return WebIDBCursor::Next;
397 }
398
399 const AtomicString& IDBCursor::directionToString(unsigned short direction)
400 {
401     switch (direction) {
402     case WebIDBCursor::Next:
403         return IDBCursor::directionNext();
404
405     case WebIDBCursor::NextNoDuplicate:
406         return IDBCursor::directionNextUnique();
407
408     case WebIDBCursor::Prev:
409         return IDBCursor::directionPrev();
410
411     case WebIDBCursor::PrevNoDuplicate:
412         return IDBCursor::directionPrevUnique();
413
414     default:
415         ASSERT_NOT_REACHED();
416         return IDBCursor::directionNext();
417     }
418 }
419
420 } // namespace WebCore