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