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