Update std::expected to match libc++ coding style
[WebKit-https.git] / Source / WebKit / NetworkProcess / cache / CacheStorageEngineCaches.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 "NetworkCacheCoders.h"
30 #include "NetworkCacheIOChannel.h"
31 #include <WebCore/SecurityOrigin.h>
32 #include <wtf/RunLoop.h>
33 #include <wtf/UUID.h>
34 #include <wtf/text/StringBuilder.h>
35
36 using namespace WebCore::DOMCacheEngine;
37 using namespace WebKit::NetworkCache;
38
39 namespace WebKit {
40
41 namespace CacheStorage {
42
43 static inline String cachesListFilename(const String& cachesRootPath)
44 {
45     return WebCore::FileSystem::pathByAppendingComponent(cachesRootPath, ASCIILiteral("cacheslist"));
46 }
47
48 static inline String cachesOriginFilename(const String& cachesRootPath)
49 {
50     return WebCore::FileSystem::pathByAppendingComponent(cachesRootPath, ASCIILiteral("origin"));
51 }
52
53 void Caches::retrieveOriginFromDirectory(const String& folderPath, WorkQueue& queue, WTF::CompletionHandler<void(std::optional<WebCore::SecurityOriginData>&&)>&& completionHandler)
54 {
55     queue.dispatch([completionHandler = WTFMove(completionHandler), folderPath = folderPath.isolatedCopy()]() mutable {
56         if (!WebCore::FileSystem::fileExists(cachesListFilename(folderPath))) {
57             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() mutable {
58                 completionHandler(std::nullopt);
59             });
60             return;
61         }
62
63         auto channel = IOChannel::open(cachesOriginFilename(folderPath), IOChannel::Type::Read);
64         channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [completionHandler = WTFMove(completionHandler)](const Data& data, int error) mutable {
65             ASSERT(RunLoop::isMain());
66             if (error) {
67                 completionHandler(std::nullopt);
68                 return;
69             }
70             completionHandler(readOrigin(data));
71         });
72     });
73 }
74
75 Caches::Caches(Engine& engine, String&& origin, String&& rootPath, uint64_t quota)
76     : m_engine(&engine)
77     , m_origin(WebCore::SecurityOriginData::fromSecurityOrigin(WebCore::SecurityOrigin::createFromString(origin)))
78     , m_rootPath(WTFMove(rootPath))
79     , m_quota(quota)
80 {
81 }
82
83 void Caches::storeOrigin(CompletionCallback&& completionHandler)
84 {
85     WTF::Persistence::Encoder encoder;
86     encoder << m_origin.protocol;
87     encoder << m_origin.host;
88     encoder << m_origin.port;
89     m_engine->writeFile(cachesOriginFilename(m_rootPath), Data { encoder.buffer(), encoder.bufferSize() }, [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (std::optional<Error>&& error) mutable {
90         completionHandler(WTFMove(error));
91     });
92 }
93
94 std::optional<WebCore::SecurityOriginData> Caches::readOrigin(const Data& data)
95 {
96     // FIXME: We should be able to use modern decoders for persistent data.
97     WebCore::SecurityOriginData origin;
98     WTF::Persistence::Decoder decoder(data.data(), data.size());
99
100     if (!decoder.decode(origin.protocol))
101         return std::nullopt;
102     if (!decoder.decode(origin.host))
103         return std::nullopt;
104     if (!decoder.decode(origin.port))
105         return std::nullopt;
106     return WTFMove(origin);
107 }
108
109 void Caches::initialize(WebCore::DOMCacheEngine::CompletionCallback&& callback)
110 {
111     if (m_isInitialized) {
112         callback(std::nullopt);
113         return;
114     }
115
116     if (m_rootPath.isNull()) {
117         makeDirty();
118         m_isInitialized = true;
119         callback(std::nullopt);
120         return;
121     }
122
123     if (m_storage) {
124         m_pendingInitializationCallbacks.append(WTFMove(callback));
125         return;
126     }
127
128     auto storage = Storage::open(m_rootPath, Storage::Mode::Normal);
129     if (!storage) {
130         callback(Error::WriteDisk);
131         return;
132     }
133     m_storage = storage.releaseNonNull();
134     m_storage->writeWithoutWaiting();
135
136     storeOrigin([this, callback = WTFMove(callback)] (std::optional<Error>&& error) mutable {
137         if (error) {
138             callback(Error::WriteDisk);
139             return;
140         }
141
142         readCachesFromDisk([this, callback = WTFMove(callback)](Expected<Vector<Cache>, Error>&& result) mutable {
143             makeDirty();
144
145             if (!result.has_value()) {
146                 callback(result.error());
147
148                 auto pendingCallbacks = WTFMove(m_pendingInitializationCallbacks);
149                 for (auto& callback : pendingCallbacks)
150                     callback(result.error());
151                 return;
152             }
153             m_caches = WTFMove(result.value());
154
155             initializeSize(WTFMove(callback));
156         });
157     });
158 }
159
160 void Caches::initializeSize(WebCore::DOMCacheEngine::CompletionCallback&& callback)
161 {
162     if (!m_storage) {
163         callback(Error::Internal);
164         return;
165     }
166
167     uint64_t size = 0;
168     m_storage->traverse({ }, 0, [protectedThis = makeRef(*this), this, protectedStorage = makeRef(*m_storage), callback = WTFMove(callback), size](const auto* storage, const auto& information) mutable {
169         if (!storage) {
170             m_size = size;
171             m_isInitialized = true;
172             callback(std::nullopt);
173
174             auto pendingCallbacks = WTFMove(m_pendingInitializationCallbacks);
175             for (auto& callback : pendingCallbacks)
176                 callback(std::nullopt);
177
178             return;
179         }
180         auto decoded = Cache::decodeRecordHeader(*storage);
181         if (decoded)
182             size += decoded->size;
183     });
184 }
185
186 void Caches::detach()
187 {
188     m_engine = nullptr;
189     m_rootPath = { };
190 }
191
192 void Caches::clear(CompletionHandler<void()>&& completionHandler)
193 {
194     if (m_engine)
195         m_engine->removeFile(cachesListFilename(m_rootPath));
196     if (m_storage) {
197         m_storage->clear(String { }, std::chrono::system_clock::time_point::min(), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
198             ASSERT(RunLoop::isMain());
199             protectedThis->clearMemoryRepresentation();
200             completionHandler();
201         });
202         return;
203     }
204     clearMemoryRepresentation();
205     completionHandler();
206 }
207
208 Cache* Caches::find(const String& name)
209 {
210     auto position = m_caches.findMatching([&](const auto& item) { return item.name() == name; });
211     return (position != notFound) ? &m_caches[position] : nullptr;
212 }
213
214 Cache* Caches::find(uint64_t identifier)
215 {
216     auto position = m_caches.findMatching([&](const auto& item) { return item.identifier() == identifier; });
217     if (position != notFound)
218         return &m_caches[position];
219
220     position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier() == identifier; });
221     return (position != notFound) ? &m_removedCaches[position] : nullptr;
222 }
223
224 void Caches::open(const String& name, CacheIdentifierCallback&& callback)
225 {
226     ASSERT(m_isInitialized);
227     ASSERT(m_engine);
228
229     if (auto* cache = find(name)) {
230         cache->open([cacheIdentifier = cache->identifier(), callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
231             if (error) {
232                 callback(makeUnexpected(error.value()));
233                 return;
234             }
235             callback(CacheIdentifierOperationResult { cacheIdentifier, false });
236         });
237         return;
238     }
239
240     makeDirty();
241
242     uint64_t cacheIdentifier = m_engine->nextCacheIdentifier();
243     m_caches.append(Cache { *this, cacheIdentifier, Cache::State::Open, String { name }, createCanonicalUUIDString() });
244
245     writeCachesToDisk([callback = WTFMove(callback), cacheIdentifier](std::optional<Error>&& error) mutable {
246         callback(CacheIdentifierOperationResult { cacheIdentifier, !!error });
247     });
248 }
249
250 void Caches::remove(uint64_t identifier, CacheIdentifierCallback&& callback)
251 {
252     ASSERT(m_isInitialized);
253     ASSERT(m_engine);
254
255     auto position = m_caches.findMatching([&](const auto& item) { return item.identifier() == identifier; });
256
257     if (position == notFound) {
258         ASSERT(m_removedCaches.findMatching([&](const auto& item) { return item.identifier() == identifier; }) != notFound);
259         callback(CacheIdentifierOperationResult { identifier, false });
260         return;
261     }
262
263     makeDirty();
264
265     m_removedCaches.append(WTFMove(m_caches[position]));
266     m_caches.remove(position);
267
268     writeCachesToDisk([callback = WTFMove(callback), identifier](std::optional<Error>&& error) mutable {
269         callback(CacheIdentifierOperationResult { identifier, !!error });
270     });
271 }
272
273 void Caches::dispose(Cache& cache)
274 {
275     auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier() == cache.identifier(); });
276     if (position != notFound) {
277         if (m_storage)
278             m_storage->remove(cache.keys(), { });
279
280         m_removedCaches.remove(position);
281         return;
282     }
283     ASSERT(m_caches.findMatching([&](const auto& item) { return item.identifier() == cache.identifier(); }) != notFound);
284     cache.clearMemoryRepresentation();
285
286     if (m_caches.findMatching([](const auto& item) { return item.isActive(); }) == notFound)
287         clearMemoryRepresentation();
288 }
289
290 static inline Data encodeCacheNames(const Vector<Cache>& caches)
291 {
292     WTF::Persistence::Encoder encoder;
293
294     uint64_t size = caches.size();
295     encoder << size;
296     for (auto& cache : caches) {
297         encoder << cache.name();
298         encoder << cache.uniqueName();
299     }
300
301     return Data { encoder.buffer(), encoder.bufferSize() };
302 }
303
304 static inline Expected<Vector<std::pair<String, String>>, Error> decodeCachesNames(const Data& data, int error)
305 {
306     if (error)
307         return makeUnexpected(Error::ReadDisk);
308
309     WTF::Persistence::Decoder decoder(data.data(), data.size());
310     uint64_t count;
311     if (!decoder.decode(count))
312         return makeUnexpected(Error::ReadDisk);
313
314     Vector<std::pair<String, String>> names;
315     names.reserveInitialCapacity(count);
316     for (size_t index = 0; index < count; ++index) {
317         String name;
318         if (!decoder.decode(name))
319             return makeUnexpected(Error::ReadDisk);
320         String uniqueName;
321         if (!decoder.decode(uniqueName))
322             return makeUnexpected(Error::ReadDisk);
323
324         names.uncheckedAppend(std::pair<String, String> { WTFMove(name), WTFMove(uniqueName) });
325     }
326     return names;
327 }
328
329 void Caches::readCachesFromDisk(WTF::Function<void(Expected<Vector<Cache>, Error>&&)>&& callback)
330 {
331     ASSERT(m_engine);
332     ASSERT(!m_isInitialized);
333     ASSERT(m_caches.isEmpty());
334
335     if (!shouldPersist()) {
336         callback(Vector<Cache> { });
337         return;
338     }
339
340     auto filename = cachesListFilename(m_rootPath);
341     if (!WebCore::FileSystem::fileExists(filename)) {
342         callback(Vector<Cache> { });
343         return;
344     }
345
346     m_engine->readFile(filename, [protectedThis = makeRef(*this), this, callback = WTFMove(callback)](const Data& data, int error) mutable {
347         if (!m_engine) {
348             callback(Vector<Cache> { });
349             return;
350         }
351
352         auto result = decodeCachesNames(data, error);
353         if (!result.has_value()) {
354             callback(makeUnexpected(result.error()));
355             return;
356         }
357         callback(WTF::map(WTFMove(result.value()), [this] (auto&& pair) {
358             return Cache { *this, m_engine->nextCacheIdentifier(), Cache::State::Uninitialized, WTFMove(pair.first), WTFMove(pair.second) };
359         }));
360     });
361 }
362
363 void Caches::writeCachesToDisk(CompletionCallback&& callback)
364 {
365     if (!shouldPersist()) {
366         callback(std::nullopt);
367         return;
368     }
369
370     ASSERT(m_engine);
371
372     if (m_caches.isEmpty()) {
373         m_engine->removeFile(cachesListFilename(m_rootPath));
374         callback(std::nullopt);
375         return;
376     }
377
378     m_engine->writeFile(cachesListFilename(m_rootPath), encodeCacheNames(m_caches), [callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
379         callback(WTFMove(error));
380     });
381 }
382
383 void Caches::readRecordsList(Cache& cache, NetworkCache::Storage::TraverseHandler&& callback)
384 {
385     ASSERT(m_isInitialized);
386
387     if (!m_storage) {
388         callback(nullptr, { });
389         return;
390     }
391     m_storage->traverse(cache.uniqueName(), 0, [protectedStorage = makeRef(*m_storage), callback = WTFMove(callback)](const auto* storage, const auto& information) {
392         callback(storage, information);
393     });
394 }
395
396 void Caches::requestSpace(uint64_t spaceRequired, WebCore::DOMCacheEngine::CompletionCallback&& callback)
397 {
398     // FIXME: Implement quota increase request.
399     ASSERT(m_quota < m_size + spaceRequired);
400     callback(Error::QuotaExceeded);
401 }
402
403 void Caches::writeRecord(const Cache& cache, const RecordInformation& recordInformation, Record&& record, uint64_t previousRecordSize, CompletionCallback&& callback)
404 {
405     ASSERT(m_isInitialized);
406
407     ASSERT(m_size >= previousRecordSize);
408     m_size += recordInformation.size;
409     m_size -= previousRecordSize;
410
411     ASSERT(m_size <= m_quota);
412
413     if (!shouldPersist()) {
414         m_volatileStorage.set(recordInformation.key, WTFMove(record));
415         return;
416     }
417
418     m_storage->store(Cache::encode(recordInformation, record), [protectedStorage = makeRef(*m_storage), callback = WTFMove(callback)](const Data&) {
419         callback(std::nullopt);
420     });
421 }
422
423 void Caches::readRecord(const NetworkCache::Key& key, WTF::Function<void(Expected<Record, Error>&&)>&& callback)
424 {
425     ASSERT(m_isInitialized);
426
427     if (!shouldPersist()) {
428         auto iterator = m_volatileStorage.find(key);
429         if (iterator == m_volatileStorage.end()) {
430             callback(makeUnexpected(Error::Internal));
431             return;
432         }
433         callback(iterator->value.copy());
434         return;
435     }
436
437     m_storage->retrieve(key, 4, [protectedStorage = makeRef(*m_storage), callback = WTFMove(callback)](std::unique_ptr<Storage::Record> storage) mutable {
438         if (!storage) {
439             callback(makeUnexpected(Error::ReadDisk));
440             return false;
441         }
442
443         auto record = Cache::decode(*storage);
444         if (!record) {
445             callback(makeUnexpected(Error::ReadDisk));
446             return false;
447         }
448
449         callback(WTFMove(record.value()));
450         return true;
451     });
452 }
453
454 void Caches::removeRecord(const RecordInformation& record)
455 {
456     ASSERT(m_isInitialized);
457
458     ASSERT(m_size >= record.size);
459     m_size -= record.size;
460     removeCacheEntry(record.key);
461 }
462
463 void Caches::removeCacheEntry(const NetworkCache::Key& key)
464 {
465     ASSERT(m_isInitialized);
466
467     if (!shouldPersist()) {
468         m_volatileStorage.remove(key);
469         return;
470     }
471     m_storage->remove(key);
472 }
473
474 void Caches::clearMemoryRepresentation()
475 {
476     makeDirty();
477     m_caches.clear();
478     m_isInitialized = false;
479     m_storage = nullptr;
480 }
481
482 bool Caches::isDirty(uint64_t updateCounter) const
483 {
484     ASSERT(m_updateCounter >= updateCounter);
485     return m_updateCounter != updateCounter;
486 }
487
488 const NetworkCache::Salt& Caches::salt() const
489 {
490     if (m_engine)
491         return m_engine->salt();
492
493     if (!m_volatileSalt)
494         m_volatileSalt = Salt { };
495
496     return m_volatileSalt.value();
497 }
498
499 CacheInfos Caches::cacheInfos(uint64_t updateCounter) const
500 {
501     Vector<CacheInfo> cacheInfos;
502     if (isDirty(updateCounter)) {
503         cacheInfos.reserveInitialCapacity(m_caches.size());
504         for (auto& cache : m_caches)
505             cacheInfos.uncheckedAppend(CacheInfo { cache.identifier(), cache.name() });
506     }
507     return { WTFMove(cacheInfos), m_updateCounter };
508 }
509
510 void Caches::appendRepresentation(StringBuilder& builder) const
511 {
512     builder.append("{ \"persistent\": [");
513
514     bool isFirst = true;
515     for (auto& cache : m_caches) {
516         if (!isFirst)
517             builder.append(", ");
518         isFirst = false;
519         builder.append("\"");
520         builder.append(cache.name());
521         builder.append("\"");
522     }
523
524     builder.append("], \"removed\": [");
525
526     isFirst = true;
527     for (auto& cache : m_removedCaches) {
528         if (!isFirst)
529             builder.append(", ");
530         isFirst = false;
531         builder.append("\"");
532         builder.append(cache.name());
533         builder.append("\"");
534     }
535     builder.append("]}\n");
536 }
537
538 uint64_t Caches::storageSize() const
539 {
540     ASSERT(m_isInitialized);
541     if (!shouldPersist())
542         return 0;
543     return m_storage->approximateSize();
544 }
545
546 } // namespace CacheStorage
547
548 } // namespace WebKit