Update std::expected to match libc++ coding style
[WebKit-https.git] / Source / WebCore / Modules / cache / DOMCacheStorage.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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DOMCacheStorage.h"
28
29 #include "CacheQueryOptions.h"
30 #include "JSDOMCache.h"
31 #include "JSFetchResponse.h"
32 #include "ScriptExecutionContext.h"
33
34
35 namespace WebCore {
36 using namespace WebCore::DOMCacheEngine;
37
38 DOMCacheStorage::DOMCacheStorage(ScriptExecutionContext& context, Ref<CacheStorageConnection>&& connection)
39     : ActiveDOMObject(&context)
40     , m_connection(WTFMove(connection))
41 {
42     suspendIfNeeded();
43 }
44
45 String DOMCacheStorage::origin() const
46 {
47     // FIXME: Do we really need to check for origin being null?
48     auto* origin = scriptExecutionContext() ? scriptExecutionContext()->securityOrigin() : nullptr;
49     return origin ? origin->toString() : String();
50 }
51
52 static void doSequentialMatch(size_t index, Vector<Ref<DOMCache>>&& caches, DOMCache::RequestInfo&& info, CacheQueryOptions&& options, DOMCache::MatchCallback&& completionHandler)
53 {
54     if (index >= caches.size()) {
55         completionHandler(nullptr);
56         return;
57     }
58
59     caches[index]->doMatch(WTFMove(info), WTFMove(options), [caches = WTFMove(caches), info, options, completionHandler = WTFMove(completionHandler), index](ExceptionOr<FetchResponse*>&& result) mutable {
60         if (result.hasException()) {
61             completionHandler(result.releaseException());
62             return;
63         }
64         if (result.returnValue()) {
65             completionHandler(result.returnValue());
66             return;
67         }
68         doSequentialMatch(++index, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
69     });
70 }
71
72 static inline void startSequentialMatch(Vector<Ref<DOMCache>>&& caches, DOMCache::RequestInfo&& info, CacheQueryOptions&& options, DOMCache::MatchCallback&& completionHandler)
73 {
74     doSequentialMatch(0, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
75 }
76
77 static inline Ref<DOMCache> copyCache(const Ref<DOMCache>& cache)
78 {
79     return cache.copyRef();
80 }
81
82 void DOMCacheStorage::match(DOMCache::RequestInfo&& info, CacheQueryOptions&& options, Ref<DeferredPromise>&& promise)
83 {
84     retrieveCaches([this, info = WTFMove(info), options = WTFMove(options), promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
85         if (exception) {
86             promise->reject(WTFMove(exception.value()));
87             return;
88         }
89
90         if (!options.cacheName.isNull()) {
91             auto position = m_caches.findMatching([&](auto& item) { return item->name() == options.cacheName; });
92             if (position != notFound) {
93                 m_caches[position]->match(WTFMove(info), WTFMove(options), WTFMove(promise));
94                 return;
95             }
96             promise->resolve();
97             return;
98         }
99
100         setPendingActivity(this);
101         startSequentialMatch(WTF::map(m_caches, copyCache), WTFMove(info), WTFMove(options), [this, promise = WTFMove(promise)](ExceptionOr<FetchResponse*>&& result) mutable {
102             if (!m_isStopped) {
103                 if (result.hasException()) {
104                     promise->reject(result.releaseException());
105                     return;
106                 }
107                 if (!result.returnValue())
108                     promise->resolve();
109                 else
110                     promise->resolve<IDLInterface<FetchResponse>>(*result.returnValue());
111             }
112             unsetPendingActivity(this);
113         });
114     });
115 }
116
117 void DOMCacheStorage::has(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
118 {
119     retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
120         if (exception) {
121             promise.reject(WTFMove(exception.value()));
122             return;
123         }
124         promise.resolve(m_caches.findMatching([&](auto& item) { return item->name() == name; }) != notFound);
125     });
126 }
127
128 Ref<DOMCache> DOMCacheStorage::findCacheOrCreate(CacheInfo&& info)
129 {
130    auto position = m_caches.findMatching([&] (const auto& cache) { return info.identifier == cache->identifier(); });
131    if (position != notFound)
132        return m_caches[position].copyRef();
133    return DOMCache::create(*scriptExecutionContext(), WTFMove(info.name), info.identifier, m_connection.copyRef());
134 }
135
136 void DOMCacheStorage::retrieveCaches(WTF::Function<void(std::optional<Exception>&&)>&& callback)
137 {
138     String origin = this->origin();
139     if (origin.isNull())
140         return;
141
142     setPendingActivity(this);
143     m_connection->retrieveCaches(origin, m_updateCounter, [this, callback = WTFMove(callback)](CacheInfosOrError&& result) mutable {
144         if (!m_isStopped) {
145             if (!result.has_value()) {
146                 callback(DOMCacheEngine::errorToException(result.error()));
147                 return;
148             }
149
150             auto& cachesInfo = result.value();
151
152             if (m_updateCounter != cachesInfo.updateCounter) {
153                 m_updateCounter = cachesInfo.updateCounter;
154
155                 m_caches = WTF::map(WTFMove(cachesInfo.infos), [this] (CacheInfo&& info) {
156                     return findCacheOrCreate(WTFMove(info));
157                 });
158             }
159             callback(std::nullopt);
160         }
161         unsetPendingActivity(this);
162     });
163 }
164
165 static void logConsolePersistencyError(ScriptExecutionContext* context, const String& cacheName)
166 {
167     if (!context)
168         return;
169
170     context->addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("There was an error making ", cacheName, " persistent on the filesystem"));
171 }
172
173 void DOMCacheStorage::open(const String& name, DOMPromiseDeferred<IDLInterface<DOMCache>>&& promise)
174 {
175     retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
176         if (exception) {
177             promise.reject(WTFMove(exception.value()));
178             return;
179         }
180
181         auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
182         if (position != notFound) {
183             auto& cache = m_caches[position];
184             promise.resolve(DOMCache::create(*scriptExecutionContext(), String { cache->name() }, cache->identifier(), m_connection.copyRef()));
185             return;
186         }
187
188         String origin = this->origin();
189         ASSERT(!origin.isNull());
190
191         setPendingActivity(this);
192         m_connection->open(origin, name, [this, name, promise = WTFMove(promise)](const CacheIdentifierOrError& result) mutable {
193             if (!m_isStopped) {
194                 if (!result.has_value())
195                     promise.reject(DOMCacheEngine::errorToException(result.error()));
196                 else {
197                     if (result.value().hadStorageError)
198                         logConsolePersistencyError(scriptExecutionContext(), name);
199
200                     auto cache = DOMCache::create(*scriptExecutionContext(), String { name }, result.value().identifier, m_connection.copyRef());
201                     promise.resolve(cache);
202                     m_caches.append(WTFMove(cache));
203                 }
204             }
205             unsetPendingActivity(this);
206         });
207     });
208 }
209
210 void DOMCacheStorage::remove(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
211 {
212     retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
213         if (exception) {
214             promise.reject(WTFMove(exception.value()));
215             return;
216         }
217
218         auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
219         if (position == notFound) {
220             promise.resolve(false);
221             return;
222         }
223
224         String origin = this->origin();
225         ASSERT(!origin.isNull());
226
227         setPendingActivity(this);
228         m_connection->remove(m_caches[position]->identifier(), [this, name, promise = WTFMove(promise)](const CacheIdentifierOrError& result) mutable {
229             if (!m_isStopped) {
230                 if (!result.has_value())
231                     promise.reject(DOMCacheEngine::errorToException(result.error()));
232                 else {
233                     if (result.value().hadStorageError)
234                         logConsolePersistencyError(scriptExecutionContext(), name);
235                     promise.resolve(true);
236                 }
237             }
238             unsetPendingActivity(this);
239         });
240         m_caches.remove(position);
241     });
242 }
243
244 void DOMCacheStorage::keys(KeysPromise&& promise)
245 {
246     retrieveCaches([this, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
247         if (exception) {
248             promise.reject(WTFMove(exception.value()));
249             return;
250         }
251
252         promise.resolve(WTF::map(m_caches, [] (const auto& cache) {
253             return cache->name();
254         }));
255     });
256 }
257
258 void DOMCacheStorage::stop()
259 {
260     m_isStopped = true;
261 }
262
263 const char* DOMCacheStorage::activeDOMObjectName() const
264 {
265     return "CacheStorage";
266 }
267
268 bool DOMCacheStorage::canSuspendForDocumentSuspension() const
269 {
270     return !hasPendingActivity();
271 }
272
273 } // namespace WebCore