Fix CompletionHandler assertions introduced today.
[WebKit-https.git] / Source / WebKit / NetworkProcess / cache / CacheStorageEngine.cpp
1 /*
2  * Copyright (C) 2017 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CacheStorageEngine.h"
28
29 #include "Logging.h"
30 #include "NetworkCacheFileSystem.h"
31 #include "NetworkCacheIOChannel.h"
32 #include "NetworkProcess.h"
33 #include "WebsiteDataType.h"
34 #include <WebCore/CacheQueryOptions.h>
35 #include <WebCore/SecurityOrigin.h>
36 #include <pal/SessionID.h>
37 #include <wtf/CallbackAggregator.h>
38 #include <wtf/NeverDestroyed.h>
39 #include <wtf/text/StringBuilder.h>
40 #include <wtf/text/StringHash.h>
41
42 namespace WebKit {
43
44 namespace CacheStorage {
45
46 using namespace WebCore::DOMCacheEngine;
47 using namespace NetworkCache;
48
49 String Engine::cachesRootPath(const WebCore::ClientOrigin& origin)
50 {
51     if (!shouldPersist())
52         return { };
53
54     Key key(origin.topOrigin.toString(), origin.clientOrigin.toString(), { }, { }, salt());
55     return WebCore::FileSystem::pathByAppendingComponent(rootPath(), key.hashAsString());
56 }
57
58 Engine::~Engine()
59 {
60     for (auto& caches : m_caches.values())
61         caches->detach();
62
63     auto initializationCallbacks = WTFMove(m_initializationCallbacks);
64     for (auto& callback : initializationCallbacks)
65         callback(Error::Internal);
66
67     auto writeCallbacks = WTFMove(m_pendingWriteCallbacks);
68     for (auto& callback : writeCallbacks.values())
69         callback(Error::Internal);
70
71     auto readCallbacks = WTFMove(m_pendingReadCallbacks);
72     for (auto& callback : readCallbacks.values())
73         callback(Data { }, 1);
74 }
75
76 void Engine::from(NetworkProcess& networkProcess, PAL::SessionID sessionID, Function<void(Engine&)>&& callback)
77 {
78     if (auto* engine = networkProcess.findCacheEngine(sessionID)) {
79         callback(*engine);
80         return;
81     }
82
83     if (sessionID.isEphemeral())
84         sessionID = PAL::SessionID::legacyPrivateSessionID();
85
86     networkProcess.cacheStorageParameters(sessionID, [networkProcess = makeRef(networkProcess), sessionID, callback = WTFMove(callback)] (auto&& rootPath, auto quota) mutable {
87         callback(networkProcess->ensureCacheEngine(sessionID, [&] {
88             return adoptRef(*new Engine { String { rootPath }, quota });
89         }));
90     });
91 }
92
93 void Engine::destroyEngine(NetworkProcess& networkProcess, PAL::SessionID sessionID)
94 {
95     ASSERT(sessionID != PAL::SessionID::defaultSessionID());
96     networkProcess.removeCacheEngine(sessionID);
97 }
98
99 void Engine::fetchEntries(NetworkProcess& networkProcess, PAL::SessionID sessionID, bool shouldComputeSize, CompletionHandler<void(Vector<WebsiteData::Entry>)>&& completionHandler)
100 {
101     from(networkProcess, sessionID, [shouldComputeSize, completionHandler = WTFMove(completionHandler)] (auto& engine) mutable {
102         engine.fetchEntries(shouldComputeSize, WTFMove(completionHandler));
103     });
104 }
105
106 void Engine::open(NetworkProcess& networkProcess, PAL::SessionID sessionID, WebCore::ClientOrigin&& origin, String&& cacheName, WebCore::DOMCacheEngine::CacheIdentifierCallback&& callback)
107 {
108     from(networkProcess, sessionID, [origin = WTFMove(origin), cacheName = WTFMove(cacheName), callback = WTFMove(callback)](auto& engine) mutable {
109         engine.open(origin, cacheName, WTFMove(callback));
110     });
111 }
112
113 void Engine::remove(NetworkProcess& networkProcess, PAL::SessionID sessionID, uint64_t cacheIdentifier, WebCore::DOMCacheEngine::CacheIdentifierCallback&& callback)
114 {
115     from(networkProcess, sessionID, [cacheIdentifier, callback = WTFMove(callback)](auto& engine) mutable {
116         engine.remove(cacheIdentifier, WTFMove(callback));
117     });
118 }
119
120 void Engine::retrieveCaches(NetworkProcess& networkProcess, PAL::SessionID sessionID, WebCore::ClientOrigin&& origin, uint64_t updateCounter, WebCore::DOMCacheEngine::CacheInfosCallback&& callback)
121 {
122     from(networkProcess, sessionID, [origin = WTFMove(origin), updateCounter, callback = WTFMove(callback)](auto& engine) mutable {
123         engine.retrieveCaches(origin, updateCounter, WTFMove(callback));
124     });
125 }
126
127
128 void Engine::retrieveRecords(NetworkProcess& networkProcess, PAL::SessionID sessionID, uint64_t cacheIdentifier, URL&& url, WebCore::DOMCacheEngine::RecordsCallback&& callback)
129 {
130     from(networkProcess, sessionID, [cacheIdentifier, url = WTFMove(url), callback = WTFMove(callback)](auto& engine) mutable {
131         engine.retrieveRecords(cacheIdentifier, WTFMove(url), WTFMove(callback));
132     });
133 }
134
135 void Engine::putRecords(NetworkProcess& networkProcess, PAL::SessionID sessionID, uint64_t cacheIdentifier, Vector<WebCore::DOMCacheEngine::Record>&& records, WebCore::DOMCacheEngine::RecordIdentifiersCallback&& callback)
136 {
137     from(networkProcess, sessionID, [cacheIdentifier, records = WTFMove(records), callback = WTFMove(callback)](auto& engine) mutable {
138         engine.putRecords(cacheIdentifier, WTFMove(records), WTFMove(callback));
139     });
140 }
141
142 void Engine::deleteMatchingRecords(NetworkProcess& networkProcess, PAL::SessionID sessionID, uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, WebCore::DOMCacheEngine::RecordIdentifiersCallback&& callback)
143 {
144     from(networkProcess, sessionID, [cacheIdentifier, request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](auto& engine) mutable {
145         engine.deleteMatchingRecords(cacheIdentifier, WTFMove(request), WTFMove(options), WTFMove(callback));
146     });
147 }
148
149 void Engine::lock(NetworkProcess& networkProcess, PAL::SessionID sessionID, uint64_t cacheIdentifier)
150 {
151     from(networkProcess, sessionID, [cacheIdentifier](auto& engine) mutable {
152         engine.lock(cacheIdentifier);
153     });
154 }
155
156 void Engine::unlock(NetworkProcess& networkProcess, PAL::SessionID sessionID, uint64_t cacheIdentifier)
157 {
158     from(networkProcess, sessionID, [cacheIdentifier](auto& engine) mutable {
159         engine.unlock(cacheIdentifier);
160     });
161 }
162
163 void Engine::clearMemoryRepresentation(NetworkProcess& networkProcess, PAL::SessionID sessionID, WebCore::ClientOrigin&& origin, WebCore::DOMCacheEngine::CompletionCallback&& callback)
164 {
165     from(networkProcess, sessionID, [origin = WTFMove(origin), callback = WTFMove(callback)](auto& engine) mutable {
166         engine.clearMemoryRepresentation(origin, WTFMove(callback));
167     });
168 }
169
170 void Engine::representation(NetworkProcess& networkProcess, PAL::SessionID sessionID, CompletionHandler<void(String&&)>&& callback)
171 {
172     from(networkProcess, sessionID, [callback = WTFMove(callback)](auto& engine) mutable {
173         callback(engine.representation());
174     });
175 }
176
177 void Engine::clearAllCaches(NetworkProcess& networkProcess, PAL::SessionID sessionID, CompletionHandler<void()>&& completionHandler)
178 {
179     from(networkProcess, sessionID, [completionHandler = WTFMove(completionHandler)](auto& engine) mutable {
180         engine.clearAllCaches(WTFMove(completionHandler));
181     });
182 }
183
184 void Engine::clearCachesForOrigin(NetworkProcess& networkProcess, PAL::SessionID sessionID, WebCore::SecurityOriginData&& originData, CompletionHandler<void()>&& completionHandler)
185 {
186     from(networkProcess, sessionID, [originData = WTFMove(originData), completionHandler = WTFMove(completionHandler)](auto& engine) mutable {
187         engine.clearCachesForOrigin(originData, WTFMove(completionHandler));
188     });
189 }
190
191 Engine::Engine(String&& rootPath, uint64_t quota)
192     : m_rootPath(WTFMove(rootPath))
193     , m_quota(quota)
194 {
195     if (!m_rootPath.isNull())
196         m_ioQueue = WorkQueue::create("com.apple.WebKit.CacheStorageEngine.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background);
197 }
198
199 void Engine::open(const WebCore::ClientOrigin& origin, const String& cacheName, CacheIdentifierCallback&& callback)
200 {
201     readCachesFromDisk(origin, [cacheName, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
202         if (!cachesOrError.has_value()) {
203             callback(makeUnexpected(cachesOrError.error()));
204             return;
205         }
206
207         cachesOrError.value().get().open(cacheName, WTFMove(callback));
208     });
209 }
210
211 void Engine::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
212 {
213     Caches* cachesToModify = nullptr;
214     for (auto& caches : m_caches.values()) {
215         auto* cacheToRemove = caches->find(cacheIdentifier);
216         if (cacheToRemove) {
217             cachesToModify = caches.get();
218             break;
219         }
220     }
221     if (!cachesToModify) {
222         callback(makeUnexpected(Error::Internal));
223         return;
224     }
225
226     cachesToModify->remove(cacheIdentifier, WTFMove(callback));
227 }
228
229 void Engine::retrieveCaches(const WebCore::ClientOrigin& origin, uint64_t updateCounter, CacheInfosCallback&& callback)
230 {
231     readCachesFromDisk(origin, [updateCounter, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
232         if (!cachesOrError.has_value()) {
233             callback(makeUnexpected(cachesOrError.error()));
234             return;
235         }
236
237         cachesOrError.value().get().cacheInfos(updateCounter, WTFMove(callback));
238     });
239 }
240
241 void Engine::retrieveRecords(uint64_t cacheIdentifier, URL&& url, RecordsCallback&& callback)
242 {
243     readCache(cacheIdentifier, [url = WTFMove(url), callback = WTFMove(callback)](CacheOrError&& result) mutable {
244         if (!result.has_value()) {
245             callback(makeUnexpected(result.error()));
246             return;
247         }
248         result.value().get().retrieveRecords(url, WTFMove(callback));
249     });
250 }
251
252 void Engine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
253 {
254     readCache(cacheIdentifier, [records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
255         if (!result.has_value()) {
256             callback(makeUnexpected(result.error()));
257             return;
258         }
259
260         result.value().get().put(WTFMove(records), WTFMove(callback));
261     });
262 }
263
264 void Engine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
265 {
266     readCache(cacheIdentifier, [request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
267         if (!result.has_value()) {
268             callback(makeUnexpected(result.error()));
269             return;
270         }
271
272         result.value().get().remove(WTFMove(request), WTFMove(options), WTFMove(callback));
273     });
274 }
275
276 void Engine::initialize(CompletionCallback&& callback)
277 {
278     if (m_salt) {
279         callback(WTF::nullopt);
280         return;
281     }
282
283     if (!shouldPersist()) {
284         m_salt = NetworkCache::Salt { };
285         callback(WTF::nullopt);
286         return;
287     }
288
289     bool shouldComputeSalt = m_initializationCallbacks.isEmpty();
290     m_initializationCallbacks.append(WTFMove(callback));
291
292     if (!shouldComputeSalt)
293         return;
294
295     String saltPath = WebCore::FileSystem::pathByAppendingComponent(m_rootPath, "salt"_s);
296     m_ioQueue->dispatch([this, weakThis = makeWeakPtr(this), saltPath = WTFMove(saltPath)] () mutable {
297         WebCore::FileSystem::makeAllDirectories(m_rootPath);
298         RunLoop::main().dispatch([this, weakThis = WTFMove(weakThis), salt = readOrMakeSalt(saltPath)]() mutable {
299             if (!weakThis)
300                 return;
301
302             m_salt = WTFMove(salt);
303
304             auto callbacks = WTFMove(m_initializationCallbacks);
305             for (auto& callback : callbacks)
306                 callback(m_salt ? WTF::nullopt : makeOptional(Error::WriteDisk));
307         });
308     });
309 }
310
311 void Engine::readCachesFromDisk(const WebCore::ClientOrigin& origin, CachesCallback&& callback)
312 {
313     initialize([this, origin, callback = WTFMove(callback)](Optional<Error>&& error) mutable {
314         auto& caches = m_caches.ensure(origin, [&origin, this] {
315             auto path = cachesRootPath(origin);
316             return Caches::create(*this, WebCore::ClientOrigin { origin }, WTFMove(path), m_quota);
317         }).iterator->value;
318
319         if (caches->isInitialized()) {
320             callback(std::reference_wrapper<Caches> { *caches });
321             return;
322         }
323
324         if (error) {
325             callback(makeUnexpected(error.value()));
326             return;
327         }
328
329         caches->initialize([callback = WTFMove(callback), caches = caches.copyRef()](Optional<Error>&& error) mutable {
330             if (error) {
331                 callback(makeUnexpected(error.value()));
332                 return;
333             }
334
335             callback(std::reference_wrapper<Caches> { *caches });
336         });
337     });
338 }
339
340 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
341 {
342     auto* cache = this->cache(cacheIdentifier);
343     if (!cache) {
344         callback(makeUnexpected(Error::Internal));
345         return;
346     }
347     if (!cache->isOpened()) {
348         cache->open([this, protectedThis = makeRef(*this), cacheIdentifier, callback = WTFMove(callback)](Optional<Error>&& error) mutable {
349             if (error) {
350                 callback(makeUnexpected(error.value()));
351                 return;
352             }
353
354             auto* cache = this->cache(cacheIdentifier);
355             if (!cache) {
356                 callback(makeUnexpected(Error::Internal));
357                 return;
358             }
359             ASSERT(cache->isOpened());
360             callback(std::reference_wrapper<Cache> { *cache });
361         });
362         return;
363     }
364     callback(std::reference_wrapper<Cache> { *cache });
365 }
366
367 Cache* Engine::cache(uint64_t cacheIdentifier)
368 {
369     Cache* result = nullptr;
370     for (auto& caches : m_caches.values()) {
371         if ((result = caches->find(cacheIdentifier)))
372             break;
373     }
374     return result;
375 }
376
377 void Engine::writeFile(const String& filename, NetworkCache::Data&& data, WebCore::DOMCacheEngine::CompletionCallback&& callback)
378 {
379     if (!shouldPersist()) {
380         callback(WTF::nullopt);
381         return;
382     }
383
384     m_pendingWriteCallbacks.add(++m_pendingCallbacksCounter, WTFMove(callback));
385     m_ioQueue->dispatch([this, weakThis = makeWeakPtr(this), identifier = m_pendingCallbacksCounter, data = WTFMove(data), filename = filename.isolatedCopy()]() mutable {
386
387         String directoryPath = WebCore::FileSystem::directoryName(filename);
388         if (!WebCore::FileSystem::fileExists(directoryPath))
389             WebCore::FileSystem::makeAllDirectories(directoryPath);
390
391         auto channel = IOChannel::open(filename, IOChannel::Type::Create);
392         channel->write(0, data, nullptr, [this, weakThis = WTFMove(weakThis), identifier](int error) mutable {
393             ASSERT(RunLoop::isMain());
394             if (!weakThis)
395                 return;
396
397             auto callback = m_pendingWriteCallbacks.take(identifier);
398             if (error) {
399                 RELEASE_LOG_ERROR(CacheStorage, "CacheStorage::Engine::writeFile failed with error %d", error);
400
401                 callback(Error::WriteDisk);
402                 return;
403             }
404             callback(WTF::nullopt);
405         });
406     });
407 }
408
409 void Engine::readFile(const String& filename, CompletionHandler<void(const NetworkCache::Data&, int error)>&& callback)
410 {
411     if (!shouldPersist()) {
412         callback(Data { }, 0);
413         return;
414     }
415
416     m_pendingReadCallbacks.add(++m_pendingCallbacksCounter, WTFMove(callback));
417     m_ioQueue->dispatch([this, weakThis = makeWeakPtr(this), identifier = m_pendingCallbacksCounter, filename = filename.isolatedCopy()]() mutable {
418         auto channel = IOChannel::open(filename, IOChannel::Type::Read);
419         if (channel->fileDescriptor() < 0) {
420             RunLoop::main().dispatch([this, weakThis = WTFMove(weakThis), identifier]() mutable {
421                 if (!weakThis)
422                     return;
423
424                 m_pendingReadCallbacks.take(identifier)(Data { }, 0);
425             });
426             return;
427         }
428
429         channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [this, weakThis = WTFMove(weakThis), identifier](const Data& data, int error) mutable {
430             RELEASE_LOG_ERROR_IF(error, CacheStorage, "CacheStorage::Engine::readFile failed with error %d", error);
431
432             // FIXME: We should do the decoding in the background thread.
433             ASSERT(RunLoop::isMain());
434
435             if (!weakThis)
436                 return;
437
438             m_pendingReadCallbacks.take(identifier)(data, error);
439         });
440     });
441 }
442
443 void Engine::removeFile(const String& filename)
444 {
445     if (!shouldPersist())
446         return;
447
448     m_ioQueue->dispatch([filename = filename.isolatedCopy()]() mutable {
449         WebCore::FileSystem::deleteFile(filename);
450     });
451 }
452
453 class ReadOriginsTaskCounter : public RefCounted<ReadOriginsTaskCounter> {
454 public:
455     static Ref<ReadOriginsTaskCounter> create(WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& callback)
456     {
457         return adoptRef(*new ReadOriginsTaskCounter(WTFMove(callback)));
458     }
459
460     ~ReadOriginsTaskCounter()
461     {
462         m_callback(WTFMove(m_entries));
463     }
464
465     void addOrigin(WebCore::SecurityOriginData&& origin, uint64_t size)
466     {
467         m_entries.append(WebsiteData::Entry { WTFMove(origin), WebsiteDataType::DOMCache, size });
468     }
469
470 private:
471     explicit ReadOriginsTaskCounter(WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& callback)
472         : m_callback(WTFMove(callback))
473     {
474     }
475
476     WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)> m_callback;
477     Vector<WebsiteData::Entry> m_entries;
478 };
479
480 void Engine::fetchEntries(bool shouldComputeSize, WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& completionHandler)
481 {
482     if (!shouldPersist()) {
483         auto entries = WTF::map(m_caches, [] (auto& pair) {
484             return WebsiteData::Entry { pair.value->origin().clientOrigin, WebsiteDataType::DOMCache, 0 };
485         });
486         completionHandler(WTFMove(entries));
487         return;
488     }
489
490     auto taskCounter = ReadOriginsTaskCounter::create(WTFMove(completionHandler));
491     for (auto& folderPath : WebCore::FileSystem::listDirectory(m_rootPath, "*")) {
492         if (!WebCore::FileSystem::fileIsDirectory(folderPath, WebCore::FileSystem::ShouldFollowSymbolicLinks::No))
493             continue;
494         Caches::retrieveOriginFromDirectory(folderPath, *m_ioQueue, [protectedThis = makeRef(*this), shouldComputeSize, taskCounter = taskCounter.copyRef()] (auto&& origin) mutable {
495             ASSERT(RunLoop::isMain());
496             if (!origin)
497                 return;
498
499             if (!shouldComputeSize) {
500                 taskCounter->addOrigin(WTFMove(origin->topOrigin), 0);
501                 taskCounter->addOrigin(WTFMove(origin->clientOrigin), 0);
502                 return;
503             }
504
505             protectedThis->readCachesFromDisk(origin.value(), [origin = origin.value(), taskCounter = WTFMove(taskCounter)] (CachesOrError&& result) mutable {
506                 if (!result.has_value())
507                     return;
508                 taskCounter->addOrigin(WTFMove(origin.topOrigin), 0);
509                 taskCounter->addOrigin(WTFMove(origin.clientOrigin), result.value().get().storageSize());
510             });
511         });
512     }
513 }
514
515 void Engine::clearAllCaches(CompletionHandler<void()>&& completionHandler)
516 {
517     ASSERT(RunLoop::isMain());
518
519     auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
520
521     for (auto& caches : m_caches.values())
522         caches->clear([callbackAggregator = callbackAggregator.copyRef()] { });
523
524     if (!shouldPersist())
525         return;
526
527     clearAllCachesFromDisk([callbackAggregator = WTFMove(callbackAggregator)] { });
528 }
529
530 void Engine::clearAllCachesFromDisk(CompletionHandler<void()>&& completionHandler)
531 {
532     ASSERT(RunLoop::isMain());
533
534     m_ioQueue->dispatch([path = m_rootPath.isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable {
535         for (auto& filename : WebCore::FileSystem::listDirectory(path, "*")) {
536             if (WebCore::FileSystem::fileIsDirectory(filename, WebCore::FileSystem::ShouldFollowSymbolicLinks::No))
537                 deleteDirectoryRecursively(filename);
538         }
539         RunLoop::main().dispatch(WTFMove(completionHandler));
540     });
541 }
542
543 void Engine::clearCachesForOrigin(const WebCore::SecurityOriginData& origin, CompletionHandler<void()>&& completionHandler)
544 {
545     ASSERT(RunLoop::isMain());
546
547     auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
548
549     for (auto& keyValue : m_caches) {
550         if (keyValue.key.topOrigin == origin || keyValue.key.clientOrigin == origin)
551             keyValue.value->clear([callbackAggregator = callbackAggregator.copyRef()] { });
552     }
553
554     if (!shouldPersist())
555         return;
556
557     clearCachesForOriginFromDisk(origin, [callbackAggregator = WTFMove(callbackAggregator)] { });
558 }
559
560 void Engine::clearCachesForOriginFromDisk(const WebCore::SecurityOriginData& origin, CompletionHandler<void()>&& completionHandler)
561 {
562     ASSERT(RunLoop::isMain());
563
564     auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
565
566     for (auto& folderPath : WebCore::FileSystem::listDirectory(m_rootPath, "*")) {
567         if (!WebCore::FileSystem::fileIsDirectory(folderPath, WebCore::FileSystem::ShouldFollowSymbolicLinks::No))
568             continue;
569         Caches::retrieveOriginFromDirectory(folderPath, *m_ioQueue, [this, protectedThis = makeRef(*this), origin, callbackAggregator = callbackAggregator.copyRef(), folderPath] (Optional<WebCore::ClientOrigin>&& folderOrigin) mutable {
570             if (!folderOrigin)
571                 return;
572             if (folderOrigin->topOrigin != origin && folderOrigin->clientOrigin != origin)
573                 return;
574
575             ASSERT(folderPath == cachesRootPath(*folderOrigin));
576             deleteDirectoryRecursivelyOnBackgroundThread(folderPath, [callbackAggregator = WTFMove(callbackAggregator)] { });
577         });
578     }
579 }
580
581 void Engine::deleteDirectoryRecursivelyOnBackgroundThread(const String& path, CompletionHandler<void()>&& completionHandler)
582 {
583     ASSERT(RunLoop::isMain());
584
585     m_ioQueue->dispatch([path = path.isolatedCopy(), completionHandler = WTFMove(completionHandler)]() mutable {
586         deleteDirectoryRecursively(path);
587
588         RunLoop::main().dispatch(WTFMove(completionHandler));
589     });
590 }
591
592 void Engine::clearMemoryRepresentation(const WebCore::ClientOrigin& origin, WebCore::DOMCacheEngine::CompletionCallback&& callback)
593 {
594     readCachesFromDisk(origin, [callback = WTFMove(callback)](CachesOrError&& result) {
595         if (!result.has_value()) {
596             callback(result.error());
597             return;
598         }
599         result.value().get().clearMemoryRepresentation();
600         callback(WTF::nullopt);
601     });
602 }
603
604 void Engine::lock(uint64_t cacheIdentifier)
605 {
606     auto& counter = m_cacheLocks.ensure(cacheIdentifier, []() {
607         return 0;
608     }).iterator->value;
609
610     ++counter;
611 }
612
613 void Engine::unlock(uint64_t cacheIdentifier)
614 {
615     auto lockCount = m_cacheLocks.find(cacheIdentifier);
616     if (lockCount == m_cacheLocks.end())
617         return;
618
619     ASSERT(lockCount->value);
620     if (--lockCount->value)
621         return;
622
623     auto* cache = this->cache(cacheIdentifier);
624     if (!cache)
625         return;
626
627     cache->dispose();
628 }
629
630 String Engine::representation()
631 {
632     bool isFirst = true;
633     StringBuilder builder;
634     builder.append("{ \"path\": \"");
635     builder.append(m_rootPath);
636     builder.append("\", \"origins\": [");
637     for (auto& keyValue : m_caches) {
638         if (!isFirst)
639             builder.append(",");
640         isFirst = false;
641
642         builder.append("\n{ \"origin\" : { \"topOrigin\" : \"");
643         builder.append(keyValue.key.topOrigin.toString());
644         builder.append("\", \"clientOrigin\": \"");
645         builder.append(keyValue.key.clientOrigin.toString());
646         builder.append("\" }, \"caches\" : ");
647         keyValue.value->appendRepresentation(builder);
648         builder.append("}");
649     }
650     builder.append("]}");
651     return builder.toString();
652 }
653
654 } // namespace CacheStorage
655
656 } // namespace WebKit