1/* 2 * Copyright (C) 2011 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 "IDBDatabaseBackend.h" 28 29#if ENABLE(INDEXED_DATABASE) 30 31#include "IDBCursorBackend.h" 32#include "IDBDatabaseCallbacks.h" 33#include "IDBDatabaseException.h" 34#include "IDBFactoryBackendInterface.h" 35#include "IDBKeyRange.h" 36#include "IDBRecordIdentifier.h" 37#include "IDBServerConnection.h" 38#include "IDBTransactionBackend.h" 39#include "IDBTransactionCoordinator.h" 40#include "Logging.h" 41#include "SharedBuffer.h" 42#include <wtf/TemporaryChange.h> 43 44namespace WebCore { 45 46PassRefPtr<IDBDatabaseBackend> IDBDatabaseBackend::create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection) 47{ 48 RefPtr<IDBDatabaseBackend> backend = adoptRef(new IDBDatabaseBackend(name, uniqueIdentifier, factory, serverConnection)); 49 backend->openInternalAsync(); 50 51 return backend.release(); 52} 53 54IDBDatabaseBackend::IDBDatabaseBackend(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection) 55 : m_metadata(name, InvalidId, 0, InvalidId) 56 , m_identifier(uniqueIdentifier) 57 , m_factory(factory) 58 , m_serverConnection(serverConnection) 59 , m_transactionCoordinator(std::make_unique<IDBTransactionCoordinator>()) 60 , m_closingConnection(false) 61 , m_didOpenInternal(false) 62{ 63 ASSERT(!m_metadata.name.isNull()); 64} 65 66void IDBDatabaseBackend::addObjectStore(const IDBObjectStoreMetadata& objectStore, int64_t newMaxObjectStoreId) 67{ 68 ASSERT(!m_metadata.objectStores.contains(objectStore.id)); 69 if (newMaxObjectStoreId != IDBObjectStoreMetadata::InvalidId) { 70 ASSERT(m_metadata.maxObjectStoreId < newMaxObjectStoreId); 71 m_metadata.maxObjectStoreId = newMaxObjectStoreId; 72 } 73 m_metadata.objectStores.set(objectStore.id, objectStore); 74} 75 76void IDBDatabaseBackend::removeObjectStore(int64_t objectStoreId) 77{ 78 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 79 m_metadata.objectStores.remove(objectStoreId); 80} 81 82void IDBDatabaseBackend::addIndex(int64_t objectStoreId, const IDBIndexMetadata& index, int64_t newMaxIndexId) 83{ 84 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 85 IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); 86 87 ASSERT(!objectStore.indexes.contains(index.id)); 88 objectStore.indexes.set(index.id, index); 89 if (newMaxIndexId != IDBIndexMetadata::InvalidId) { 90 ASSERT(objectStore.maxIndexId < newMaxIndexId); 91 objectStore.maxIndexId = newMaxIndexId; 92 } 93 m_metadata.objectStores.set(objectStoreId, objectStore); 94} 95 96void IDBDatabaseBackend::removeIndex(int64_t objectStoreId, int64_t indexId) 97{ 98 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 99 IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); 100 101 ASSERT(objectStore.indexes.contains(indexId)); 102 objectStore.indexes.remove(indexId); 103 m_metadata.objectStores.set(objectStoreId, objectStore); 104} 105 106void IDBDatabaseBackend::openInternalAsync() 107{ 108 RefPtr<IDBDatabaseBackend> self = this; 109 m_serverConnection->getOrEstablishIDBDatabaseMetadata([self](const IDBDatabaseMetadata& metadata, bool success) { 110 self->didOpenInternalAsync(metadata, success); 111 }); 112} 113 114void IDBDatabaseBackend::didOpenInternalAsync(const IDBDatabaseMetadata& metadata, bool success) 115{ 116 m_didOpenInternal = true; 117 118 if (!success) { 119 processPendingOpenCalls(false); 120 return; 121 } 122 123 m_metadata = metadata; 124 125 processPendingCalls(); 126} 127 128IDBDatabaseBackend::~IDBDatabaseBackend() 129{ 130 m_factory->removeIDBDatabaseBackend(m_identifier); 131} 132 133void IDBDatabaseBackend::createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement) 134{ 135 LOG(StorageAPI, "IDBDatabaseBackend::createObjectStore"); 136 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 137 if (!transaction) 138 return; 139 ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); 140 141 ASSERT(!m_metadata.objectStores.contains(objectStoreId)); 142 IDBObjectStoreMetadata objectStoreMetadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackend::MinimumIndexId); 143 144 transaction->scheduleCreateObjectStoreOperation(objectStoreMetadata); 145 addObjectStore(objectStoreMetadata, objectStoreId); 146} 147 148void IDBDatabaseBackend::deleteObjectStore(int64_t transactionId, int64_t objectStoreId) 149{ 150 LOG(StorageAPI, "IDBDatabaseBackend::deleteObjectStore"); 151 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 152 if (!transaction) 153 return; 154 ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); 155 156 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 157 const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId); 158 159 transaction->scheduleDeleteObjectStoreOperation(objectStoreMetadata); 160 removeObjectStore(objectStoreId); 161} 162 163void IDBDatabaseBackend::createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry) 164{ 165 LOG(StorageAPI, "IDBDatabaseBackend::createIndex"); 166 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 167 if (!transaction) 168 return; 169 ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); 170 171 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 172 const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); 173 174 ASSERT(!objectStore.indexes.contains(indexId)); 175 const IDBIndexMetadata indexMetadata(name, indexId, keyPath, unique, multiEntry); 176 177 transaction->scheduleCreateIndexOperation(objectStoreId, indexMetadata); 178 179 addIndex(objectStoreId, indexMetadata, indexId); 180} 181 182void IDBDatabaseBackend::deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId) 183{ 184 LOG(StorageAPI, "IDBDatabaseBackend::deleteIndex"); 185 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 186 if (!transaction) 187 return; 188 ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); 189 190 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 191 const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId); 192 193 ASSERT(objectStore.indexes.contains(indexId)); 194 const IDBIndexMetadata& indexMetadata = objectStore.indexes.get(indexId); 195 196 transaction->scheduleDeleteIndexOperation(objectStoreId, indexMetadata); 197 198 removeIndex(objectStoreId, indexId); 199} 200 201void IDBDatabaseBackend::commit(int64_t transactionId) 202{ 203 // The frontend suggests that we commit, but we may have previously initiated an abort, and so have disposed of the transaction. onAbort has already been dispatched to the frontend, so it will find out about that asynchronously. 204 if (m_transactions.contains(transactionId)) 205 m_transactions.get(transactionId)->commit(); 206} 207 208void IDBDatabaseBackend::abort(int64_t transactionId) 209{ 210 // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it. 211 if (m_transactions.contains(transactionId)) 212 m_transactions.get(transactionId)->abort(); 213} 214 215void IDBDatabaseBackend::abort(int64_t transactionId, PassRefPtr<IDBDatabaseError> error) 216{ 217 // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it. 218 if (m_transactions.contains(transactionId)) 219 m_transactions.get(transactionId)->abort(error); 220} 221 222void IDBDatabaseBackend::get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, bool keyOnly, PassRefPtr<IDBCallbacks> callbacks) 223{ 224 LOG(StorageAPI, "IDBDatabaseBackend::get"); 225 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 226 if (!transaction) 227 return; 228 229 transaction->scheduleGetOperation(m_metadata, objectStoreId, indexId, keyRange, keyOnly ? IndexedDB::CursorType::KeyOnly : IndexedDB::CursorType::KeyAndValue, callbacks); 230} 231 232void IDBDatabaseBackend::put(int64_t transactionId, int64_t objectStoreId, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys) 233{ 234 LOG(StorageAPI, "IDBDatabaseBackend::put"); 235 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 236 if (!transaction) 237 return; 238 ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly); 239 240 const IDBObjectStoreMetadata objectStoreMetadata = m_metadata.objectStores.get(objectStoreId); 241 242 ASSERT(objectStoreMetadata.autoIncrement || key.get()); 243 244 transaction->schedulePutOperation(objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys); 245} 246 247void IDBDatabaseBackend::setIndexKeys(int64_t transactionID, int64_t objectStoreID, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIDs, const Vector<IndexKeys>& indexKeys) 248{ 249 LOG(StorageAPI, "IDBDatabaseBackend::setIndexKeys"); 250 ASSERT(prpPrimaryKey); 251 ASSERT(m_metadata.objectStores.contains(objectStoreID)); 252 253 RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionID); 254 if (!transaction) 255 return; 256 ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange); 257 258 RefPtr<IDBKey> primaryKey = prpPrimaryKey; 259 m_serverConnection->setIndexKeys(transactionID, m_metadata.id, objectStoreID, m_metadata.objectStores.get(objectStoreID), *primaryKey, indexIDs, indexKeys, [transaction](PassRefPtr<IDBDatabaseError> error) { 260 if (error) 261 transaction->abort(error); 262 }); 263} 264 265void IDBDatabaseBackend::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t>& indexIds) 266{ 267 LOG(StorageAPI, "IDBDatabaseBackend::setIndexesReady"); 268 269 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 270 if (!transaction) 271 return; 272 273 transaction->scheduleSetIndexesReadyOperation(indexIds.size()); 274} 275 276void IDBDatabaseBackend::openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, bool keyOnly, TaskType taskType, PassRefPtr<IDBCallbacks> callbacks) 277{ 278 LOG(StorageAPI, "IDBDatabaseBackend::openCursor"); 279 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 280 if (!transaction) 281 return; 282 283 transaction->scheduleOpenCursorOperation(objectStoreId, indexId, keyRange, direction, keyOnly ? IndexedDB::CursorType::KeyOnly : IndexedDB::CursorType::KeyAndValue, taskType, callbacks); 284} 285 286void IDBDatabaseBackend::count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks) 287{ 288 LOG(StorageAPI, "IDBDatabaseBackend::count"); 289 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 290 if (!transaction) 291 return; 292 293 ASSERT(m_metadata.objectStores.contains(objectStoreId)); 294 transaction->scheduleCountOperation(objectStoreId, indexId, keyRange, callbacks); 295} 296 297 298void IDBDatabaseBackend::deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks) 299{ 300 LOG(StorageAPI, "IDBDatabaseBackend::deleteRange"); 301 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 302 if (!transaction) 303 return; 304 ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly); 305 306 transaction->scheduleDeleteRangeOperation(objectStoreId, keyRange, callbacks); 307} 308 309void IDBDatabaseBackend::clearObjectStore(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks) 310{ 311 LOG(StorageAPI, "IDBDatabaseBackend::clearObjectStore %lli in transaction %lli", static_cast<long long>(objectStoreId), static_cast<long long>(transactionId)); 312 IDBTransactionBackend* transaction = m_transactions.get(transactionId); 313 if (!transaction) 314 return; 315 ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly); 316 317 transaction->scheduleClearObjectStoreOperation(objectStoreId, callbacks); 318} 319 320void IDBDatabaseBackend::transactionStarted(IDBTransactionBackend* transaction) 321{ 322 if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) { 323 ASSERT(!m_runningVersionChangeTransaction); 324 m_runningVersionChangeTransaction = transaction; 325 } 326} 327 328void IDBDatabaseBackend::transactionFinished(IDBTransactionBackend* rawTransaction) 329{ 330 RefPtr<IDBTransactionBackend> transaction = rawTransaction; 331 ASSERT(m_transactions.contains(transaction->id())); 332 ASSERT(m_transactions.get(transaction->id()) == transaction.get()); 333 m_transactions.remove(transaction->id()); 334 if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) { 335 ASSERT(transaction.get() == m_runningVersionChangeTransaction.get()); 336 m_runningVersionChangeTransaction.clear(); 337 } 338} 339 340void IDBDatabaseBackend::transactionFinishedAndAbortFired(IDBTransactionBackend* rawTransaction) 341{ 342 RefPtr<IDBTransactionBackend> transaction = rawTransaction; 343 if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) { 344 // If this was an open-with-version call, there will be a "second 345 // half" open call waiting for us in processPendingCalls. 346 // FIXME: When we no longer support setVersion, assert such a thing. 347 if (m_pendingSecondHalfOpen) { 348 m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "Version change transaction was aborted in upgradeneeded event handler.")); 349 m_pendingSecondHalfOpen = nullptr; 350 } 351 processPendingCalls(); 352 } 353} 354 355void IDBDatabaseBackend::transactionFinishedAndCompleteFired(IDBTransactionBackend* rawTransaction) 356{ 357 RefPtr<IDBTransactionBackend> transaction = rawTransaction; 358 if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) 359 processPendingCalls(); 360} 361 362size_t IDBDatabaseBackend::connectionCount() 363{ 364 // This does not include pending open calls, as those should not block version changes and deletes. 365 return m_databaseCallbacksSet.size(); 366} 367 368void IDBDatabaseBackend::processPendingCalls() 369{ 370 // processPendingCalls() will be called again after openInternalAsync() completes. 371 if (!m_didOpenInternal) 372 return; 373 374 if (m_pendingSecondHalfOpen) { 375 ASSERT(m_pendingSecondHalfOpen->version() == m_metadata.version); 376 ASSERT(m_metadata.id != InvalidId); 377 m_pendingSecondHalfOpen->callbacks()->onSuccess(this, this->metadata()); 378 m_pendingSecondHalfOpen = nullptr; 379 // Fall through when complete, as pending deletes may be (partially) unblocked. 380 } 381 382 // Note that this check is only an optimization to reduce queue-churn and 383 // not necessary for correctness; deleteDatabase and openConnection will 384 // requeue their calls if this condition is true. 385 if (m_runningVersionChangeTransaction) 386 return; 387 388 if (!m_pendingDeleteCalls.isEmpty() && isDeleteDatabaseBlocked()) 389 return; 390 while (!m_pendingDeleteCalls.isEmpty()) { 391 std::unique_ptr<IDBPendingDeleteCall> pendingDeleteCall = m_pendingDeleteCalls.takeFirst(); 392 m_deleteCallbacksWaitingCompletion.add(pendingDeleteCall->callbacks()); 393 deleteDatabaseAsync(pendingDeleteCall->callbacks()); 394 } 395 396 // deleteDatabaseAsync should never re-queue calls. 397 ASSERT(m_pendingDeleteCalls.isEmpty()); 398 399 // If there are any database deletions waiting for completion, we're done for now. 400 // Further callbacks will be handled in a future call to processPendingCalls(). 401 if (!m_deleteCallbacksWaitingCompletion.isEmpty()) 402 return; 403 404 if (m_runningVersionChangeTransaction) 405 return; 406 407 processPendingOpenCalls(true); 408} 409 410void IDBDatabaseBackend::processPendingOpenCalls(bool success) 411{ 412 // Open calls can be requeued if an open call started a version change transaction or deletes the database. 413 Deque<std::unique_ptr<IDBPendingOpenCall>> pendingOpenCalls; 414 m_pendingOpenCalls.swap(pendingOpenCalls); 415 416 while (!pendingOpenCalls.isEmpty()) { 417 std::unique_ptr<IDBPendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst(); 418 if (success) { 419 if (m_metadata.id == InvalidId) { 420 // This database was deleted then quickly re-opened. 421 // openInternalAsync() will recreate it in the backing store and then resume processing pending callbacks. 422 pendingOpenCalls.prepend(WTF::move(pendingOpenCall)); 423 pendingOpenCalls.swap(m_pendingOpenCalls); 424 425 openInternalAsync(); 426 return; 427 } 428 openConnectionInternal(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks(), pendingOpenCall->transactionId(), pendingOpenCall->version()); 429 } else { 430 String message; 431 if (pendingOpenCall->version() == IDBDatabaseMetadata::NoIntVersion) 432 message = "Internal error opening database with no version specified."; 433 else 434 message = String::format("Internal error opening database with version %llu", static_cast<unsigned long long>(pendingOpenCall->version())); 435 pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message)); 436 } 437 } 438} 439 440void IDBDatabaseBackend::createTransaction(int64_t transactionID, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIDs, IndexedDB::TransactionMode mode) 441{ 442 RefPtr<IDBTransactionBackend> transaction = IDBTransactionBackend::create(this, transactionID, callbacks, objectStoreIDs, mode); 443 444 ASSERT(!m_transactions.contains(transactionID)); 445 m_transactions.add(transactionID, transaction.get()); 446} 447 448void IDBDatabaseBackend::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version) 449{ 450 RefPtr<IDBCallbacks> callbacks = prpCallbacks; 451 RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks; 452 453 m_pendingOpenCalls.append(std::make_unique<IDBPendingOpenCall>(*callbacks, *databaseCallbacks, transactionId, version)); 454 455 processPendingCalls(); 456} 457 458void IDBDatabaseBackend::openConnectionInternal(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version) 459{ 460 ASSERT(m_pendingDeleteCalls.isEmpty()); 461 ASSERT(!m_runningVersionChangeTransaction); 462 463 RefPtr<IDBCallbacks> callbacks = prpCallbacks; 464 RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks; 465 466 // We infer that the database didn't exist from its lack of version. 467 bool isNewDatabase = m_metadata.version == IDBDatabaseMetadata::NoIntVersion; 468 469 if (version == IDBDatabaseMetadata::DefaultIntVersion && !isNewDatabase) { 470 m_databaseCallbacksSet.add(databaseCallbacks); 471 callbacks->onSuccess(this, this->metadata()); 472 return; 473 } 474 475 if (isNewDatabase && version == IDBDatabaseMetadata::DefaultIntVersion) { 476 // Spec says: If no version is specified and no database exists, set database version to 1. 477 version = 1; 478 } 479 480 if (version > m_metadata.version || m_metadata.version == IDBDatabaseMetadata::NoIntVersion) { 481 runIntVersionChangeTransaction(callbacks, databaseCallbacks, transactionId, version); 482 return; 483 } 484 485 if (version < m_metadata.version && m_metadata.version != IDBDatabaseMetadata::NoIntVersion) { 486 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::VersionError, String::format("The requested version (%llu) is less than the existing version (%llu).", static_cast<unsigned long long>(version), static_cast<unsigned long long>(m_metadata.version)))); 487 return; 488 } 489 490 ASSERT(version == m_metadata.version); 491 m_databaseCallbacksSet.add(databaseCallbacks); 492 callbacks->onSuccess(this, this->metadata()); 493} 494 495void IDBDatabaseBackend::runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion) 496{ 497 RefPtr<IDBCallbacks> callbacks = prpCallbacks; 498 RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks; 499 ASSERT(callbacks); 500 for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) { 501 // Front end ensures the event is not fired at connections that have closePending set. 502 if (*it != databaseCallbacks) 503 (*it)->onVersionChange(m_metadata.version, requestedVersion, IndexedDB::VersionNullness::Null); 504 } 505 // The spec dictates we wait until all the version change events are 506 // delivered and then check m_databaseCallbacks.empty() before proceeding 507 // or firing a blocked event, but instead we should be consistent with how 508 // the old setVersion (incorrectly) did it. 509 // FIXME: Remove the call to onBlocked and instead wait until the frontend 510 // tells us that all the blocked events have been delivered. See 511 // https://bugs.webkit.org/show_bug.cgi?id=71130 512 if (connectionCount()) 513 callbacks->onBlocked(m_metadata.version); 514 // FIXME: Add test for m_runningVersionChangeTransaction. 515 if (m_runningVersionChangeTransaction || connectionCount()) { 516 m_pendingOpenCalls.append(std::make_unique<IDBPendingOpenCall>(*callbacks, *databaseCallbacks, transactionId, requestedVersion)); 517 return; 518 } 519 520 Vector<int64_t> objectStoreIds; 521 createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionMode::VersionChange); 522 RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionId); 523 524 transaction->scheduleVersionChangeOperation(requestedVersion, callbacks, databaseCallbacks, m_metadata); 525 526 ASSERT(!m_pendingSecondHalfOpen); 527 m_databaseCallbacksSet.add(databaseCallbacks); 528} 529 530void IDBDatabaseBackend::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks) 531{ 532 RefPtr<IDBCallbacks> callbacks = prpCallbacks; 533 if (isDeleteDatabaseBlocked()) { 534 for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) { 535 // Front end ensures the event is not fired at connections that have closePending set. 536 (*it)->onVersionChange(m_metadata.version, 0, IndexedDB::VersionNullness::Null); 537 } 538 // FIXME: Only fire onBlocked if there are open connections after the 539 // VersionChangeEvents are received, not just set up to fire. 540 // https://bugs.webkit.org/show_bug.cgi?id=71130 541 callbacks->onBlocked(m_metadata.version); 542 m_pendingDeleteCalls.append(std::make_unique<IDBPendingDeleteCall>(callbacks.release())); 543 return; 544 } 545 deleteDatabaseAsync(callbacks.release()); 546} 547 548bool IDBDatabaseBackend::isDeleteDatabaseBlocked() 549{ 550 return connectionCount(); 551} 552 553void IDBDatabaseBackend::deleteDatabaseAsync(PassRefPtr<IDBCallbacks> prpCallbacks) 554{ 555 ASSERT(!isDeleteDatabaseBlocked()); 556 557 RefPtr<IDBDatabaseBackend> self(this); 558 RefPtr<IDBCallbacks> callbacks = prpCallbacks; 559 m_serverConnection->deleteDatabase(m_metadata.name, [self, callbacks](bool success) { 560 self->m_deleteCallbacksWaitingCompletion.remove(callbacks); 561 562 // If this IDBDatabaseBackend was closed while waiting for deleteDatabase to complete, no point in performing any callbacks. 563 if (!self->m_serverConnection->isClosed()) 564 return; 565 566 if (success) { 567 self->m_metadata.id = InvalidId; 568 self->m_metadata.version = IDBDatabaseMetadata::NoIntVersion; 569 self->m_metadata.objectStores.clear(); 570 callbacks->onSuccess(); 571 } else 572 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error deleting database.")); 573 574 self->processPendingCalls(); 575 }); 576} 577 578void IDBDatabaseBackend::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks) 579{ 580 RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks; 581 ASSERT(m_databaseCallbacksSet.contains(callbacks)); 582 583 m_databaseCallbacksSet.remove(callbacks); 584 if (m_pendingSecondHalfOpen && m_pendingSecondHalfOpen->databaseCallbacks() == callbacks) { 585 m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed.")); 586 m_pendingSecondHalfOpen = nullptr; 587 } 588 589 if (connectionCount() > 1) 590 return; 591 592 // processPendingCalls allows the inspector to process a pending open call 593 // and call close, reentering IDBDatabaseBackend::close. Then the 594 // backend would be removed both by the inspector closing its connection, and 595 // by the connection that first called close. 596 // To avoid that situation, don't proceed in case of reentrancy. 597 if (m_closingConnection) 598 return; 599 TemporaryChange<bool> closingConnection(m_closingConnection, true); 600 processPendingCalls(); 601 602 // FIXME: Add a test for the m_pendingOpenCalls cases below. 603 if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingDeleteCalls.size()) { 604 TransactionMap transactions(m_transactions); 605 RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Connection is closing."); 606 for (TransactionMap::const_iterator::Values it = transactions.values().begin(), end = transactions.values().end(); it != end; ++it) 607 (*it)->abort(error); 608 609 ASSERT(m_transactions.isEmpty()); 610 611 m_serverConnection->close(); 612 613 // This check should only be false in unit tests. 614 ASSERT(m_factory); 615 if (m_factory) 616 m_factory->removeIDBDatabaseBackend(m_identifier); 617 } 618} 619 620} // namespace WebCore 621 622#endif // ENABLE(INDEXED_DATABASE) 623