1/*
2 * Copyright (C) 2015 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "IDBServer.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "IDBRequestData.h"
32#include "IDBResultData.h"
33#include "Logging.h"
34#include "MemoryIDBBackingStore.h"
35#include "SQLiteDatabase.h"
36#include "SQLiteDatabaseTracker.h"
37#include "SQLiteFileSystem.h"
38#include "SQLiteIDBBackingStore.h"
39#include "SQLiteStatement.h"
40#include "SecurityOrigin.h"
41#include "StorageQuotaManager.h"
42#include <wtf/CrossThreadCopier.h>
43#include <wtf/Locker.h>
44#include <wtf/MainThread.h>
45
46namespace WebCore {
47namespace IDBServer {
48
49Ref<IDBServer> IDBServer::create(PAL::SessionID sessionID, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter)
50{
51 return adoptRef(*new IDBServer(sessionID, fileHandler, WTFMove(quotaManagerGetter)));
52}
53
54Ref<IDBServer> IDBServer::create(PAL::SessionID sessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter)
55{
56 return adoptRef(*new IDBServer(sessionID, databaseDirectoryPath, fileHandler, WTFMove(quotaManagerGetter)));
57}
58
59IDBServer::IDBServer(PAL::SessionID sessionID, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter)
60 : CrossThreadTaskHandler("IndexedDatabase Server")
61 , m_sessionID(sessionID)
62 , m_backingStoreTemporaryFileHandler(fileHandler)
63 , m_quotaManagerGetter(WTFMove(quotaManagerGetter))
64{
65}
66
67IDBServer::IDBServer(PAL::SessionID sessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter)
68 : CrossThreadTaskHandler("IndexedDatabase Server")
69 , m_sessionID(sessionID)
70 , m_databaseDirectoryPath(databaseDirectoryPath)
71 , m_backingStoreTemporaryFileHandler(fileHandler)
72 , m_quotaManagerGetter(WTFMove(quotaManagerGetter))
73{
74 LOG(IndexedDB, "IDBServer created at path %s", databaseDirectoryPath.utf8().data());
75 postDatabaseTask(createCrossThreadTask(*this, &IDBServer::upgradeFilesIfNecessary));
76}
77
78void IDBServer::registerConnection(IDBConnectionToClient& connection)
79{
80 ASSERT(!m_connectionMap.contains(connection.identifier()));
81 m_connectionMap.set(connection.identifier(), &connection);
82}
83
84void IDBServer::unregisterConnection(IDBConnectionToClient& connection)
85{
86 ASSERT(m_connectionMap.contains(connection.identifier()));
87 ASSERT(m_connectionMap.get(connection.identifier()) == &connection);
88
89 connection.connectionToClientClosed();
90
91 m_connectionMap.remove(connection.identifier());
92}
93
94void IDBServer::registerTransaction(UniqueIDBDatabaseTransaction& transaction)
95{
96 ASSERT(!m_transactions.contains(transaction.info().identifier()));
97 m_transactions.set(transaction.info().identifier(), &transaction);
98}
99
100void IDBServer::unregisterTransaction(UniqueIDBDatabaseTransaction& transaction)
101{
102 ASSERT(m_transactions.contains(transaction.info().identifier()));
103 ASSERT(m_transactions.get(transaction.info().identifier()) == &transaction);
104
105 m_transactions.remove(transaction.info().identifier());
106}
107
108void IDBServer::registerDatabaseConnection(UniqueIDBDatabaseConnection& connection)
109{
110 ASSERT(!m_databaseConnections.contains(connection.identifier()));
111 m_databaseConnections.set(connection.identifier(), &connection);
112}
113
114void IDBServer::unregisterDatabaseConnection(UniqueIDBDatabaseConnection& connection)
115{
116 ASSERT(m_databaseConnections.contains(connection.identifier()));
117 m_databaseConnections.remove(connection.identifier());
118}
119
120UniqueIDBDatabase& IDBServer::getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier& identifier)
121{
122 ASSERT(isMainThread());
123
124 auto uniqueIDBDatabase = m_uniqueIDBDatabaseMap.add(identifier, nullptr);
125 if (uniqueIDBDatabase.isNewEntry)
126 uniqueIDBDatabase.iterator->value = std::make_unique<UniqueIDBDatabase>(*this, identifier);
127
128 return *uniqueIDBDatabase.iterator->value;
129}
130
131std::unique_ptr<IDBBackingStore> IDBServer::createBackingStore(const IDBDatabaseIdentifier& identifier)
132{
133 ASSERT(!isMainThread());
134
135 if (m_databaseDirectoryPath.isEmpty())
136 return MemoryIDBBackingStore::create(identifier);
137
138 return std::make_unique<SQLiteIDBBackingStore>(identifier, m_databaseDirectoryPath, m_backingStoreTemporaryFileHandler, m_perOriginQuota);
139}
140
141void IDBServer::openDatabase(const IDBRequestData& requestData)
142{
143 LOG(IndexedDB, "IDBServer::openDatabase");
144
145 auto& uniqueIDBDatabase = getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier());
146
147 auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier());
148 if (!connection) {
149 // If the connection back to the client is gone, there's no way to open the database as
150 // well as no way to message back failure.
151 return;
152 }
153
154 uniqueIDBDatabase.openDatabaseConnection(*connection, requestData);
155}
156
157void IDBServer::deleteDatabase(const IDBRequestData& requestData)
158{
159 LOG(IndexedDB, "IDBServer::deleteDatabase - %s", requestData.databaseIdentifier().debugString().utf8().data());
160 ASSERT(isMainThread());
161
162 auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier());
163 if (!connection) {
164 // If the connection back to the client is gone, there's no way to delete the database as
165 // well as no way to message back failure.
166 return;
167 }
168
169 auto* database = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier());
170 if (!database)
171 database = &getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier());
172
173 database->handleDelete(*connection, requestData);
174}
175
176std::unique_ptr<UniqueIDBDatabase> IDBServer::closeAndTakeUniqueIDBDatabase(UniqueIDBDatabase& database)
177{
178 LOG(IndexedDB, "IDBServer::closeUniqueIDBDatabase");
179 ASSERT(isMainThread());
180
181 auto uniquePointer = m_uniqueIDBDatabaseMap.take(database.identifier());
182 ASSERT(uniquePointer);
183
184 return uniquePointer;
185}
186
187void IDBServer::abortTransaction(const IDBResourceIdentifier& transactionIdentifier)
188{
189 LOG(IndexedDB, "IDBServer::abortTransaction");
190
191 auto transaction = m_transactions.get(transactionIdentifier);
192 if (!transaction) {
193 // If there is no transaction there is nothing to abort.
194 // We also have no access to a connection over which to message failure-to-abort.
195 return;
196 }
197
198 transaction->abort();
199}
200
201void IDBServer::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info)
202{
203 LOG(IndexedDB, "IDBServer::createObjectStore");
204
205 auto transaction = m_transactions.get(requestData.transactionIdentifier());
206 if (!transaction)
207 return;
208
209 ASSERT(transaction->isVersionChange());
210 transaction->createObjectStore(requestData, info);
211}
212
213void IDBServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName)
214{
215 LOG(IndexedDB, "IDBServer::deleteObjectStore");
216
217 auto transaction = m_transactions.get(requestData.transactionIdentifier());
218 if (!transaction)
219 return;
220
221 ASSERT(transaction->isVersionChange());
222 transaction->deleteObjectStore(requestData, objectStoreName);
223}
224
225void IDBServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
226{
227 LOG(IndexedDB, "IDBServer::renameObjectStore");
228
229 auto transaction = m_transactions.get(requestData.transactionIdentifier());
230 if (!transaction)
231 return;
232
233 ASSERT(transaction->isVersionChange());
234 transaction->renameObjectStore(requestData, objectStoreIdentifier, newName);
235}
236
237void IDBServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier)
238{
239 LOG(IndexedDB, "IDBServer::clearObjectStore");
240
241 auto transaction = m_transactions.get(requestData.transactionIdentifier());
242 if (!transaction)
243 return;
244
245 transaction->clearObjectStore(requestData, objectStoreIdentifier);
246}
247
248void IDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info)
249{
250 LOG(IndexedDB, "IDBServer::createIndex");
251
252 auto transaction = m_transactions.get(requestData.transactionIdentifier());
253 if (!transaction)
254 return;
255
256 ASSERT(transaction->isVersionChange());
257 transaction->createIndex(requestData, info);
258}
259
260void IDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
261{
262 LOG(IndexedDB, "IDBServer::deleteIndex");
263
264 auto transaction = m_transactions.get(requestData.transactionIdentifier());
265 if (!transaction)
266 return;
267
268 ASSERT(transaction->isVersionChange());
269 transaction->deleteIndex(requestData, objectStoreIdentifier, indexName);
270}
271
272void IDBServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
273{
274 LOG(IndexedDB, "IDBServer::renameIndex");
275
276 auto transaction = m_transactions.get(requestData.transactionIdentifier());
277 if (!transaction)
278 return;
279
280 ASSERT(transaction->isVersionChange());
281 transaction->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName);
282}
283
284void IDBServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
285{
286 LOG(IndexedDB, "IDBServer::putOrAdd");
287
288 auto transaction = m_transactions.get(requestData.transactionIdentifier());
289 if (!transaction)
290 return;
291
292 transaction->putOrAdd(requestData, keyData, value, overwriteMode);
293}
294
295void IDBServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData)
296{
297 LOG(IndexedDB, "IDBServer::getRecord");
298
299 auto transaction = m_transactions.get(requestData.transactionIdentifier());
300 if (!transaction)
301 return;
302
303 transaction->getRecord(requestData, getRecordData);
304}
305
306void IDBServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData)
307{
308 LOG(IndexedDB, "IDBServer::getAllRecords");
309
310 auto transaction = m_transactions.get(requestData.transactionIdentifier());
311 if (!transaction)
312 return;
313
314 transaction->getAllRecords(requestData, getAllRecordsData);
315}
316
317void IDBServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
318{
319 LOG(IndexedDB, "IDBServer::getCount");
320
321 auto transaction = m_transactions.get(requestData.transactionIdentifier());
322 if (!transaction)
323 return;
324
325 transaction->getCount(requestData, keyRangeData);
326}
327
328void IDBServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
329{
330 LOG(IndexedDB, "IDBServer::deleteRecord");
331
332 auto transaction = m_transactions.get(requestData.transactionIdentifier());
333 if (!transaction)
334 return;
335
336 transaction->deleteRecord(requestData, keyRangeData);
337}
338
339void IDBServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info)
340{
341 LOG(IndexedDB, "IDBServer::openCursor");
342
343 auto transaction = m_transactions.get(requestData.transactionIdentifier());
344 if (!transaction)
345 return;
346
347 transaction->openCursor(requestData, info);
348}
349
350void IDBServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data)
351{
352 LOG(IndexedDB, "IDBServer::iterateCursor");
353
354 auto transaction = m_transactions.get(requestData.transactionIdentifier());
355 if (!transaction)
356 return;
357
358 transaction->iterateCursor(requestData, data);
359}
360
361void IDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info)
362{
363 LOG(IndexedDB, "IDBServer::establishTransaction");
364
365 auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
366 if (!databaseConnection)
367 return;
368
369 databaseConnection->establishTransaction(info);
370}
371
372void IDBServer::commitTransaction(const IDBResourceIdentifier& transactionIdentifier)
373{
374 LOG(IndexedDB, "IDBServer::commitTransaction");
375
376 auto transaction = m_transactions.get(transactionIdentifier);
377 if (!transaction) {
378 // If there is no transaction there is nothing to commit.
379 // We also have no access to a connection over which to message failure-to-commit.
380 return;
381 }
382
383 transaction->commit();
384}
385
386void IDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
387{
388 LOG(IndexedDB, "IDBServer::didFinishHandlingVersionChangeTransaction - %s", transactionIdentifier.loggingString().utf8().data());
389
390 auto* connection = m_databaseConnections.get(databaseConnectionIdentifier);
391 if (!connection)
392 return;
393
394 connection->didFinishHandlingVersionChange(transactionIdentifier);
395}
396
397void IDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier)
398{
399 LOG(IndexedDB, "IDBServer::databaseConnectionPendingClose - %" PRIu64, databaseConnectionIdentifier);
400
401 auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
402 if (!databaseConnection)
403 return;
404
405 databaseConnection->connectionPendingCloseFromClient();
406}
407
408void IDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier)
409{
410 LOG(IndexedDB, "IDBServer::databaseConnectionClosed - %" PRIu64, databaseConnectionIdentifier);
411
412 auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
413 if (!databaseConnection)
414 return;
415
416 databaseConnection->connectionClosedFromClient();
417}
418
419void IDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
420{
421 LOG(IndexedDB, "IDBServer::abortOpenAndUpgradeNeeded");
422
423 auto transaction = m_transactions.get(transactionIdentifier);
424 if (transaction)
425 transaction->abortWithoutCallback();
426
427 auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
428 if (!databaseConnection)
429 return;
430
431 databaseConnection->connectionClosedFromClient();
432}
433
434void IDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
435{
436 LOG(IndexedDB, "IDBServer::didFireVersionChangeEvent");
437
438 if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier))
439 databaseConnection->didFireVersionChangeEvent(requestIdentifier);
440}
441
442void IDBServer::openDBRequestCancelled(const IDBRequestData& requestData)
443{
444 LOG(IndexedDB, "IDBServer::openDBRequestCancelled");
445 ASSERT(isMainThread());
446
447 auto* uniqueIDBDatabase = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier());
448 if (!uniqueIDBDatabase)
449 return;
450
451 uniqueIDBDatabase->openDBRequestCancelled(requestData.requestIdentifier());
452}
453
454void IDBServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier)
455{
456 LOG(IndexedDB, "IDBServer::confirmDidCloseFromServer");
457
458 if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier))
459 databaseConnection->confirmDidCloseFromServer();
460}
461
462void IDBServer::getAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
463{
464 postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performGetAllDatabaseNames, serverConnectionIdentifier, mainFrameOrigin, openingOrigin, callbackID));
465}
466
467void IDBServer::performGetAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
468{
469 String oldDirectory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(mainFrameOrigin, openingOrigin, m_databaseDirectoryPath, "v0");
470 Vector<String> files = FileSystem::listDirectory(oldDirectory, "*"_s);
471 Vector<String> databases;
472 for (auto& file : files) {
473 String encodedName = FileSystem::lastComponentOfPathIgnoringTrailingSlash(file);
474 databases.append(SQLiteIDBBackingStore::databaseNameFromEncodedFilename(encodedName));
475 }
476
477 String directory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(mainFrameOrigin, openingOrigin, m_databaseDirectoryPath, "v1");
478 files = FileSystem::listDirectory(directory, "*"_s);
479 for (auto& file : files) {
480 auto databaseName = SQLiteIDBBackingStore::databaseNameFromFile(SQLiteIDBBackingStore::fullDatabasePathForDirectory(file));
481 if (!databaseName.isEmpty())
482 databases.append(databaseName);
483 }
484
485 postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didGetAllDatabaseNames, serverConnectionIdentifier, callbackID, databases));
486}
487
488void IDBServer::didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint64_t callbackID, const Vector<String>& databaseNames)
489{
490 auto connection = m_connectionMap.get(serverConnectionIdentifier);
491 if (!connection)
492 return;
493
494 connection->didGetAllDatabaseNames(callbackID, databaseNames);
495}
496
497void IDBServer::postDatabaseTask(CrossThreadTask&& task)
498{
499 postTask(WTFMove(task));
500}
501
502void IDBServer::postDatabaseTaskReply(CrossThreadTask&& task)
503{
504 postTaskReply(WTFMove(task));
505}
506
507static uint64_t generateDeleteCallbackID()
508{
509 ASSERT(isMainThread());
510 static uint64_t currentID = 0;
511 return ++currentID;
512}
513
514void IDBServer::closeAndDeleteDatabasesModifiedSince(WallTime modificationTime, Function<void ()>&& completionHandler)
515{
516 uint64_t callbackID = generateDeleteCallbackID();
517 auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler));
518 ASSERT_UNUSED(addResult, addResult.isNewEntry);
519
520 // If the modification time is in the future, don't both doing anything.
521 if (modificationTime > WallTime::now()) {
522 postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
523 return;
524 }
525
526 HashSet<UniqueIDBDatabase*> openDatabases;
527 for (auto& database : m_uniqueIDBDatabaseMap.values())
528 openDatabases.add(database.get());
529
530 for (auto& database : openDatabases)
531 database->immediateCloseForUserDelete();
532
533 postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesModifiedSince, modificationTime, callbackID));
534}
535
536void IDBServer::closeAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, Function<void ()>&& completionHandler)
537{
538 uint64_t callbackID = generateDeleteCallbackID();
539 auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler));
540 ASSERT_UNUSED(addResult, addResult.isNewEntry);
541
542 HashSet<UniqueIDBDatabase*> openDatabases;
543 for (auto& database : m_uniqueIDBDatabaseMap.values()) {
544 const auto& identifier = database->identifier();
545 for (auto& origin : origins) {
546 if (identifier.isRelatedToOrigin(origin)) {
547 openDatabases.add(database.get());
548 break;
549 }
550 }
551 }
552
553 for (auto& database : openDatabases)
554 database->immediateCloseForUserDelete();
555
556 postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesForOrigins, origins, callbackID));
557}
558
559static void removeAllDatabasesForFullOriginPath(const String& originPath, WallTime modifiedSince)
560{
561 LOG(IndexedDB, "removeAllDatabasesForOriginPath with originPath %s", originPath.utf8().data());
562 Vector<String> databasePaths = FileSystem::listDirectory(originPath, "*");
563
564 for (auto& databasePath : databasePaths) {
565 String databaseFile = FileSystem::pathByAppendingComponent(databasePath, "IndexedDB.sqlite3");
566 if (modifiedSince > -WallTime::infinity() && FileSystem::fileExists(databaseFile)) {
567 auto modificationTime = FileSystem::getFileModificationTime(databaseFile);
568 if (!modificationTime)
569 continue;
570
571 if (modificationTime.value() < modifiedSince)
572 continue;
573 }
574
575 // Deleting this database means we need to delete all files that represent it.
576 // This includes:
577 // - The directory itself, which is named after the database.
578 // - IndexedDB.sqlite3 and related SQLite files.
579 // - Blob files that we stored in the directory.
580 //
581 // To be conservative, we should *not* try to delete files that are unexpected;
582 // We should only delete files we think we put there.
583 //
584 // IndexedDB blob files are named "N.blob" where N is a decimal integer,
585 // so those are the only blob files we should be trying to delete.
586 for (auto& blobPath : FileSystem::listDirectory(databasePath, "[0-9]*.blob")) {
587 // Globbing can't give us only filenames starting with 1-or-more digits.
588 // The above globbing gives us files that start with a digit and ends with ".blob", but there might be non-digits in between.
589 // We need to validate that each filename contains only digits before deleting it, as any other files are not ones we put there.
590 String filename = FileSystem::pathGetFileName(blobPath);
591 auto filenameLength = filename.length();
592
593 ASSERT(filenameLength >= 6);
594 ASSERT(filename.endsWith(".blob"));
595
596 if (filename.length() < 6)
597 continue;
598 if (!filename.endsWith(".blob"))
599 continue;
600
601 bool validFilename = true;
602 for (unsigned i = 0; i < filenameLength - 5; ++i) {
603 if (!isASCIIDigit(filename[i])) {
604 validFilename = false;
605 break;
606 }
607 }
608
609 if (validFilename)
610 FileSystem::deleteFile(blobPath);
611 }
612
613 // Now delete IndexedDB.sqlite3 and related SQLite files.
614 SQLiteFileSystem::deleteDatabaseFile(databaseFile);
615
616 // And finally, if we can, delete the empty directory.
617 FileSystem::deleteEmptyDirectory(databasePath);
618 }
619
620 // If no databases remain for this origin, we can delete the origin directory as well.
621 FileSystem::deleteEmptyDirectory(originPath);
622}
623
624static void removeAllDatabasesForOriginPath(const String& originPath, WallTime modifiedSince)
625{
626 String databaseIdentifier = FileSystem::lastComponentOfPathIgnoringTrailingSlash(originPath);
627 if (!SecurityOriginData::fromDatabaseIdentifier(databaseIdentifier))
628 return;
629
630 auto directories = FileSystem::listDirectory(originPath, "*"_s);
631 for (auto& directory : directories) {
632 String databaseIdentifier = FileSystem::lastComponentOfPathIgnoringTrailingSlash(directory);
633 if (auto securityOrigin = SecurityOriginData::fromDatabaseIdentifier(databaseIdentifier))
634 removeAllDatabasesForFullOriginPath(directory, modifiedSince);
635 }
636
637 removeAllDatabasesForFullOriginPath(originPath, modifiedSince);
638}
639
640void IDBServer::removeDatabasesModifiedSinceForVersion(WallTime modifiedSince, const String& version)
641{
642 String versionPath = FileSystem::pathByAppendingComponent(m_databaseDirectoryPath, version);
643 for (auto& originPath : FileSystem::listDirectory(versionPath, "*")) {
644 String databaseIdentifier = FileSystem::lastComponentOfPathIgnoringTrailingSlash(originPath);
645 if (auto securityOrigin = SecurityOriginData::fromDatabaseIdentifier(databaseIdentifier))
646 removeAllDatabasesForOriginPath(originPath, modifiedSince);
647 }
648}
649
650void IDBServer::performCloseAndDeleteDatabasesModifiedSince(WallTime modifiedSince, uint64_t callbackID)
651{
652 if (!m_databaseDirectoryPath.isEmpty()) {
653 removeDatabasesModifiedSinceForVersion(modifiedSince, "v0");
654 removeDatabasesModifiedSinceForVersion(modifiedSince, "v1");
655 }
656
657 postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
658}
659
660void IDBServer::removeDatabasesWithOriginsForVersion(const Vector<SecurityOriginData> &origins, const String& version)
661{
662 String versionPath = FileSystem::pathByAppendingComponent(m_databaseDirectoryPath, version);
663 for (const auto& origin : origins) {
664 String originPath = FileSystem::pathByAppendingComponent(versionPath, origin.databaseIdentifier());
665 removeAllDatabasesForOriginPath(originPath, -WallTime::infinity());
666
667 for (auto& topOriginPath : FileSystem::listDirectory(versionPath, "*")) {
668 originPath = FileSystem::pathByAppendingComponent(topOriginPath, origin.databaseIdentifier());
669 removeAllDatabasesForOriginPath(originPath, -WallTime::infinity());
670 }
671 }
672}
673
674void IDBServer::performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, uint64_t callbackID)
675{
676 if (!m_databaseDirectoryPath.isEmpty()) {
677 removeDatabasesWithOriginsForVersion(origins, "v0");
678 removeDatabasesWithOriginsForVersion(origins, "v1");
679 }
680
681 postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
682}
683
684void IDBServer::didPerformCloseAndDeleteDatabases(uint64_t callbackID)
685{
686 for (auto& user : m_quotaUsers.values())
687 user->resetSpaceUsed();
688
689 auto callback = m_deleteDatabaseCompletionHandlers.take(callbackID);
690 ASSERT(callback);
691 callback();
692}
693
694void IDBServer::setPerOriginQuota(uint64_t quota)
695{
696 m_perOriginQuota = quota;
697
698 for (auto& database : m_uniqueIDBDatabaseMap.values())
699 database->setQuota(quota);
700}
701
702IDBServer::QuotaUser::QuotaUser(IDBServer& server, StorageQuotaManager* manager, ClientOrigin&& origin)
703 : m_server(server)
704 , m_manager(makeWeakPtr(manager))
705 , m_origin(WTFMove(origin))
706 , m_isInitialized(m_server.m_sessionID.isEphemeral())
707{
708 if (manager)
709 manager->addUser(*this);
710}
711
712IDBServer::QuotaUser::~QuotaUser()
713{
714 if (m_manager)
715 m_manager->removeUser(*this);
716}
717
718void IDBServer::QuotaUser::resetSpaceUsed()
719{
720 m_spaceUsed = 0;
721 m_estimatedSpaceIncrease = 0;
722
723 if (!m_manager)
724 return;
725
726 if (m_server.m_sessionID.isEphemeral())
727 return;
728
729 if (!m_isInitialized)
730 return;
731
732 ASSERT(!m_initializationCallback);
733
734 m_isInitialized = false;
735
736 // Do add/remove to trigger call to whenInitialized.
737 m_manager->removeUser(*this);
738 m_manager->addUser(*this);
739}
740
741void IDBServer::QuotaUser::whenInitialized(CompletionHandler<void()>&& callback)
742{
743 if (m_isInitialized) {
744 callback();
745 return;
746 }
747 m_initializationCallback = WTFMove(callback);
748 m_server.startComputingSpaceUsedForOrigin(m_origin);
749}
750
751void IDBServer::QuotaUser::initializeSpaceUsed(uint64_t spaceUsed)
752{
753 ASSERT(m_isInitialized || !m_estimatedSpaceIncrease);
754 m_spaceUsed = spaceUsed;
755 m_isInitialized = true;
756
757 if (auto callback = WTFMove(m_initializationCallback))
758 callback();
759}
760
761IDBServer::QuotaUser& IDBServer::ensureQuotaUser(const ClientOrigin& origin)
762{
763 return *m_quotaUsers.ensure(origin, [this, &origin] {
764 return std::make_unique<QuotaUser>(*this, m_quotaManagerGetter(m_sessionID, origin), ClientOrigin { origin });
765 }).iterator->value;
766}
767
768void IDBServer::startComputingSpaceUsedForOrigin(const ClientOrigin& origin)
769{
770 ASSERT(!m_sessionID.isEphemeral());
771 postDatabaseTask(createCrossThreadTask(*this, &IDBServer::computeSpaceUsedForOrigin, origin));
772}
773
774void IDBServer::computeSpaceUsedForOrigin(const ClientOrigin& origin)
775{
776 ASSERT(!isMainThread());
777
778 auto oldVersionOriginDirectory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(origin.topOrigin, origin.clientOrigin, m_databaseDirectoryPath, "v0");
779 auto newVersionOriginDirectory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(origin.topOrigin, origin.clientOrigin, m_databaseDirectoryPath, "v1");
780 auto size = SQLiteIDBBackingStore::databasesSizeForFolder(oldVersionOriginDirectory) + SQLiteIDBBackingStore::databasesSizeForFolder(newVersionOriginDirectory);
781
782 postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::finishComputingSpaceUsedForOrigin, origin, size));
783}
784
785void IDBServer::finishComputingSpaceUsedForOrigin(const ClientOrigin& origin, uint64_t spaceUsed)
786{
787 ensureQuotaUser(origin).initializeSpaceUsed(spaceUsed);
788}
789
790void IDBServer::requestSpace(const ClientOrigin& origin, uint64_t taskSize, CompletionHandler<void(StorageQuotaManager::Decision)>&& callback)
791{
792 auto* quotaManager = ensureQuotaUser(origin).manager();
793 if (!quotaManager) {
794 callback(StorageQuotaManager::Decision::Deny);
795 return;
796 }
797
798 quotaManager->requestSpace(taskSize, WTFMove(callback));
799}
800
801void IDBServer::resetSpaceUsed(const ClientOrigin& origin)
802{
803 if (auto* user = m_quotaUsers.get(origin))
804 user->resetSpaceUsed();
805}
806
807void IDBServer::setSpaceUsed(const ClientOrigin& origin, uint64_t taskSize)
808{
809 ensureQuotaUser(origin).setSpaceUsed(taskSize);
810}
811
812void IDBServer::increasePotentialSpaceUsed(const ClientOrigin& origin, uint64_t taskSize)
813{
814 ensureQuotaUser(origin).increasePotentialSpaceUsed(taskSize);
815}
816
817void IDBServer::decreasePotentialSpaceUsed(const ClientOrigin& origin, uint64_t spaceUsed)
818{
819 ensureQuotaUser(origin).decreasePotentialSpaceUsed(spaceUsed);
820}
821
822void IDBServer::upgradeFilesIfNecessary()
823{
824 if (m_databaseDirectoryPath.isEmpty() || !FileSystem::fileExists(m_databaseDirectoryPath))
825 return;
826
827 String newVersionDirectory = FileSystem::pathByAppendingComponent(m_databaseDirectoryPath, "v1");
828 if (!FileSystem::fileExists(newVersionDirectory))
829 FileSystem::makeAllDirectories(newVersionDirectory);
830}
831
832void IDBServer::tryStop(ShouldForceStop shouldForceStop)
833{
834 // Only stop non-ephemeral IDBServers that can hold locked database files.
835 if (m_sessionID.isEphemeral())
836 return;
837
838 suspendAndWait();
839 if (shouldForceStop == ShouldForceStop::No && SQLiteDatabaseTracker::hasTransactionInProgress()) {
840 CrossThreadTaskHandler::resume();
841 return;
842 }
843
844 for (auto& database : m_uniqueIDBDatabaseMap.values())
845 database->finishActiveTransactions();
846}
847
848void IDBServer::resume()
849{
850 if (m_sessionID.isEphemeral())
851 return;
852
853 CrossThreadTaskHandler::resume();
854}
855
856} // namespace IDBServer
857} // namespace WebCore
858
859#endif // ENABLE(INDEXED_DATABASE)
860