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