30081a1d702a170466b449312bc6b351fd204a1a
[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 "NetworkCacheIOChannel.h"
30 #include "NetworkProcess.h"
31 #include <WebCore/CacheQueryOptions.h>
32 #include <pal/SessionID.h>
33 #include <wtf/MainThread.h>
34 #include <wtf/NeverDestroyed.h>
35 #include <wtf/text/StringHash.h>
36
37 using namespace WebCore::DOMCache;
38 using namespace WebKit::NetworkCache;
39
40 namespace WebKit {
41
42 namespace CacheStorage {
43
44 static HashMap<PAL::SessionID, RefPtr<Engine>>& globalEngineMap()
45 {
46     static NeverDestroyed<HashMap<PAL::SessionID, RefPtr<Engine>>> map;
47
48     return map;
49 }
50
51 Engine::~Engine()
52 {
53     for (auto& caches : m_caches.values())
54         caches->detach();
55 }
56
57 Engine& Engine::from(PAL::SessionID sessionID)
58 {
59     auto addResult = globalEngineMap().add(sessionID, nullptr);
60     if (addResult.isNewEntry)
61         addResult.iterator->value = Engine::create(NetworkProcess::singleton().cacheStorageDirectory(sessionID));
62     return *addResult.iterator->value;
63 }
64
65 void Engine::destroyEngine(PAL::SessionID sessionID)
66 {
67     ASSERT(sessionID != PAL::SessionID::defaultSessionID());
68     globalEngineMap().remove(sessionID);
69 }
70
71 Engine& Engine::defaultEngine()
72 {
73     auto sessionID = PAL::SessionID::defaultSessionID();
74     static NeverDestroyed<Ref<Engine>> defaultEngine = { Engine::create(NetworkProcess::singleton().cacheStorageDirectory(sessionID)) };
75     return defaultEngine.get();
76 }
77
78 Engine::Engine(String&& rootPath)
79     : m_rootPath(WTFMove(rootPath))
80 {
81     if (!m_rootPath.isNull())
82         m_ioQueue = WorkQueue::create("com.apple.WebKit.CacheStorageEngine.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background);
83 }
84
85 void Engine::open(const String& origin, const String& cacheName, CacheIdentifierCallback&& callback)
86 {
87     readCachesFromDisk(origin, [this, cacheName, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
88         if (!cachesOrError.hasValue()) {
89             callback(makeUnexpected(cachesOrError.error()));
90             return;
91         }
92
93         Caches& caches = cachesOrError.value();
94
95         if (auto* cache = caches.find(cacheName)) {
96             callback(cache->identifier());
97             return;
98         }
99
100         caches.open(String { cacheName }, [callback = WTFMove(callback)](const CacheIdentifierOrError& result) mutable {
101             callback(result);
102         });
103     });
104 }
105
106 void Engine::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
107 {
108     Caches* cachesToModify = nullptr;
109     for (auto& caches : m_caches.values()) {
110         auto* cacheToRemove = caches->find(cacheIdentifier);
111         if (cacheToRemove) {
112             cachesToModify = caches.ptr();
113             break;
114         }
115     }
116     if (!cachesToModify) {
117         callback(makeUnexpected(Error::Internal));
118         return;
119     }
120
121     cachesToModify->remove(cacheIdentifier, [cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
122         if (error) {
123             callback(makeUnexpected(error.value()));
124             return;
125         }
126         callback(cacheIdentifier);
127     });
128 }
129
130 void Engine::retrieveCaches(const String& origin, CacheInfosCallback&& callback)
131 {
132     readCachesFromDisk(origin, [callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
133         if (!cachesOrError.hasValue()) {
134             callback(makeUnexpected(cachesOrError.error()));
135             return;
136         }
137
138         callback(cachesOrError.value().get().cacheInfos());
139     });
140 }
141
142 void Engine::retrieveRecords(uint64_t cacheIdentifier, RecordsCallback&& callback)
143 {
144     readCache(cacheIdentifier, [callback = WTFMove(callback)](CacheOrError&& result) mutable {
145         if (!result.hasValue()) {
146             callback(makeUnexpected(result.error()));
147             return;
148         }
149
150         callback(result.value().get().records());
151     });
152 }
153
154 void Engine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
155 {
156     readCache(cacheIdentifier, [this, records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
157         if (!result.hasValue()) {
158             callback(makeUnexpected(result.error()));
159             return;
160         }
161
162         result.value().get().put(WTFMove(records), WTFMove(callback));
163     });
164 }
165
166 void Engine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
167 {
168     readCache(cacheIdentifier, [this, request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
169         if (!result.hasValue()) {
170             callback(makeUnexpected(result.error()));
171             return;
172         }
173
174         result.value().get().remove(WTFMove(request), WTFMove(options), WTFMove(callback));
175     });
176 }
177
178 void Engine::initialize(Function<void(std::optional<Error>&&)>&& callback)
179 {
180     if (m_salt) {
181         callback(std::nullopt);
182         return;
183     }
184
185     if (!shouldPersist()) {
186         callback(std::nullopt);
187         return;
188     }
189
190     String saltPath = WebCore::pathByAppendingComponent(m_rootPath, ASCIILiteral("salt"));
191     m_ioQueue->dispatch([protectedThis = makeRef(*this), this, callback = WTFMove(callback), saltPath = WTFMove(saltPath)] () mutable {
192         WebCore::makeAllDirectories(m_rootPath);
193         RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), this, salt = readOrMakeSalt(saltPath), callback = WTFMove(callback)]() mutable {
194             if (!salt) {
195                 callback(Error::WriteDisk);
196                 return;
197             }
198             m_salt = WTFMove(salt);
199             callback(std::nullopt);
200         });
201     });
202 }
203
204 void Engine::readCachesFromDisk(const String& origin, CachesCallback&& callback)
205 {
206     auto& caches = m_caches.ensure(origin, [&origin, this] {
207         return Caches::create(*this, origin);
208     }).iterator->value;
209
210     if (caches->isInitialized()) {
211         callback(std::reference_wrapper<Caches> { caches.get() });
212         return;
213     }
214
215     initialize([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
216         if (error) {
217             callback(makeUnexpected(error.value()));
218             return;
219         }
220
221         auto caches = m_caches.get(origin);
222         ASSERT(caches);
223
224         caches->initialize([callback = WTFMove(callback), caches](std::optional<Error>&& error) mutable {
225             if (error) {
226                 callback(makeUnexpected(error.value()));
227                 return;
228             }
229
230             callback(std::reference_wrapper<Caches> { *caches });
231         });
232     });
233 }
234
235 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
236 {
237     // FIXME: Implement reading.
238     auto* cache = this->cache(cacheIdentifier);
239     if (!cache) {
240         callback(makeUnexpected(Error::Internal));
241         return;
242     }
243     callback(std::reference_wrapper<Cache> { *cache });
244 }
245
246 Cache* Engine::cache(uint64_t cacheIdentifier)
247 {
248     Cache* result = nullptr;
249     for (auto& caches : m_caches.values()) {
250         if ((result = caches->find(cacheIdentifier)))
251             break;
252     }
253     return result;
254 }
255
256 void Engine::writeFile(const String& filename, NetworkCache::Data&& data, WebCore::DOMCache::CompletionCallback&& callback)
257 {
258     if (!shouldPersist()) {
259         callback(std::nullopt);
260         return;
261     }
262
263     m_ioQueue->dispatch([callback = WTFMove(callback), data = WTFMove(data), filename = filename.isolatedCopy()] () mutable {
264         auto channel = IOChannel::open(filename, IOChannel::Type::Create);
265         channel->write(0, data, nullptr, [callback = WTFMove(callback)](int error) mutable {
266             ASSERT(RunLoop::isMain());
267             if (error) {
268                 callback(Error::WriteDisk);
269                 return;
270             }
271             callback(std::nullopt);
272         });
273     });
274 }
275
276 void Engine::readFile(const String& filename, WTF::Function<void(const NetworkCache::Data&, int error)>&& callback)
277 {
278     if (!shouldPersist()) {
279         callback(Data { }, 0);
280         return;
281     }
282
283     m_ioQueue->dispatch([callback = WTFMove(callback), filename = filename.isolatedCopy()]() mutable {
284         auto channel = IOChannel::open(filename, IOChannel::Type::Read);
285         if (channel->fileDescriptor() < 0) {
286             RunLoop::main().dispatch([callback = WTFMove(callback)]() mutable {
287                 callback(Data { }, 0);
288             });
289             return;
290         }
291
292         channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [callback = WTFMove(callback)](const Data& data, int error) mutable {
293             // FIXME: We should do the decoding in the background thread.
294             ASSERT(RunLoop::isMain());
295             callback(data, error);
296         });
297     });
298 }
299
300 void Engine::removeFile(const String& filename)
301 {
302     if (!shouldPersist())
303         return;
304
305     m_ioQueue->dispatch([filename = filename.isolatedCopy()]() mutable {
306         WebCore::deleteFile(filename);
307     });
308 }
309
310 void Engine::clearMemoryRepresentation(const String& origin)
311 {
312     readCachesFromDisk(origin, [](CachesOrError&& result) {
313         if (!result.hasValue())
314             return;
315         result.value().get().clearMemoryRepresentation();
316     });
317 }
318
319 } // namespace CacheStorage
320
321 } // namespace WebKit