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 "CacheStorage.h"
29 #include "CacheQueryOptions.h"
31 #include "JSFetchResponse.h"
32 #include "ScriptExecutionContext.h"
34 using namespace WebCore::DOMCache;
38 CacheStorage::CacheStorage(ScriptExecutionContext& context, Ref<CacheStorageConnection>&& connection)
39 : ActiveDOMObject(&context)
40 , m_connection(WTFMove(connection))
45 String CacheStorage::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<Cache>>&& caches, Cache::RequestInfo&& info, CacheQueryOptions&& options, Cache::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](FetchResponse* value) mutable {
61 completionHandler(value);
64 doSequentialMatch(++index, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
68 static inline void startSequentialMatch(Vector<Ref<Cache>>&& caches, Cache::RequestInfo&& info, CacheQueryOptions&& options, Cache::MatchCallback&& completionHandler)
70 doSequentialMatch(0, WTFMove(caches), WTFMove(info), WTFMove(options), WTFMove(completionHandler));
73 static inline Vector<Ref<Cache>> copyCaches(const Vector<Ref<Cache>>& caches)
75 Vector<Ref<Cache>> copy;
76 copy.reserveInitialCapacity(caches.size());
77 for (auto& cache : caches)
78 copy.uncheckedAppend(cache.copyRef());
82 void CacheStorage::match(Cache::RequestInfo&& info, CacheQueryOptions&& options, Ref<DeferredPromise>&& promise)
84 retrieveCaches([this, info = WTFMove(info), options = WTFMove(options), promise = WTFMove(promise)]() mutable {
85 if (!options.cacheName.isNull()) {
86 auto position = m_caches.findMatching([&](auto& item) { return item->name() == options.cacheName; });
87 if (position != notFound) {
88 m_caches[position]->match(WTFMove(info), WTFMove(options), WTFMove(promise));
95 setPendingActivity(this);
96 startSequentialMatch(copyCaches(m_caches), WTFMove(info), WTFMove(options), [this, promise = WTFMove(promise)](FetchResponse* result) mutable {
101 promise->resolve<IDLInterface<FetchResponse>>(*result);
103 unsetPendingActivity(this);
108 void CacheStorage::has(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
110 retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
111 promise.resolve(m_caches.findMatching([&](auto& item) { return item->name() == name; }) != notFound);
115 void CacheStorage::retrieveCaches(WTF::Function<void()>&& callback)
117 String origin = this->origin();
121 setPendingActivity(this);
122 m_connection->retrieveCaches(origin, [this, callback = WTFMove(callback)](CacheInfosOrError&& result) {
124 // FIXME: We should probably propagate that error up to the promise based operation.
125 ASSERT(result.hasValue());
126 if (!result.hasValue()) {
130 auto& cachesInfo = result.value();
132 ASSERT(scriptExecutionContext());
133 m_caches.removeAllMatching([&](auto& cache) {
134 return cachesInfo.findMatching([&](const auto& info) { return info.identifier == cache->identifier(); }) == notFound;
136 for (auto& info : cachesInfo) {
137 if (m_caches.findMatching([&](const auto& cache) { return info.identifier == cache->identifier(); }) == notFound)
138 m_caches.append(Cache::create(*scriptExecutionContext(), WTFMove(info.name), info.identifier, m_connection.copyRef()));
141 std::sort(m_caches.begin(), m_caches.end(), [&](auto& a, auto& b) {
142 return a->identifier() < b->identifier();
147 unsetPendingActivity(this);
151 void CacheStorage::open(const String& name, DOMPromiseDeferred<IDLInterface<Cache>>&& promise)
153 retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
154 auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
155 if (position != notFound) {
156 auto& cache = m_caches[position];
157 promise.resolve(Cache::create(*scriptExecutionContext(), String { cache->name() }, cache->identifier(), m_connection.copyRef()));
161 String origin = this->origin();
162 ASSERT(!origin.isNull());
164 setPendingActivity(this);
165 m_connection->open(origin, name, [this, name, promise = WTFMove(promise)](const CacheIdentifierOrError& result) mutable {
168 if (!result.hasValue())
169 promise.reject(DOMCache::errorToException(result.error()));
171 auto cache = Cache::create(*scriptExecutionContext(), String { name }, result.value(), m_connection.copyRef());
172 promise.resolve(cache);
173 m_caches.append(WTFMove(cache));
176 unsetPendingActivity(this);
181 void CacheStorage::remove(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
183 retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
184 auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
185 if (position == notFound) {
186 promise.resolve(false);
190 String origin = this->origin();
191 ASSERT(!origin.isNull());
193 setPendingActivity(this);
194 m_connection->remove(m_caches[position]->identifier(), [this, name, promise = WTFMove(promise)](const CacheIdentifierOrError& result) mutable {
196 if (!result.hasValue())
197 promise.reject(DOMCache::errorToException(result.error()));
199 promise.resolve(true);
201 unsetPendingActivity(this);
203 m_caches.remove(position);
207 void CacheStorage::keys(KeysPromise&& promise)
209 retrieveCaches([this, promise = WTFMove(promise)]() mutable {
211 keys.reserveInitialCapacity(m_caches.size());
212 for (auto& cache : m_caches)
213 keys.uncheckedAppend(cache->name());
214 promise.resolve(keys);
218 void CacheStorage::stop()
223 const char* CacheStorage::activeDOMObjectName() const
225 return "CacheStorage";
228 bool CacheStorage::canSuspendForDocumentSuspension() const
230 return !hasPendingActivity();
233 } // namespace WebCore