2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "IDBDatabaseBackendImpl.h"
29 #if ENABLE(INDEXED_DATABASE)
31 #include "IDBBackingStoreInterface.h"
32 #include "IDBCursorBackend.h"
33 #include "IDBDatabaseCallbacks.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBFactoryBackendInterface.h"
36 #include "IDBIndexWriter.h"
37 #include "IDBKeyRange.h"
38 #include "IDBRecordIdentifier.h"
39 #include "IDBTransactionBackendInterface.h"
40 #include "IDBTransactionCoordinator.h"
42 #include "SharedBuffer.h"
43 #include <wtf/TemporaryChange.h>
47 PassRefPtr<IDBDatabaseBackendImpl> IDBDatabaseBackendImpl::create(const String& name, IDBBackingStoreInterface* backingStore, IDBFactoryBackendInterface* factory, const String& uniqueIdentifier)
49 RefPtr<IDBDatabaseBackendImpl> backend = adoptRef(new IDBDatabaseBackendImpl(name, backingStore, factory, uniqueIdentifier));
50 backend->openInternalAsync();
52 return backend.release();
55 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStoreInterface* backingStore, IDBFactoryBackendInterface* factory, const String& uniqueIdentifier)
56 : m_backingStore(backingStore)
57 , m_metadata(name, InvalidId, 0, InvalidId)
58 , m_identifier(uniqueIdentifier)
60 , m_transactionCoordinator(IDBTransactionCoordinator::create())
61 , m_closingConnection(false)
63 ASSERT(!m_metadata.name.isNull());
66 void IDBDatabaseBackendImpl::addObjectStore(const IDBObjectStoreMetadata& objectStore, int64_t newMaxObjectStoreId)
68 ASSERT(!m_metadata.objectStores.contains(objectStore.id));
69 if (newMaxObjectStoreId != IDBObjectStoreMetadata::InvalidId) {
70 ASSERT(m_metadata.maxObjectStoreId < newMaxObjectStoreId);
71 m_metadata.maxObjectStoreId = newMaxObjectStoreId;
73 m_metadata.objectStores.set(objectStore.id, objectStore);
76 void IDBDatabaseBackendImpl::removeObjectStore(int64_t objectStoreId)
78 ASSERT(m_metadata.objectStores.contains(objectStoreId));
79 m_metadata.objectStores.remove(objectStoreId);
82 void IDBDatabaseBackendImpl::addIndex(int64_t objectStoreId, const IDBIndexMetadata& index, int64_t newMaxIndexId)
84 ASSERT(m_metadata.objectStores.contains(objectStoreId));
85 IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
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;
93 m_metadata.objectStores.set(objectStoreId, objectStore);
96 void IDBDatabaseBackendImpl::removeIndex(int64_t objectStoreId, int64_t indexId)
98 ASSERT(m_metadata.objectStores.contains(objectStoreId));
99 IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
101 ASSERT(objectStore.indexes.contains(indexId));
102 objectStore.indexes.remove(indexId);
103 m_metadata.objectStores.set(objectStoreId, objectStore);
106 void IDBDatabaseBackendImpl::openInternalAsync()
108 RefPtr<IDBDatabaseBackendImpl> self = this;
109 m_backingStore->getOrEstablishIDBDatabaseMetadata(m_metadata.name, [self](const IDBDatabaseMetadata& metadata, bool success) {
110 self->didOpenInternalAsync(metadata, success);
114 void IDBDatabaseBackendImpl::didOpenInternalAsync(const IDBDatabaseMetadata& metadata, bool success)
117 processPendingOpenCalls(false);
121 m_metadata = metadata;
123 processPendingCalls();
126 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
130 IDBBackingStoreInterface* IDBDatabaseBackendImpl::backingStore() const
132 return m_backingStore.get();
135 void IDBDatabaseBackendImpl::createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
137 LOG(StorageAPI, "IDBDatabaseBackendImpl::createObjectStore");
138 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
141 ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
143 ASSERT(!m_metadata.objectStores.contains(objectStoreId));
144 IDBObjectStoreMetadata objectStoreMetadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackendInterface::MinimumIndexId);
146 transaction->scheduleCreateObjectStoreOperation(objectStoreMetadata);
147 addObjectStore(objectStoreMetadata, objectStoreId);
150 void IDBDatabaseBackendImpl::deleteObjectStore(int64_t transactionId, int64_t objectStoreId)
152 LOG(StorageAPI, "IDBDatabaseBackendImpl::deleteObjectStore");
153 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
156 ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
158 ASSERT(m_metadata.objectStores.contains(objectStoreId));
159 const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
161 transaction->scheduleDeleteObjectStoreOperation(objectStoreMetadata);
162 removeObjectStore(objectStoreId);
165 void IDBDatabaseBackendImpl::createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry)
167 LOG(StorageAPI, "IDBDatabaseBackendImpl::createIndex");
168 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
171 ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
173 ASSERT(m_metadata.objectStores.contains(objectStoreId));
174 const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
176 ASSERT(!objectStore.indexes.contains(indexId));
177 const IDBIndexMetadata indexMetadata(name, indexId, keyPath, unique, multiEntry);
179 transaction->scheduleCreateIndexOperation(objectStoreId, indexMetadata);
181 addIndex(objectStoreId, indexMetadata, indexId);
184 void IDBDatabaseBackendImpl::deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId)
186 LOG(StorageAPI, "IDBDatabaseBackendImpl::deleteIndex");
187 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
190 ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
192 ASSERT(m_metadata.objectStores.contains(objectStoreId));
193 const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
195 ASSERT(objectStore.indexes.contains(indexId));
196 const IDBIndexMetadata& indexMetadata = objectStore.indexes.get(indexId);
198 transaction->scheduleDeleteIndexOperation(objectStoreId, indexMetadata);
200 removeIndex(objectStoreId, indexId);
203 void IDBDatabaseBackendImpl::commit(int64_t transactionId)
205 // 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.
206 if (m_transactions.contains(transactionId))
207 m_transactions.get(transactionId)->commit();
210 void IDBDatabaseBackendImpl::abort(int64_t transactionId)
212 // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it.
213 if (m_transactions.contains(transactionId))
214 m_transactions.get(transactionId)->abort();
217 void IDBDatabaseBackendImpl::abort(int64_t transactionId, PassRefPtr<IDBDatabaseError> error)
219 // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it.
220 if (m_transactions.contains(transactionId))
221 m_transactions.get(transactionId)->abort(error);
224 void IDBDatabaseBackendImpl::get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, bool keyOnly, PassRefPtr<IDBCallbacks> callbacks)
226 LOG(StorageAPI, "IDBDatabaseBackendImpl::get");
227 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
231 transaction->scheduleGetOperation(m_metadata, objectStoreId, indexId, keyRange, keyOnly ? IndexedDB::CursorKeyOnly : IndexedDB::CursorKeyAndValue, callbacks);
234 void IDBDatabaseBackendImpl::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)
236 LOG(StorageAPI, "IDBDatabaseBackendImpl::put");
237 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
240 ASSERT(transaction->mode() != IndexedDB::TransactionReadOnly);
242 const IDBObjectStoreMetadata objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
244 ASSERT(objectStoreMetadata.autoIncrement || key.get());
246 transaction->schedulePutOperation(objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys);
249 void IDBDatabaseBackendImpl::setIndexKeys(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
251 LOG(StorageAPI, "IDBDatabaseBackendImpl::setIndexKeys");
252 ASSERT(prpPrimaryKey);
254 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
257 ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
259 RefPtr<IDBKey> primaryKey = prpPrimaryKey;
260 RefPtr<IDBBackingStoreInterface> store = m_backingStore;
261 // FIXME: This method could be asynchronous, but we need to evaluate if it's worth the extra complexity.
262 RefPtr<IDBRecordIdentifier> recordIdentifier;
263 bool ok = store->keyExistsInObjectStore(transaction->backingStoreTransaction(), m_metadata.id, objectStoreId, *primaryKey, recordIdentifier);
265 transaction->abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error setting index keys."));
268 if (!recordIdentifier) {
269 RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error setting index keys for object store."));
270 transaction->abort(error.release());
274 Vector<RefPtr<IDBIndexWriter>> indexWriters;
276 bool obeysConstraints = false;
277 ASSERT(m_metadata.objectStores.contains(objectStoreId));
278 const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
279 bool backingStoreSuccess = store->makeIndexWriters(*transaction, id(), objectStoreMetadata, *primaryKey, false, indexIds, indexKeys, indexWriters, &errorMessage, obeysConstraints);
280 if (!backingStoreSuccess) {
281 transaction->abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error updating index keys."));
284 if (!obeysConstraints) {
285 transaction->abort(IDBDatabaseError::create(IDBDatabaseException::ConstraintError, errorMessage));
289 for (size_t i = 0; i < indexWriters.size(); ++i) {
290 IDBIndexWriter* indexWriter = indexWriters[i].get();
291 indexWriter->writeIndexKeys(recordIdentifier.get(), *store.get(), transaction->backingStoreTransaction(), id(), objectStoreId);
295 void IDBDatabaseBackendImpl::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t>& indexIds)
297 LOG(StorageAPI, "IDBDatabaseBackendImpl::setIndexesReady");
299 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
303 transaction->scheduleSetIndexesReadyOperation(indexIds.size());
306 void IDBDatabaseBackendImpl::openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, bool keyOnly, TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
308 LOG(StorageAPI, "IDBDatabaseBackendImpl::openCursor");
309 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
313 transaction->scheduleOpenCursorOperation(objectStoreId, indexId, keyRange, direction, keyOnly ? IndexedDB::CursorKeyOnly : IndexedDB::CursorKeyAndValue, taskType, callbacks);
316 void IDBDatabaseBackendImpl::count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
318 LOG(StorageAPI, "IDBDatabaseBackendImpl::count");
319 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
323 ASSERT(m_metadata.objectStores.contains(objectStoreId));
324 transaction->scheduleCountOperation(objectStoreId, indexId, keyRange, callbacks);
328 void IDBDatabaseBackendImpl::deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
330 LOG(StorageAPI, "IDBDatabaseBackendImpl::deleteRange");
331 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
334 ASSERT(transaction->mode() != IndexedDB::TransactionReadOnly);
336 transaction->scheduleDeleteRangeOperation(objectStoreId, keyRange, callbacks);
339 void IDBDatabaseBackendImpl::clear(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
341 LOG(StorageAPI, "IDBDatabaseBackendImpl::clear");
342 IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
345 ASSERT(transaction->mode() != IndexedDB::TransactionReadOnly);
347 transaction->scheduleClearOperation(objectStoreId, callbacks);
350 void IDBDatabaseBackendImpl::transactionStarted(IDBTransactionBackendInterface* transaction)
352 if (transaction->mode() == IndexedDB::TransactionVersionChange) {
353 ASSERT(!m_runningVersionChangeTransaction);
354 m_runningVersionChangeTransaction = transaction;
358 void IDBDatabaseBackendImpl::transactionFinished(IDBTransactionBackendInterface* rawTransaction)
360 RefPtr<IDBTransactionBackendInterface> transaction = rawTransaction;
361 ASSERT(m_transactions.contains(transaction->id()));
362 ASSERT(m_transactions.get(transaction->id()) == transaction.get());
363 m_transactions.remove(transaction->id());
364 if (transaction->mode() == IndexedDB::TransactionVersionChange) {
365 ASSERT(transaction.get() == m_runningVersionChangeTransaction.get());
366 m_runningVersionChangeTransaction.clear();
370 void IDBDatabaseBackendImpl::transactionFinishedAndAbortFired(IDBTransactionBackendInterface* rawTransaction)
372 RefPtr<IDBTransactionBackendInterface> transaction = rawTransaction;
373 if (transaction->mode() == IndexedDB::TransactionVersionChange) {
374 // If this was an open-with-version call, there will be a "second
375 // half" open call waiting for us in processPendingCalls.
376 // FIXME: When we no longer support setVersion, assert such a thing.
377 if (m_pendingSecondHalfOpen) {
378 m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "Version change transaction was aborted in upgradeneeded event handler."));
379 m_pendingSecondHalfOpen.release();
381 processPendingCalls();
385 void IDBDatabaseBackendImpl::transactionFinishedAndCompleteFired(IDBTransactionBackendInterface* rawTransaction)
387 RefPtr<IDBTransactionBackendInterface> transaction = rawTransaction;
388 if (transaction->mode() == IndexedDB::TransactionVersionChange)
389 processPendingCalls();
392 size_t IDBDatabaseBackendImpl::connectionCount()
394 // This does not include pending open calls, as those should not block version changes and deletes.
395 return m_databaseCallbacksSet.size();
398 void IDBDatabaseBackendImpl::processPendingCalls()
400 if (m_pendingSecondHalfOpen) {
401 ASSERT(m_pendingSecondHalfOpen->version() == m_metadata.version);
402 ASSERT(m_metadata.id != InvalidId);
403 m_pendingSecondHalfOpen->callbacks()->onSuccess(this, this->metadata());
404 m_pendingSecondHalfOpen.release();
405 // Fall through when complete, as pending deletes may be (partially) unblocked.
408 // Note that this check is only an optimization to reduce queue-churn and
409 // not necessary for correctness; deleteDatabase and openConnection will
410 // requeue their calls if this condition is true.
411 if (m_runningVersionChangeTransaction)
414 if (!m_pendingDeleteCalls.isEmpty() && isDeleteDatabaseBlocked())
416 while (!m_pendingDeleteCalls.isEmpty()) {
417 OwnPtr<IDBPendingDeleteCall> pendingDeleteCall = m_pendingDeleteCalls.takeFirst();
418 m_deleteCallbacksWaitingCompletion.add(pendingDeleteCall->callbacks());
419 deleteDatabaseAsync(pendingDeleteCall->callbacks());
422 // deleteDatabaseAsync should never re-queue calls.
423 ASSERT(m_pendingDeleteCalls.isEmpty());
425 // If there are any database deletions waiting for completion, we're done for now.
426 // Further callbacks will be handled in a future call to processPendingCalls().
427 if (m_deleteCallbacksWaitingCompletion.isEmpty())
430 if (m_runningVersionChangeTransaction)
433 processPendingOpenCalls(true);
436 void IDBDatabaseBackendImpl::processPendingOpenCalls(bool success)
438 // Open calls can be requeued if an open call started a version change transaction or deletes the database.
439 Deque<OwnPtr<IDBPendingOpenCall>> pendingOpenCalls;
440 m_pendingOpenCalls.swap(pendingOpenCalls);
442 while (!pendingOpenCalls.isEmpty()) {
443 OwnPtr<IDBPendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst();
445 if (m_metadata.id == InvalidId) {
446 // This database was deleted then quickly re-opened.
447 // openInternalAsync() will recreate it in the backing store and then resume processing pending callbacks.
448 pendingOpenCalls.prepend(pendingOpenCall.release());
449 pendingOpenCalls.swap(m_pendingOpenCalls);
454 openConnectionInternal(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks(), pendingOpenCall->transactionId(), pendingOpenCall->version());
457 if (pendingOpenCall->version() == IDBDatabaseMetadata::NoIntVersion)
458 message = "Internal error opening database with no version specified.";
460 message = String::format("Internal error opening database with version %llu", static_cast<unsigned long long>(pendingOpenCall->version()));
461 pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
466 void IDBDatabaseBackendImpl::createTransaction(int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, unsigned short mode)
468 RefPtr<IDBTransactionBackendInterface> transaction = m_factory->maybeCreateTransactionBackend(this, transactionId, callbacks, objectStoreIds, static_cast<IndexedDB::TransactionMode>(mode));
473 ASSERT(!m_transactions.contains(transactionId));
474 m_transactions.add(transactionId, transaction.get());
477 void IDBDatabaseBackendImpl::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
479 RefPtr<IDBCallbacks> callbacks = prpCallbacks;
480 RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
482 m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version));
484 processPendingCalls();
488 void IDBDatabaseBackendImpl::openConnectionInternal(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
490 ASSERT(m_backingStore.get());
491 ASSERT(m_pendingDeleteCalls.isEmpty());
492 ASSERT(!m_runningVersionChangeTransaction);
494 RefPtr<IDBCallbacks> callbacks = prpCallbacks;
495 RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
497 // We infer that the database didn't exist from its lack of either type of version.
498 bool isNewDatabase = m_metadata.version == IDBDatabaseMetadata::NoIntVersion;
500 if (version == IDBDatabaseMetadata::DefaultIntVersion) {
501 // FIXME: this comments was related to Chromium code. It may be incorrect
502 // For unit tests only - skip upgrade steps. Calling from script with DefaultIntVersion throws exception.
503 ASSERT(isNewDatabase);
504 m_databaseCallbacksSet.add(databaseCallbacks);
505 callbacks->onSuccess(this, this->metadata());
509 if (version == IDBDatabaseMetadata::NoIntVersion) {
510 if (!isNewDatabase) {
511 m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(databaseCallbacks));
512 callbacks->onSuccess(this, this->metadata());
515 // Spec says: If no version is specified and no database exists, set database version to 1.
519 if (version > m_metadata.version) {
520 runIntVersionChangeTransaction(callbacks, databaseCallbacks, transactionId, version);
524 if (version < m_metadata.version) {
525 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))));
529 ASSERT(version == m_metadata.version);
530 m_databaseCallbacksSet.add(databaseCallbacks);
531 callbacks->onSuccess(this, this->metadata());
534 void IDBDatabaseBackendImpl::runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion)
536 RefPtr<IDBCallbacks> callbacks = prpCallbacks;
537 RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
539 for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
540 // Front end ensures the event is not fired at connections that have closePending set.
541 if (*it != databaseCallbacks)
542 (*it)->onVersionChange(m_metadata.version, requestedVersion, IndexedDB::NullVersion);
544 // The spec dictates we wait until all the version change events are
545 // delivered and then check m_databaseCallbacks.empty() before proceeding
546 // or firing a blocked event, but instead we should be consistent with how
547 // the old setVersion (incorrectly) did it.
548 // FIXME: Remove the call to onBlocked and instead wait until the frontend
549 // tells us that all the blocked events have been delivered. See
550 // https://bugs.webkit.org/show_bug.cgi?id=71130
551 if (connectionCount())
552 callbacks->onBlocked(m_metadata.version);
553 // FIXME: Add test for m_runningVersionChangeTransaction.
554 if (m_runningVersionChangeTransaction || connectionCount()) {
555 m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, requestedVersion));
559 Vector<int64_t> objectStoreIds;
560 createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionVersionChange);
561 RefPtr<IDBTransactionBackendInterface> transaction = m_transactions.get(transactionId);
563 transaction->scheduleVersionChangeOperation(transactionId, requestedVersion, callbacks, databaseCallbacks, m_metadata);
565 ASSERT(!m_pendingSecondHalfOpen);
566 m_databaseCallbacksSet.add(databaseCallbacks);
569 void IDBDatabaseBackendImpl::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
571 RefPtr<IDBCallbacks> callbacks = prpCallbacks;
572 if (isDeleteDatabaseBlocked()) {
573 for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
574 // Front end ensures the event is not fired at connections that have closePending set.
575 (*it)->onVersionChange(m_metadata.version, 0, IndexedDB::NullVersion);
577 // FIXME: Only fire onBlocked if there are open connections after the
578 // VersionChangeEvents are received, not just set up to fire.
579 // https://bugs.webkit.org/show_bug.cgi?id=71130
580 callbacks->onBlocked(m_metadata.version);
581 m_pendingDeleteCalls.append(IDBPendingDeleteCall::create(callbacks.release()));
584 deleteDatabaseAsync(callbacks.release());
587 bool IDBDatabaseBackendImpl::isDeleteDatabaseBlocked()
589 return connectionCount();
592 void IDBDatabaseBackendImpl::deleteDatabaseAsync(PassRefPtr<IDBCallbacks> callbacks)
594 ASSERT(!isDeleteDatabaseBlocked());
595 ASSERT(m_backingStore);
597 RefPtr<IDBDatabaseBackendImpl> self(this);
598 m_backingStore->deleteDatabase(m_metadata.name, [self, callbacks](bool success) {
599 ASSERT(self->m_deleteCallbacksWaitingCompletion.contains(callbacks));
600 self->m_deleteCallbacksWaitingCompletion.remove(callbacks);
602 // If this IDBDatabaseBackend was closed while waiting for deleteDatabase to complete, no point in performing any callbacks.
603 if (!self->m_backingStore)
607 self->m_metadata.id = InvalidId;
608 self->m_metadata.version = IDBDatabaseMetadata::NoIntVersion;
609 self->m_metadata.objectStores.clear();
610 callbacks->onSuccess();
612 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error deleting database."));
614 self->processPendingCalls();
618 void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
620 RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
621 ASSERT(m_databaseCallbacksSet.contains(callbacks));
623 m_databaseCallbacksSet.remove(callbacks);
624 if (m_pendingSecondHalfOpen && m_pendingSecondHalfOpen->databaseCallbacks() == callbacks) {
625 m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed."));
626 m_pendingSecondHalfOpen.release();
629 if (connectionCount() > 1)
632 // processPendingCalls allows the inspector to process a pending open call
633 // and call close, reentering IDBDatabaseBackendImpl::close. Then the
634 // backend would be removed both by the inspector closing its connection, and
635 // by the connection that first called close.
636 // To avoid that situation, don't proceed in case of reentrancy.
637 if (m_closingConnection)
639 TemporaryChange<bool> closingConnection(m_closingConnection, true);
640 processPendingCalls();
642 // FIXME: Add a test for the m_pendingOpenCalls cases below.
643 if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingDeleteCalls.size()) {
644 TransactionMap transactions(m_transactions);
645 RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Connection is closing.");
646 for (TransactionMap::const_iterator::Values it = transactions.values().begin(), end = transactions.values().end(); it != end; ++it)
649 ASSERT(m_transactions.isEmpty());
651 m_backingStore.clear();
653 // This check should only be false in unit tests.
656 m_factory->removeIDBDatabaseBackend(m_identifier);
660 } // namespace WebCore
662 #endif // ENABLE(INDEXED_DATABASE)