2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DOMCacheStorage.h"
29 #include "CacheQueryOptions.h"
30 #include "JSDOMCache.h"
31 #include "JSFetchResponse.h"
32 #include "ScriptExecutionContext.h"
36 using namespace WebCore::DOMCacheEngine;
38 DOMCacheStorage::DOMCacheStorage(ScriptExecutionContext& context, Ref<CacheStorageConnection>&& connection)
39 : ActiveDOMObject(&context)
40 , m_connection(WTFMove(connection))
45 String DOMCacheStorage::origin() const
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();
52 static void doSequentialMatch(size_t index, Vector<Ref<DOMCache>>&& caches, DOMCache::RequestInfo&& info, CacheQueryOptions&& options, DOMCache::MatchCallback&& completionHandler)
54 if (index >= caches.size()) {
55 completionHandler(nullptr);
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());
64 if (result.returnValue()) {
65 completionHandler(result.returnValue());
68 doSequentialMatch(++index, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
72 static inline void startSequentialMatch(Vector<Ref<DOMCache>>&& caches, DOMCache::RequestInfo&& info, CacheQueryOptions&& options, DOMCache::MatchCallback&& completionHandler)
74 doSequentialMatch(0, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
77 static inline Ref<DOMCache> copyCache(const Ref<DOMCache>& cache)
79 return cache.copyRef();
82 void DOMCacheStorage::match(DOMCache::RequestInfo&& info, CacheQueryOptions&& options, Ref<DeferredPromise>&& promise)
84 retrieveCaches([this, info = WTFMove(info), options = WTFMove(options), promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
86 promise->reject(WTFMove(exception.value()));
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));
100 setPendingActivity(this);
101 startSequentialMatch(WTF::map(m_caches, copyCache), WTFMove(info), WTFMove(options), [this, promise = WTFMove(promise)](ExceptionOr<FetchResponse*>&& result) mutable {
103 if (result.hasException()) {
104 promise->reject(result.releaseException());
107 if (!result.returnValue())
110 promise->resolve<IDLInterface<FetchResponse>>(*result.returnValue());
112 unsetPendingActivity(this);
117 void DOMCacheStorage::has(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
119 retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
121 promise.reject(WTFMove(exception.value()));
124 promise.resolve(m_caches.findMatching([&](auto& item) { return item->name() == name; }) != notFound);
128 Ref<DOMCache> DOMCacheStorage::findCacheOrCreate(CacheInfo&& info)
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());
136 void DOMCacheStorage::retrieveCaches(WTF::Function<void(std::optional<Exception>&&)>&& callback)
138 String origin = this->origin();
142 setPendingActivity(this);
143 m_connection->retrieveCaches(origin, m_updateCounter, [this, callback = WTFMove(callback)](CacheInfosOrError&& result) mutable {
145 if (!result.has_value()) {
146 callback(DOMCacheEngine::errorToException(result.error()));
150 auto& cachesInfo = result.value();
152 if (m_updateCounter != cachesInfo.updateCounter) {
153 m_updateCounter = cachesInfo.updateCounter;
155 m_caches = WTF::map(WTFMove(cachesInfo.infos), [this] (CacheInfo&& info) {
156 return findCacheOrCreate(WTFMove(info));
159 callback(std::nullopt);
161 unsetPendingActivity(this);
165 static void logConsolePersistencyError(ScriptExecutionContext* context, const String& cacheName)
170 context->addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("There was an error making ", cacheName, " persistent on the filesystem"));
173 void DOMCacheStorage::open(const String& name, DOMPromiseDeferred<IDLInterface<DOMCache>>&& promise)
175 retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
177 promise.reject(WTFMove(exception.value()));
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()));
188 String origin = this->origin();
189 ASSERT(!origin.isNull());
191 setPendingActivity(this);
192 m_connection->open(origin, name, [this, name, promise = WTFMove(promise)](const CacheIdentifierOrError& result) mutable {
194 if (!result.has_value())
195 promise.reject(DOMCacheEngine::errorToException(result.error()));
197 if (result.value().hadStorageError)
198 logConsolePersistencyError(scriptExecutionContext(), name);
200 auto cache = DOMCache::create(*scriptExecutionContext(), String { name }, result.value().identifier, m_connection.copyRef());
201 promise.resolve(cache);
202 m_caches.append(WTFMove(cache));
205 unsetPendingActivity(this);
210 void DOMCacheStorage::remove(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
212 retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
214 promise.reject(WTFMove(exception.value()));
218 auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
219 if (position == notFound) {
220 promise.resolve(false);
224 String origin = this->origin();
225 ASSERT(!origin.isNull());
227 setPendingActivity(this);
228 m_connection->remove(m_caches[position]->identifier(), [this, name, promise = WTFMove(promise)](const CacheIdentifierOrError& result) mutable {
230 if (!result.has_value())
231 promise.reject(DOMCacheEngine::errorToException(result.error()));
233 if (result.value().hadStorageError)
234 logConsolePersistencyError(scriptExecutionContext(), name);
235 promise.resolve(true);
238 unsetPendingActivity(this);
240 m_caches.remove(position);
244 void DOMCacheStorage::keys(KeysPromise&& promise)
246 retrieveCaches([this, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
248 promise.reject(WTFMove(exception.value()));
252 promise.resolve(WTF::map(m_caches, [] (const auto& cache) {
253 return cache->name();
258 void DOMCacheStorage::stop()
263 const char* DOMCacheStorage::activeDOMObjectName() const
265 return "CacheStorage";
268 bool DOMCacheStorage::canSuspendForDocumentSuspension() const
270 return !hasPendingActivity();
273 } // namespace WebCore