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