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