1
2/*
3 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "WorkerCacheStorageConnection.h"
29
30#include "CacheQueryOptions.h"
31#include "CacheStorageProvider.h"
32#include "ClientOrigin.h"
33#include "Document.h"
34#include "Page.h"
35#include "WorkerGlobalScope.h"
36#include "WorkerLoaderProxy.h"
37#include "WorkerRunLoop.h"
38#include "WorkerThread.h"
39
40namespace WebCore {
41using namespace WebCore::DOMCacheEngine;
42
43struct CrossThreadRecordData {
44 uint64_t identifier;
45 uint64_t updateResponseCounter;
46
47 FetchHeaders::Guard requestHeadersGuard;
48 ResourceRequest request;
49
50 FetchOptions options;
51 String referrer;
52
53 FetchHeaders::Guard responseHeadersGuard;
54 ResourceResponse::CrossThreadData response;
55 ResponseBody responseBody;
56 uint64_t responseBodySize;
57};
58
59static CrossThreadRecordData toCrossThreadRecordData(const Record& record)
60{
61 return CrossThreadRecordData {
62 record.identifier,
63 record.updateResponseCounter,
64 record.requestHeadersGuard,
65 record.request.isolatedCopy(),
66 record.options.isolatedCopy(),
67 record.referrer.isolatedCopy(),
68 record.responseHeadersGuard,
69 record.response.crossThreadData(),
70 isolatedResponseBody(record.responseBody),
71 record.responseBodySize
72 };
73}
74
75static Record fromCrossThreadRecordData(CrossThreadRecordData&& data)
76{
77 return Record {
78 data.identifier,
79 data.updateResponseCounter,
80 data.requestHeadersGuard,
81 WTFMove(data.request),
82 WTFMove(data.options),
83 WTFMove(data.referrer),
84 data.responseHeadersGuard,
85 ResourceResponse::fromCrossThreadData(WTFMove(data.response)),
86 WTFMove(data.responseBody),
87 data.responseBodySize
88 };
89}
90
91static inline Vector<CrossThreadRecordData> recordsDataFromRecords(const Vector<Record>& records)
92{
93 return WTF::map(records, toCrossThreadRecordData);
94}
95
96static inline Expected<Vector<CrossThreadRecordData>, Error> recordsDataOrErrorFromRecords(const RecordsOrError& result)
97{
98 if (!result.has_value())
99 return makeUnexpected(result.error());
100
101 return recordsDataFromRecords(result.value());
102}
103
104static inline Vector<Record> recordsFromRecordsData(Vector<CrossThreadRecordData>&& recordsData)
105{
106 return WTF::map(WTFMove(recordsData), fromCrossThreadRecordData);
107}
108
109static inline RecordsOrError recordsOrErrorFromRecordsData(Expected<Vector<CrossThreadRecordData>, Error>&& recordsData)
110{
111 if (!recordsData.has_value())
112 return makeUnexpected(recordsData.error());
113 return recordsFromRecordsData(WTFMove(recordsData.value()));
114}
115
116Ref<WorkerCacheStorageConnection> WorkerCacheStorageConnection::create(WorkerGlobalScope& scope)
117{
118 auto connection = adoptRef(*new WorkerCacheStorageConnection(scope));
119 callOnMainThreadAndWait([workerThread = makeRef(scope.thread()), connection = connection.ptr()]() mutable {
120 connection->m_mainThreadConnection = workerThread->workerLoaderProxy().createCacheStorageConnection();
121 });
122 ASSERT(connection->m_mainThreadConnection);
123 return connection;
124}
125
126WorkerCacheStorageConnection::WorkerCacheStorageConnection(WorkerGlobalScope& scope)
127 : m_scope(scope)
128{
129}
130
131WorkerCacheStorageConnection::~WorkerCacheStorageConnection()
132{
133 if (m_mainThreadConnection)
134 callOnMainThread([mainThreadConnection = WTFMove(m_mainThreadConnection)]() mutable { });
135}
136
137void WorkerCacheStorageConnection::open(const ClientOrigin& origin, const String& cacheName, CacheIdentifierCallback&& callback)
138{
139 uint64_t requestIdentifier = ++m_lastRequestIdentifier;
140 m_openAndRemoveCachePendingRequests.add(requestIdentifier, WTFMove(callback));
141
142 callOnMainThread([workerThread = makeRef(m_scope.thread()), mainThreadConnection = m_mainThreadConnection, requestIdentifier, origin = origin.isolatedCopy(), cacheName = cacheName.isolatedCopy()] () mutable {
143 mainThreadConnection->open(origin, cacheName, [workerThread = WTFMove(workerThread), requestIdentifier] (const CacheIdentifierOrError& result) mutable {
144 workerThread->runLoop().postTaskForMode([requestIdentifier, result] (auto& scope) mutable {
145 downcast<WorkerGlobalScope>(scope).cacheStorageConnection().openOrRemoveCompleted(requestIdentifier, result);
146 }, WorkerRunLoop::defaultMode());
147 });
148 });
149}
150
151void WorkerCacheStorageConnection::openOrRemoveCompleted(uint64_t requestIdentifier, const CacheIdentifierOrError& result)
152{
153 if (auto callback = m_openAndRemoveCachePendingRequests.take(requestIdentifier))
154 callback(result);
155}
156
157void WorkerCacheStorageConnection::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
158{
159 uint64_t requestIdentifier = ++m_lastRequestIdentifier;
160 m_openAndRemoveCachePendingRequests.add(requestIdentifier, WTFMove(callback));
161
162 callOnMainThread([workerThread = makeRef(m_scope.thread()), mainThreadConnection = m_mainThreadConnection, requestIdentifier, cacheIdentifier] () mutable {
163 mainThreadConnection->remove(cacheIdentifier, [workerThread = WTFMove(workerThread), requestIdentifier, cacheIdentifier] (const CacheIdentifierOrError& result) mutable {
164 ASSERT_UNUSED(cacheIdentifier, !result.has_value() || !result.value().identifier || result.value().identifier == cacheIdentifier);
165 workerThread->runLoop().postTaskForMode([requestIdentifier, result] (auto& scope) mutable {
166 downcast<WorkerGlobalScope>(scope).cacheStorageConnection().openOrRemoveCompleted(requestIdentifier, result);
167 }, WorkerRunLoop::defaultMode());
168 });
169 });
170}
171
172void WorkerCacheStorageConnection::retrieveCaches(const ClientOrigin& origin, uint64_t updateCounter, CacheInfosCallback&& callback)
173{
174 uint64_t requestIdentifier = ++m_lastRequestIdentifier;
175 m_retrieveCachesPendingRequests.add(requestIdentifier, WTFMove(callback));
176
177 callOnMainThread([workerThread = makeRef(m_scope.thread()), mainThreadConnection = m_mainThreadConnection, requestIdentifier, origin = origin.isolatedCopy(), updateCounter] () mutable {
178 mainThreadConnection->retrieveCaches(origin, updateCounter, [workerThread = WTFMove(workerThread), requestIdentifier] (CacheInfosOrError&& result) mutable {
179 CacheInfosOrError isolatedResult;
180 if (!result.has_value())
181 isolatedResult = WTFMove(result);
182 else
183 isolatedResult = result.value().isolatedCopy();
184
185 workerThread->runLoop().postTaskForMode([requestIdentifier, result = WTFMove(isolatedResult)] (auto& scope) mutable {
186 downcast<WorkerGlobalScope>(scope).cacheStorageConnection().retrieveCachesCompleted(requestIdentifier, WTFMove(result));
187 }, WorkerRunLoop::defaultMode());
188 });
189 });
190}
191
192void WorkerCacheStorageConnection::retrieveCachesCompleted(uint64_t requestIdentifier, CacheInfosOrError&& result)
193{
194 if (auto callback = m_retrieveCachesPendingRequests.take(requestIdentifier))
195 callback(WTFMove(result));
196}
197
198void WorkerCacheStorageConnection::retrieveRecords(uint64_t cacheIdentifier, const URL& url, RecordsCallback&& callback)
199{
200 uint64_t requestIdentifier = ++m_lastRequestIdentifier;
201 m_retrieveRecordsPendingRequests.add(requestIdentifier, WTFMove(callback));
202
203 callOnMainThread([workerThread = makeRef(m_scope.thread()), mainThreadConnection = m_mainThreadConnection, requestIdentifier, cacheIdentifier, url = url.isolatedCopy()]() mutable {
204 mainThreadConnection->retrieveRecords(cacheIdentifier, url, [workerThread = WTFMove(workerThread), requestIdentifier](RecordsOrError&& result) mutable {
205 workerThread->runLoop().postTaskForMode([result = recordsDataOrErrorFromRecords(result), requestIdentifier] (auto& scope) mutable {
206 downcast<WorkerGlobalScope>(scope).cacheStorageConnection().retrieveRecordsCompleted(requestIdentifier, recordsOrErrorFromRecordsData(WTFMove(result)));
207 }, WorkerRunLoop::defaultMode());
208 });
209 });
210}
211
212void WorkerCacheStorageConnection::retrieveRecordsCompleted(uint64_t requestIdentifier, RecordsOrError&& result)
213{
214 if (auto callback = m_retrieveRecordsPendingRequests.take(requestIdentifier))
215 callback(WTFMove(result));
216}
217
218void WorkerCacheStorageConnection::batchDeleteOperation(uint64_t cacheIdentifier, const ResourceRequest& request, CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
219{
220 uint64_t requestIdentifier = ++m_lastRequestIdentifier;
221 m_batchDeleteAndPutPendingRequests.add(requestIdentifier, WTFMove(callback));
222
223 callOnMainThread([workerThread = makeRef(m_scope.thread()), mainThreadConnection = m_mainThreadConnection, requestIdentifier, cacheIdentifier, request = request.isolatedCopy(), options = options.isolatedCopy()]() mutable {
224 mainThreadConnection->batchDeleteOperation(cacheIdentifier, request, WTFMove(options), [workerThread = WTFMove(workerThread), requestIdentifier](RecordIdentifiersOrError&& result) mutable {
225 workerThread->runLoop().postTaskForMode([requestIdentifier, result = WTFMove(result)] (auto& scope) mutable {
226 downcast<WorkerGlobalScope>(scope).cacheStorageConnection().deleteRecordsCompleted(requestIdentifier, WTFMove(result));
227 }, WorkerRunLoop::defaultMode());
228 });
229 });
230}
231
232void WorkerCacheStorageConnection::deleteRecordsCompleted(uint64_t requestIdentifier, Expected<Vector<uint64_t>, Error>&& result)
233{
234 if (auto callback = m_batchDeleteAndPutPendingRequests.take(requestIdentifier))
235 callback(WTFMove(result));
236}
237
238void WorkerCacheStorageConnection::batchPutOperation(uint64_t cacheIdentifier, Vector<DOMCacheEngine::Record>&& records, DOMCacheEngine::RecordIdentifiersCallback&& callback)
239{
240 uint64_t requestIdentifier = ++m_lastRequestIdentifier;
241 m_batchDeleteAndPutPendingRequests.add(requestIdentifier, WTFMove(callback));
242
243 callOnMainThread([workerThread = makeRef(m_scope.thread()), mainThreadConnection = m_mainThreadConnection, requestIdentifier, cacheIdentifier, recordsData = recordsDataFromRecords(records)]() mutable {
244 mainThreadConnection->batchPutOperation(cacheIdentifier, recordsFromRecordsData(WTFMove(recordsData)), [workerThread = WTFMove(workerThread), requestIdentifier] (RecordIdentifiersOrError&& result) mutable {
245 workerThread->runLoop().postTaskForMode([requestIdentifier, result = WTFMove(result)] (auto& scope) mutable {
246 downcast<WorkerGlobalScope>(scope).cacheStorageConnection().putRecordsCompleted(requestIdentifier, WTFMove(result));
247 }, WorkerRunLoop::defaultMode());
248 });
249 });
250}
251
252void WorkerCacheStorageConnection::putRecordsCompleted(uint64_t requestIdentifier, Expected<Vector<uint64_t>, Error>&& result)
253{
254 if (auto callback = m_batchDeleteAndPutPendingRequests.take(requestIdentifier))
255 callback(WTFMove(result));
256}
257
258void WorkerCacheStorageConnection::reference(uint64_t cacheIdentifier)
259{
260 callOnMainThread([mainThreadConnection = m_mainThreadConnection, cacheIdentifier]() {
261 mainThreadConnection->reference(cacheIdentifier);
262 });
263}
264
265void WorkerCacheStorageConnection::dereference(uint64_t cacheIdentifier)
266{
267 callOnMainThread([mainThreadConnection = m_mainThreadConnection, cacheIdentifier]() {
268 mainThreadConnection->dereference(cacheIdentifier);
269 });
270}
271
272void WorkerCacheStorageConnection::clearPendingRequests()
273{
274 auto openAndRemoveCachePendingRequests = WTFMove(m_openAndRemoveCachePendingRequests);
275 for (auto& callback : openAndRemoveCachePendingRequests.values())
276 callback(makeUnexpected(DOMCacheEngine::Error::Stopped));
277
278 auto retrieveCachesPendingRequests = WTFMove(m_retrieveCachesPendingRequests);
279 for (auto& callback : retrieveCachesPendingRequests.values())
280 callback(makeUnexpected(DOMCacheEngine::Error::Stopped));
281
282 auto retrieveRecordsPendingRequests = WTFMove(m_retrieveRecordsPendingRequests);
283 for (auto& callback : retrieveRecordsPendingRequests.values())
284 callback(makeUnexpected(DOMCacheEngine::Error::Stopped));
285
286 auto batchDeleteAndPutPendingRequests = WTFMove(m_batchDeleteAndPutPendingRequests);
287 for (auto& callback : batchDeleteAndPutPendingRequests.values())
288 callback(makeUnexpected(DOMCacheEngine::Error::Stopped));
289}
290
291} // namespace WebCore
292