IndexedDB 2.0: Prefetch cursor records in the server.
[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 "SQLiteFileSystem.h"
36 #include "SQLiteIDBBackingStore.h"
37 #include "SecurityOrigin.h"
38 #include <wtf/CrossThreadCopier.h>
39 #include <wtf/Locker.h>
40 #include <wtf/MainThread.h>
41
42 namespace WebCore {
43 namespace IDBServer {
44
45 Ref<IDBServer> IDBServer::create(IDBBackingStoreTemporaryFileHandler& fileHandler)
46 {
47     return adoptRef(*new IDBServer(fileHandler));
48 }
49
50 Ref<IDBServer> IDBServer::create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
51 {
52     return adoptRef(*new IDBServer(databaseDirectoryPath, fileHandler));
53 }
54
55 IDBServer::IDBServer(IDBBackingStoreTemporaryFileHandler& fileHandler)
56     : m_backingStoreTemporaryFileHandler(fileHandler)
57 {
58     Locker<Lock> locker(m_databaseThreadCreationLock);
59     m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server");
60 }
61
62 IDBServer::IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
63     : m_databaseDirectoryPath(databaseDirectoryPath)
64     , m_backingStoreTemporaryFileHandler(fileHandler)
65 {
66     LOG(IndexedDB, "IDBServer created at path %s", databaseDirectoryPath.utf8().data());
67
68     Locker<Lock> locker(m_databaseThreadCreationLock);
69     m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server");
70 }
71
72 void IDBServer::registerConnection(IDBConnectionToClient& connection)
73 {
74     ASSERT(!m_connectionMap.contains(connection.identifier()));
75     m_connectionMap.set(connection.identifier(), &connection);
76 }
77
78 void IDBServer::unregisterConnection(IDBConnectionToClient& connection)
79 {
80     ASSERT(m_connectionMap.contains(connection.identifier()));
81     ASSERT(m_connectionMap.get(connection.identifier()) == &connection);
82
83     connection.connectionToClientClosed();
84
85     m_connectionMap.remove(connection.identifier());
86 }
87
88 void IDBServer::registerTransaction(UniqueIDBDatabaseTransaction& transaction)
89 {
90     ASSERT(!m_transactions.contains(transaction.info().identifier()));
91     m_transactions.set(transaction.info().identifier(), &transaction);
92 }
93
94 void IDBServer::unregisterTransaction(UniqueIDBDatabaseTransaction& transaction)
95 {
96     ASSERT(m_transactions.contains(transaction.info().identifier()));
97     ASSERT(m_transactions.get(transaction.info().identifier()) == &transaction);
98
99     m_transactions.remove(transaction.info().identifier());
100 }
101
102 void IDBServer::registerDatabaseConnection(UniqueIDBDatabaseConnection& connection)
103 {
104     ASSERT(!m_databaseConnections.contains(connection.identifier()));
105     m_databaseConnections.set(connection.identifier(), &connection);
106 }
107
108 void IDBServer::unregisterDatabaseConnection(UniqueIDBDatabaseConnection& connection)
109 {
110     ASSERT(m_databaseConnections.contains(connection.identifier()));
111     m_databaseConnections.remove(connection.identifier());
112 }
113
114 UniqueIDBDatabase& IDBServer::getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier& identifier)
115 {
116     ASSERT(isMainThread());
117
118     auto uniqueIDBDatabase = m_uniqueIDBDatabaseMap.add(identifier, nullptr);
119     if (uniqueIDBDatabase.isNewEntry)
120         uniqueIDBDatabase.iterator->value = UniqueIDBDatabase::create(*this, identifier);
121
122     return *uniqueIDBDatabase.iterator->value;
123 }
124
125 std::unique_ptr<IDBBackingStore> IDBServer::createBackingStore(const IDBDatabaseIdentifier& identifier)
126 {
127     ASSERT(!isMainThread());
128
129     if (m_databaseDirectoryPath.isEmpty())
130         return MemoryIDBBackingStore::create(identifier);
131
132     return std::make_unique<SQLiteIDBBackingStore>(identifier, m_databaseDirectoryPath, m_backingStoreTemporaryFileHandler);
133 }
134
135 void IDBServer::openDatabase(const IDBRequestData& requestData)
136 {
137     LOG(IndexedDB, "IDBServer::openDatabase");
138
139     auto& uniqueIDBDatabase = getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier());
140
141     auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier());
142     if (!connection) {
143         // If the connection back to the client is gone, there's no way to open the database as
144         // well as no way to message back failure.
145         return;
146     }
147
148     uniqueIDBDatabase.openDatabaseConnection(*connection, requestData);
149 }
150
151 void IDBServer::deleteDatabase(const IDBRequestData& requestData)
152 {
153     LOG(IndexedDB, "IDBServer::deleteDatabase - %s", requestData.databaseIdentifier().debugString().utf8().data());
154     ASSERT(isMainThread());
155
156     auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier());
157     if (!connection) {
158         // If the connection back to the client is gone, there's no way to delete the database as
159         // well as no way to message back failure.
160         return;
161     }
162
163     auto* database = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier());
164     if (!database)
165         database = &getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier());
166
167     database->handleDelete(*connection, requestData);
168 }
169
170 void IDBServer::closeUniqueIDBDatabase(UniqueIDBDatabase& database)
171 {
172     LOG(IndexedDB, "IDBServer::closeUniqueIDBDatabase");
173     ASSERT(isMainThread());
174
175     m_uniqueIDBDatabaseMap.remove(database.identifier());
176 }
177
178 void IDBServer::abortTransaction(const IDBResourceIdentifier& transactionIdentifier)
179 {
180     LOG(IndexedDB, "IDBServer::abortTransaction");
181
182     auto transaction = m_transactions.get(transactionIdentifier);
183     if (!transaction) {
184         // If there is no transaction there is nothing to abort.
185         // We also have no access to a connection over which to message failure-to-abort.
186         return;
187     }
188
189     transaction->abort();
190 }
191
192 void IDBServer::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info)
193 {
194     LOG(IndexedDB, "IDBServer::createObjectStore");
195
196     auto transaction = m_transactions.get(requestData.transactionIdentifier());
197     if (!transaction)
198         return;
199
200     ASSERT(transaction->isVersionChange());
201     transaction->createObjectStore(requestData, info);
202 }
203
204 void IDBServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName)
205 {
206     LOG(IndexedDB, "IDBServer::deleteObjectStore");
207
208     auto transaction = m_transactions.get(requestData.transactionIdentifier());
209     if (!transaction)
210         return;
211
212     ASSERT(transaction->isVersionChange());
213     transaction->deleteObjectStore(requestData, objectStoreName);
214 }
215
216 void IDBServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
217 {
218     LOG(IndexedDB, "IDBServer::renameObjectStore");
219
220     auto transaction = m_transactions.get(requestData.transactionIdentifier());
221     if (!transaction)
222         return;
223
224     ASSERT(transaction->isVersionChange());
225     transaction->renameObjectStore(requestData, objectStoreIdentifier, newName);
226 }
227
228 void IDBServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier)
229 {
230     LOG(IndexedDB, "IDBServer::clearObjectStore");
231
232     auto transaction = m_transactions.get(requestData.transactionIdentifier());
233     if (!transaction)
234         return;
235
236     transaction->clearObjectStore(requestData, objectStoreIdentifier);
237 }
238
239 void IDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info)
240 {
241     LOG(IndexedDB, "IDBServer::createIndex");
242
243     auto transaction = m_transactions.get(requestData.transactionIdentifier());
244     if (!transaction)
245         return;
246
247     ASSERT(transaction->isVersionChange());
248     transaction->createIndex(requestData, info);
249 }
250
251 void IDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
252 {
253     LOG(IndexedDB, "IDBServer::deleteIndex");
254
255     auto transaction = m_transactions.get(requestData.transactionIdentifier());
256     if (!transaction)
257         return;
258
259     ASSERT(transaction->isVersionChange());
260     transaction->deleteIndex(requestData, objectStoreIdentifier, indexName);
261 }
262
263 void IDBServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
264 {
265     LOG(IndexedDB, "IDBServer::renameIndex");
266
267     auto transaction = m_transactions.get(requestData.transactionIdentifier());
268     if (!transaction)
269         return;
270
271     ASSERT(transaction->isVersionChange());
272     transaction->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName);
273 }
274
275 void IDBServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
276 {
277     LOG(IndexedDB, "IDBServer::putOrAdd");
278
279     auto transaction = m_transactions.get(requestData.transactionIdentifier());
280     if (!transaction)
281         return;
282
283     transaction->putOrAdd(requestData, keyData, value, overwriteMode);
284 }
285
286 void IDBServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData)
287 {
288     LOG(IndexedDB, "IDBServer::getRecord");
289
290     auto transaction = m_transactions.get(requestData.transactionIdentifier());
291     if (!transaction)
292         return;
293
294     transaction->getRecord(requestData, getRecordData);
295 }
296
297 void IDBServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData)
298 {
299     LOG(IndexedDB, "IDBServer::getAllRecords");
300
301     auto transaction = m_transactions.get(requestData.transactionIdentifier());
302     if (!transaction)
303         return;
304
305     transaction->getAllRecords(requestData, getAllRecordsData);
306 }
307
308 void IDBServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
309 {
310     LOG(IndexedDB, "IDBServer::getCount");
311
312     auto transaction = m_transactions.get(requestData.transactionIdentifier());
313     if (!transaction)
314         return;
315
316     transaction->getCount(requestData, keyRangeData);
317 }
318
319 void IDBServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
320 {
321     LOG(IndexedDB, "IDBServer::deleteRecord");
322
323     auto transaction = m_transactions.get(requestData.transactionIdentifier());
324     if (!transaction)
325         return;
326
327     transaction->deleteRecord(requestData, keyRangeData);
328 }
329
330 void IDBServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info)
331 {
332     LOG(IndexedDB, "IDBServer::openCursor");
333
334     auto transaction = m_transactions.get(requestData.transactionIdentifier());
335     if (!transaction)
336         return;
337
338     transaction->openCursor(requestData, info);
339 }
340
341 void IDBServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data)
342 {
343     LOG(IndexedDB, "IDBServer::iterateCursor");
344
345     auto transaction = m_transactions.get(requestData.transactionIdentifier());
346     if (!transaction)
347         return;
348
349     transaction->iterateCursor(requestData, data);
350 }
351
352 void IDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info)
353 {
354     LOG(IndexedDB, "IDBServer::establishTransaction");
355
356     auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
357     if (!databaseConnection)
358         return;
359
360     databaseConnection->establishTransaction(info);
361 }
362
363 void IDBServer::commitTransaction(const IDBResourceIdentifier& transactionIdentifier)
364 {
365     LOG(IndexedDB, "IDBServer::commitTransaction");
366
367     auto transaction = m_transactions.get(transactionIdentifier);
368     if (!transaction) {
369         // If there is no transaction there is nothing to commit.
370         // We also have no access to a connection over which to message failure-to-commit.
371         return;
372     }
373
374     transaction->commit();
375 }
376
377 void IDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
378 {
379     LOG(IndexedDB, "IDBServer::didFinishHandlingVersionChangeTransaction - %s", transactionIdentifier.loggingString().utf8().data());
380
381     auto* connection = m_databaseConnections.get(databaseConnectionIdentifier);
382     if (!connection)
383         return;
384
385     connection->didFinishHandlingVersionChange(transactionIdentifier);
386 }
387
388 void IDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier)
389 {
390     LOG(IndexedDB, "IDBServer::databaseConnectionPendingClose - %" PRIu64, databaseConnectionIdentifier);
391
392     auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
393     if (!databaseConnection)
394         return;
395
396     databaseConnection->connectionPendingCloseFromClient();
397 }
398
399 void IDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier)
400 {
401     LOG(IndexedDB, "IDBServer::databaseConnectionClosed - %" PRIu64, databaseConnectionIdentifier);
402
403     auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
404     if (!databaseConnection)
405         return;
406
407     databaseConnection->connectionClosedFromClient();
408 }
409
410 void IDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
411 {
412     LOG(IndexedDB, "IDBServer::abortOpenAndUpgradeNeeded");
413
414     auto transaction = m_transactions.get(transactionIdentifier);
415     if (transaction)
416         transaction->abortWithoutCallback();
417
418     auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
419     if (!databaseConnection)
420         return;
421
422     databaseConnection->connectionClosedFromClient();
423 }
424
425 void IDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
426 {
427     LOG(IndexedDB, "IDBServer::didFireVersionChangeEvent");
428
429     if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier))
430         databaseConnection->didFireVersionChangeEvent(requestIdentifier);
431 }
432
433 void IDBServer::openDBRequestCancelled(const IDBRequestData& requestData)
434 {
435     LOG(IndexedDB, "IDBServer::openDBRequestCancelled");
436     ASSERT(isMainThread());
437
438     auto* uniqueIDBDatabase = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier());
439     if (!uniqueIDBDatabase)
440         return;
441
442     uniqueIDBDatabase->openDBRequestCancelled(requestData.requestIdentifier());
443 }
444
445 void IDBServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier)
446 {
447     LOG(IndexedDB, "IDBServer::confirmDidCloseFromServer");
448
449     if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier))
450         databaseConnection->confirmDidCloseFromServer();
451 }
452
453 void IDBServer::getAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
454 {
455     postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performGetAllDatabaseNames, serverConnectionIdentifier, mainFrameOrigin, openingOrigin, callbackID));
456 }
457
458 void IDBServer::performGetAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
459 {
460     String directory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(mainFrameOrigin, openingOrigin, m_databaseDirectoryPath);
461
462     Vector<String> entries = listDirectory(directory, ASCIILiteral("*"));
463     Vector<String> databases;
464     databases.reserveInitialCapacity(entries.size());
465     for (auto& entry : entries) {
466         String encodedName = lastComponentOfPathIgnoringTrailingSlash(entry);
467         databases.uncheckedAppend(SQLiteIDBBackingStore::databaseNameFromEncodedFilename(encodedName));
468     }
469
470     postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didGetAllDatabaseNames, serverConnectionIdentifier, callbackID, databases));
471 }
472
473 void IDBServer::didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint64_t callbackID, const Vector<String>& databaseNames)
474 {
475     auto connection = m_connectionMap.get(serverConnectionIdentifier);
476     if (!connection)
477         return;
478
479     connection->didGetAllDatabaseNames(callbackID, databaseNames);
480 }
481
482 void IDBServer::postDatabaseTask(CrossThreadTask&& task)
483 {
484     m_databaseQueue.append(WTFMove(task));
485 }
486
487 void IDBServer::postDatabaseTaskReply(CrossThreadTask&& task)
488 {
489     ASSERT(!isMainThread());
490     m_databaseReplyQueue.append(WTFMove(task));
491
492
493     Locker<Lock> locker(m_mainThreadReplyLock);
494     if (m_mainThreadReplyScheduled)
495         return;
496
497     m_mainThreadReplyScheduled = true;
498     callOnMainThread([this] {
499         handleTaskRepliesOnMainThread();
500     });
501 }
502
503 void IDBServer::databaseThreadEntry(void* threadData)
504 {
505     ASSERT(threadData);
506     IDBServer* server = reinterpret_cast<IDBServer*>(threadData);
507     server->databaseRunLoop();
508 }
509
510 void IDBServer::databaseRunLoop()
511 {
512     ASSERT(!isMainThread());
513     {
514         Locker<Lock> locker(m_databaseThreadCreationLock);
515     }
516
517     while (!m_databaseQueue.isKilled())
518         m_databaseQueue.waitForMessage().performTask();
519 }
520
521 void IDBServer::handleTaskRepliesOnMainThread()
522 {
523     {
524         Locker<Lock> locker(m_mainThreadReplyLock);
525         m_mainThreadReplyScheduled = false;
526     }
527
528     while (auto task = m_databaseReplyQueue.tryGetMessage())
529         task->performTask();
530 }
531
532 static uint64_t generateDeleteCallbackID()
533 {
534     ASSERT(isMainThread());
535     static uint64_t currentID = 0;
536     return ++currentID;
537 }
538
539 void IDBServer::closeAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point modificationTime, std::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     // If the modification time is in the future, don't both doing anything.
546     if (modificationTime > std::chrono::system_clock::now()) {
547         postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
548         return;
549     }
550
551     HashSet<RefPtr<UniqueIDBDatabase>> openDatabases;
552     for (auto* connection : m_databaseConnections.values())
553         openDatabases.add(&connection->database());
554
555     for (auto& database : openDatabases)
556         database->immediateCloseForUserDelete();
557
558     postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesModifiedSince, modificationTime, callbackID));
559 }
560
561 void IDBServer::closeAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, std::function<void ()> completionHandler)
562 {
563     uint64_t callbackID = generateDeleteCallbackID();
564     auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler));
565     ASSERT_UNUSED(addResult, addResult.isNewEntry);
566
567     HashSet<RefPtr<UniqueIDBDatabase>> openDatabases;
568     for (auto* connection : m_databaseConnections.values()) {
569         const auto& identifier = connection->database().identifier();
570         for (auto& origin : origins) {
571             if (identifier.isRelatedToOrigin(origin)) {
572                 openDatabases.add(&connection->database());
573                 break;
574             }
575         }
576     }
577
578     for (auto& database : openDatabases)
579         database->immediateCloseForUserDelete();
580
581     postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesForOrigins, origins, callbackID));
582 }
583
584 static void removeAllDatabasesForOriginPath(const String& originPath, std::chrono::system_clock::time_point modifiedSince)
585 {
586     Vector<String> databasePaths = listDirectory(originPath, "*");
587
588     for (auto& databasePath : databasePaths) {
589         String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3");
590
591         if (modifiedSince > std::chrono::system_clock::time_point::min() && fileExists(databaseFile)) {
592             time_t modificationTime;
593             if (!getFileModificationTime(databaseFile, modificationTime))
594                 continue;
595
596             if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince)
597                 continue;
598         }
599
600         // Deleting this database means we need to delete all files that represent it.
601         // This includes:
602         //     - The directory itself, which is named after the database.
603         //     - IndexedDB.sqlite3 and related SQLite files.
604         //     - Blob files that we stored in the directory.
605         //
606         // To be conservative, we should *not* try to delete files that are unexpected;
607         // We should only delete files we think we put there.
608         //
609         // IndexedDB blob files are named "N.blob" where N is a decimal integer,
610         // so those are the only blob files we should be trying to delete.
611         for (auto& blobPath : listDirectory(databasePath, "[0-9]*.blob")) {
612             // Globbing can't give us only filenames starting with 1-or-more digits.
613             // The above globbing gives us files that start with a digit and ends with ".blob", but there might be non-digits in between.
614             // We need to validate that each filename contains only digits before deleting it, as any other files are not ones we put there.
615             String filename = pathGetFileName(blobPath);
616             auto filenameLength = filename.length();
617
618             ASSERT(filenameLength >= 6);
619             ASSERT(filename.endsWith(".blob"));
620
621             if (filename.length() < 6)
622                 continue;
623             if (!filename.endsWith(".blob"))
624                 continue;
625
626             bool validFilename = true;
627             for (unsigned i = 0; i < filenameLength - 5; ++i) {
628                 if (!isASCIIDigit(filename[i])) {
629                     validFilename = false;
630                     break;
631                 }
632             }
633
634             if (validFilename)
635                 deleteFile(blobPath);
636         }
637
638         // Now delete IndexedDB.sqlite3 and related SQLite files.
639         SQLiteFileSystem::deleteDatabaseFile(databaseFile);
640
641         // And finally, if we can, delete the empty directory.
642         deleteEmptyDirectory(databasePath);
643     }
644
645     // If no databases remain for this origin, we can delete the origin directory as well.
646     deleteEmptyDirectory(originPath);
647 }
648
649 void IDBServer::performCloseAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID)
650 {
651     if (!m_databaseDirectoryPath.isEmpty()) {
652         Vector<String> originPaths = listDirectory(m_databaseDirectoryPath, "*");
653         for (auto& originPath : originPaths)
654             removeAllDatabasesForOriginPath(originPath, modifiedSince);
655     }
656
657     postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
658 }
659
660 void IDBServer::performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, uint64_t callbackID)
661 {
662     if (!m_databaseDirectoryPath.isEmpty()) {
663         for (const auto& origin : origins) {
664             String originPath = pathByAppendingComponent(m_databaseDirectoryPath, origin.databaseIdentifier());
665             removeAllDatabasesForOriginPath(originPath, std::chrono::system_clock::time_point::min());
666         }
667     }
668
669     postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
670 }
671
672 void IDBServer::didPerformCloseAndDeleteDatabases(uint64_t callbackID)
673 {
674     auto callback = m_deleteDatabaseCompletionHandlers.take(callbackID);
675     ASSERT(callback);
676     callback();
677 }
678
679 } // namespace IDBServer
680 } // namespace WebCore
681
682 #endif // ENABLE(INDEXED_DATABASE)