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