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