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 "ClientOrigin.h"
31 #include "JSDOMCache.h"
32 #include "JSFetchResponse.h"
33 #include "ScriptExecutionContext.h"
37 using namespace WebCore::DOMCacheEngine;
39 DOMCacheStorage::DOMCacheStorage(ScriptExecutionContext& context, Ref<CacheStorageConnection>&& connection)
40 : ActiveDOMObject(&context)
41 , m_connection(WTFMove(connection))
46 std::optional<ClientOrigin> DOMCacheStorage::origin() const
48 auto* origin = scriptExecutionContext() ? scriptExecutionContext()->securityOrigin() : nullptr;
52 return ClientOrigin { SecurityOriginData::fromSecurityOrigin(scriptExecutionContext()->topOrigin()), SecurityOriginData::fromSecurityOrigin(*origin) };
55 static void doSequentialMatch(size_t index, Vector<Ref<DOMCache>>&& caches, DOMCache::RequestInfo&& info, CacheQueryOptions&& options, DOMCache::MatchCallback&& completionHandler)
57 if (index >= caches.size()) {
58 completionHandler(nullptr);
62 caches[index]->doMatch(WTFMove(info), WTFMove(options), [caches = WTFMove(caches), info, options, completionHandler = WTFMove(completionHandler), index](ExceptionOr<FetchResponse*>&& result) mutable {
63 if (result.hasException()) {
64 completionHandler(result.releaseException());
67 if (result.returnValue()) {
68 completionHandler(result.returnValue());
71 doSequentialMatch(++index, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
75 static inline void startSequentialMatch(Vector<Ref<DOMCache>>&& caches, DOMCache::RequestInfo&& info, CacheQueryOptions&& options, DOMCache::MatchCallback&& completionHandler)
77 doSequentialMatch(0, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
80 static inline Ref<DOMCache> copyCache(const Ref<DOMCache>& cache)
82 return cache.copyRef();
85 void DOMCacheStorage::match(DOMCache::RequestInfo&& info, CacheQueryOptions&& options, Ref<DeferredPromise>&& promise)
87 retrieveCaches([this, info = WTFMove(info), options = WTFMove(options), promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
89 promise->reject(WTFMove(exception.value()));
93 if (!options.cacheName.isNull()) {
94 auto position = m_caches.findMatching([&](auto& item) { return item->name() == options.cacheName; });
95 if (position != notFound) {
96 m_caches[position]->match(WTFMove(info), WTFMove(options), WTFMove(promise));
103 setPendingActivity(this);
104 startSequentialMatch(WTF::map(m_caches, copyCache), WTFMove(info), WTFMove(options), [this, promise = WTFMove(promise)](ExceptionOr<FetchResponse*>&& result) mutable {
106 if (result.hasException()) {
107 promise->reject(result.releaseException());
110 if (!result.returnValue())
113 promise->resolve<IDLInterface<FetchResponse>>(*result.returnValue());
115 unsetPendingActivity(this);
120 void DOMCacheStorage::has(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
122 retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
124 promise.reject(WTFMove(exception.value()));
127 promise.resolve(m_caches.findMatching([&](auto& item) { return item->name() == name; }) != notFound);
131 Ref<DOMCache> DOMCacheStorage::findCacheOrCreate(CacheInfo&& info)
133 auto position = m_caches.findMatching([&] (const auto& cache) { return info.identifier == cache->identifier(); });
134 if (position != notFound)
135 return m_caches[position].copyRef();
136 return DOMCache::create(*scriptExecutionContext(), WTFMove(info.name), info.identifier, m_connection.copyRef());
139 void DOMCacheStorage::retrieveCaches(WTF::Function<void(std::optional<Exception>&&)>&& callback)
141 auto origin = this->origin();
145 m_connection->retrieveCaches(*origin, m_updateCounter, [this, callback = WTFMove(callback), pendingActivity = makePendingActivity(*this)](CacheInfosOrError&& result) mutable {
147 if (!result.has_value()) {
148 callback(DOMCacheEngine::errorToException(result.error()));
152 auto& cachesInfo = result.value();
154 if (m_updateCounter != cachesInfo.updateCounter) {
155 m_updateCounter = cachesInfo.updateCounter;
157 m_caches = WTF::map(WTFMove(cachesInfo.infos), [this] (CacheInfo&& info) {
158 return findCacheOrCreate(WTFMove(info));
161 callback(std::nullopt);
166 static void logConsolePersistencyError(ScriptExecutionContext* context, const String& cacheName)
171 context->addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("There was an error making ", cacheName, " persistent on the filesystem"));
174 void DOMCacheStorage::open(const String& name, DOMPromiseDeferred<IDLInterface<DOMCache>>&& promise)
176 retrieveCaches([this, name, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
178 promise.reject(WTFMove(exception.value()));
181 doOpen(name, WTFMove(promise));
185 void DOMCacheStorage::doOpen(const String& name, DOMPromiseDeferred<IDLInterface<DOMCache>>&& promise)
187 auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
188 if (position != notFound) {
189 auto& cache = m_caches[position];
190 promise.resolve(DOMCache::create(*scriptExecutionContext(), String { cache->name() }, cache->identifier(), m_connection.copyRef()));
194 m_connection->open(*origin(), name, [this, name, promise = WTFMove(promise), pendingActivity = makePendingActivity(*this)](const CacheIdentifierOrError& result) mutable {
196 if (!result.has_value())
197 promise.reject(DOMCacheEngine::errorToException(result.error()));
199 if (result.value().hadStorageError)
200 logConsolePersistencyError(scriptExecutionContext(), name);
202 auto cache = DOMCache::create(*scriptExecutionContext(), String { name }, result.value().identifier, m_connection.copyRef());
203 promise.resolve(cache);
204 m_caches.append(WTFMove(cache));
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()));
217 doRemove(name, WTFMove(promise));
221 void DOMCacheStorage::doRemove(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
223 auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
224 if (position == notFound) {
225 promise.resolve(false);
229 m_connection->remove(m_caches[position]->identifier(), [this, name, promise = WTFMove(promise), pendingActivity = makePendingActivity(*this)](const CacheIdentifierOrError& result) mutable {
231 if (!result.has_value())
232 promise.reject(DOMCacheEngine::errorToException(result.error()));
234 if (result.value().hadStorageError)
235 logConsolePersistencyError(scriptExecutionContext(), name);
236 promise.resolve(true);
240 m_caches.remove(position);
243 void DOMCacheStorage::keys(KeysPromise&& promise)
245 retrieveCaches([this, promise = WTFMove(promise)](std::optional<Exception>&& exception) mutable {
247 promise.reject(WTFMove(exception.value()));
251 promise.resolve(WTF::map(m_caches, [] (const auto& cache) {
252 return cache->name();
257 void DOMCacheStorage::stop()
262 const char* DOMCacheStorage::activeDOMObjectName() const
264 return "CacheStorage";
267 bool DOMCacheStorage::canSuspendForDocumentSuspension() const
269 return !hasPendingActivity();
272 } // namespace WebCore