37b2253b77be954205c011de192d95f16d1b0f2a
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / server / UniqueIDBDatabase.cpp
1 /*
2  * Copyright (C) 2015 Apple 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "UniqueIDBDatabase.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBResultData.h"
32 #include "IDBServer.h"
33 #include "IDBTransactionInfo.h"
34 #include "Logging.h"
35 #include "UniqueIDBDatabaseConnection.h"
36 #include <wtf/MainThread.h>
37 #include <wtf/ThreadSafeRefCounted.h>
38
39 namespace WebCore {
40 namespace IDBServer {
41     
42 UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier)
43     : m_server(server)
44     , m_identifier(identifier)
45     , m_transactionSchedulingTimer(*this, &UniqueIDBDatabase::transactionSchedulingTimerFired)
46 {
47 }
48
49 const IDBDatabaseInfo& UniqueIDBDatabase::info() const
50 {
51     RELEASE_ASSERT(m_databaseInfo);
52     return *m_databaseInfo;
53 }
54
55 void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData)
56 {
57     auto operation = IDBServerOperation::create(connection, requestData);
58     m_pendingOpenDatabaseOperations.append(WTF::move(operation));
59
60     if (m_databaseInfo) {
61         handleOpenDatabaseOperations();
62         return;
63     }
64     
65     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
66 }
67
68 void UniqueIDBDatabase::handleOpenDatabaseOperations()
69 {
70     ASSERT(isMainThread());
71     LOG(IndexedDB, "(main) UniqueIDBDatabase::handleOpenDatabaseOperations");
72
73     // If a version change transaction is currently in progress, no new connections can be opened right now.
74     // We will try again later.
75     if (m_versionChangeDatabaseConnection)
76         return;
77
78     auto operation = m_pendingOpenDatabaseOperations.takeFirst();
79
80     // 3.3.1 Opening a database
81     // If requested version is undefined, then let requested version be 1 if db was created in the previous step,
82     // or the current version of db otherwise.
83     uint64_t requestedVersion = operation->requestData().requestedVersion();
84     if (!requestedVersion)
85         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
86
87     // 3.3.1 Opening a database
88     // If the database version higher than the requested version, abort these steps and return a VersionError.
89     if (requestedVersion < m_databaseInfo->version()) {
90         auto result = IDBResultData::error(operation->requestData().requestIdentifier(), IDBError(IDBExceptionCode::VersionError));
91         operation->connection().didOpenDatabase(result);
92         return;
93     }
94
95     Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, operation->connection());
96     UniqueIDBDatabaseConnection* rawConnection = &connection.get();
97
98     if (requestedVersion == m_databaseInfo->version()) {
99         addOpenDatabaseConnection(WTF::move(connection));
100
101         auto result = IDBResultData::openDatabaseSuccess(operation->requestData().requestIdentifier(), *rawConnection);
102         operation->connection().didOpenDatabase(result);
103         return;
104     }
105
106     ASSERT(!m_versionChangeOperation);
107     ASSERT(!m_versionChangeDatabaseConnection);
108
109     m_versionChangeOperation = adoptRef(operation.leakRef());
110     m_versionChangeDatabaseConnection = rawConnection;
111
112     // 3.3.7 "versionchange" transaction steps
113     // If there's no other open connections to this database, the version change process can begin immediately.
114     if (!hasAnyOpenConnections()) {
115         startVersionChangeTransaction();
116         return;
117     }
118
119     // Otherwise we have to notify all those open connections and wait for them to close.
120     notifyConnectionsOfVersionChange();
121 }
122
123 bool UniqueIDBDatabase::hasAnyOpenConnections() const
124 {
125     return !m_openDatabaseConnections.isEmpty();
126 }
127
128 static uint64_t generateUniqueCallbackIdentifier()
129 {
130     ASSERT(isMainThread());
131     static uint64_t currentID = 0;
132     return ++currentID;
133 }
134
135 uint64_t UniqueIDBDatabase::storeCallback(ErrorCallback callback)
136 {
137     uint64_t identifier = generateUniqueCallbackIdentifier();
138     m_errorCallbacks.set(identifier, callback);
139     return identifier;
140 }
141
142 uint64_t UniqueIDBDatabase::storeCallback(KeyDataCallback callback)
143 {
144     uint64_t identifier = generateUniqueCallbackIdentifier();
145     m_keyDataCallbacks.set(identifier, callback);
146     return identifier;
147 }
148
149 uint64_t UniqueIDBDatabase::storeCallback(ValueDataCallback callback)
150 {
151     uint64_t identifier = generateUniqueCallbackIdentifier();
152     m_valueDataCallbacks.set(identifier, callback);
153     return identifier;
154 }
155
156 void UniqueIDBDatabase::startVersionChangeTransaction()
157 {
158     LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
159
160     ASSERT(!m_versionChangeTransaction);
161     ASSERT(m_versionChangeOperation);
162     ASSERT(m_versionChangeDatabaseConnection);
163
164     auto operation = m_versionChangeOperation;
165     m_versionChangeOperation = nullptr;
166
167     uint64_t requestedVersion = operation->requestData().requestedVersion();
168     if (!requestedVersion)
169         requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
170
171     addOpenDatabaseConnection(*m_versionChangeDatabaseConnection);
172
173     m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion);
174     m_inProgressTransactions.set(m_versionChangeTransaction->info().identifier(), m_versionChangeTransaction);
175     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::beginTransactionInBackingStore, m_versionChangeTransaction->info()));
176
177     auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction);
178     operation->connection().didOpenDatabase(result);
179 }
180
181 void UniqueIDBDatabase::beginTransactionInBackingStore(const IDBTransactionInfo& info)
182 {
183     LOG(IndexedDB, "(db) UniqueIDBDatabase::beginTransactionInBackingStore");
184     m_backingStore->beginTransaction(info);
185 }
186
187 void UniqueIDBDatabase::notifyConnectionsOfVersionChange()
188 {
189     ASSERT(m_versionChangeOperation);
190     ASSERT(m_versionChangeDatabaseConnection);
191
192     uint64_t requestedVersion = m_versionChangeOperation->requestData().requestedVersion();
193
194     LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, requestedVersion);
195
196     // 3.3.7 "versionchange" transaction steps
197     // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
198     // The event must not be fired on connections which has the closePending flag set.
199     for (auto connection : m_openDatabaseConnections) {
200         if (connection->closePending())
201             continue;
202
203         connection->fireVersionChangeEvent(requestedVersion);
204     }
205 }
206
207 void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
208 {
209     ASSERT(!m_openDatabaseConnections.contains(&connection.get()));
210     m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
211 }
212
213 void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
214 {
215     ASSERT(!isMainThread());
216     LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore");
217
218     ASSERT(!m_backingStore);
219     m_backingStore = m_server.createBackingStore(identifier);
220     auto databaseInfo = m_backingStore->getOrEstablishDatabaseInfo();
221
222     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo));
223 }
224
225 void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info)
226 {
227     ASSERT(isMainThread());
228     LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
229     
230     m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
231
232     handleOpenDatabaseOperations();
233 }
234
235 void UniqueIDBDatabase::createObjectStore(UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback)
236 {
237     ASSERT(isMainThread());
238     LOG(IndexedDB, "(main) UniqueIDBDatabase::createObjectStore");
239
240     uint64_t callbackID = storeCallback(callback);
241     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateObjectStore, callbackID, transaction.info().identifier(), info));
242 }
243
244 void UniqueIDBDatabase::performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info)
245 {
246     ASSERT(!isMainThread());
247     LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateObjectStore");
248
249     ASSERT(m_backingStore);
250     m_backingStore->createObjectStore(transactionIdentifier, info);
251
252     IDBError error;
253     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateObjectStore, callbackIdentifier, error, info));
254 }
255
256 void UniqueIDBDatabase::didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError& error, const IDBObjectStoreInfo& info)
257 {
258     ASSERT(isMainThread());
259     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateObjectStore");
260
261     if (error.isNull())
262         m_databaseInfo->addExistingObjectStore(info);
263
264     performErrorCallback(callbackIdentifier, error);
265 }
266
267 void UniqueIDBDatabase::deleteObjectStore(UniqueIDBDatabaseTransaction& transaction, const String& objectStoreName, ErrorCallback callback)
268 {
269     ASSERT(isMainThread());
270     LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteObjectStore");
271
272     uint64_t callbackID = storeCallback(callback);
273     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteObjectStore, callbackID, transaction.info().identifier(), objectStoreName));
274 }
275
276 void UniqueIDBDatabase::performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const String& objectStoreName)
277 {
278     ASSERT(!isMainThread());
279     LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteObjectStore");
280
281     ASSERT(m_backingStore);
282     m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreName);
283
284     IDBError error;
285     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteObjectStore, callbackIdentifier, error, objectStoreName));
286 }
287
288 void UniqueIDBDatabase::didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError& error, const String& objectStoreName)
289 {
290     ASSERT(isMainThread());
291     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteObjectStore");
292
293     if (error.isNull())
294         m_databaseInfo->deleteObjectStore(objectStoreName);
295
296     performErrorCallback(callbackIdentifier, error);
297 }
298
299 void UniqueIDBDatabase::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const ThreadSafeDataBuffer& valueData, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback)
300 {
301     ASSERT(isMainThread());
302     LOG(IndexedDB, "(main) UniqueIDBDatabase::putOrAdd");
303
304     uint64_t callbackID = storeCallback(callback);
305     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPutOrAdd, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData, valueData, overwriteMode));
306 }
307
308 void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, const ThreadSafeDataBuffer& valueData, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
309 {
310     ASSERT(!isMainThread());
311     LOG(IndexedDB, "(db) UniqueIDBDatabase::performPutOrAdd");
312
313     ASSERT(m_backingStore);
314     ASSERT(objectStoreIdentifier);
315
316     IDBKeyData usedKey;
317     IDBError error;
318
319     auto objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
320     if (!objectStoreInfo) {
321         error = IDBError(IDBExceptionCode::InvalidStateError, ASCIILiteral("Object store cannot be found in the backing store"));
322         m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
323         return;
324     }
325
326     if (objectStoreInfo->autoIncrement() && !keyData.isValid()) {
327         // FIXME: This is where generated key support goes
328         error = IDBError(IDBExceptionCode::Unknown, ASCIILiteral("Key generators not supported yet"));
329         m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
330         return;
331     }
332
333     usedKey = keyData;
334
335     if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) {
336         bool keyExists;
337         error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists);
338         if (error.isNull() && keyExists)
339             error = IDBError(IDBExceptionCode::ConstraintError, ASCIILiteral("Key already exists in the object store"));
340
341         if (!error.isNull()) {
342             m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
343             return;
344         }
345     }
346
347     // 3.4.1 Object Store Storage Operation
348     // ...If a record already exists in store ...
349     // then remove the record from store using the steps for deleting records from an object store...
350     // This is important because formally deleting it from from the object store also removes it from the appropriate indexes.
351     error = m_backingStore->deleteRecord(transactionIdentifier, objectStoreIdentifier, usedKey);
352     if (!error.isNull()) {
353         m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
354         return;
355     }
356
357     error = m_backingStore->putRecord(transactionIdentifier, objectStoreIdentifier, usedKey, valueData);
358
359     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
360 }
361
362 void UniqueIDBDatabase::didPerformPutOrAdd(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
363 {
364     ASSERT(isMainThread());
365     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformPutOrAdd");
366
367     performKeyDataCallback(callbackIdentifier, error, resultKey);
368 }
369
370 void UniqueIDBDatabase::getRecord(const IDBRequestData& requestData, const IDBKeyData& keyData, ValueDataCallback callback)
371 {
372     ASSERT(isMainThread());
373     LOG(IndexedDB, "(main) UniqueIDBDatabase::getRecord");
374
375     uint64_t callbackID = storeCallback(callback);
376     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData));
377 }
378
379 void UniqueIDBDatabase::performGetRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData)
380 {
381     ASSERT(!isMainThread());
382     LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetRecord");
383
384     ASSERT(m_backingStore);
385     ASSERT(objectStoreIdentifier);
386
387     ThreadSafeDataBuffer valueData;
388     IDBError error = m_backingStore->getRecord(transactionIdentifier, objectStoreIdentifier, keyData, valueData);
389
390     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, valueData));
391 }
392
393 void UniqueIDBDatabase::didPerformGetRecord(uint64_t callbackIdentifier, const IDBError& error, const ThreadSafeDataBuffer& resultData)
394 {
395     ASSERT(isMainThread());
396     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetRecord");
397
398     performValueDataCallback(callbackIdentifier, error, resultData);
399 }
400
401 void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
402 {
403     ASSERT(isMainThread());
404     LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction");
405
406     ASSERT(&transaction.databaseConnection().database() == this);
407
408     if (m_versionChangeTransaction == &transaction) {
409         ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
410         m_databaseInfo->setVersion(transaction.info().newVersion());
411         m_versionChangeTransaction = nullptr;
412         m_versionChangeDatabaseConnection = nullptr;
413     }
414
415     uint64_t callbackID = storeCallback(callback);
416     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier()));
417 }
418
419 void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
420 {
421     ASSERT(!isMainThread());
422     LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction");
423
424     IDBError error = m_backingStore->commitTransaction(transactionIdentifier);
425     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error, transactionIdentifier));
426 }
427
428 void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
429 {
430     ASSERT(isMainThread());
431     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction");
432
433     inProgressTransactionCompleted(transactionIdentifier);
434
435     performErrorCallback(callbackIdentifier, error);
436 }
437
438 void UniqueIDBDatabase::abortTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
439 {
440     ASSERT(isMainThread());
441     LOG(IndexedDB, "(main) UniqueIDBDatabase::abortTransaction");
442
443     ASSERT(&transaction.databaseConnection().database() == this);
444
445     uint64_t callbackID = storeCallback(callback);
446     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performAbortTransaction, callbackID, transaction.info().identifier()));
447 }
448
449 void UniqueIDBDatabase::performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
450 {
451     ASSERT(!isMainThread());
452     LOG(IndexedDB, "(db) UniqueIDBDatabase::performAbortTransaction");
453
454     IDBError error = m_backingStore->abortTransaction(transactionIdentifier);
455     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformAbortTransaction, callbackIdentifier, error, transactionIdentifier));
456 }
457
458 void UniqueIDBDatabase::didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
459 {
460     ASSERT(isMainThread());
461     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformAbortTransaction");
462
463     if (m_versionChangeTransaction && m_versionChangeTransaction->info().identifier() == transactionIdentifier) {
464         ASSERT(&m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
465         ASSERT(m_versionChangeTransaction->originalDatabaseInfo());
466         m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*m_versionChangeTransaction->originalDatabaseInfo());
467
468         m_versionChangeTransaction = nullptr;
469         m_versionChangeDatabaseConnection = nullptr;
470     }
471
472     inProgressTransactionCompleted(transactionIdentifier);
473
474     performErrorCallback(callbackIdentifier, error);
475 }
476
477 void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction)
478 {
479     if (m_versionChangeTransaction == &transaction)
480         m_versionChangeTransaction = nullptr;
481 }
482
483 void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection& connection)
484 {
485     ASSERT(isMainThread());
486     LOG(IndexedDB, "(main) UniqueIDBDatabase::connectionClosedFromClient");
487
488     if (m_versionChangeDatabaseConnection == &connection)
489         m_versionChangeDatabaseConnection = nullptr;
490
491     ASSERT(m_openDatabaseConnections.contains(&connection));
492
493     auto removedConnection = m_openDatabaseConnections.take(&connection);
494     if (removedConnection->hasNonFinishedTransactions()) {
495         m_closePendingDatabaseConnections.add(WTF::move(removedConnection));
496         return;
497     }
498
499     // Now that a database connection has closed, previously blocked transactions might be runnable.
500     invokeTransactionScheduler();
501 }
502
503 void UniqueIDBDatabase::enqueueTransaction(Ref<UniqueIDBDatabaseTransaction>&& transaction)
504 {
505     LOG(IndexedDB, "UniqueIDBDatabase::enqueueTransaction");
506
507     ASSERT(transaction->info().mode() != IndexedDB::TransactionMode::VersionChange);
508
509     m_pendingTransactions.append(WTF::move(transaction));
510
511     invokeTransactionScheduler();
512 }
513
514 void UniqueIDBDatabase::invokeTransactionScheduler()
515 {
516     if (!m_transactionSchedulingTimer.isActive())
517         m_transactionSchedulingTimer.startOneShot(0);
518 }
519
520 void UniqueIDBDatabase::transactionSchedulingTimerFired()
521 {
522     LOG(IndexedDB, "(main) UniqueIDBDatabase::transactionSchedulingTimerFired");
523
524     if (m_pendingTransactions.isEmpty()) {
525         if (!hasAnyOpenConnections() && m_versionChangeOperation) {
526             startVersionChangeTransaction();
527             return;
528         }
529     }
530
531     bool hadDeferredTransactions = false;
532     auto transaction = takeNextRunnableTransaction(hadDeferredTransactions);
533
534     if (transaction) {
535         m_inProgressTransactions.set(transaction->info().identifier(), transaction);
536         for (auto objectStore : transaction->objectStoreIdentifiers())
537             m_objectStoreTransactionCounts.add(objectStore);
538
539         activateTransactionInBackingStore(*transaction);
540
541         // If no transactions were deferred, it's possible we can start another transaction right now.
542         if (!hadDeferredTransactions)
543             invokeTransactionScheduler();
544     }
545 }
546
547 void UniqueIDBDatabase::activateTransactionInBackingStore(UniqueIDBDatabaseTransaction& transaction)
548 {
549     LOG(IndexedDB, "(main) UniqueIDBDatabase::activateTransactionInBackingStore");
550
551     RefPtr<UniqueIDBDatabase> self(this);
552     RefPtr<UniqueIDBDatabaseTransaction> refTransaction(&transaction);
553
554     auto callback = [this, self, refTransaction](const IDBError& error) {
555         refTransaction->didActivateInBackingStore(error);
556     };
557
558     uint64_t callbackID = storeCallback(callback);
559     m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performActivateTransactionInBackingStore, callbackID, transaction.info()));
560 }
561
562 void UniqueIDBDatabase::performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo& info)
563 {
564     LOG(IndexedDB, "(db) UniqueIDBDatabase::performActivateTransactionInBackingStore");
565
566     IDBError error = m_backingStore->beginTransaction(info);
567     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformActivateTransactionInBackingStore, callbackIdentifier, error));
568 }
569
570 void UniqueIDBDatabase::didPerformActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBError& error)
571 {
572     LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformActivateTransactionInBackingStore");
573
574     invokeTransactionScheduler();
575
576     performErrorCallback(callbackIdentifier, error);
577 }
578
579 template<typename T> bool scopesOverlap(const T& aScopes, const Vector<uint64_t>& bScopes)
580 {
581     for (auto scope : bScopes) {
582         if (aScopes.contains(scope))
583             return true;
584     }
585
586     return false;
587 }
588
589 RefPtr<UniqueIDBDatabaseTransaction> UniqueIDBDatabase::takeNextRunnableTransaction(bool& hadDeferredTransactions)
590 {
591     Deque<RefPtr<UniqueIDBDatabaseTransaction>> deferredTransactions;
592     RefPtr<UniqueIDBDatabaseTransaction> currentTransaction;
593
594     while (!m_pendingTransactions.isEmpty()) {
595         currentTransaction = m_pendingTransactions.takeFirst();
596
597         switch (currentTransaction->info().mode()) {
598         case IndexedDB::TransactionMode::ReadOnly:
599             // If there are any deferred transactions, the first one is a read-write transaction we need to unblock.
600             // Therefore, skip this read-only transaction if its scope overlaps with that read-write transaction.
601             if (!deferredTransactions.isEmpty()) {
602                 ASSERT(deferredTransactions.first()->info().mode() == IndexedDB::TransactionMode::ReadWrite);
603                 if (scopesOverlap(deferredTransactions.first()->objectStoreIdentifiers(), currentTransaction->objectStoreIdentifiers()))
604                     deferredTransactions.append(WTF::move(currentTransaction));
605             }
606
607             break;
608         case IndexedDB::TransactionMode::ReadWrite:
609             // If this read-write transaction's scope overlaps with running transactions, it must be deferred.
610             if (scopesOverlap(m_objectStoreTransactionCounts, currentTransaction->objectStoreIdentifiers()))
611                 deferredTransactions.append(WTF::move(currentTransaction));
612
613             break;
614         case IndexedDB::TransactionMode::VersionChange:
615             // Version change transactions should never be scheduled in the traditional manner.
616             RELEASE_ASSERT_NOT_REACHED();
617         }
618
619         // If we didn't defer the currentTransaction above, it can be run now.
620         if (currentTransaction)
621             break;
622     }
623
624     hadDeferredTransactions = !deferredTransactions.isEmpty();
625     if (!hadDeferredTransactions)
626         return WTF::move(currentTransaction);
627
628     // Prepend the deferred transactions back on the beginning of the deque for future scheduling passes.
629     while (!deferredTransactions.isEmpty())
630         m_pendingTransactions.prepend(deferredTransactions.takeLast());
631
632     return WTF::move(currentTransaction);
633 }
634
635 void UniqueIDBDatabase::inProgressTransactionCompleted(const IDBResourceIdentifier& transactionIdentifier)
636 {
637     auto transaction = m_inProgressTransactions.take(transactionIdentifier);
638     ASSERT(transaction);
639
640     if (m_versionChangeTransaction == transaction)
641         m_versionChangeTransaction = nullptr;
642
643     for (auto objectStore : transaction->objectStoreIdentifiers())
644         m_objectStoreTransactionCounts.remove(objectStore);
645
646     // Previously blocked transactions might now be unblocked.
647     invokeTransactionScheduler();
648 }
649
650 void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
651 {
652     auto callback = m_errorCallbacks.take(callbackIdentifier);
653     ASSERT(callback);
654     callback(error);
655 }
656
657 void UniqueIDBDatabase::performKeyDataCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
658 {
659     auto callback = m_keyDataCallbacks.take(callbackIdentifier);
660     ASSERT(callback);
661     callback(error, resultKey);
662 }
663
664 void UniqueIDBDatabase::performValueDataCallback(uint64_t callbackIdentifier, const IDBError& error, const ThreadSafeDataBuffer& resultData)
665 {
666     auto callback = m_valueDataCallbacks.take(callbackIdentifier);
667     ASSERT(callback);
668     callback(error, resultData);
669 }
670
671 } // namespace IDBServer
672 } // namespace WebCore
673
674 #endif // ENABLE(INDEXED_DATABASE)