1/*
2 * Copyright (C) 2015, 2016 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 "IDBBindingUtilities.h"
32#include "IDBCursorInfo.h"
33#include "IDBGetAllRecordsData.h"
34#include "IDBGetAllResult.h"
35#include "IDBGetRecordData.h"
36#include "IDBIterateCursorData.h"
37#include "IDBKeyRangeData.h"
38#include "IDBResultData.h"
39#include "IDBServer.h"
40#include "IDBTransactionInfo.h"
41#include "IDBValue.h"
42#include "Logging.h"
43#include "SerializedScriptValue.h"
44#include "StorageQuotaManager.h"
45#include "UniqueIDBDatabaseConnection.h"
46#include <JavaScriptCore/AuxiliaryBarrierInlines.h>
47#include <JavaScriptCore/HeapInlines.h>
48#include <JavaScriptCore/StrongInlines.h>
49#include <JavaScriptCore/StructureInlines.h>
50#include <wtf/MainThread.h>
51#include <wtf/NeverDestroyed.h>
52#include <wtf/Scope.h>
53
54namespace WebCore {
55using namespace JSC;
56namespace IDBServer {
57
58static const uint64_t defaultWriteOperationCost = 4;
59
60static inline uint64_t estimateSize(const IDBKeyData& keyData)
61{
62 uint64_t size = 4;
63 switch (keyData.type()) {
64 case IndexedDB::KeyType::String:
65 size += keyData.string().sizeInBytes();
66 break;
67 case IndexedDB::KeyType::Binary: {
68 size += keyData.binary().size();
69 break;
70 }
71 case IndexedDB::KeyType::Array:
72 for (auto& data : keyData.array())
73 size += estimateSize(data);
74 break;
75 default:
76 break;
77 }
78 return size;
79}
80
81static inline uint64_t estimateSize(const IDBValue& value)
82{
83 uint64_t size = 4;
84 size += value.data().size();
85 for (auto& url : value.blobURLs())
86 size += url.sizeInBytes();
87 for (auto& path : value.blobFilePaths())
88 size += path.sizeInBytes();
89 return size;
90}
91
92static inline uint64_t estimateSize(const IDBIndexInfo& info)
93{
94 uint64_t size = 4;
95 size += info.name().sizeInBytes();
96 return size;
97}
98
99static inline uint64_t estimateSize(const IDBObjectStoreInfo& info)
100{
101 uint64_t size = 4;
102 size += info.name().sizeInBytes();
103 // FIXME: estimate keyPath.
104 for (auto& indexInfo : info.indexMap().values())
105 size += estimateSize(indexInfo);
106 return size;
107}
108
109UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier)
110 : m_server(server)
111 , m_identifier(identifier)
112 , m_operationAndTransactionTimer(*this, &UniqueIDBDatabase::operationAndTransactionTimerFired)
113{
114 LOG(IndexedDB, "UniqueIDBDatabase::UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data());
115}
116
117UniqueIDBDatabase::~UniqueIDBDatabase()
118{
119 LOG(IndexedDB, "UniqueIDBDatabase::~UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data());
120 ASSERT(isMainThread());
121 ASSERT(!hasAnyPendingCallbacks());
122 ASSERT(!hasUnfinishedTransactions());
123 ASSERT(m_pendingTransactions.isEmpty());
124 ASSERT(m_openDatabaseConnections.isEmpty());
125 ASSERT(m_clientClosePendingDatabaseConnections.isEmpty());
126 ASSERT(m_serverClosePendingDatabaseConnections.isEmpty());
127
128 RELEASE_ASSERT(m_databaseQueue.isKilled());
129 RELEASE_ASSERT(m_databaseReplyQueue.isKilled());
130 RELEASE_ASSERT(!m_backingStore);
131}
132
133const IDBDatabaseInfo& UniqueIDBDatabase::info() const
134{
135 RELEASE_ASSERT(m_databaseInfo);
136 return *m_databaseInfo;
137}
138
139void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData)
140{
141 LOG(IndexedDB, "UniqueIDBDatabase::openDatabaseConnection");
142 ASSERT(!m_hardClosedForUserDelete);
143 ASSERT(isMainThread());
144
145 m_pendingOpenDBRequests.add(ServerOpenDBRequest::create(connection, requestData));
146
147 // An open operation is already in progress, so we can't possibly handle this one yet.
148 if (m_isOpeningBackingStore)
149 return;
150
151 handleDatabaseOperations();
152}
153
154bool UniqueIDBDatabase::hasAnyPendingCallbacks() const
155{
156 return !m_errorCallbacks.isEmpty()
157 || !m_keyDataCallbacks.isEmpty()
158 || !m_getResultCallbacks.isEmpty()
159 || !m_getAllResultsCallbacks.isEmpty()
160 || !m_countCallbacks.isEmpty();
161}
162
163bool UniqueIDBDatabase::isVersionChangeInProgress()
164{
165#if !LOG_DISABLED
166 if (m_versionChangeTransaction)
167 ASSERT(m_versionChangeDatabaseConnection);
168#endif
169
170 return m_versionChangeDatabaseConnection;
171}
172
173static inline String quotaErrorMessageName(const char* taskName)
174{
175 return makeString("Failed to ", taskName, " in database because not enough space for domain");
176}
177
178void UniqueIDBDatabase::requestSpace(uint64_t taskSize, const char* taskName, CompletionHandler<void(Optional<IDBError>&&)>&& callback)
179{
180 m_server->requestSpace(m_identifier.origin(), taskSize, [weakThis = makeWeakPtr(this), this, taskName, callback = WTFMove(callback)](auto decision) mutable {
181 if (!weakThis) {
182 callback(IDBError { UnknownError });
183 return;
184 }
185
186 if (m_owningPointerForClose) {
187 // We are closing the database, there is no point in trying to modify the database at that point.
188 callback(IDBError { UnknownError });
189 return;
190 }
191
192 switch (decision) {
193 case StorageQuotaManager::Decision::Deny:
194 callback(IDBError { QuotaExceededError, quotaErrorMessageName(taskName) });
195 return;
196 case StorageQuotaManager::Decision::Grant:
197 callback({ });
198 };
199 });
200}
201
202void UniqueIDBDatabase::waitForRequestSpaceCompletion(CompletionHandler<void(Optional<IDBError>&&)>&& callback)
203{
204 requestSpace(0, "", WTFMove(callback));
205}
206
207void UniqueIDBDatabase::performCurrentOpenOperation()
208{
209 LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation (%p)", this);
210
211 ASSERT(m_currentOpenDBRequest);
212 ASSERT(m_currentOpenDBRequest->isOpenRequest());
213
214 if (!m_databaseInfo) {
215 if (!m_isOpeningBackingStore) {
216 m_isOpeningBackingStore = true;
217 // We do not know whether this is an existing or a new database.
218 // We set a small cost so that it is not possible to open an infinite number of database.
219 m_server->requestSpace(m_identifier.origin(), defaultWriteOperationCost, [this, weakThis = makeWeakPtr(this)](auto decision) mutable {
220 if (!weakThis)
221 return;
222
223 if (m_owningPointerForClose)
224 return;
225
226 switch (decision) {
227 case StorageQuotaManager::Decision::Deny: {
228 auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError { QuotaExceededError, quotaErrorMessageName("openDatabase") });
229 m_currentOpenDBRequest->connection().didOpenDatabase(result);
230 m_currentOpenDBRequest = nullptr;
231 m_isOpeningBackingStore = false;
232 break;
233 }
234 case StorageQuotaManager::Decision::Grant:
235 this->postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
236 };
237 });
238 }
239 return;
240 }
241
242 // If we previously started a version change operation but were blocked by having open connections,
243 // we might now be unblocked.
244 if (m_versionChangeDatabaseConnection) {
245 if (!m_versionChangeTransaction && !hasAnyOpenConnections())
246 startVersionChangeTransaction();
247 return;
248 }
249
250 // 3.3.1 Opening a database
251 // If requested version is undefined, then let requested version be 1 if db was created in the previous step,
252 // or the current version of db otherwise.
253 uint64_t requestedVersion = m_currentOpenDBRequest->requestData().requestedVersion();
254 if (!requestedVersion)
255 requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
256
257 // 3.3.1 Opening a database
258 // If the database version higher than the requested version, abort these steps and return a VersionError.
259 if (requestedVersion < m_databaseInfo->version()) {
260 auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError(VersionError));
261 m_currentOpenDBRequest->connection().didOpenDatabase(result);
262 m_currentOpenDBRequest = nullptr;
263
264 return;
265 }
266
267 if (!m_backingStoreOpenError.isNull()) {
268 auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), m_backingStoreOpenError);
269 m_currentOpenDBRequest->connection().didOpenDatabase(result);
270 m_currentOpenDBRequest = nullptr;
271
272 return;
273 }
274
275 Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, *m_currentOpenDBRequest);
276
277 if (requestedVersion == m_databaseInfo->version()) {
278 auto* rawConnection = &connection.get();
279 addOpenDatabaseConnection(WTFMove(connection));
280
281 auto result = IDBResultData::openDatabaseSuccess(m_currentOpenDBRequest->requestData().requestIdentifier(), *rawConnection);
282 m_currentOpenDBRequest->connection().didOpenDatabase(result);
283 m_currentOpenDBRequest = nullptr;
284
285 return;
286 }
287
288 ASSERT(!m_versionChangeDatabaseConnection);
289 m_versionChangeDatabaseConnection = WTFMove(connection);
290
291 // 3.3.7 "versionchange" transaction steps
292 // If there's no other open connections to this database, the version change process can begin immediately.
293 if (!hasAnyOpenConnections()) {
294 startVersionChangeTransaction();
295 return;
296 }
297
298 // Otherwise we have to notify all those open connections and wait for them to close.
299 maybeNotifyConnectionsOfVersionChange();
300}
301
302void UniqueIDBDatabase::performCurrentDeleteOperation()
303{
304 ASSERT(isMainThread());
305 LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentDeleteOperation - %s", m_identifier.debugString().utf8().data());
306
307 ASSERT(m_currentOpenDBRequest);
308 ASSERT(m_currentOpenDBRequest->isDeleteRequest());
309
310 if (m_deleteBackingStoreInProgress)
311 return;
312
313 if (hasAnyOpenConnections()) {
314 maybeNotifyConnectionsOfVersionChange();
315 return;
316 }
317
318 if (hasUnfinishedTransactions())
319 return;
320
321 ASSERT(!hasAnyPendingCallbacks());
322 ASSERT(m_pendingTransactions.isEmpty());
323 ASSERT(m_openDatabaseConnections.isEmpty());
324
325 // It's possible to have multiple delete requests queued up in a row.
326 // In that scenario only the first request will actually have to delete the database.
327 // Subsequent requests can immediately notify their completion.
328
329 if (!m_deleteBackingStoreInProgress) {
330 if (!m_databaseInfo && m_mostRecentDeletedDatabaseInfo)
331 didDeleteBackingStore(0);
332 else {
333 m_deleteBackingStoreInProgress = true;
334 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteBackingStore, m_identifier));
335 }
336 }
337}
338
339void UniqueIDBDatabase::deleteBackingStore(const IDBDatabaseIdentifier& identifier)
340{
341 ASSERT(!isMainThread());
342 LOG(IndexedDB, "(db) UniqueIDBDatabase::deleteBackingStore");
343
344 uint64_t deletedVersion = 0;
345
346 if (m_backingStore) {
347 m_backingStore->deleteBackingStore();
348 m_backingStore = nullptr;
349 m_backingStoreSupportsSimultaneousTransactions = false;
350 m_backingStoreIsEphemeral = false;
351 } else {
352 auto backingStore = m_server->createBackingStore(identifier);
353
354 IDBDatabaseInfo databaseInfo;
355 auto error = backingStore->getOrEstablishDatabaseInfo(databaseInfo);
356 if (!error.isNull())
357 LOG_ERROR("Error getting database info from database %s that we are trying to delete", identifier.debugString().utf8().data());
358
359 deletedVersion = databaseInfo.version();
360 backingStore->deleteBackingStore();
361 }
362
363 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteBackingStore, deletedVersion));
364}
365
366void UniqueIDBDatabase::performUnconditionalDeleteBackingStore()
367{
368 ASSERT(!isMainThread());
369 LOG(IndexedDB, "(db) UniqueIDBDatabase::performUnconditionalDeleteBackingStore");
370
371 if (m_backingStore)
372 m_backingStore->deleteBackingStore();
373
374 shutdownForClose();
375}
376
377void UniqueIDBDatabase::scheduleShutdownForClose()
378{
379 ASSERT(isMainThread());
380
381 m_operationAndTransactionTimer.stop();
382
383 RELEASE_ASSERT(!m_owningPointerForClose);
384 m_owningPointerForClose = m_server->closeAndTakeUniqueIDBDatabase(*this);
385
386 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::shutdownForClose));
387}
388
389void UniqueIDBDatabase::shutdownForClose()
390{
391 ASSERT(!isMainThread());
392 ASSERT(m_owningPointerForClose.get() == this);
393
394 LOG(IndexedDB, "(db) UniqueIDBDatabase::shutdownForClose");
395
396 m_backingStore = nullptr;
397 m_backingStoreSupportsSimultaneousTransactions = false;
398 m_backingStoreIsEphemeral = false;
399
400 if (!m_databaseQueue.isEmpty()) {
401 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::shutdownForClose));
402 return;
403 }
404 m_databaseQueue.kill();
405
406 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didShutdownForClose));
407}
408
409void UniqueIDBDatabase::didShutdownForClose()
410{
411 ASSERT(m_databaseReplyQueue.isEmpty());
412 m_databaseReplyQueue.kill();
413}
414
415void UniqueIDBDatabase::didDeleteBackingStore(uint64_t deletedVersion)
416{
417 ASSERT(isMainThread());
418 LOG(IndexedDB, "(main) UniqueIDBDatabase::didDeleteBackingStore");
419
420 ASSERT(!hasAnyPendingCallbacks());
421 ASSERT(!hasUnfinishedTransactions());
422 ASSERT(m_pendingTransactions.isEmpty());
423 ASSERT(m_openDatabaseConnections.isEmpty());
424 ASSERT(!m_backingStore);
425
426 // It's possible that the openDBRequest was cancelled from client-side after the delete was already dispatched to the backingstore.
427 // So it's okay if we don't have a currentOpenDBRequest, but if we do it has to be a deleteRequest.
428 ASSERT(!m_currentOpenDBRequest || m_currentOpenDBRequest->isDeleteRequest());
429
430 if (m_databaseInfo)
431 m_mostRecentDeletedDatabaseInfo = WTFMove(m_databaseInfo);
432
433 // If this UniqueIDBDatabase was brought into existence for the purpose of deleting the file on disk,
434 // we won't have a m_mostRecentDeletedDatabaseInfo. In that case, we'll manufacture one using the
435 // passed in deletedVersion argument.
436 if (!m_mostRecentDeletedDatabaseInfo)
437 m_mostRecentDeletedDatabaseInfo = std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), deletedVersion);
438
439 if (m_currentOpenDBRequest) {
440 m_currentOpenDBRequest->notifyDidDeleteDatabase(*m_mostRecentDeletedDatabaseInfo);
441 m_currentOpenDBRequest = nullptr;
442 }
443
444 m_deleteBackingStoreInProgress = false;
445
446 if (m_hardClosedForUserDelete)
447 return;
448
449 invokeOperationAndTransactionTimer();
450}
451
452void UniqueIDBDatabase::clearStalePendingOpenDBRequests()
453{
454 while (!m_pendingOpenDBRequests.isEmpty() && m_pendingOpenDBRequests.first()->connection().isClosed())
455 m_pendingOpenDBRequests.removeFirst();
456}
457
458void UniqueIDBDatabase::handleDatabaseOperations()
459{
460 ASSERT(isMainThread());
461 LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDatabaseOperations - There are %u pending", m_pendingOpenDBRequests.size());
462 ASSERT(!m_hardClosedForUserDelete);
463
464 if (m_deleteBackingStoreInProgress || m_isOpeningBackingStore)
465 return;
466
467 clearStalePendingOpenDBRequests();
468
469 if (m_versionChangeDatabaseConnection || m_versionChangeTransaction || (m_currentOpenDBRequest && !m_currentOpenDBRequest->connection().isClosed())) {
470 // We can't start any new open-database operations right now, but we might be able to start handling a delete operation.
471 if (!m_currentOpenDBRequest && !m_pendingOpenDBRequests.isEmpty() && m_pendingOpenDBRequests.first()->isDeleteRequest())
472 m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst();
473
474 // Some operations (such as the first open operation after a delete) require multiple passes to completely handle
475 if (m_currentOpenDBRequest)
476 handleCurrentOperation();
477
478 return;
479 }
480
481 if (m_pendingOpenDBRequests.isEmpty()) {
482 m_currentOpenDBRequest = nullptr;
483 return;
484 }
485
486 m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst();
487 LOG(IndexedDB, "UniqueIDBDatabase::handleDatabaseOperations - Popped an operation, now there are %u pending", m_pendingOpenDBRequests.size());
488
489 handleCurrentOperation();
490}
491
492void UniqueIDBDatabase::handleCurrentOperation()
493{
494 LOG(IndexedDB, "(main) UniqueIDBDatabase::handleCurrentOperation");
495 ASSERT(!m_hardClosedForUserDelete);
496 ASSERT(m_currentOpenDBRequest);
497
498 if (m_currentOpenDBRequest->isOpenRequest())
499 performCurrentOpenOperation();
500 else if (m_currentOpenDBRequest->isDeleteRequest())
501 performCurrentDeleteOperation();
502 else
503 ASSERT_NOT_REACHED();
504
505 if (!m_currentOpenDBRequest)
506 invokeOperationAndTransactionTimer();
507}
508
509bool UniqueIDBDatabase::hasAnyOpenConnections() const
510{
511 return !m_openDatabaseConnections.isEmpty();
512}
513
514bool UniqueIDBDatabase::allConnectionsAreClosedOrClosing() const
515{
516 for (auto& connection : m_openDatabaseConnections) {
517 if (!connection->connectionIsClosing())
518 return false;
519 }
520
521 return true;
522}
523
524static uint64_t generateUniqueCallbackIdentifier()
525{
526 ASSERT(isMainThread());
527 static uint64_t currentID = 0;
528 return ++currentID;
529}
530
531uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback&& callback, uint64_t taskSize)
532{
533 if (m_hardClosedForUserDelete) {
534 callback(IDBError::userDeleteError());
535 return 0;
536 }
537
538 uint64_t identifier = generateUniqueCallbackIdentifier();
539 ASSERT(!m_errorCallbacks.contains(identifier));
540 m_errorCallbacks.add(identifier, WTFMove(callback));
541
542 if (taskSize) {
543 m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize);
544 m_pendingSpaceIncreasingTasks.add(identifier, taskSize);
545 }
546
547 m_callbackQueue.append(identifier);
548 return identifier;
549}
550
551uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback&& callback, uint64_t taskSize)
552{
553 if (m_hardClosedForUserDelete) {
554 callback(IDBError::userDeleteError(), { });
555 return 0;
556 }
557
558 uint64_t identifier = generateUniqueCallbackIdentifier();
559 ASSERT(!m_keyDataCallbacks.contains(identifier));
560 m_keyDataCallbacks.add(identifier, WTFMove(callback));
561
562 if (taskSize) {
563 m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize);
564 m_pendingSpaceIncreasingTasks.add(identifier, taskSize);
565 }
566
567 m_callbackQueue.append(identifier);
568 return identifier;
569}
570
571uint64_t UniqueIDBDatabase::storeCallbackOrFireError(GetResultCallback&& callback)
572{
573 if (m_hardClosedForUserDelete) {
574 callback(IDBError::userDeleteError(), { });
575 return 0;
576 }
577
578 uint64_t identifier = generateUniqueCallbackIdentifier();
579 ASSERT(!m_getResultCallbacks.contains(identifier));
580 m_getResultCallbacks.add(identifier, WTFMove(callback));
581 m_callbackQueue.append(identifier);
582 return identifier;
583}
584
585uint64_t UniqueIDBDatabase::storeCallbackOrFireError(GetAllResultsCallback&& callback)
586{
587 if (m_hardClosedForUserDelete) {
588 callback(IDBError::userDeleteError(), { });
589 return 0;
590 }
591
592 uint64_t identifier = generateUniqueCallbackIdentifier();
593 ASSERT(!m_getAllResultsCallbacks.contains(identifier));
594 m_getAllResultsCallbacks.add(identifier, WTFMove(callback));
595 m_callbackQueue.append(identifier);
596 return identifier;
597}
598
599uint64_t UniqueIDBDatabase::storeCallbackOrFireError(CountCallback&& callback)
600{
601 if (m_hardClosedForUserDelete) {
602 callback(IDBError::userDeleteError(), 0);
603 return 0;
604 }
605
606 uint64_t identifier = generateUniqueCallbackIdentifier();
607 ASSERT(!m_countCallbacks.contains(identifier));
608 m_countCallbacks.add(identifier, WTFMove(callback));
609 m_callbackQueue.append(identifier);
610 return identifier;
611}
612
613void UniqueIDBDatabase::handleDelete(IDBConnectionToClient& connection, const IDBRequestData& requestData)
614{
615 LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDelete");
616 ASSERT(!m_hardClosedForUserDelete);
617
618 m_pendingOpenDBRequests.add(ServerOpenDBRequest::create(connection, requestData));
619 handleDatabaseOperations();
620}
621
622void UniqueIDBDatabase::startVersionChangeTransaction()
623{
624 LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
625
626 ASSERT(!m_versionChangeTransaction);
627 ASSERT(m_currentOpenDBRequest);
628 ASSERT(m_currentOpenDBRequest->isOpenRequest());
629 ASSERT(m_versionChangeDatabaseConnection);
630
631 auto operation = WTFMove(m_currentOpenDBRequest);
632
633 uint64_t requestedVersion = operation->requestData().requestedVersion();
634 if (!requestedVersion)
635 requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
636
637 addOpenDatabaseConnection(*m_versionChangeDatabaseConnection);
638
639 m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion);
640 m_databaseInfo->setVersion(requestedVersion);
641
642 m_inProgressTransactions.set(m_versionChangeTransaction->info().identifier(), m_versionChangeTransaction);
643 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::beginTransactionInBackingStore, m_versionChangeTransaction->info()));
644
645 auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction);
646 operation->connection().didOpenDatabase(result);
647}
648
649void UniqueIDBDatabase::beginTransactionInBackingStore(const IDBTransactionInfo& info)
650{
651 LOG(IndexedDB, "(db) UniqueIDBDatabase::beginTransactionInBackingStore");
652 m_backingStore->beginTransaction(info);
653}
654
655void UniqueIDBDatabase::maybeNotifyConnectionsOfVersionChange()
656{
657 ASSERT(m_currentOpenDBRequest);
658
659 if (m_currentOpenDBRequest->hasNotifiedConnectionsOfVersionChange())
660 return;
661
662 uint64_t newVersion = m_currentOpenDBRequest->isOpenRequest() ? m_currentOpenDBRequest->requestData().requestedVersion() : 0;
663 auto requestIdentifier = m_currentOpenDBRequest->requestData().requestIdentifier();
664
665 LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, newVersion);
666
667 // 3.3.7 "versionchange" transaction steps
668 // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
669 // The event must not be fired on connections which has the closePending flag set.
670 HashSet<uint64_t> connectionIdentifiers;
671 for (const auto& connection : m_openDatabaseConnections) {
672 if (connection->closePending())
673 continue;
674
675 connection->fireVersionChangeEvent(requestIdentifier, newVersion);
676 connectionIdentifiers.add(connection->identifier());
677 }
678
679 if (!connectionIdentifiers.isEmpty())
680 m_currentOpenDBRequest->notifiedConnectionsOfVersionChange(WTFMove(connectionIdentifiers));
681 else
682 m_currentOpenDBRequest->maybeNotifyRequestBlocked(m_databaseInfo->version());
683}
684
685void UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier)
686{
687 LOG(IndexedDB, "UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent - %" PRIu64, connectionIdentifier);
688
689 ASSERT(m_currentOpenDBRequest);
690
691 m_currentOpenDBRequest->connectionClosedOrFiredVersionChangeEvent(connectionIdentifier);
692
693 if (m_currentOpenDBRequest->hasConnectionsPendingVersionChangeEvent())
694 return;
695
696 if (!hasAnyOpenConnections() || allConnectionsAreClosedOrClosing()) {
697 invokeOperationAndTransactionTimer();
698 return;
699 }
700
701 // Since all open connections have fired their version change events but not all of them have closed,
702 // this request is officially blocked.
703 m_currentOpenDBRequest->maybeNotifyRequestBlocked(m_databaseInfo->version());
704}
705
706void UniqueIDBDatabase::didFireVersionChangeEvent(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier)
707{
708 LOG(IndexedDB, "UniqueIDBDatabase::didFireVersionChangeEvent");
709
710 if (!m_currentOpenDBRequest)
711 return;
712
713 ASSERT_UNUSED(requestIdentifier, m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier);
714
715 notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier());
716}
717
718void UniqueIDBDatabase::openDBRequestCancelled(const IDBResourceIdentifier& requestIdentifier)
719{
720 LOG(IndexedDB, "UniqueIDBDatabase::openDBRequestCancelled - %s", requestIdentifier.loggingString().utf8().data());
721
722 if (m_currentOpenDBRequest && m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier)
723 m_currentOpenDBRequest = nullptr;
724
725 if (m_versionChangeDatabaseConnection && m_versionChangeDatabaseConnection->openRequestIdentifier() == requestIdentifier) {
726 ASSERT(!m_versionChangeTransaction || m_versionChangeTransaction->databaseConnection().openRequestIdentifier() == requestIdentifier);
727 ASSERT(!m_versionChangeTransaction || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
728
729 connectionClosedFromClient(*m_versionChangeDatabaseConnection);
730 }
731
732 for (auto& request : m_pendingOpenDBRequests) {
733 if (request->requestData().requestIdentifier() == requestIdentifier) {
734 m_pendingOpenDBRequests.remove(request);
735 return;
736 }
737 }
738}
739
740void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
741{
742 ASSERT(!m_openDatabaseConnections.contains(&connection.get()));
743 m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
744}
745
746void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
747{
748 ASSERT(!isMainThread());
749 LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore (%p)", this);
750
751 ASSERT(!m_backingStore);
752 m_backingStore = m_server->createBackingStore(identifier);
753 m_backingStoreSupportsSimultaneousTransactions = m_backingStore->supportsSimultaneousTransactions();
754 m_backingStoreIsEphemeral = m_backingStore->isEphemeral();
755
756 IDBDatabaseInfo databaseInfo;
757 auto error = m_backingStore->getOrEstablishDatabaseInfo(databaseInfo);
758
759 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo, error));
760}
761
762void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info, const IDBError& error)
763{
764 ASSERT(isMainThread());
765 LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
766
767 m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
768 m_backingStoreOpenError = error;
769
770 ASSERT(m_isOpeningBackingStore);
771 m_isOpeningBackingStore = false;
772
773 if (m_hardClosedForUserDelete)
774 return;
775
776 handleDatabaseOperations();
777}
778
779void UniqueIDBDatabase::createObjectStore(UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback)
780{
781 ASSERT(isMainThread());
782 LOG(IndexedDB, "(main) UniqueIDBDatabase::createObjectStore");
783
784 auto taskSize = defaultWriteOperationCost + estimateSize(info);
785 requestSpace(taskSize, "createObjectStore", [this, taskSize, transaction = makeRef(transaction), info, callback = WTFMove(callback)](auto error) mutable {
786 if (error) {
787 callback(WTFMove(*error));
788 return;
789 }
790 this->createObjectStoreAfterQuotaCheck(taskSize, transaction.get(), info, WTFMove(callback));
791 });
792}
793
794void UniqueIDBDatabase::createObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback)
795{
796 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
797 if (!callbackID)
798 return;
799
800 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateObjectStore, callbackID, transaction.info().identifier(), info));
801}
802
803void UniqueIDBDatabase::performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info)
804{
805 ASSERT(!isMainThread());
806 LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateObjectStore");
807
808 ASSERT(m_backingStore);
809 m_backingStore->createObjectStore(transactionIdentifier, info);
810
811 IDBError error;
812 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateObjectStore, callbackIdentifier, error, info));
813}
814
815void UniqueIDBDatabase::didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError& error, const IDBObjectStoreInfo& info)
816{
817 ASSERT(isMainThread());
818 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateObjectStore");
819
820 if (error.isNull())
821 m_databaseInfo->addExistingObjectStore(info);
822
823 performErrorCallback(callbackIdentifier, error);
824}
825
826void UniqueIDBDatabase::deleteObjectStore(UniqueIDBDatabaseTransaction& transaction, const String& objectStoreName, ErrorCallback callback)
827{
828 ASSERT(isMainThread());
829 LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteObjectStore");
830
831 waitForRequestSpaceCompletion([this, transaction = makeRef(transaction), objectStoreName, callback = WTFMove(callback)](auto error) mutable {
832 if (error) {
833 callback(WTFMove(*error));
834 return;
835 }
836 this->deleteObjectStoreAfterQuotaCheck(transaction, objectStoreName, WTFMove(callback));
837 });
838}
839
840void UniqueIDBDatabase::deleteObjectStoreAfterQuotaCheck(UniqueIDBDatabaseTransaction& transaction, const String& objectStoreName, ErrorCallback callback)
841{
842 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
843 if (!callbackID)
844 return;
845
846 auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreName);
847 if (!info) {
848 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to delete non-existant object store"_s });
849 return;
850 }
851
852 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteObjectStore, callbackID, transaction.info().identifier(), info->identifier()));
853}
854
855void UniqueIDBDatabase::performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
856{
857 ASSERT(!isMainThread());
858 LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteObjectStore");
859
860 ASSERT(m_backingStore);
861 m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreIdentifier);
862
863 IDBError error;
864 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteObjectStore, callbackIdentifier, error, objectStoreIdentifier));
865}
866
867void UniqueIDBDatabase::didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier)
868{
869 ASSERT(isMainThread());
870 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteObjectStore");
871
872 if (error.isNull())
873 m_databaseInfo->deleteObjectStore(objectStoreIdentifier);
874
875 performErrorCallback(callbackIdentifier, error);
876}
877
878void UniqueIDBDatabase::renameObjectStore(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback callback)
879{
880 ASSERT(isMainThread());
881 LOG(IndexedDB, "(main) UniqueIDBDatabase::renameObjectStore");
882
883 auto taskSize = defaultWriteOperationCost + newName.sizeInBytes();
884 requestSpace(taskSize, "renameObjectStore", [this, taskSize, transaction = makeRef(transaction), objectStoreIdentifier, newName, callback = WTFMove(callback)](auto error) mutable {
885 if (error) {
886 callback(WTFMove(*error));
887 return;
888 }
889 this->renameObjectStoreAfterQuotaCheck(taskSize, transaction.get(), objectStoreIdentifier, newName, WTFMove(callback));
890 });
891}
892
893void UniqueIDBDatabase::renameObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback callback)
894{
895 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
896 if (!callbackID)
897 return;
898
899 auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
900 if (!info) {
901 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to rename non-existant object store"_s });
902 return;
903 }
904
905 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier, newName));
906}
907
908void UniqueIDBDatabase::performRenameObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName)
909{
910 ASSERT(!isMainThread());
911 LOG(IndexedDB, "(db) UniqueIDBDatabase::performRenameObjectStore");
912
913 ASSERT(m_backingStore);
914 m_backingStore->renameObjectStore(transactionIdentifier, objectStoreIdentifier, newName);
915
916 IDBError error;
917 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformRenameObjectStore, callbackIdentifier, error, objectStoreIdentifier, newName));
918}
919
920void UniqueIDBDatabase::didPerformRenameObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, const String& newName)
921{
922 ASSERT(isMainThread());
923 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameObjectStore");
924
925 if (error.isNull())
926 m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName);
927
928 performErrorCallback(callbackIdentifier, error);
929}
930
931void UniqueIDBDatabase::clearObjectStore(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, ErrorCallback callback)
932{
933 ASSERT(isMainThread());
934 LOG(IndexedDB, "(main) UniqueIDBDatabase::clearObjectStore");
935
936 waitForRequestSpaceCompletion([this, transaction = makeRef(transaction), objectStoreIdentifier, callback = WTFMove(callback)](auto error) mutable {
937 if (error) {
938 callback(WTFMove(*error));
939 return;
940 }
941 this->clearObjectStoreAfetQuotaCheck(transaction, objectStoreIdentifier, WTFMove(callback));
942 });
943}
944
945void UniqueIDBDatabase::clearObjectStoreAfetQuotaCheck(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, ErrorCallback callback)
946{
947 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
948 if (!callbackID)
949 return;
950 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performClearObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier));
951}
952
953void UniqueIDBDatabase::performClearObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
954{
955 ASSERT(!isMainThread());
956 LOG(IndexedDB, "(db) UniqueIDBDatabase::performClearObjectStore");
957
958 ASSERT(m_backingStore);
959 m_backingStore->clearObjectStore(transactionIdentifier, objectStoreIdentifier);
960
961 IDBError error;
962 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformClearObjectStore, callbackIdentifier, error));
963}
964
965void UniqueIDBDatabase::didPerformClearObjectStore(uint64_t callbackIdentifier, const IDBError& error)
966{
967 ASSERT(isMainThread());
968 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformClearObjectStore");
969
970 performErrorCallback(callbackIdentifier, error);
971}
972
973void UniqueIDBDatabase::createIndex(UniqueIDBDatabaseTransaction& transaction, const IDBIndexInfo& info, ErrorCallback callback)
974{
975 ASSERT(isMainThread());
976 LOG(IndexedDB, "(main) UniqueIDBDatabase::createIndex");
977
978 auto taskSize = defaultWriteOperationCost + estimateSize(info);
979 requestSpace(taskSize, "createIndex", [this, taskSize, transaction = makeRef(transaction), info, callback = WTFMove(callback)](auto error) mutable {
980 if (error) {
981 callback(WTFMove(*error));
982 return;
983 }
984 this->createIndexAfterQuotaCheck(taskSize, transaction.get(), info, WTFMove(callback));
985 });
986}
987
988void UniqueIDBDatabase::createIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, const IDBIndexInfo& info, ErrorCallback callback)
989{
990 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
991 if (!callbackID)
992 return;
993 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateIndex, callbackID, transaction.info().identifier(), info));
994}
995
996void UniqueIDBDatabase::performCreateIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info)
997{
998 ASSERT(!isMainThread());
999 LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateIndex");
1000
1001 IDBError error;
1002 ASSERT(m_backingStore);
1003 if (!m_backingStore) {
1004 RELEASE_LOG_ERROR(IndexedDB, "%p - UniqueIDBDatabase::performCreateIndex: m_backingStore is null", this);
1005 error = IDBError(InvalidStateError, "Backing store is invalid for call to create index"_s);
1006 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateIndex, callbackIdentifier, error, info));
1007 return;
1008 }
1009
1010 error = m_backingStore->createIndex(transactionIdentifier, info);
1011 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateIndex, callbackIdentifier, error, info));
1012}
1013
1014void UniqueIDBDatabase::didPerformCreateIndex(uint64_t callbackIdentifier, const IDBError& error, const IDBIndexInfo& info)
1015{
1016 ASSERT(isMainThread());
1017 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateIndex");
1018
1019 if (error.isNull()) {
1020 ASSERT(m_databaseInfo);
1021 auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier());
1022 ASSERT(objectStoreInfo);
1023 objectStoreInfo->addExistingIndex(info);
1024 }
1025
1026 performErrorCallback(callbackIdentifier, error);
1027}
1028
1029void UniqueIDBDatabase::deleteIndex(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& indexName, ErrorCallback callback)
1030{
1031 ASSERT(isMainThread());
1032 LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteIndex");
1033
1034 waitForRequestSpaceCompletion([this, transaction = makeRef(transaction), objectStoreIdentifier, indexName, callback = WTFMove(callback)](auto error) mutable {
1035 if (error) {
1036 callback(WTFMove(*error));
1037 return;
1038 }
1039 this->deleteIndexAfterQuotaCheck(transaction, objectStoreIdentifier, indexName, WTFMove(callback));
1040 });
1041}
1042
1043void UniqueIDBDatabase::deleteIndexAfterQuotaCheck(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& indexName, ErrorCallback callback)
1044{
1045 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1046 if (!callbackID)
1047 return;
1048
1049 auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
1050 if (!objectStoreInfo) {
1051 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to delete index from non-existant object store"_s });
1052 return;
1053 }
1054
1055 auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexName);
1056 if (!indexInfo) {
1057 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to delete non-existant index"_s });
1058 return;
1059 }
1060
1061 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexInfo->identifier()));
1062}
1063
1064void UniqueIDBDatabase::performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const uint64_t indexIdentifier)
1065{
1066 ASSERT(!isMainThread());
1067 LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteIndex");
1068
1069 ASSERT(m_backingStore);
1070 m_backingStore->deleteIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier);
1071
1072 IDBError error;
1073 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier));
1074}
1075
1076void UniqueIDBDatabase::didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier)
1077{
1078 ASSERT(isMainThread());
1079 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteIndex");
1080
1081 if (error.isNull()) {
1082 auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
1083 if (objectStoreInfo)
1084 objectStoreInfo->deleteIndex(indexIdentifier);
1085 }
1086
1087 performErrorCallback(callbackIdentifier, error);
1088}
1089
1090void UniqueIDBDatabase::renameIndex(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback callback)
1091{
1092 ASSERT(isMainThread());
1093 LOG(IndexedDB, "(main) UniqueIDBDatabase::renameIndex");
1094
1095 auto taskSize = defaultWriteOperationCost + newName.sizeInBytes();
1096 requestSpace(taskSize, "renameIndex", [this, taskSize, transaction = makeRef(transaction), objectStoreIdentifier, indexIdentifier, newName, callback = WTFMove(callback)](auto error) mutable {
1097 if (error) {
1098 callback(WTFMove(*error));
1099 return;
1100 }
1101 this->renameIndexAfterQuotaCheck(taskSize, transaction.get(), objectStoreIdentifier, indexIdentifier, newName, WTFMove(callback));
1102 });
1103}
1104
1105void UniqueIDBDatabase::renameIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback callback)
1106{
1107 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
1108 if (!callbackID)
1109 return;
1110
1111 auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
1112 if (!objectStoreInfo) {
1113 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to rename index in non-existant object store"_s });
1114 return;
1115 }
1116
1117 auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
1118 if (!indexInfo) {
1119 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to rename non-existant index"_s });
1120 return;
1121 }
1122
1123 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexIdentifier, newName));
1124}
1125
1126void UniqueIDBDatabase::performRenameIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
1127{
1128 ASSERT(!isMainThread());
1129 LOG(IndexedDB, "(db) UniqueIDBDatabase::performRenameIndex");
1130
1131 ASSERT(m_backingStore);
1132 m_backingStore->renameIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier, newName);
1133
1134 IDBError error;
1135 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformRenameIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier, newName));
1136}
1137
1138void UniqueIDBDatabase::didPerformRenameIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
1139{
1140 ASSERT(isMainThread());
1141 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameIndex");
1142
1143 if (error.isNull()) {
1144 auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
1145 ASSERT(objectStoreInfo);
1146 if (objectStoreInfo) {
1147 auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
1148 ASSERT(indexInfo);
1149 indexInfo->rename(newName);
1150 }
1151 }
1152
1153 performErrorCallback(callbackIdentifier, error);
1154}
1155
1156void UniqueIDBDatabase::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback)
1157{
1158 ASSERT(isMainThread());
1159 LOG(IndexedDB, "(main) UniqueIDBDatabase::putOrAdd");
1160
1161 auto taskSize = defaultWriteOperationCost + estimateSize(keyData) + estimateSize(value);
1162 requestSpace(taskSize, "putOrAdd", [this, taskSize, requestData, keyData, value, callback = WTFMove(callback), overwriteMode](auto error) mutable {
1163 if (error) {
1164 callback(WTFMove(*error), { });
1165 return;
1166 }
1167 this->putOrAddAfterQuotaCheck(taskSize, requestData, keyData, value, overwriteMode, WTFMove(callback));
1168 });
1169}
1170
1171void UniqueIDBDatabase::putOrAddAfterQuotaCheck(uint64_t taskSize, const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback)
1172{
1173 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize);
1174 if (!callbackID)
1175 return;
1176 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPutOrAdd, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData, value, overwriteMode));
1177}
1178
1179VM& UniqueIDBDatabase::databaseThreadVM()
1180{
1181 ASSERT(!isMainThread());
1182 static VM* vm = &VM::create().leakRef();
1183 return *vm;
1184}
1185
1186ExecState& UniqueIDBDatabase::databaseThreadExecState()
1187{
1188 ASSERT(!isMainThread());
1189
1190 static NeverDestroyed<Strong<JSGlobalObject>> globalObject(databaseThreadVM(), JSGlobalObject::create(databaseThreadVM(), JSGlobalObject::createStructure(databaseThreadVM(), jsNull())));
1191
1192 RELEASE_ASSERT(globalObject.get()->globalExec());
1193 return *globalObject.get()->globalExec();
1194}
1195
1196void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, const IDBValue& originalRecordValue, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
1197{
1198 ASSERT(!isMainThread());
1199 LOG(IndexedDB, "(db) UniqueIDBDatabase::performPutOrAdd");
1200
1201 ASSERT(m_backingStore);
1202 ASSERT(objectStoreIdentifier);
1203
1204 IDBKeyData usedKey;
1205 IDBError error;
1206
1207 if (!m_backingStore) {
1208 RELEASE_LOG_ERROR(IndexedDB, "%p - UniqueIDBDatabase::performPutOrAdd: m_backingStore is null", this);
1209 error = IDBError(InvalidStateError, "Backing store is invalid for call to put or add"_s);
1210 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1211 return;
1212 }
1213
1214 auto* objectStoreInfo = m_backingStore->infoForObjectStore(objectStoreIdentifier);
1215 if (!objectStoreInfo) {
1216 error = IDBError(InvalidStateError, "Object store cannot be found in the backing store"_s);
1217 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1218 return;
1219 }
1220
1221 bool usedKeyIsGenerated = false;
1222 uint64_t keyNumber;
1223 auto generatedKeyResetter = WTF::makeScopeExit([this, transactionIdentifier, objectStoreIdentifier, &keyNumber, &usedKeyIsGenerated]() {
1224 if (usedKeyIsGenerated)
1225 m_backingStore->revertGeneratedKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber);
1226 });
1227 if (objectStoreInfo->autoIncrement() && !keyData.isValid()) {
1228 error = m_backingStore->generateKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber);
1229 if (!error.isNull()) {
1230 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1231 return;
1232 }
1233
1234 usedKey.setNumberValue(keyNumber);
1235 usedKeyIsGenerated = true;
1236 } else
1237 usedKey = keyData;
1238
1239 if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) {
1240 bool keyExists;
1241 error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists);
1242 if (error.isNull() && keyExists)
1243 error = IDBError(ConstraintError, "Key already exists in the object store"_s);
1244
1245 if (!error.isNull()) {
1246 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1247 return;
1248 }
1249 }
1250
1251 // 3.4.1 Object Store Storage Operation
1252 // ...If a record already exists in store ...
1253 // then remove the record from store using the steps for deleting records from an object store...
1254 // This is important because formally deleting it from from the object store also removes it from the appropriate indexes.
1255 error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, usedKey);
1256 if (!error.isNull()) {
1257 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1258 return;
1259 }
1260
1261 error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, originalRecordValue);
1262
1263 if (!error.isNull()) {
1264 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1265 return;
1266 }
1267
1268 if (overwriteMode != IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor && objectStoreInfo->autoIncrement() && keyData.type() == IndexedDB::KeyType::Number)
1269 error = m_backingStore->maybeUpdateKeyGeneratorNumber(transactionIdentifier, objectStoreIdentifier, keyData.number());
1270
1271 generatedKeyResetter.release();
1272 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
1273}
1274
1275void UniqueIDBDatabase::didPerformPutOrAdd(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
1276{
1277 ASSERT(isMainThread());
1278 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformPutOrAdd");
1279
1280 performKeyDataCallback(callbackIdentifier, error, resultKey);
1281}
1282
1283void UniqueIDBDatabase::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData, GetResultCallback callback)
1284{
1285 ASSERT(isMainThread());
1286 LOG(IndexedDB, "(main) UniqueIDBDatabase::getRecord");
1287
1288 waitForRequestSpaceCompletion([this, requestData, getRecordData, callback = WTFMove(callback)](auto error) mutable {
1289 if (error) {
1290 callback(WTFMove(*error), { });
1291 return;
1292 }
1293 this->getRecordAfterQuotaCheck(requestData, getRecordData, WTFMove(callback));
1294 });
1295}
1296
1297void UniqueIDBDatabase::getRecordAfterQuotaCheck(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData, GetResultCallback callback)
1298{
1299 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1300 if (!callbackID)
1301 return;
1302
1303 if (uint64_t indexIdentifier = requestData.indexIdentifier())
1304 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetIndexRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), indexIdentifier, requestData.indexRecordType(), getRecordData.keyRangeData));
1305 else
1306 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), getRecordData.keyRangeData, getRecordData.type));
1307}
1308
1309void UniqueIDBDatabase::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData, GetAllResultsCallback callback)
1310{
1311 ASSERT(isMainThread());
1312 LOG(IndexedDB, "(main) UniqueIDBDatabase::getAllRecords");
1313
1314 waitForRequestSpaceCompletion([this, requestData, getAllRecordsData, callback = WTFMove(callback)](auto error) mutable {
1315 if (error) {
1316 callback(WTFMove(*error), { });
1317 return;
1318 }
1319 this->getAllRecordsAfterQuotaCheck(requestData, getAllRecordsData, WTFMove(callback));
1320 });
1321}
1322
1323void UniqueIDBDatabase::getAllRecordsAfterQuotaCheck(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData, GetAllResultsCallback callback)
1324{
1325 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1326 if (!callbackID)
1327 return;
1328
1329 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetAllRecords, callbackID, requestData.transactionIdentifier(), getAllRecordsData));
1330}
1331
1332void UniqueIDBDatabase::performGetRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& keyRangeData, IDBGetRecordDataType type)
1333{
1334 ASSERT(!isMainThread());
1335 LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetRecord");
1336
1337 ASSERT(m_backingStore);
1338
1339 IDBGetResult result;
1340 IDBError error = m_backingStore->getRecord(transactionIdentifier, objectStoreIdentifier, keyRangeData, type, result);
1341
1342 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, result));
1343}
1344
1345void UniqueIDBDatabase::performGetIndexRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range)
1346{
1347 ASSERT(!isMainThread());
1348 LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetIndexRecord");
1349
1350 ASSERT(m_backingStore);
1351
1352 IDBGetResult result;
1353 IDBError error = m_backingStore->getIndexRecord(transactionIdentifier, objectStoreIdentifier, indexIdentifier, recordType, range, result);
1354
1355 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, result));
1356}
1357
1358void UniqueIDBDatabase::didPerformGetRecord(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
1359{
1360 ASSERT(isMainThread());
1361 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetRecord");
1362
1363 performGetResultCallback(callbackIdentifier, error, result);
1364}
1365
1366void UniqueIDBDatabase::performGetAllRecords(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData)
1367{
1368 ASSERT(!isMainThread());
1369 LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetAllRecords");
1370
1371 ASSERT(m_backingStore);
1372
1373 IDBGetAllResult result;
1374 IDBError error = m_backingStore->getAllRecords(transactionIdentifier, getAllRecordsData, result);
1375
1376 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetAllRecords, callbackIdentifier, error, WTFMove(result)));
1377}
1378
1379void UniqueIDBDatabase::didPerformGetAllRecords(uint64_t callbackIdentifier, const IDBError& error, const IDBGetAllResult& result)
1380{
1381 ASSERT(isMainThread());
1382 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetAllRecords");
1383
1384 performGetAllResultsCallback(callbackIdentifier, error, result);
1385}
1386
1387void UniqueIDBDatabase::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& range, CountCallback callback)
1388{
1389 ASSERT(isMainThread());
1390 LOG(IndexedDB, "(main) UniqueIDBDatabase::getCount");
1391
1392 waitForRequestSpaceCompletion([this, requestData, range, callback = WTFMove(callback)](auto error) mutable {
1393 if (error) {
1394 callback(WTFMove(*error), { });
1395 return;
1396 }
1397 this->getCountAfterQuotaCheck(requestData, range, WTFMove(callback));
1398 });
1399}
1400
1401void UniqueIDBDatabase::getCountAfterQuotaCheck(const IDBRequestData& requestData, const IDBKeyRangeData& range, CountCallback callback)
1402{
1403 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1404 if (!callbackID)
1405 return;
1406 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetCount, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), requestData.indexIdentifier(), range));
1407}
1408
1409void UniqueIDBDatabase::performGetCount(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& keyRangeData)
1410{
1411 ASSERT(!isMainThread());
1412 LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetCount");
1413
1414 ASSERT(m_backingStore);
1415 ASSERT(objectStoreIdentifier);
1416
1417 uint64_t count;
1418 IDBError error = m_backingStore->getCount(transactionIdentifier, objectStoreIdentifier, indexIdentifier, keyRangeData, count);
1419
1420 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetCount, callbackIdentifier, error, count));
1421}
1422
1423void UniqueIDBDatabase::didPerformGetCount(uint64_t callbackIdentifier, const IDBError& error, uint64_t count)
1424{
1425 ASSERT(isMainThread());
1426 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetCount");
1427
1428 performCountCallback(callbackIdentifier, error, count);
1429}
1430
1431void UniqueIDBDatabase::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData, ErrorCallback callback)
1432{
1433 ASSERT(isMainThread());
1434 LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteRecord");
1435
1436 waitForRequestSpaceCompletion([this, requestData, keyRangeData, callback = WTFMove(callback)](auto error) mutable {
1437 if (error) {
1438 callback(WTFMove(*error));
1439 return;
1440 }
1441 this->deleteRecordAfterQuotaCheck(requestData, keyRangeData, WTFMove(callback));
1442 });
1443}
1444
1445void UniqueIDBDatabase::deleteRecordAfterQuotaCheck(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData, ErrorCallback callback)
1446{
1447 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1448 if (!callbackID)
1449 return;
1450 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyRangeData));
1451}
1452
1453void UniqueIDBDatabase::performDeleteRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range)
1454{
1455 ASSERT(!isMainThread());
1456 LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteRecord");
1457
1458 IDBError error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, range);
1459
1460 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteRecord, callbackIdentifier, error));
1461}
1462
1463void UniqueIDBDatabase::didPerformDeleteRecord(uint64_t callbackIdentifier, const IDBError& error)
1464{
1465 ASSERT(isMainThread());
1466 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteRecord");
1467
1468 performErrorCallback(callbackIdentifier, error);
1469}
1470
1471void UniqueIDBDatabase::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info, GetResultCallback callback)
1472{
1473 ASSERT(isMainThread());
1474 LOG(IndexedDB, "(main) UniqueIDBDatabase::openCursor");
1475
1476 waitForRequestSpaceCompletion([this, requestData, info, callback = WTFMove(callback)](auto error) mutable {
1477 if (error) {
1478 callback(WTFMove(*error), { });
1479 return;
1480 }
1481 this->openCursorAfterQuotaCheck(requestData, info, WTFMove(callback));
1482 });
1483}
1484
1485void UniqueIDBDatabase::openCursorAfterQuotaCheck(const IDBRequestData& requestData, const IDBCursorInfo& info, GetResultCallback callback)
1486{
1487 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1488 if (!callbackID)
1489 return;
1490 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performOpenCursor, callbackID, requestData.transactionIdentifier(), info));
1491}
1492
1493void UniqueIDBDatabase::performOpenCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info)
1494{
1495 ASSERT(!isMainThread());
1496 LOG(IndexedDB, "(db) UniqueIDBDatabase::performOpenCursor");
1497
1498 IDBGetResult result;
1499 IDBError error = m_backingStore->openCursor(transactionIdentifier, info, result);
1500
1501 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformOpenCursor, callbackIdentifier, error, result));
1502}
1503
1504void UniqueIDBDatabase::didPerformOpenCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
1505{
1506 ASSERT(isMainThread());
1507 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformOpenCursor");
1508
1509 performGetResultCallback(callbackIdentifier, error, result);
1510}
1511
1512void UniqueIDBDatabase::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data, GetResultCallback callback)
1513{
1514 ASSERT(isMainThread());
1515 LOG(IndexedDB, "(main) UniqueIDBDatabase::iterateCursor");
1516
1517 waitForRequestSpaceCompletion([this, requestData, data, callback = WTFMove(callback)](auto error) mutable {
1518 if (error) {
1519 callback(WTFMove(*error), { });
1520 return;
1521 }
1522 this->iterateCursorAfterQuotaCheck(requestData, data, WTFMove(callback));
1523 });
1524}
1525
1526void UniqueIDBDatabase::iterateCursorAfterQuotaCheck(const IDBRequestData& requestData, const IDBIterateCursorData& data, GetResultCallback callback)
1527{
1528 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1529 if (!callbackID)
1530 return;
1531 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performIterateCursor, callbackID, requestData.transactionIdentifier(), requestData.cursorIdentifier(), data));
1532}
1533
1534void UniqueIDBDatabase::performIterateCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data)
1535{
1536 ASSERT(!isMainThread());
1537 LOG(IndexedDB, "(db) UniqueIDBDatabase::performIterateCursor");
1538
1539 IDBGetResult result;
1540 IDBError error = m_backingStore->iterateCursor(transactionIdentifier, cursorIdentifier, data, result);
1541
1542 if (error.isNull()) {
1543 auto addResult = m_cursorPrefetches.add(cursorIdentifier);
1544 if (addResult.isNewEntry)
1545 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier));
1546 }
1547
1548 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformIterateCursor, callbackIdentifier, error, result));
1549}
1550
1551void UniqueIDBDatabase::performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
1552{
1553 ASSERT(!isMainThread());
1554 ASSERT(m_cursorPrefetches.contains(cursorIdentifier));
1555 LOG(IndexedDB, "(db) UniqueIDBDatabase::performPrefetchCursor");
1556
1557 if (m_hardClosedForUserDelete || !m_backingStore->prefetchCursor(transactionIdentifier, cursorIdentifier))
1558 m_cursorPrefetches.remove(cursorIdentifier);
1559 else
1560 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier));
1561}
1562
1563void UniqueIDBDatabase::didPerformIterateCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
1564{
1565 ASSERT(isMainThread());
1566 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformIterateCursor");
1567
1568 performGetResultCallback(callbackIdentifier, error, result);
1569}
1570
1571bool UniqueIDBDatabase::prepareToFinishTransaction(UniqueIDBDatabaseTransaction& transaction, UniqueIDBDatabaseTransaction::State state)
1572{
1573 auto takenTransaction = m_inProgressTransactions.take(transaction.info().identifier());
1574 if (!takenTransaction)
1575 return false;
1576
1577 ASSERT(!m_finishingTransactions.contains(transaction.info().identifier()));
1578 takenTransaction->setState(state);
1579 m_finishingTransactions.set(transaction.info().identifier(), WTFMove(takenTransaction));
1580
1581 return true;
1582}
1583
1584void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
1585{
1586 ASSERT(isMainThread());
1587 LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction - %s", transaction.info().identifier().loggingString().utf8().data());
1588
1589 ASSERT(transaction.databaseConnection().database() == this);
1590
1591 waitForRequestSpaceCompletion([this, transaction = makeRef(transaction), callback = WTFMove(callback)](auto error) mutable {
1592 if (error) {
1593 callback(WTFMove(*error));
1594 return;
1595 }
1596 this->commitTransactionAfterQuotaCheck(transaction, WTFMove(callback));
1597 });
1598}
1599
1600void UniqueIDBDatabase::commitTransactionAfterQuotaCheck(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
1601{
1602 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1603 if (!callbackID)
1604 return;
1605
1606 if (!prepareToFinishTransaction(transaction, UniqueIDBDatabaseTransaction::State::Committing)) {
1607 if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) {
1608 // This database connection is closing or has already closed, so there is no point in messaging back to it about the commit failing.
1609 forgetErrorCallback(callbackID);
1610 return;
1611 }
1612
1613 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to commit transaction that is already finishing"_s });
1614 return;
1615 }
1616
1617 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier()));
1618}
1619
1620void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
1621{
1622 ASSERT(!isMainThread());
1623 LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
1624
1625 IDBError error = m_backingStore->commitTransaction(transactionIdentifier);
1626 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error, transactionIdentifier));
1627}
1628
1629void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
1630{
1631 ASSERT(isMainThread());
1632 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
1633
1634 IDBError result = error;
1635 auto transaction = m_finishingTransactions.get(transactionIdentifier);
1636 switch (transaction->state()) {
1637 case UniqueIDBDatabaseTransaction::State::Aborted:
1638 result = IDBError { UnknownError, "Transaction is already aborted"_s };
1639 break;
1640 case UniqueIDBDatabaseTransaction::State::Committed:
1641 result = transaction->result();
1642 break;
1643 case UniqueIDBDatabaseTransaction::State::Committing:
1644 break;
1645 case UniqueIDBDatabaseTransaction::State::Running:
1646 case UniqueIDBDatabaseTransaction::State::Aborting:
1647 ASSERT_NOT_REACHED();
1648 }
1649
1650 performErrorCallback(callbackIdentifier, result);
1651
1652 transactionCompleted(m_finishingTransactions.take(transactionIdentifier));
1653}
1654
1655void UniqueIDBDatabase::abortTransaction(UniqueIDBDatabaseTransaction& transaction, WaitForPendingTasks waitForPendingTasks, ErrorCallback callback)
1656{
1657 ASSERT(isMainThread());
1658 LOG(IndexedDB, "(main) UniqueIDBDatabase::abortTransaction - %s", transaction.info().identifier().loggingString().utf8().data());
1659
1660 ASSERT(transaction.databaseConnection().database() == this);
1661
1662 if (waitForPendingTasks == WaitForPendingTasks::Yes) {
1663 waitForRequestSpaceCompletion([this, transaction = makeRef(transaction), callback = WTFMove(callback)](auto&& error) mutable {
1664 if (error) {
1665 callback(WTFMove(*error));
1666 return;
1667 }
1668 this->abortTransaction(transaction, WaitForPendingTasks::No, WTFMove(callback));
1669 });
1670 return;
1671 }
1672
1673 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1674 if (!callbackID)
1675 return;
1676
1677 if (!prepareToFinishTransaction(transaction, UniqueIDBDatabaseTransaction::State::Aborting)) {
1678 if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) {
1679 // This database connection is closing or has already closed, so there is no point in messaging back to it about the abort failing.
1680 forgetErrorCallback(callbackID);
1681 return;
1682 }
1683
1684 performErrorCallback(callbackID, IDBError { UnknownError, "Attempt to abort transaction that is already finishing"_s });
1685 return;
1686 }
1687
1688 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performAbortTransaction, callbackID, transaction.info().identifier()));
1689}
1690
1691void UniqueIDBDatabase::didFinishHandlingVersionChange(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& transactionIdentifier)
1692{
1693 ASSERT(isMainThread());
1694 LOG(IndexedDB, "(main) UniqueIDBDatabase::didFinishHandlingVersionChange");
1695
1696 ASSERT_UNUSED(transactionIdentifier, !m_versionChangeTransaction || m_versionChangeTransaction->info().identifier() == transactionIdentifier);
1697 ASSERT_UNUSED(connection, !m_versionChangeDatabaseConnection || m_versionChangeDatabaseConnection.get() == &connection);
1698
1699 m_versionChangeTransaction = nullptr;
1700 m_versionChangeDatabaseConnection = nullptr;
1701
1702 if (m_hardClosedForUserDelete) {
1703 maybeFinishHardClose();
1704 return;
1705 }
1706
1707 invokeOperationAndTransactionTimer();
1708}
1709
1710void UniqueIDBDatabase::performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
1711{
1712 ASSERT(!isMainThread());
1713 LOG(IndexedDB, "(db) UniqueIDBDatabase::performAbortTransaction - %s", transactionIdentifier.loggingString().utf8().data());
1714
1715 IDBError error = m_backingStore->abortTransaction(transactionIdentifier);
1716 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformAbortTransaction, callbackIdentifier, error, transactionIdentifier));
1717}
1718
1719void UniqueIDBDatabase::didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
1720{
1721 ASSERT(isMainThread());
1722 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformAbortTransaction - %s", transactionIdentifier.loggingString().utf8().data());
1723
1724 auto transaction = m_finishingTransactions.take(transactionIdentifier);
1725 ASSERT(transaction);
1726
1727 if (m_versionChangeTransaction && m_versionChangeTransaction->info().identifier() == transactionIdentifier) {
1728 ASSERT(m_versionChangeTransaction == transaction);
1729 ASSERT(!m_versionChangeDatabaseConnection || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
1730 ASSERT(m_versionChangeTransaction->originalDatabaseInfo());
1731 m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*m_versionChangeTransaction->originalDatabaseInfo());
1732 }
1733
1734 IDBError result = transaction->state() == UniqueIDBDatabaseTransaction::State::Aborted ? transaction->result() : error;
1735 performErrorCallback(callbackIdentifier, result);
1736
1737 transactionCompleted(WTFMove(transaction));
1738}
1739
1740void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction)
1741{
1742 if (m_versionChangeTransaction == &transaction)
1743 m_versionChangeTransaction = nullptr;
1744}
1745
1746void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection& connection)
1747{
1748 ASSERT(isMainThread());
1749 LOG(IndexedDB, "(main) UniqueIDBDatabase::connectionClosedFromClient - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier());
1750
1751 if (m_serverClosePendingDatabaseConnections.contains(&connection)) {
1752 m_serverClosePendingDatabaseConnections.remove(&connection);
1753 if (m_hardClosedForUserDelete)
1754 maybeFinishHardClose();
1755 return;
1756 }
1757
1758 Ref<UniqueIDBDatabaseConnection> protectedConnection(connection);
1759 m_openDatabaseConnections.remove(&connection);
1760
1761 if (m_versionChangeDatabaseConnection == &connection) {
1762 if (m_versionChangeTransaction) {
1763 m_clientClosePendingDatabaseConnections.add(WTFMove(m_versionChangeDatabaseConnection));
1764
1765 auto transactionIdentifier = m_versionChangeTransaction->info().identifier();
1766 if (m_inProgressTransactions.contains(transactionIdentifier)) {
1767 ASSERT(!m_finishingTransactions.contains(transactionIdentifier));
1768 connection.abortTransactionWithoutCallback(*m_versionChangeTransaction);
1769 }
1770
1771 return;
1772 }
1773
1774 m_versionChangeDatabaseConnection = nullptr;
1775 }
1776
1777 Deque<RefPtr<UniqueIDBDatabaseTransaction>> pendingTransactions;
1778 while (!m_pendingTransactions.isEmpty()) {
1779 auto transaction = m_pendingTransactions.takeFirst();
1780 if (&transaction->databaseConnection() != &connection)
1781 pendingTransactions.append(WTFMove(transaction));
1782 }
1783
1784 if (!pendingTransactions.isEmpty())
1785 m_pendingTransactions.swap(pendingTransactions);
1786
1787 Deque<RefPtr<UniqueIDBDatabaseTransaction>> transactionsToAbort;
1788 for (auto& transaction : m_inProgressTransactions.values()) {
1789 if (&transaction->databaseConnection() == &connection)
1790 transactionsToAbort.append(transaction);
1791 }
1792
1793 for (auto& transaction : transactionsToAbort)
1794 transaction->abortWithoutCallback();
1795
1796 if (m_currentOpenDBRequest)
1797 notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier());
1798
1799 if (connection.hasNonFinishedTransactions()) {
1800 m_clientClosePendingDatabaseConnections.add(WTFMove(protectedConnection));
1801 return;
1802 }
1803
1804 if (m_hardClosedForUserDelete) {
1805 maybeFinishHardClose();
1806 return;
1807 }
1808
1809 // Now that a database connection has closed, previously blocked operations might be runnable.
1810 invokeOperationAndTransactionTimer();
1811}
1812
1813void UniqueIDBDatabase::connectionClosedFromServer(UniqueIDBDatabaseConnection& connection)
1814{
1815 ASSERT(isMainThread());
1816 LOG(IndexedDB, "UniqueIDBDatabase::connectionClosedFromServer - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier());
1817
1818 if (m_clientClosePendingDatabaseConnections.contains(&connection)) {
1819 ASSERT(!m_openDatabaseConnections.contains(&connection));
1820 ASSERT(!m_serverClosePendingDatabaseConnections.contains(&connection));
1821 return;
1822 }
1823
1824 Ref<UniqueIDBDatabaseConnection> protectedConnection(connection);
1825 m_openDatabaseConnections.remove(&connection);
1826
1827 connection.connectionToClient().didCloseFromServer(connection, IDBError::userDeleteError());
1828
1829 m_serverClosePendingDatabaseConnections.add(WTFMove(protectedConnection));
1830}
1831
1832void UniqueIDBDatabase::confirmDidCloseFromServer(UniqueIDBDatabaseConnection& connection)
1833{
1834 ASSERT(isMainThread());
1835 LOG(IndexedDB, "UniqueIDBDatabase::confirmDidCloseFromServer - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier());
1836
1837 if (!m_serverClosePendingDatabaseConnections.contains(&connection))
1838 return;
1839 m_serverClosePendingDatabaseConnections.remove(&connection);
1840
1841 if (m_hardClosedForUserDelete)
1842 maybeFinishHardClose();
1843}
1844
1845void UniqueIDBDatabase::enqueueTransaction(Ref<UniqueIDBDatabaseTransaction>&& transaction)
1846{
1847 LOG(IndexedDB, "UniqueIDBDatabase::enqueueTransaction - %s", transaction->info().loggingString().utf8().data());
1848 ASSERT(!m_hardClosedForUserDelete);
1849
1850 ASSERT(transaction->info().mode() != IDBTransactionMode::Versionchange);
1851
1852 m_pendingTransactions.append(WTFMove(transaction));
1853
1854 invokeOperationAndTransactionTimer();
1855}
1856
1857bool UniqueIDBDatabase::isCurrentlyInUse() const
1858{
1859 return !m_openDatabaseConnections.isEmpty() || !m_clientClosePendingDatabaseConnections.isEmpty() || !m_pendingOpenDBRequests.isEmpty() || m_currentOpenDBRequest || m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_isOpeningBackingStore || m_deleteBackingStoreInProgress;
1860}
1861
1862bool UniqueIDBDatabase::hasUnfinishedTransactions() const
1863{
1864 return !m_inProgressTransactions.isEmpty() || !m_finishingTransactions.isEmpty();
1865}
1866
1867void UniqueIDBDatabase::invokeOperationAndTransactionTimer()
1868{
1869 LOG(IndexedDB, "UniqueIDBDatabase::invokeOperationAndTransactionTimer()");
1870 RELEASE_ASSERT(!m_hardClosedForUserDelete);
1871 RELEASE_ASSERT(!m_owningPointerForClose);
1872
1873 if (!m_operationAndTransactionTimer.isActive())
1874 m_operationAndTransactionTimer.startOneShot(0_s);
1875}
1876
1877void UniqueIDBDatabase::operationAndTransactionTimerFired()
1878{
1879 LOG(IndexedDB, "(main) UniqueIDBDatabase::operationAndTransactionTimerFired");
1880 ASSERT(!m_hardClosedForUserDelete);
1881 ASSERT(isMainThread());
1882
1883 // This UniqueIDBDatabase might be no longer in use by any web page.
1884 // Assuming it is not ephemeral, the server should now close it to free up resources.
1885 if (!m_backingStoreIsEphemeral && !isCurrentlyInUse()) {
1886 ASSERT(m_pendingTransactions.isEmpty());
1887 ASSERT(!hasUnfinishedTransactions());
1888
1889 scheduleShutdownForClose();
1890 return;
1891 }
1892
1893 // The current operation might require multiple attempts to handle, so try to
1894 // make further progress on it now.
1895 if (m_currentOpenDBRequest && !m_currentOpenDBRequest->connection().isClosed())
1896 handleCurrentOperation();
1897 else
1898 handleDatabaseOperations();
1899
1900 bool hadDeferredTransactions = false;
1901 auto transaction = takeNextRunnableTransaction(hadDeferredTransactions);
1902
1903 if (transaction) {
1904 m_inProgressTransactions.set(transaction->info().identifier(), transaction);
1905 for (auto objectStore : transaction->objectStoreIdentifiers()) {
1906 m_objectStoreTransactionCounts.add(objectStore);
1907 if (!transaction->isReadOnly()) {
1908 m_objectStoreWriteTransactions.add(objectStore);
1909 ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1 || m_hardClosedForUserDelete);
1910 }
1911 }
1912
1913 activateTransactionInBackingStore(*transaction);
1914
1915 // If no transactions were deferred, it's possible we can start another transaction right now.
1916 if (!hadDeferredTransactions)
1917 invokeOperationAndTransactionTimer();
1918 }
1919}
1920
1921void UniqueIDBDatabase::activateTransactionInBackingStore(UniqueIDBDatabaseTransaction& transaction)
1922{
1923 LOG(IndexedDB, "(main) UniqueIDBDatabase::activateTransactionInBackingStore");
1924 ASSERT(isMainThread());
1925
1926 RefPtr<UniqueIDBDatabaseTransaction> refTransaction(&transaction);
1927
1928 ErrorCallback callback = [refTransaction](const IDBError& error) {
1929 refTransaction->didActivateInBackingStore(error);
1930 };
1931
1932 uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback));
1933 if (!callbackID)
1934 return;
1935 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performActivateTransactionInBackingStore, callbackID, transaction.info()));
1936}
1937
1938void UniqueIDBDatabase::performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo& info)
1939{
1940 LOG(IndexedDB, "(db) UniqueIDBDatabase::performActivateTransactionInBackingStore");
1941
1942 IDBError error = m_backingStore->beginTransaction(info);
1943 postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformActivateTransactionInBackingStore, callbackIdentifier, error));
1944}
1945
1946void UniqueIDBDatabase::didPerformActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBError& error)
1947{
1948 LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformActivateTransactionInBackingStore");
1949
1950 if (m_hardClosedForUserDelete)
1951 return;
1952
1953 invokeOperationAndTransactionTimer();
1954
1955 performErrorCallback(callbackIdentifier, error);
1956}
1957
1958template<typename T> bool scopesOverlap(const T& aScopes, const Vector<uint64_t>& bScopes)
1959{
1960 for (auto scope : bScopes) {
1961 if (aScopes.contains(scope))
1962 return true;
1963 }
1964
1965 return false;
1966}
1967
1968RefPtr<UniqueIDBDatabaseTransaction> UniqueIDBDatabase::takeNextRunnableTransaction(bool& hadDeferredTransactions)
1969{
1970 hadDeferredTransactions = false;
1971
1972 if (m_pendingTransactions.isEmpty())
1973 return nullptr;
1974
1975 if (!m_backingStoreSupportsSimultaneousTransactions && hasUnfinishedTransactions()) {
1976 LOG(IndexedDB, "UniqueIDBDatabase::takeNextRunnableTransaction - Backing store only supports 1 transaction, and we already have 1");
1977 return nullptr;
1978 }
1979
1980 Deque<RefPtr<UniqueIDBDatabaseTransaction>> deferredTransactions;
1981 RefPtr<UniqueIDBDatabaseTransaction> currentTransaction;
1982
1983 HashSet<uint64_t> deferredReadWriteScopes;
1984
1985 while (!m_pendingTransactions.isEmpty()) {
1986 currentTransaction = m_pendingTransactions.takeFirst();
1987
1988 switch (currentTransaction->info().mode()) {
1989 case IDBTransactionMode::Readonly: {
1990 bool hasOverlappingScopes = scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers());
1991 hasOverlappingScopes |= scopesOverlap(m_objectStoreWriteTransactions, currentTransaction->objectStoreIdentifiers());
1992
1993 if (hasOverlappingScopes)
1994 deferredTransactions.append(WTFMove(currentTransaction));
1995
1996 break;
1997 }
1998 case IDBTransactionMode::Readwrite: {
1999 bool hasOverlappingScopes = scopesOverlap(m_objectStoreTransactionCounts, currentTransaction->objectStoreIdentifiers());
2000 hasOverlappingScopes |= scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers());
2001
2002 if (hasOverlappingScopes) {
2003 for (auto objectStore : currentTransaction->objectStoreIdentifiers())
2004 deferredReadWriteScopes.add(objectStore);
2005 deferredTransactions.append(WTFMove(currentTransaction));
2006 }
2007
2008 break;
2009 }
2010 case IDBTransactionMode::Versionchange:
2011 // Version change transactions should never be scheduled in the traditional manner.
2012 RELEASE_ASSERT_NOT_REACHED();
2013 }
2014
2015 // If we didn't defer the currentTransaction above, it can be run now.
2016 if (currentTransaction)
2017 break;
2018 }
2019
2020 hadDeferredTransactions = !deferredTransactions.isEmpty();
2021 if (!hadDeferredTransactions)
2022 return currentTransaction;
2023
2024 // Prepend the deferred transactions back on the beginning of the deque for future scheduling passes.
2025 while (!deferredTransactions.isEmpty())
2026 m_pendingTransactions.prepend(deferredTransactions.takeLast());
2027
2028 return currentTransaction;
2029}
2030
2031void UniqueIDBDatabase::transactionCompleted(RefPtr<UniqueIDBDatabaseTransaction>&& transaction)
2032{
2033 ASSERT(transaction);
2034 ASSERT(!m_inProgressTransactions.contains(transaction->info().identifier()));
2035 ASSERT(!m_finishingTransactions.contains(transaction->info().identifier()));
2036 ASSERT(isMainThread());
2037
2038 for (auto objectStore : transaction->objectStoreIdentifiers()) {
2039 if (!transaction->isReadOnly()) {
2040 m_objectStoreWriteTransactions.remove(objectStore);
2041 ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1 || m_hardClosedForUserDelete);
2042 }
2043 m_objectStoreTransactionCounts.remove(objectStore);
2044 }
2045
2046 if (!transaction->databaseConnection().hasNonFinishedTransactions())
2047 m_clientClosePendingDatabaseConnections.remove(&transaction->databaseConnection());
2048
2049 if (m_versionChangeTransaction == transaction)
2050 m_versionChangeTransaction = nullptr;
2051
2052 // It's possible that this database had its backing store deleted but there were a few outstanding asynchronous operations.
2053 // If this transaction completing was the last of those operations, we can finally delete this UniqueIDBDatabase.
2054 if (m_clientClosePendingDatabaseConnections.isEmpty() && m_pendingOpenDBRequests.isEmpty() && !m_databaseInfo) {
2055 scheduleShutdownForClose();
2056 return;
2057 }
2058
2059 // Previously blocked operations might be runnable.
2060 if (!m_hardClosedForUserDelete)
2061 invokeOperationAndTransactionTimer();
2062 else
2063 maybeFinishHardClose();
2064}
2065
2066void UniqueIDBDatabase::postDatabaseTask(CrossThreadTask&& task)
2067{
2068 m_databaseQueue.append(WTFMove(task));
2069 m_server->postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTask));
2070}
2071
2072void UniqueIDBDatabase::postDatabaseTaskReply(CrossThreadTask&& task)
2073{
2074 // FIXME: We might want to compute total size only for modification operations.
2075 if (m_backingStore)
2076 m_databasesSizeForOrigin = m_backingStore->databasesSizeForOrigin();
2077 m_databaseReplyQueue.append(WTFMove(task));
2078 m_server->postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTaskReply));
2079}
2080
2081void UniqueIDBDatabase::executeNextDatabaseTask()
2082{
2083 ASSERT(!isMainThread());
2084 ASSERT(!m_databaseQueue.isKilled());
2085
2086 auto task = m_databaseQueue.tryGetMessage();
2087 ASSERT(task);
2088
2089 task->performTask();
2090}
2091
2092void UniqueIDBDatabase::executeNextDatabaseTaskReply()
2093{
2094 ASSERT(isMainThread());
2095 ASSERT(!m_databaseReplyQueue.isKilled());
2096
2097 auto task = m_databaseReplyQueue.tryGetMessage();
2098 ASSERT(task);
2099
2100 task->performTask();
2101
2102 // If this database was force closed (e.g. for a user delete) and there are no more
2103 // cleanup tasks left, delete this.
2104 maybeFinishHardClose();
2105}
2106
2107void UniqueIDBDatabase::maybeFinishHardClose()
2108{
2109 if (m_owningPointerForClose && isDoneWithHardClose()) {
2110 if (m_owningPointerReleaseScheduled)
2111 return;
2112 m_owningPointerReleaseScheduled = true;
2113
2114 callOnMainThread([this] {
2115 ASSERT(isDoneWithHardClose());
2116 m_owningPointerForClose = nullptr;
2117 });
2118 }
2119}
2120
2121bool UniqueIDBDatabase::isDoneWithHardClose()
2122{
2123 return m_databaseReplyQueue.isKilled() && m_clientClosePendingDatabaseConnections.isEmpty() && m_serverClosePendingDatabaseConnections.isEmpty();
2124}
2125
2126static void errorOpenDBRequestForUserDelete(ServerOpenDBRequest& request)
2127{
2128 auto result = IDBResultData::error(request.requestData().requestIdentifier(), IDBError::userDeleteError());
2129 if (request.isOpenRequest())
2130 request.connection().didOpenDatabase(result);
2131 else
2132 request.connection().didDeleteDatabase(result);
2133}
2134
2135void UniqueIDBDatabase::immediateCloseForUserDelete()
2136{
2137 LOG(IndexedDB, "UniqueIDBDatabase::immediateCloseForUserDelete - Cancelling (%i, %i, %i, %i) callbacks", m_errorCallbacks.size(), m_keyDataCallbacks.size(), m_getResultCallbacks.size(), m_countCallbacks.size());
2138
2139 ASSERT(isMainThread());
2140
2141 m_pendingSpaceIncreasingTasks.clear();
2142 m_server->resetSpaceUsed(m_identifier.origin());
2143
2144 // Error out all transactions
2145 for (auto& identifier : copyToVector(m_inProgressTransactions.keys()))
2146 m_inProgressTransactions.get(identifier)->abortWithoutCallback();
2147
2148 ASSERT(m_inProgressTransactions.isEmpty());
2149
2150 for (auto& transaction : m_pendingTransactions)
2151 transaction->databaseConnection().deleteTransaction(*transaction);
2152 m_pendingTransactions.clear();
2153 m_objectStoreTransactionCounts.clear();
2154 m_objectStoreWriteTransactions.clear();
2155
2156 // Error out all pending callbacks
2157 IDBError error = IDBError::userDeleteError();
2158 IDBKeyData keyData;
2159 IDBGetResult getResult;
2160 IDBGetAllResult getAllResult;
2161
2162 while (!m_callbackQueue.isEmpty()) {
2163 auto identifier = m_callbackQueue.first();
2164 if (m_errorCallbacks.contains(identifier))
2165 performErrorCallback(identifier, error);
2166 else if (m_keyDataCallbacks.contains(identifier))
2167 performKeyDataCallback(identifier, error, keyData);
2168 else if (m_getResultCallbacks.contains(identifier))
2169 performGetResultCallback(identifier, error, getResult);
2170 else if (m_countCallbacks.contains(identifier))
2171 performCountCallback(identifier, error, 0);
2172 else if (m_getAllResultsCallbacks.contains(identifier))
2173 performGetAllResultsCallback(identifier, error, getAllResult);
2174 else
2175 ASSERT_NOT_REACHED();
2176 }
2177
2178 // Error out all IDBOpenDBRequests
2179 if (m_currentOpenDBRequest) {
2180 errorOpenDBRequestForUserDelete(*m_currentOpenDBRequest);
2181 m_currentOpenDBRequest = nullptr;
2182 }
2183
2184 for (auto& request : m_pendingOpenDBRequests)
2185 errorOpenDBRequestForUserDelete(*request);
2186
2187 m_pendingOpenDBRequests.clear();
2188
2189 // Close all open connections
2190 auto openDatabaseConnections = m_openDatabaseConnections;
2191 for (auto& connection : openDatabaseConnections)
2192 connectionClosedFromServer(*connection);
2193
2194 // Cancel the operation timer
2195 m_operationAndTransactionTimer.stop();
2196
2197 // Set up the database to remain alive-but-inert until all of its background activity finishes and all
2198 // database connections confirm that they have closed.
2199 m_hardClosedForUserDelete = true;
2200
2201 // If this database already owns itself, it is already closing on the background thread.
2202 // After that close completes, the next database thread task will be "delete all currently closed databases"
2203 // which will also cover this database.
2204 if (m_owningPointerForClose)
2205 return;
2206
2207 // Otherwise, this database is still potentially active.
2208 // So we'll have it own itself and then perform a clean unconditional delete on the background thread.
2209 m_owningPointerForClose = m_server->closeAndTakeUniqueIDBDatabase(*this);
2210 postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performUnconditionalDeleteBackingStore));
2211}
2212
2213void UniqueIDBDatabase::updateSpaceUsedIfNeeded(uint64_t callbackIdentifier)
2214{
2215 auto iterator = m_pendingSpaceIncreasingTasks.find(callbackIdentifier);
2216 if (iterator == m_pendingSpaceIncreasingTasks.end())
2217 return;
2218
2219 m_server->decreasePotentialSpaceUsed(m_identifier.origin(), iterator->value);
2220 m_server->setSpaceUsed(m_identifier.origin(), m_databasesSizeForOrigin);
2221 m_pendingSpaceIncreasingTasks.remove(iterator);
2222}
2223
2224void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
2225{
2226 updateSpaceUsedIfNeeded(callbackIdentifier);
2227
2228 auto callback = m_errorCallbacks.take(callbackIdentifier);
2229 ASSERT(callback || m_hardClosedForUserDelete);
2230 if (callback) {
2231 callback(error);
2232 ASSERT(m_callbackQueue.first() == callbackIdentifier);
2233 m_callbackQueue.removeFirst();
2234 }
2235}
2236
2237void UniqueIDBDatabase::performKeyDataCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
2238{
2239 updateSpaceUsedIfNeeded(callbackIdentifier);
2240
2241 auto callback = m_keyDataCallbacks.take(callbackIdentifier);
2242 ASSERT(callback || m_hardClosedForUserDelete);
2243 if (callback) {
2244 callback(error, resultKey);
2245 ASSERT(m_callbackQueue.first() == callbackIdentifier);
2246 m_callbackQueue.removeFirst();
2247 }
2248}
2249
2250void UniqueIDBDatabase::performGetResultCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& resultData)
2251{
2252 auto callback = m_getResultCallbacks.take(callbackIdentifier);
2253 ASSERT(callback || m_hardClosedForUserDelete);
2254 if (callback) {
2255 callback(error, resultData);
2256 ASSERT(m_callbackQueue.first() == callbackIdentifier);
2257 m_callbackQueue.removeFirst();
2258 }
2259}
2260
2261void UniqueIDBDatabase::performGetAllResultsCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBGetAllResult& resultData)
2262{
2263 auto callback = m_getAllResultsCallbacks.take(callbackIdentifier);
2264 ASSERT(callback || m_hardClosedForUserDelete);
2265 if (callback) {
2266 callback(error, resultData);
2267 ASSERT(m_callbackQueue.first() == callbackIdentifier);
2268 m_callbackQueue.removeFirst();
2269 }
2270}
2271
2272void UniqueIDBDatabase::performCountCallback(uint64_t callbackIdentifier, const IDBError& error, uint64_t count)
2273{
2274 auto callback = m_countCallbacks.take(callbackIdentifier);
2275 ASSERT(callback || m_hardClosedForUserDelete);
2276 if (callback) {
2277 callback(error, count);
2278 ASSERT(m_callbackQueue.first() == callbackIdentifier);
2279 m_callbackQueue.removeFirst();
2280 }
2281}
2282
2283void UniqueIDBDatabase::forgetErrorCallback(uint64_t callbackIdentifier)
2284{
2285 updateSpaceUsedIfNeeded(callbackIdentifier);
2286
2287 ASSERT(m_errorCallbacks.contains(callbackIdentifier));
2288 ASSERT(m_callbackQueue.last() == callbackIdentifier);
2289 m_callbackQueue.removeLast();
2290 m_errorCallbacks.remove(callbackIdentifier);
2291}
2292
2293void UniqueIDBDatabase::setQuota(uint64_t quota)
2294{
2295 if (m_backingStore)
2296 m_backingStore->setQuota(quota);
2297}
2298
2299void UniqueIDBDatabase::abortTransactionOnMainThread(UniqueIDBDatabaseTransaction& transaction)
2300{
2301 transaction.setResult(m_backingStore->abortTransaction(transaction.info().identifier()));
2302 transaction.setState(UniqueIDBDatabaseTransaction::State::Aborted);
2303}
2304
2305void UniqueIDBDatabase::commitTransactionOnMainThread(UniqueIDBDatabaseTransaction& transaction)
2306{
2307 transaction.setResult(m_backingStore->commitTransaction(transaction.info().identifier()));
2308 transaction.setState(UniqueIDBDatabaseTransaction::State::Committed);
2309}
2310
2311void UniqueIDBDatabase::finishActiveTransactions()
2312{
2313 ASSERT(isMainThread());
2314
2315 for (auto& identifier : copyToVector(m_inProgressTransactions.keys())) {
2316 auto transaction = m_inProgressTransactions.get(identifier);
2317 abortTransactionOnMainThread(*transaction);
2318 }
2319
2320 for (auto& identifier : copyToVector(m_finishingTransactions.keys())) {
2321 if (!m_backingStore->hasTransaction(identifier))
2322 continue;
2323
2324 auto transaction = m_finishingTransactions.get(identifier);
2325 switch (transaction->state()) {
2326 case UniqueIDBDatabaseTransaction::State::Aborting:
2327 abortTransactionOnMainThread(*transaction);
2328 break;
2329 case UniqueIDBDatabaseTransaction::State::Committing:
2330 commitTransactionOnMainThread(*transaction);
2331 break;
2332 case UniqueIDBDatabaseTransaction::State::Running:
2333 case UniqueIDBDatabaseTransaction::State::Aborted:
2334 case UniqueIDBDatabaseTransaction::State::Committed:
2335 ASSERT_NOT_REACHED();
2336 }
2337 }
2338}
2339
2340} // namespace IDBServer
2341} // namespace WebCore
2342
2343#endif // ENABLE(INDEXED_DATABASE)
2344