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 "IDBObjectStore.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "DOMStringList.h"
32#include "IDBAny.h"
33#include "IDBBindingUtilities.h"
34#include "IDBCursorWithValue.h"
35#include "IDBDatabase.h"
36#include "IDBDatabaseException.h"
37#include "IDBIndex.h"
38#include "IDBKey.h"
39#include "IDBKeyPath.h"
40#include "IDBKeyRange.h"
41#include "IDBTracing.h"
42#include "IDBTransaction.h"
43#include "ScriptExecutionContext.h"
44#include "SerializedScriptValue.h"
45#include "SharedBuffer.h"
46
47namespace WebCore {
48
49static const unsigned short defaultDirection = IndexedDB::CursorNext;
50
51IDBObjectStore::IDBObjectStore(const IDBObjectStoreMetadata& metadata, IDBTransaction* transaction)
52    : m_metadata(metadata)
53    , m_transaction(transaction)
54    , m_deleted(false)
55{
56    ASSERT(m_transaction);
57    // We pass a reference to this object before it can be adopted.
58    relaxAdoptionRequirement();
59}
60
61PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const
62{
63    IDB_TRACE("IDBObjectStore::indexNames");
64    RefPtr<DOMStringList> indexNames = DOMStringList::create();
65    for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it)
66        indexNames->append(it->value.name);
67    indexNames->sort();
68    return indexNames.release();
69}
70
71PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
72{
73    IDB_TRACE("IDBObjectStore::get");
74    if (m_deleted) {
75        ec = IDBDatabaseException::InvalidStateError;
76        return 0;
77    }
78    if (!keyRange) {
79        ec = IDBDatabaseException::DataError;
80        return 0;
81    }
82    if (!m_transaction->isActive()) {
83        ec = IDBDatabaseException::TransactionInactiveError;
84        return 0;
85    }
86    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
87    backendDB()->get(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange, false, request);
88    return request.release();
89}
90
91PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, const ScriptValue& key, ExceptionCode& ec)
92{
93    RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
94    if (ec)
95        return 0;
96    return get(context, keyRange.release(), ec);
97}
98
99static void generateIndexKeysForValue(DOMRequestState* requestState, const IDBIndexMetadata& indexMetadata, const ScriptValue& objectValue, IDBObjectStore::IndexKeys* indexKeys)
100{
101    ASSERT(indexKeys);
102    RefPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(requestState, objectValue, indexMetadata.keyPath);
103
104    if (!indexKey)
105        return;
106
107    if (!indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) {
108        if (!indexKey->isValid())
109            return;
110
111        indexKeys->append(indexKey);
112    } else {
113        ASSERT(indexMetadata.multiEntry);
114        ASSERT(indexKey->type() == IDBKey::ArrayType);
115        indexKey = IDBKey::createMultiEntryArray(indexKey->array());
116
117        for (size_t i = 0; i < indexKey->array().size(); ++i)
118            indexKeys->append(indexKey->array()[i]);
119    }
120}
121
122PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptState* state, ScriptValue& value, const ScriptValue& key, ExceptionCode& ec)
123{
124    IDB_TRACE("IDBObjectStore::add");
125    return put(IDBDatabaseBackendInterface::AddOnly, IDBAny::create(this), state, value, key, ec);
126}
127
128PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptState* state, ScriptValue& value, ExceptionCode& ec)
129{
130    IDB_TRACE("IDBObjectStore::add");
131    return put(IDBDatabaseBackendInterface::AddOnly, IDBAny::create(this), state, value, static_cast<IDBKey*>(0), ec);
132}
133
134PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptState* state, ScriptValue& value, const ScriptValue& key, ExceptionCode& ec)
135{
136    IDB_TRACE("IDBObjectStore::put");
137    return put(IDBDatabaseBackendInterface::AddOrUpdate, IDBAny::create(this), state, value, key, ec);
138}
139
140PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptState* state, ScriptValue& value, ExceptionCode& ec)
141{
142    IDB_TRACE("IDBObjectStore::put");
143    return put(IDBDatabaseBackendInterface::AddOrUpdate, IDBAny::create(this), state, value, static_cast<IDBKey*>(0), ec);
144}
145
146PassRefPtr<IDBRequest> IDBObjectStore::put(IDBDatabaseBackendInterface::PutMode putMode, PassRefPtr<IDBAny> source, ScriptState* state, ScriptValue& value, const ScriptValue& keyValue, ExceptionCode& ec)
147{
148    ScriptExecutionContext* context = scriptExecutionContextFromScriptState(state);
149    DOMRequestState requestState(context);
150    RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
151    return put(putMode, source, state, value, key.release(), ec);
152}
153
154PassRefPtr<IDBRequest> IDBObjectStore::put(IDBDatabaseBackendInterface::PutMode putMode, PassRefPtr<IDBAny> source, ScriptState* state, ScriptValue& value, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
155{
156    RefPtr<IDBKey> key = prpKey;
157    if (m_deleted) {
158        ec = IDBDatabaseException::InvalidStateError;
159        return 0;
160    }
161    if (!m_transaction->isActive()) {
162        ec = IDBDatabaseException::TransactionInactiveError;
163        return 0;
164    }
165    if (m_transaction->isReadOnly()) {
166        ec = IDBDatabaseException::ReadOnlyError;
167        return 0;
168    }
169
170    // FIXME: Expose the JS engine exception state through ScriptState.
171    bool didThrow = false;
172    RefPtr<SerializedScriptValue> serializedValue = value.serialize(state, 0, 0, didThrow);
173    if (didThrow) {
174        // Setting an explicit ExceptionCode here would defer handling the already thrown exception.
175        return 0;
176    }
177
178    if (serializedValue->hasBlobURLs()) {
179        // FIXME: Add Blob/File/FileList support
180        ec = IDBDatabaseException::DataCloneError;
181        return 0;
182    }
183
184    const IDBKeyPath& keyPath = m_metadata.keyPath;
185    const bool usesInLineKeys = !keyPath.isNull();
186    const bool hasKeyGenerator = autoIncrement();
187
188    ScriptExecutionContext* context = scriptExecutionContextFromScriptState(state);
189    DOMRequestState requestState(context);
190
191    if (putMode != IDBDatabaseBackendInterface::CursorUpdate && usesInLineKeys && key) {
192        ec = IDBDatabaseException::DataError;
193        return 0;
194    }
195    if (!usesInLineKeys && !hasKeyGenerator && !key) {
196        ec = IDBDatabaseException::DataError;
197        return 0;
198    }
199    if (usesInLineKeys) {
200        RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(&requestState, value, keyPath);
201        if (keyPathKey && !keyPathKey->isValid()) {
202            ec = IDBDatabaseException::DataError;
203            return 0;
204        }
205        if (!hasKeyGenerator && !keyPathKey) {
206            ec = IDBDatabaseException::DataError;
207            return 0;
208        }
209        if (hasKeyGenerator && !keyPathKey) {
210            if (!canInjectIDBKeyIntoScriptValue(&requestState, value, keyPath)) {
211                ec = IDBDatabaseException::DataError;
212                return 0;
213            }
214        }
215        if (keyPathKey)
216            key = keyPathKey;
217    }
218    if (key && !key->isValid()) {
219        ec = IDBDatabaseException::DataError;
220        return 0;
221    }
222
223    Vector<int64_t> indexIds;
224    Vector<IndexKeys> indexKeys;
225    for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
226        IndexKeys keys;
227        generateIndexKeysForValue(&requestState, it->value, value, &keys);
228        indexIds.append(it->key);
229        indexKeys.append(keys);
230    }
231
232    RefPtr<IDBRequest> request = IDBRequest::create(context, source, m_transaction.get());
233    Vector<uint8_t> valueBytes = serializedValue->toWireBytes();
234    // This is a hack to account for disagreements about whether SerializedScriptValue should deal in Vector<uint8_t> or Vector<char>.
235    // See https://lists.webkit.org/pipermail/webkit-dev/2013-February/023682.html
236    Vector<char>* valueBytesSigned = reinterpret_cast<Vector<char>*>(&valueBytes);
237    RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(*valueBytesSigned);
238    backendDB()->put(m_transaction->id(), id(), valueBuffer, key.release(), static_cast<IDBDatabaseBackendInterface::PutMode>(putMode), request, indexIds, indexKeys);
239    return request.release();
240}
241
242PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
243{
244    IDB_TRACE("IDBObjectStore::delete");
245    if (m_deleted) {
246        ec = IDBDatabaseException::InvalidStateError;
247        return 0;
248    }
249    if (!m_transaction->isActive()) {
250        ec = IDBDatabaseException::TransactionInactiveError;
251        return 0;
252    }
253    if (m_transaction->isReadOnly()) {
254        ec = IDBDatabaseException::ReadOnlyError;
255        return 0;
256    }
257    if (!keyRange) {
258        ec = IDBDatabaseException::DataError;
259        return 0;
260    }
261
262    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
263    backendDB()->deleteRange(m_transaction->id(), id(), keyRange, request);
264    return request.release();
265}
266
267PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, const ScriptValue& key, ExceptionCode& ec)
268{
269    RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
270    if (ec)
271        return 0;
272    return deleteFunction(context, keyRange.release(), ec);
273}
274
275PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
276{
277    IDB_TRACE("IDBObjectStore::clear");
278    if (m_deleted) {
279        ec = IDBDatabaseException::InvalidStateError;
280        return 0;
281    }
282    if (!m_transaction->isActive()) {
283        ec = IDBDatabaseException::TransactionInactiveError;
284        return 0;
285    }
286    if (m_transaction->isReadOnly()) {
287        ec = IDBDatabaseException::ReadOnlyError;
288        return 0;
289    }
290
291    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
292    backendDB()->clear(m_transaction->id(), id(), request);
293    return request.release();
294}
295
296namespace {
297// This class creates the index keys for a given index by extracting
298// them from the SerializedScriptValue, for all the existing values in
299// the objectStore. It only needs to be kept alive by virtue of being
300// a listener on an IDBRequest object, in the same way that JavaScript
301// cursor success handlers are kept alive.
302class IndexPopulator : public EventListener {
303public:
304    static PassRefPtr<IndexPopulator> create(PassRefPtr<IDBDatabaseBackendInterface> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
305    {
306        return adoptRef(new IndexPopulator(backend, transactionId, objectStoreId, indexMetadata));
307    }
308
309    virtual bool operator==(const EventListener& other)
310    {
311        return this == &other;
312    }
313
314private:
315    IndexPopulator(PassRefPtr<IDBDatabaseBackendInterface> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
316        : EventListener(CPPEventListenerType)
317        , m_databaseBackend(backend)
318        , m_transactionId(transactionId)
319        , m_objectStoreId(objectStoreId)
320        , m_indexMetadata(indexMetadata)
321    {
322    }
323
324    virtual void handleEvent(ScriptExecutionContext*, Event* event)
325    {
326        ASSERT(event->type() == eventNames().successEvent);
327        EventTarget* target = event->target();
328        IDBRequest* request = static_cast<IDBRequest*>(target);
329
330        RefPtr<IDBAny> cursorAny = request->result(ASSERT_NO_EXCEPTION);
331        RefPtr<IDBCursorWithValue> cursor;
332        if (cursorAny->type() == IDBAny::IDBCursorWithValueType)
333            cursor = cursorAny->idbCursorWithValue();
334
335        Vector<int64_t, 1> indexIds;
336        indexIds.append(m_indexMetadata.id);
337        if (cursor) {
338            cursor->continueFunction(static_cast<IDBKey*>(0), ASSERT_NO_EXCEPTION);
339
340            RefPtr<IDBKey> primaryKey = cursor->idbPrimaryKey();
341            ScriptValue value = cursor->value();
342
343            IDBObjectStore::IndexKeys indexKeys;
344            generateIndexKeysForValue(request->requestState(), m_indexMetadata, value, &indexKeys);
345
346            Vector<IDBObjectStore::IndexKeys, 1> indexKeysList;
347            indexKeysList.append(indexKeys);
348
349            m_databaseBackend->setIndexKeys(m_transactionId, m_objectStoreId, primaryKey, indexIds, indexKeysList);
350        } else {
351            // Now that we are done indexing, tell the backend to go
352            // back to processing tasks of type NormalTask.
353            m_databaseBackend->setIndexesReady(m_transactionId, m_objectStoreId, indexIds);
354            m_databaseBackend.clear();
355        }
356
357    }
358
359    RefPtr<IDBDatabaseBackendInterface> m_databaseBackend;
360    const int64_t m_transactionId;
361    const int64_t m_objectStoreId;
362    const IDBIndexMetadata m_indexMetadata;
363};
364}
365
366PassRefPtr<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec)
367{
368    bool unique = false;
369    options.get("unique", unique);
370
371    bool multiEntry = false;
372    options.get("multiEntry", multiEntry);
373
374    return createIndex(context, name, keyPath, unique, multiEntry, ec);
375}
376
377PassRefPtr<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec)
378{
379    IDB_TRACE("IDBObjectStore::createIndex");
380    if (!m_transaction->isVersionChange() || m_deleted) {
381        ec = IDBDatabaseException::InvalidStateError;
382        return 0;
383    }
384    if (!m_transaction->isActive()) {
385        ec = IDBDatabaseException::TransactionInactiveError;
386        return 0;
387    }
388    if (!keyPath.isValid()) {
389        ec = IDBDatabaseException::SyntaxError;
390        return 0;
391    }
392    if (name.isNull()) {
393        ec = TypeError;
394        return 0;
395    }
396    if (containsIndex(name)) {
397        ec = IDBDatabaseException::ConstraintError;
398        return 0;
399    }
400
401    if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) {
402        ec = IDBDatabaseException::InvalidAccessError;
403        return 0;
404    }
405
406    int64_t indexId = m_metadata.maxIndexId + 1;
407    backendDB()->createIndex(m_transaction->id(), id(), indexId, name, keyPath, unique, multiEntry);
408
409    ++m_metadata.maxIndexId;
410
411    IDBIndexMetadata metadata(name, indexId, keyPath, unique, multiEntry);
412    RefPtr<IDBIndex> index = IDBIndex::create(metadata, this, m_transaction.get());
413    m_indexMap.set(name, index);
414    m_metadata.indexes.set(indexId, metadata);
415
416    ASSERT(!ec);
417    if (ec)
418        return 0;
419
420    RefPtr<IDBRequest> indexRequest = openCursor(context, static_cast<IDBKeyRange*>(0), IDBCursor::directionNext(), IDBDatabaseBackendInterface::PreemptiveTask, ec);
421    ASSERT(!ec);
422    if (ec)
423        return 0;
424    indexRequest->preventPropagation();
425
426    // This is kept alive by being the success handler of the request, which is in turn kept alive by the owning transaction.
427    RefPtr<IndexPopulator> indexPopulator = IndexPopulator::create(backendDB(), m_transaction->id(), id(), metadata);
428    indexRequest->setOnsuccess(indexPopulator);
429
430    return index.release();
431}
432
433PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
434{
435    IDB_TRACE("IDBObjectStore::index");
436    if (m_deleted) {
437        ec = IDBDatabaseException::InvalidStateError;
438        return 0;
439    }
440    if (m_transaction->isFinished()) {
441        ec = IDBDatabaseException::InvalidStateError;
442        return 0;
443    }
444
445    IDBIndexMap::iterator it = m_indexMap.find(name);
446    if (it != m_indexMap.end())
447        return it->value;
448
449    int64_t indexId = findIndexId(name);
450    if (indexId == IDBIndexMetadata::InvalidId) {
451        ec = IDBDatabaseException::NotFoundError;
452        return 0;
453    }
454
455    const IDBIndexMetadata* indexMetadata(0);
456    for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
457        if (it->value.name == name) {
458            indexMetadata = &it->value;
459            break;
460        }
461    }
462    ASSERT(indexMetadata);
463    ASSERT(indexMetadata->id != IDBIndexMetadata::InvalidId);
464
465    RefPtr<IDBIndex> index = IDBIndex::create(*indexMetadata, this, m_transaction.get());
466    m_indexMap.set(name, index);
467    return index.release();
468}
469
470void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
471{
472    IDB_TRACE("IDBObjectStore::deleteIndex");
473    if (!m_transaction->isVersionChange() || m_deleted) {
474        ec = IDBDatabaseException::InvalidStateError;
475        return;
476    }
477    if (!m_transaction->isActive()) {
478        ec = IDBDatabaseException::TransactionInactiveError;
479        return;
480    }
481    int64_t indexId = findIndexId(name);
482    if (indexId == IDBIndexMetadata::InvalidId) {
483        ec = IDBDatabaseException::NotFoundError;
484        return;
485    }
486
487    backendDB()->deleteIndex(m_transaction->id(), id(), indexId);
488
489    m_metadata.indexes.remove(indexId);
490    IDBIndexMap::iterator it = m_indexMap.find(name);
491    if (it != m_indexMap.end()) {
492        it->value->markDeleted();
493        m_indexMap.remove(name);
494    }
495}
496
497PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& directionString, IDBDatabaseBackendInterface::TaskType taskType, ExceptionCode& ec)
498{
499    IDB_TRACE("IDBObjectStore::openCursor");
500    if (m_deleted) {
501        ec = IDBDatabaseException::InvalidStateError;
502        return 0;
503    }
504    if (!m_transaction->isActive()) {
505        ec = IDBDatabaseException::TransactionInactiveError;
506        return 0;
507    }
508    IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec);
509    if (ec)
510        return 0;
511
512    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
513    request->setCursorDetails(IndexedDB::CursorKeyAndValue, direction);
514
515    backendDB()->openCursor(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, direction, false, static_cast<IDBDatabaseBackendInterface::TaskType>(taskType), request);
516    return request.release();
517}
518
519PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const ScriptValue& key, const String& direction, ExceptionCode& ec)
520{
521    RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
522    if (ec)
523        return 0;
524    return openCursor(context, keyRange.release(), direction, ec);
525}
526
527PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, ExceptionCode& ec)
528{
529    IDB_TRACE("IDBObjectStore::count");
530    if (m_deleted) {
531        ec = IDBDatabaseException::InvalidStateError;
532        return 0;
533    }
534    if (!m_transaction->isActive()) {
535        ec = IDBDatabaseException::TransactionInactiveError;
536        return 0;
537    }
538    RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
539    backendDB()->count(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, request);
540    return request.release();
541}
542
543PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, const ScriptValue& key, ExceptionCode& ec)
544{
545    RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
546    if (ec)
547        return 0;
548    return count(context, keyRange.release(), ec);
549}
550
551void IDBObjectStore::transactionFinished()
552{
553    ASSERT(m_transaction->isFinished());
554
555    // Break reference cycles.
556    m_indexMap.clear();
557}
558
559int64_t IDBObjectStore::findIndexId(const String& name) const
560{
561    for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
562        if (it->value.name == name) {
563            ASSERT(it->key != IDBIndexMetadata::InvalidId);
564            return it->key;
565        }
566    }
567    return IDBIndexMetadata::InvalidId;
568}
569
570IDBDatabaseBackendInterface* IDBObjectStore::backendDB() const
571{
572    return m_transaction->backendDB();
573}
574
575} // namespace WebCore
576
577#endif // ENABLE(INDEXED_DATABASE)
578