a0c29de9bd423be5a244d6bdc18714e18e6fc97c
[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                 if (error != CacheStorageConnection::Error::None)
106                     promise.reject(CacheStorageConnection::exceptionFromError(error));
107                 else {
108                     auto cache = Cache::create(*scriptExecutionContext(), String { name }, cacheIdentifier, m_connection.copyRef());
109                     promise.resolve(cache);
110                     m_caches.append(WTFMove(cache));
111                 }
112             }
113             unsetPendingActivity(this);
114         });
115     });
116 }
117
118 void CacheStorage::remove(const String& name, DOMPromiseDeferred<IDLBoolean>&& promise)
119 {
120     retrieveCaches([this, name, promise = WTFMove(promise)]() mutable {
121         auto position = m_caches.findMatching([&](auto& item) { return item->name() == name; });
122         if (position == notFound) {
123             promise.resolve(false);
124             return;
125         }
126
127         String origin = this->origin();
128         ASSERT(!origin.isNull());
129
130         setPendingActivity(this);
131         m_connection->remove(m_caches[position]->identifier(), [this, name, promise = WTFMove(promise)](uint64_t cacheIdentifier, CacheStorageConnection::Error error) mutable {
132             UNUSED_PARAM(cacheIdentifier);
133             if (!m_isStopped) {
134                 if (error != CacheStorageConnection::Error::None)
135                     promise.reject(CacheStorageConnection::exceptionFromError(error));
136                 else
137                     promise.resolve(true);
138             }
139             unsetPendingActivity(this);
140         });
141         m_caches.remove(position);
142     });
143 }
144
145 void CacheStorage::keys(KeysPromise&& promise)
146 {
147     retrieveCaches([this, promise = WTFMove(promise)]() mutable {
148         Vector<String> keys;
149         keys.reserveInitialCapacity(m_caches.size());
150         for (auto& cache : m_caches)
151             keys.uncheckedAppend(cache->name());
152         promise.resolve(keys);
153     });
154 }
155
156 void CacheStorage::stop()
157 {
158     m_isStopped = true;
159 }
160
161 const char* CacheStorage::activeDOMObjectName() const
162 {
163     return "CacheStorage";
164 }
165
166 bool CacheStorage::canSuspendForDocumentSuspension() const
167 {
168     return !hasPendingActivity();
169 }
170
171 } // namespace WebCore