Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / indexeddb / IDBObjectStore.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/IDBObjectStore.h"
28
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "bindings/v8/IDBBindingUtilities.h"
32 #include "bindings/v8/ScriptState.h"
33 #include "core/dom/DOMStringList.h"
34 #include "core/dom/ExceptionCode.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "modules/indexeddb/IDBAny.h"
37 #include "modules/indexeddb/IDBCursorWithValue.h"
38 #include "modules/indexeddb/IDBDatabase.h"
39 #include "modules/indexeddb/IDBKeyPath.h"
40 #include "modules/indexeddb/IDBTracing.h"
41 #include "modules/indexeddb/WebIDBCallbacksImpl.h"
42 #include "platform/SharedBuffer.h"
43 #include "public/platform/WebBlobInfo.h"
44 #include "public/platform/WebData.h"
45 #include "public/platform/WebIDBKey.h"
46 #include "public/platform/WebIDBKeyRange.h"
47 #include "public/platform/WebVector.h"
48
49 using blink::WebBlobInfo;
50 using blink::WebIDBCallbacks;
51 using blink::WebIDBCursor;
52 using blink::WebIDBDatabase;
53 using blink::WebVector;
54
55 namespace WebCore {
56
57 IDBObjectStore::IDBObjectStore(const IDBObjectStoreMetadata& metadata, IDBTransaction* transaction)
58     : m_metadata(metadata)
59     , m_transaction(transaction)
60     , m_deleted(false)
61 {
62     ASSERT(m_transaction);
63 #if !ENABLE(OILPAN)
64     // We pass a reference to this object before it can be adopted.
65     relaxAdoptionRequirement();
66 #endif
67     ScriptWrappable::init(this);
68 }
69
70 void IDBObjectStore::trace(Visitor* visitor)
71 {
72     visitor->trace(m_transaction);
73     visitor->trace(m_indexMap);
74 }
75
76 ScriptValue IDBObjectStore::keyPath(ScriptState* scriptState) const
77 {
78     return idbAnyToScriptValue(scriptState, IDBAny::create(m_metadata.keyPath));
79 }
80
81 PassRefPtrWillBeRawPtr<DOMStringList> IDBObjectStore::indexNames() const
82 {
83     IDB_TRACE("IDBObjectStore::indexNames");
84     RefPtrWillBeRawPtr<DOMStringList> indexNames = DOMStringList::create();
85     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it)
86         indexNames->append(it->value.name);
87     indexNames->sort();
88     return indexNames.release();
89 }
90
91 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::get(ExecutionContext* context, const ScriptValue& key, ExceptionState& exceptionState)
92 {
93     IDB_TRACE("IDBObjectStore::get");
94     if (isDeleted()) {
95         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
96         return nullptr;
97     }
98     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
99         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
100         return nullptr;
101     }
102     if (!m_transaction->isActive()) {
103         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
104         return nullptr;
105     }
106     RefPtrWillBeRawPtr<IDBKeyRange> keyRange = IDBKeyRange::fromScriptValue(context, key, exceptionState);
107     if (exceptionState.hadException())
108         return nullptr;
109     if (!keyRange) {
110         exceptionState.throwDOMException(DataError, IDBDatabase::noKeyOrKeyRangeErrorMessage);
111         return nullptr;
112     }
113     if (!backendDB()) {
114         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
115         return nullptr;
116     }
117
118     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
119     backendDB()->get(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange.release(), false, WebIDBCallbacksImpl::create(request).leakPtr());
120     return request.release();
121 }
122
123 static void generateIndexKeysForValue(v8::Isolate* isolate, const IDBIndexMetadata& indexMetadata, const ScriptValue& objectValue, IDBObjectStore::IndexKeys* indexKeys)
124 {
125     ASSERT(indexKeys);
126     RefPtrWillBeRawPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(isolate, objectValue, indexMetadata.keyPath);
127
128     if (!indexKey)
129         return;
130
131     if (!indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) {
132         if (!indexKey->isValid())
133             return;
134
135         indexKeys->append(indexKey);
136     } else {
137         ASSERT(indexMetadata.multiEntry);
138         ASSERT(indexKey->type() == IDBKey::ArrayType);
139         indexKey = IDBKey::createMultiEntryArray(indexKey->array());
140
141         for (size_t i = 0; i < indexKey->array().size(); ++i)
142             indexKeys->append(indexKey->array()[i]);
143     }
144 }
145
146 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::add(ExecutionContext* executionContext, ScriptValue& value, const ScriptValue& key, ExceptionState& exceptionState)
147 {
148     IDB_TRACE("IDBObjectStore::add");
149     return put(executionContext, WebIDBDatabase::AddOnly, IDBAny::create(this), value, key, exceptionState);
150 }
151
152 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::put(ExecutionContext* executionContext, ScriptValue& value, const ScriptValue& key, ExceptionState& exceptionState)
153 {
154     IDB_TRACE("IDBObjectStore::put");
155     return put(executionContext, WebIDBDatabase::AddOrUpdate, IDBAny::create(this), value, key, exceptionState);
156 }
157
158 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::put(ExecutionContext* executionContext, WebIDBDatabase::PutMode putMode, PassRefPtrWillBeRawPtr<IDBAny> source, ScriptValue& value, const ScriptValue& keyValue, ExceptionState& exceptionState)
159 {
160     RefPtrWillBeRawPtr<IDBKey> key = keyValue.isUndefined() ? nullptr : scriptValueToIDBKey(toIsolate(executionContext), keyValue);
161     return put(executionContext, putMode, source, value, key.release(), exceptionState);
162 }
163
164 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::put(ExecutionContext* executionContext, WebIDBDatabase::PutMode putMode, PassRefPtrWillBeRawPtr<IDBAny> source, ScriptValue& value, PassRefPtrWillBeRawPtr<IDBKey> prpKey, ExceptionState& exceptionState)
165 {
166     RefPtrWillBeRawPtr<IDBKey> key = prpKey;
167     if (isDeleted()) {
168         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
169         return nullptr;
170     }
171     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
172         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
173         return nullptr;
174     }
175     if (!m_transaction->isActive()) {
176         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
177         return nullptr;
178     }
179     if (m_transaction->isReadOnly()) {
180         exceptionState.throwDOMException(ReadOnlyError, IDBDatabase::transactionReadOnlyErrorMessage);
181         return nullptr;
182     }
183
184     Vector<WebBlobInfo> blobInfo;
185
186     RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(value, &blobInfo, exceptionState, toIsolate(executionContext));
187     if (exceptionState.hadException())
188         return nullptr;
189
190     if (serializedValue->containsBlobs()) {
191         // FIXME: Add Blob/File/FileList support
192         exceptionState.throwDOMException(DataCloneError, "The object store currently does not support blob values.");
193         return nullptr;
194     }
195     ASSERT(blobInfo.isEmpty());
196
197     const IDBKeyPath& keyPath = m_metadata.keyPath;
198     const bool usesInLineKeys = !keyPath.isNull();
199     const bool hasKeyGenerator = autoIncrement();
200
201     if (putMode != WebIDBDatabase::CursorUpdate && usesInLineKeys && key) {
202         exceptionState.throwDOMException(DataError, "The object store uses in-line keys and the key parameter was provided.");
203         return nullptr;
204     }
205     if (!usesInLineKeys && !hasKeyGenerator && !key) {
206         exceptionState.throwDOMException(DataError, "The object store uses out-of-line keys and has no key generator and the key parameter was not provided.");
207         return nullptr;
208     }
209     if (usesInLineKeys) {
210         RefPtrWillBeRawPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(toIsolate(executionContext), value, keyPath);
211         if (keyPathKey && !keyPathKey->isValid()) {
212             exceptionState.throwDOMException(DataError, "Evaluating the object store's key path yielded a value that is not a valid key.");
213             return nullptr;
214         }
215         if (!hasKeyGenerator && !keyPathKey) {
216             exceptionState.throwDOMException(DataError, "Evaluating the object store's key path did not yield a value.");
217             return nullptr;
218         }
219         if (hasKeyGenerator && !keyPathKey) {
220             if (!canInjectIDBKeyIntoScriptValue(toIsolate(executionContext), value, keyPath)) {
221                 exceptionState.throwDOMException(DataError, "A generated key could not be inserted into the value.");
222                 return nullptr;
223             }
224         }
225         if (keyPathKey)
226             key = keyPathKey;
227     }
228     if (key && !key->isValid()) {
229         exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage);
230         return nullptr;
231     }
232
233     if (!backendDB()) {
234         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
235         return nullptr;
236     }
237
238     Vector<int64_t> indexIds;
239     WillBeHeapVector<IndexKeys> indexKeys;
240     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
241         IndexKeys keys;
242         generateIndexKeysForValue(toIsolate(executionContext), it->value, value, &keys);
243         indexIds.append(it->key);
244         indexKeys.append(keys);
245     }
246
247     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(executionContext, source, m_transaction.get());
248     Vector<char> wireBytes;
249     serializedValue->toWireBytes(wireBytes);
250     RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(wireBytes);
251
252     backendDB()->put(m_transaction->id(), id(), blink::WebData(valueBuffer), blobInfo, key.release(), static_cast<WebIDBDatabase::PutMode>(putMode), WebIDBCallbacksImpl::create(request).leakPtr(), indexIds, indexKeys);
253     return request.release();
254 }
255
256 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::deleteFunction(ExecutionContext* context, const ScriptValue& key, ExceptionState& exceptionState)
257 {
258     IDB_TRACE("IDBObjectStore::delete");
259     if (isDeleted()) {
260         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
261         return nullptr;
262     }
263     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
264         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
265         return nullptr;
266     }
267     if (!m_transaction->isActive()) {
268         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
269         return nullptr;
270     }
271     if (m_transaction->isReadOnly()) {
272         exceptionState.throwDOMException(ReadOnlyError, IDBDatabase::transactionReadOnlyErrorMessage);
273         return nullptr;
274     }
275
276     RefPtrWillBeRawPtr<IDBKeyRange> keyRange = IDBKeyRange::fromScriptValue(context, key, exceptionState);
277     if (exceptionState.hadException())
278         return nullptr;
279     if (!keyRange) {
280         exceptionState.throwDOMException(DataError, IDBDatabase::noKeyOrKeyRangeErrorMessage);
281         return nullptr;
282     }
283     if (!backendDB()) {
284         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
285         return nullptr;
286     }
287
288     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
289     backendDB()->deleteRange(m_transaction->id(), id(), keyRange.release(), WebIDBCallbacksImpl::create(request).leakPtr());
290     return request.release();
291 }
292
293 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::clear(ExecutionContext* context, ExceptionState& exceptionState)
294 {
295     IDB_TRACE("IDBObjectStore::clear");
296     if (isDeleted()) {
297         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
298         return nullptr;
299     }
300     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
301         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
302         return nullptr;
303     }
304     if (!m_transaction->isActive()) {
305         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
306         return nullptr;
307     }
308     if (m_transaction->isReadOnly()) {
309         exceptionState.throwDOMException(ReadOnlyError, IDBDatabase::transactionReadOnlyErrorMessage);
310         return nullptr;
311     }
312     if (!backendDB()) {
313         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
314         return nullptr;
315     }
316
317     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
318     backendDB()->clear(m_transaction->id(), id(), WebIDBCallbacksImpl::create(request).leakPtr());
319     return request.release();
320 }
321
322 namespace {
323 // This class creates the index keys for a given index by extracting
324 // them from the SerializedScriptValue, for all the existing values in
325 // the objectStore. It only needs to be kept alive by virtue of being
326 // a listener on an IDBRequest object, in the same way that JavaScript
327 // cursor success handlers are kept alive.
328 class IndexPopulator FINAL : public EventListener {
329 public:
330     static PassRefPtr<IndexPopulator> create(ScriptState* scriptState, PassRefPtrWillBeRawPtr<IDBDatabase> database, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
331     {
332         return adoptRef(new IndexPopulator(scriptState, database, transactionId, objectStoreId, indexMetadata));
333     }
334
335     virtual bool operator==(const EventListener& other) OVERRIDE
336     {
337         return this == &other;
338     }
339
340 private:
341     IndexPopulator(ScriptState* scriptState, PassRefPtrWillBeRawPtr<IDBDatabase> database, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
342         : EventListener(CPPEventListenerType)
343         , m_scriptState(scriptState)
344         , m_database(database)
345         , m_transactionId(transactionId)
346         , m_objectStoreId(objectStoreId)
347         , m_indexMetadata(indexMetadata)
348     {
349     }
350
351     virtual void handleEvent(ExecutionContext* context, Event* event) OVERRIDE
352     {
353         ASSERT(event->type() == EventTypeNames::success);
354         EventTarget* target = event->target();
355         IDBRequest* request = static_cast<IDBRequest*>(target);
356
357         if (!m_database->backend()) // If database is stopped?
358             return;
359
360         RefPtrWillBeRawPtr<IDBAny> cursorAny = request->resultAsAny();
361         RefPtrWillBeRawPtr<IDBCursorWithValue> cursor = nullptr;
362         if (cursorAny->type() == IDBAny::IDBCursorWithValueType)
363             cursor = cursorAny->idbCursorWithValue();
364
365         Vector<int64_t> indexIds;
366         indexIds.append(m_indexMetadata.id);
367         if (cursor && !cursor->isDeleted()) {
368             cursor->continueFunction(static_cast<IDBKey*>(0), static_cast<IDBKey*>(0), ASSERT_NO_EXCEPTION);
369
370             RefPtrWillBeRawPtr<IDBKey> primaryKey = cursor->idbPrimaryKey();
371             ScriptValue value = cursor->value(m_scriptState.get());
372
373             IDBObjectStore::IndexKeys indexKeys;
374             generateIndexKeysForValue(toIsolate(context), m_indexMetadata, value, &indexKeys);
375
376             WillBeHeapVector<IDBObjectStore::IndexKeys> indexKeysList;
377             indexKeysList.append(indexKeys);
378
379             m_database->backend()->setIndexKeys(m_transactionId, m_objectStoreId, primaryKey.release(), indexIds, indexKeysList);
380         } else {
381             // Now that we are done indexing, tell the backend to go
382             // back to processing tasks of type NormalTask.
383             m_database->backend()->setIndexesReady(m_transactionId, m_objectStoreId, indexIds);
384             m_database.clear();
385         }
386
387     }
388
389     RefPtr<ScriptState> m_scriptState;
390     RefPtrWillBePersistent<IDBDatabase> m_database;
391     const int64_t m_transactionId;
392     const int64_t m_objectStoreId;
393     const IDBIndexMetadata m_indexMetadata;
394 };
395 }
396
397 PassRefPtrWillBeRawPtr<IDBIndex> IDBObjectStore::createIndex(ScriptState* scriptState, const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionState& exceptionState)
398 {
399     bool unique = false;
400     options.get("unique", unique);
401
402     bool multiEntry = false;
403     options.get("multiEntry", multiEntry);
404
405     return createIndex(scriptState, name, keyPath, unique, multiEntry, exceptionState);
406 }
407
408 PassRefPtrWillBeRawPtr<IDBIndex> IDBObjectStore::createIndex(ScriptState* scriptState, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionState& exceptionState)
409 {
410     IDB_TRACE("IDBObjectStore::createIndex");
411     if (!m_transaction->isVersionChange()) {
412         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::notVersionChangeTransactionErrorMessage);
413         return nullptr;
414     }
415     if (isDeleted()) {
416         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
417         return nullptr;
418     }
419     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
420         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
421         return nullptr;
422     }
423     if (!m_transaction->isActive()) {
424         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
425         return nullptr;
426     }
427     if (!keyPath.isValid()) {
428         exceptionState.throwDOMException(SyntaxError, "The keyPath argument contains an invalid key path.");
429         return nullptr;
430     }
431     if (name.isNull()) {
432         exceptionState.throwTypeError("The name provided is null.");
433         return nullptr;
434     }
435     if (containsIndex(name)) {
436         exceptionState.throwDOMException(ConstraintError, "An index with the specified name already exists.");
437         return nullptr;
438     }
439
440     if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) {
441         exceptionState.throwDOMException(InvalidAccessError, "The keyPath argument was an array and the multiEntry option is true.");
442         return nullptr;
443     }
444     if (!backendDB()) {
445         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
446         return nullptr;
447     }
448
449     int64_t indexId = m_metadata.maxIndexId + 1;
450     backendDB()->createIndex(m_transaction->id(), id(), indexId, name, keyPath, unique, multiEntry);
451
452     ++m_metadata.maxIndexId;
453
454     IDBIndexMetadata metadata(name, indexId, keyPath, unique, multiEntry);
455     RefPtrWillBeRawPtr<IDBIndex> index = IDBIndex::create(metadata, this, m_transaction.get());
456     m_indexMap.set(name, index);
457     m_metadata.indexes.set(indexId, metadata);
458     m_transaction->db()->indexCreated(id(), metadata);
459
460     ASSERT(!exceptionState.hadException());
461     if (exceptionState.hadException())
462         return nullptr;
463
464     RefPtrWillBeRawPtr<IDBRequest> indexRequest = openCursor(scriptState->executionContext(), static_cast<IDBKeyRange*>(0), blink::WebIDBCursor::Next, WebIDBDatabase::PreemptiveTask);
465     indexRequest->preventPropagation();
466
467     // This is kept alive by being the success handler of the request, which is in turn kept alive by the owning transaction.
468     RefPtr<IndexPopulator> indexPopulator = IndexPopulator::create(scriptState, transaction()->db(), m_transaction->id(), id(), metadata);
469     indexRequest->setOnsuccess(indexPopulator);
470     return index.release();
471 }
472
473 PassRefPtrWillBeRawPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionState& exceptionState)
474 {
475     IDB_TRACE("IDBObjectStore::index");
476     if (isDeleted()) {
477         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
478         return nullptr;
479     }
480     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
481         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
482         return nullptr;
483     }
484
485     IDBIndexMap::iterator it = m_indexMap.find(name);
486     if (it != m_indexMap.end())
487         return it->value;
488
489     int64_t indexId = findIndexId(name);
490     if (indexId == IDBIndexMetadata::InvalidId) {
491         exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchIndexErrorMessage);
492         return nullptr;
493     }
494
495     const IDBIndexMetadata* indexMetadata(0);
496     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
497         if (it->value.name == name) {
498             indexMetadata = &it->value;
499             break;
500         }
501     }
502     ASSERT(indexMetadata);
503     ASSERT(indexMetadata->id != IDBIndexMetadata::InvalidId);
504
505     RefPtrWillBeRawPtr<IDBIndex> index = IDBIndex::create(*indexMetadata, this, m_transaction.get());
506     m_indexMap.set(name, index);
507     return index.release();
508 }
509
510 void IDBObjectStore::deleteIndex(const String& name, ExceptionState& exceptionState)
511 {
512     IDB_TRACE("IDBObjectStore::deleteIndex");
513     if (!m_transaction->isVersionChange()) {
514         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::notVersionChangeTransactionErrorMessage);
515         return;
516     }
517     if (isDeleted()) {
518         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
519         return;
520     }
521     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
522         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
523         return;
524     }
525     if (!m_transaction->isActive()) {
526         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
527         return;
528     }
529     int64_t indexId = findIndexId(name);
530     if (indexId == IDBIndexMetadata::InvalidId) {
531         exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchIndexErrorMessage);
532         return;
533     }
534     if (!backendDB()) {
535         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
536         return;
537     }
538
539     backendDB()->deleteIndex(m_transaction->id(), id(), indexId);
540
541     m_metadata.indexes.remove(indexId);
542     m_transaction->db()->indexDeleted(id(), indexId);
543     IDBIndexMap::iterator it = m_indexMap.find(name);
544     if (it != m_indexMap.end()) {
545         it->value->markDeleted();
546         m_indexMap.remove(name);
547     }
548 }
549
550 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::openCursor(ExecutionContext* context, const ScriptValue& range, const String& directionString, ExceptionState& exceptionState)
551 {
552     IDB_TRACE("IDBObjectStore::openCursor");
553     if (isDeleted()) {
554         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
555         return nullptr;
556     }
557     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
558         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
559         return nullptr;
560     }
561     if (!m_transaction->isActive()) {
562         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
563         return nullptr;
564     }
565
566     WebIDBCursor::Direction direction = IDBCursor::stringToDirection(directionString, exceptionState);
567     if (exceptionState.hadException())
568         return nullptr;
569
570     RefPtrWillBeRawPtr<IDBKeyRange> keyRange = IDBKeyRange::fromScriptValue(context, range, exceptionState);
571     if (exceptionState.hadException())
572         return nullptr;
573
574     if (!backendDB()) {
575         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
576         return nullptr;
577     }
578
579     return openCursor(context, keyRange, direction, WebIDBDatabase::NormalTask);
580 }
581
582 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::openCursor(ExecutionContext* context, PassRefPtrWillBeRawPtr<IDBKeyRange> range, WebIDBCursor::Direction direction, WebIDBDatabase::TaskType taskType)
583 {
584     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
585     request->setCursorDetails(IndexedDB::CursorKeyAndValue, direction);
586
587     backendDB()->openCursor(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, direction, false, taskType, WebIDBCallbacksImpl::create(request).leakPtr());
588     return request.release();
589 }
590
591 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::openKeyCursor(ExecutionContext* context, const ScriptValue& range, const String& directionString, ExceptionState& exceptionState)
592 {
593     IDB_TRACE("IDBObjectStore::openKeyCursor");
594     if (isDeleted()) {
595         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
596         return nullptr;
597     }
598     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
599         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
600         return nullptr;
601     }
602     if (!m_transaction->isActive()) {
603         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
604         return nullptr;
605     }
606
607     WebIDBCursor::Direction direction = IDBCursor::stringToDirection(directionString, exceptionState);
608     if (exceptionState.hadException())
609         return nullptr;
610
611     RefPtrWillBeRawPtr<IDBKeyRange> keyRange = IDBKeyRange::fromScriptValue(context, range, exceptionState);
612     if (exceptionState.hadException())
613         return nullptr;
614
615     if (!backendDB()) {
616         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
617         return nullptr;
618     }
619
620     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
621     request->setCursorDetails(IndexedDB::CursorKeyOnly, direction);
622
623     backendDB()->openCursor(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange.release(), direction, true, WebIDBDatabase::NormalTask, WebIDBCallbacksImpl::create(request).leakPtr());
624     return request.release();
625 }
626
627 PassRefPtrWillBeRawPtr<IDBRequest> IDBObjectStore::count(ExecutionContext* context, const ScriptValue& range, ExceptionState& exceptionState)
628 {
629     IDB_TRACE("IDBObjectStore::count");
630     if (isDeleted()) {
631         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage);
632         return nullptr;
633     }
634     if (m_transaction->isFinished() || m_transaction->isFinishing()) {
635         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
636         return nullptr;
637     }
638     if (!m_transaction->isActive()) {
639         exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
640         return nullptr;
641     }
642
643     RefPtrWillBeRawPtr<IDBKeyRange> keyRange = IDBKeyRange::fromScriptValue(context, range, exceptionState);
644     if (exceptionState.hadException())
645         return nullptr;
646
647     if (!backendDB()) {
648         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage);
649         return nullptr;
650     }
651
652     RefPtrWillBeRawPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
653     backendDB()->count(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange.release(), WebIDBCallbacksImpl::create(request).leakPtr());
654     return request.release();
655 }
656
657 void IDBObjectStore::transactionFinished()
658 {
659     ASSERT(m_transaction->isFinished());
660
661     // Break reference cycles.
662     m_indexMap.clear();
663 }
664
665 int64_t IDBObjectStore::findIndexId(const String& name) const
666 {
667     for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
668         if (it->value.name == name) {
669             ASSERT(it->key != IDBIndexMetadata::InvalidId);
670             return it->key;
671         }
672     }
673     return IDBIndexMetadata::InvalidId;
674 }
675
676 WebIDBDatabase* IDBObjectStore::backendDB() const
677 {
678     return m_transaction->backendDB();
679 }
680
681 } // namespace WebCore