094801573d5128e3d4ced5abd3290dfac923d51f
[WebKit-https.git] / Source / WebCore / Modules / cache / CacheStorage.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 "CacheStorage.h"
28
29 #include "CacheQueryOptions.h"
30 #include "JSCache.h"
31 #include "ScriptExecutionContext.h"
32
33 namespace WebCore {
34
35 CacheStorage::CacheStorage(ScriptExecutionContext& context, Ref<CacheStorageConnection>&& connection)
36     : ActiveDOMObject(&context)
37     , m_connection(WTFMove(connection))
38 {
39     suspendIfNeeded();
40 }
41
42 String CacheStorage::origin() const
43 {
44     // FIXME: Do we really need to check for origin being null?
45     auto* origin = scriptExecutionContext() ? scriptExecutionContext()->securityOrigin() : nullptr;
46     return origin ? origin->toString() : String();
47 }
48
49 void CacheStorage::match(Cache::RequestInfo&&, CacheQueryOptions&&, Ref<DeferredPromise>&& promise)
50 {
51     promise->reject(Exception { NotSupportedError, ASCIILiteral("Not implemented")});
52 }
53
54 void CacheStorage::has(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
55 {
56     retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
57         promise.resolve(m_caches.findMatching([&](auto& item) { return item->name() == name; }) != notFound);
58     });
59 }
60
61 void CacheStorage::retrieveCaches(WTF::Function<void()>&& callback)
62 {
63     String origin = this->origin();
64     if (origin.isNull())
65         return;
66
67     setPendingActivity(this);
68     m_connection->retrieveCaches(origin, [this, callback = WTFMove(callback)](Vector<CacheStorageConnection::CacheInfo>&& cachesInfo) {
69         if (!m_isStopped) {
70             ASSERT(scriptExecutionContext());
71             m_caches.removeAllMatching([&](auto& cache) {
72                 return cachesInfo.findMatching([&](const auto& info) { return info.identifier == cache->identifier(); }) == notFound;
73             });
74             for (auto& info : cachesInfo) {
75                 if (m_caches.findMatching([&](const auto& cache) { return info.identifier == cache->identifier(); }) == notFound)
76                     m_caches.append(Cache::create(*scriptExecutionContext(), WTFMove(info.name), info.identifier, m_connection.copyRef()));
77             }
78
79             std::sort(m_caches.begin(), m_caches.end(), [&](auto& a, auto& b) {
80                 return a->identifier() < b->identifier();
81             });
82
83             callback();
84         }
85         unsetPendingActivity(this);
86     });
87 }
88
89 void CacheStorage::open(const String& name, DOMPromiseDeferred<IDLInterface<Cache>>&& promise)
90 {
91     retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
92         auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
93         if (position != notFound) {
94             auto& cache = m_caches[position];
95             promise.resolve(Cache::create(*scriptExecutionContext(), String { cache->name() }, cache->identifier(), m_connection.copyRef()));
96             return;
97         }
98
99         String origin = this->origin();
100         ASSERT(!origin.isNull());
101
102         setPendingActivity(this);
103         m_connection->open(origin, name, [this, name, promise = WTFMove(promise)](uint64_t cacheIdentifier, CacheStorageConnection::Error error) mutable {
104             if (!m_isStopped) {
105                 auto result = CacheStorageConnection::errorToException(error);
106                 if (result.hasException())
107                     promise.reject(result.releaseException());
108                 else {
109                     auto cache = Cache::create(*scriptExecutionContext(), String { name }, cacheIdentifier, m_connection.copyRef());
110                     promise.resolve(cache);
111                     m_caches.append(WTFMove(cache));
112                 }
113             }
114             unsetPendingActivity(this);
115         });
116     });
117 }
118
119 void CacheStorage::remove(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
120 {
121     retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
122         auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
123         if (position == notFound) {
124             promise.resolve(false);
125             return;
126         }
127
128         String origin = this->origin();
129         ASSERT(!origin.isNull());
130
131         setPendingActivity(this);
132         m_connection->remove(m_caches[position]->identifier(), [this, name, promise = WTFMove(promise)](uint64_t cacheIdentifier, CacheStorageConnection::Error error) mutable {
133             UNUSED_PARAM(cacheIdentifier);
134             if (!m_isStopped)
135                 promise.settle(CacheStorageConnection::exceptionOrResult(true, error));
136
137             unsetPendingActivity(this);
138         });
139         m_caches.remove(position);
140     });
141 }
142
143 void CacheStorage::keys(KeysPromise&& promise)
144 {
145     retrieveCaches([this, promise = WTFMove(promise)]() mutable {
146         Vector<String> keys;
147         keys.reserveInitialCapacity(m_caches.size());
148         for (auto& cache : m_caches)
149             keys.uncheckedAppend(cache->name());
150         promise.resolve(keys);
151     });
152 }
153
154 void CacheStorage::stop()
155 {
156     m_isStopped = true;
157 }
158
159 const char* CacheStorage::activeDOMObjectName() const
160 {
161     return "CacheStorage";
162 }
163
164 bool CacheStorage::canSuspendForDocumentSuspension() const
165 {
166     return !hasPendingActivity();
167 }
168
169 } // namespace WebCore