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