Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / legacy / IDBDatabaseBackend.cpp
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
44 namespace WebCore {
45
46 Ref<IDBDatabaseBackend> IDBDatabaseBackend::create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection)
47 {
48     Ref<IDBDatabaseBackend> backend = adoptRef(*new IDBDatabaseBackend(name, uniqueIdentifier, factory, serverConnection));
49     backend->openInternalAsync();
50     
51     return backend;
52 }
53
54 IDBDatabaseBackend::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
66 void 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
76 void IDBDatabaseBackend::removeObjectStore(int64_t objectStoreId)
77 {
78     ASSERT(m_metadata.objectStores.contains(objectStoreId));
79     m_metadata.objectStores.remove(objectStoreId);
80 }
81
82 void 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
96 void 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
106 void 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
114 void 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
128 IDBDatabaseBackend::~IDBDatabaseBackend()
129 {
130     m_factory->removeIDBDatabaseBackend(m_identifier);
131 }
132
133 void 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
148 void 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
163 void 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
182 void 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
201 void 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
208 void 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
215 void 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
222 void 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
232 void 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
247 void IDBDatabaseBackend::setIndexKeys(int64_t transactionID, int64_t objectStoreID, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t, 1>& indexIDs, const Vector<IndexKeys, 1>& 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
265 void IDBDatabaseBackend::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t, 1>& 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
276 void 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
286 void 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
298 void 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
309 void 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
320 void IDBDatabaseBackend::transactionStarted(IDBTransactionBackend* transaction)
321 {
322     if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) {
323         ASSERT(!m_runningVersionChangeTransaction);
324         m_runningVersionChangeTransaction = transaction;
325     }
326 }
327
328 void 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 = nullptr;
337     }
338 }
339
340 void 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
355 void IDBDatabaseBackend::transactionFinishedAndCompleteFired(IDBTransactionBackend* rawTransaction)
356 {
357     RefPtr<IDBTransactionBackend> transaction = rawTransaction;
358     if (transaction->mode() == IndexedDB::TransactionMode::VersionChange)
359         processPendingCalls();
360 }
361
362 size_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
368 void 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
410 void 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(WTFMove(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 %" PRIu64, pendingOpenCall->version());
435             pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
436         }
437     }
438 }
439
440 void 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
448 void 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
458 void 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 (%" PRIu64 ") is less than the existing version (%" PRIu64 ").", version, 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
495 void 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 (auto& callback : m_databaseCallbacksSet) {
501         // Front end ensures the event is not fired at connections that have closePending set.
502         if (callback != databaseCallbacks)
503             callback->onVersionChange(m_metadata.version, requestedVersion);
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
530 void IDBDatabaseBackend::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
531 {
532     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
533     if (isDeleteDatabaseBlocked()) {
534         for (auto& callback : m_databaseCallbacksSet) {
535             // Front end ensures the event is not fired at connections that have closePending set.
536             callback->onVersionChange(m_metadata.version, 0);
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
548 bool IDBDatabaseBackend::isDeleteDatabaseBlocked()
549 {
550     return connectionCount();
551 }
552
553 void 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
578 void 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 (auto& transaction : transactions.values())
607             transaction->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)