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
42namespace WebKit {
43
44namespace CacheStorage {
45
46using namespace WebCore::DOMCacheEngine;
47using namespace NetworkCache;
48
49String 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
58Engine::~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
76void 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
90void Engine::destroyEngine(NetworkProcess& networkProcess, PAL::SessionID sessionID)
91{
92 ASSERT(sessionID != PAL::SessionID::defaultSessionID());
93 networkProcess.removeCacheEngine(sessionID);
94}
95
96void 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
103void 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
110void 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
117void 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
125void 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
132void 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
139void 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
146void 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
153void 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
160void 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
167void 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
174void 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
181void 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
188void 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
197Engine::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
206void 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
218void 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
236void 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
248void 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
259void 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
271void 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
283void 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
318void 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
347void 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
374Cache* 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
384void 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
416void 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->isOpened()) {
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
450void 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
460class ReadOriginsTaskCounter : public RefCounted<ReadOriginsTaskCounter> {
461public:
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
477private:
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
487void 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
522void Engine::clearAllCaches(CompletionHandler<void()>&& completionHandler)
523{
524 ASSERT(RunLoop::isMain());
525
526 auto callbackAggregator = CallbackAggregator::create([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
527 if (!this->shouldPersist())
528 return completionHandler();
529
530 this->clearAllCachesFromDisk(WTFMove(completionHandler));
531 });
532
533 for (auto& caches : m_caches.values())
534 caches->clear([callbackAggregator = callbackAggregator.copyRef()] { });
535}
536
537void 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
550void Engine::clearCachesForOrigin(const WebCore::SecurityOriginData& origin, CompletionHandler<void()>&& completionHandler)
551{
552 ASSERT(RunLoop::isMain());
553
554 auto callbackAggregator = CallbackAggregator::create([this, protectedThis = makeRef(*this), origin, completionHandler = WTFMove(completionHandler)]() mutable {
555 if (!this->shouldPersist())
556 return completionHandler();
557
558 this->clearCachesForOriginFromDisk(origin, [completionHandler = WTFMove(completionHandler)]() mutable {
559 completionHandler();
560 });
561 });
562
563 for (auto& keyValue : m_caches) {
564 if (keyValue.key.topOrigin == origin || keyValue.key.clientOrigin == origin)
565 keyValue.value->clear([callbackAggregator = callbackAggregator.copyRef()] { });
566 }
567}
568
569void 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 : FileSystem::listDirectory(m_rootPath, "*")) {
576 if (!FileSystem::fileIsDirectory(folderPath, FileSystem::ShouldFollowSymbolicLinks::No))
577 continue;
578 Caches::retrieveOriginFromDirectory(folderPath, *m_ioQueue, [this, protectedThis = makeRef(*this), origin, callbackAggregator = callbackAggregator.copyRef(), folderPath] (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
590void 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
601void Engine::clearMemoryRepresentation(const WebCore::ClientOrigin& origin, WebCore::DOMCacheEngine::CompletionCallback&& callback)
602{
603 readCachesFromDisk(origin, [callback = WTFMove(callback)](CachesOrError&& result) mutable {
604 if (!result.has_value()) {
605 callback(result.error());
606 return;
607 }
608 result.value().get().clearMemoryRepresentation();
609 callback(WTF::nullopt);
610 });
611}
612
613void Engine::lock(uint64_t cacheIdentifier)
614{
615 auto& counter = m_cacheLocks.ensure(cacheIdentifier, []() {
616 return 0;
617 }).iterator->value;
618
619 ++counter;
620}
621
622void 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
639String 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
666