[Cache API] Unify WebCore and WebKit error handling
[WebKit-https.git] / Source / WebCore / Modules / cache / WorkerCacheStorageConnection.cpp
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 "Document.h"
33 #include "Page.h"
34 #include "WorkerGlobalScope.h"
35 #include "WorkerLoaderProxy.h"
36 #include "WorkerRunLoop.h"
37 #include "WorkerThread.h"
38
39 using namespace WebCore::DOMCache;
40
41 namespace WebCore {
42
43 struct 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 };
57
58 static CrossThreadRecordData toCrossThreadRecordData(const Record& record)
59 {
60     return CrossThreadRecordData {
61         record.identifier,
62         record.updateResponseCounter,
63         record.requestHeadersGuard,
64         record.request.isolatedCopy(),
65         record.options.isolatedCopy(),
66         record.referrer.isolatedCopy(),
67         record.responseHeadersGuard,
68         record.response.crossThreadData(),
69         isolatedResponseBody(record.responseBody)
70     };
71 }
72
73 static Record fromCrossThreadRecordData(CrossThreadRecordData&& data)
74 {
75     return Record {
76         data.identifier,
77         data.updateResponseCounter,
78         data.requestHeadersGuard,
79         WTFMove(data.request),
80         WTFMove(data.options),
81         WTFMove(data.referrer),
82         data.responseHeadersGuard,
83         ResourceResponse::fromCrossThreadData(WTFMove(data.response)),
84         WTFMove(data.responseBody)
85     };
86 }
87
88 Ref<WorkerCacheStorageConnection> WorkerCacheStorageConnection::create(WorkerGlobalScope& scope)
89 {
90     auto connection = adoptRef(*new WorkerCacheStorageConnection(scope));
91     connection->m_proxy.postTaskToLoader([protectedConnection = makeRef(connection.get())](ScriptExecutionContext& context) mutable {
92         ASSERT(isMainThread());
93         Document& document = downcast<Document>(context);
94
95         ASSERT(document.page());
96         protectedConnection->m_mainThreadConnection = document.page()->cacheStorageProvider().createCacheStorageConnection(document.page()->sessionID());
97     });
98     return connection;
99 }
100
101 WorkerCacheStorageConnection::WorkerCacheStorageConnection(WorkerGlobalScope& scope)
102     : m_scope(scope)
103     , m_proxy(m_scope.thread().workerLoaderProxy())
104     , m_taskMode(WorkerRunLoop::defaultMode().isolatedCopy())
105 {
106 }
107
108 WorkerCacheStorageConnection::~WorkerCacheStorageConnection()
109 {
110     if (m_mainThreadConnection)
111         m_proxy.postTaskToLoader([mainThreadConnection = WTFMove(m_mainThreadConnection)](ScriptExecutionContext&) mutable { });
112 }
113
114 void WorkerCacheStorageConnection::doOpen(uint64_t requestIdentifier, const String& origin, const String& cacheName)
115 {
116     m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), requestIdentifier, origin = origin.isolatedCopy(), cacheName = cacheName.isolatedCopy()](ScriptExecutionContext&) mutable {
117         ASSERT(isMainThread());
118         ASSERT(m_mainThreadConnection);
119
120         m_mainThreadConnection->open(origin, cacheName, [this, protectedThis = WTFMove(protectedThis), requestIdentifier](const CacheIdentifierOrError& result) mutable {
121             m_proxy.postTaskForModeToWorkerGlobalScope([this, protectedThis = WTFMove(protectedThis), requestIdentifier, result](ScriptExecutionContext& context) mutable {
122                 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
123                 openCompleted(requestIdentifier, result);
124             }, m_taskMode);
125         });
126     });
127 }
128
129 void WorkerCacheStorageConnection::doRemove(uint64_t requestIdentifier, uint64_t cacheIdentifier)
130 {
131     m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), requestIdentifier, cacheIdentifier](ScriptExecutionContext&) mutable {
132         ASSERT(isMainThread());
133         ASSERT(m_mainThreadConnection);
134
135         m_mainThreadConnection->remove(cacheIdentifier, [this, protectedThis = WTFMove(protectedThis), requestIdentifier, cacheIdentifier](const CacheIdentifierOrError& result) mutable {
136             ASSERT(!result.hasValue() || result.value() == cacheIdentifier);
137             m_proxy.postTaskForModeToWorkerGlobalScope([this, protectedThis = WTFMove(protectedThis), requestIdentifier, result](ScriptExecutionContext& context) mutable {
138                 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
139                 removeCompleted(requestIdentifier, result);
140             }, m_taskMode);
141         });
142     });
143 }
144
145 void WorkerCacheStorageConnection::doRetrieveCaches(uint64_t requestIdentifier, const String& origin)
146 {
147     m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), requestIdentifier, origin = origin.isolatedCopy()](ScriptExecutionContext&) mutable {
148         ASSERT(isMainThread());
149         ASSERT(m_mainThreadConnection);
150
151         m_mainThreadConnection->retrieveCaches(origin, [this, protectedThis = WTFMove(protectedThis), requestIdentifier](CacheInfosOrError&& result) mutable {
152             CacheInfosOrError isolatedResult;
153             if (!result.hasValue())
154                 isolatedResult = WTFMove(result);
155             else {
156                 Vector<CacheInfo> isolatedCaches;
157                 isolatedCaches.reserveInitialCapacity(result.value().size());
158                 for (const auto& cache : result.value())
159                     isolatedCaches.uncheckedAppend(CacheInfo { cache.identifier, cache.name.isolatedCopy() });
160                 isolatedResult = WTFMove(isolatedCaches);
161             }
162             m_proxy.postTaskForModeToWorkerGlobalScope([this, protectedThis = WTFMove(protectedThis), requestIdentifier, result = WTFMove(isolatedResult)](ScriptExecutionContext& context) mutable {
163                 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
164                 updateCaches(requestIdentifier, WTFMove(result));
165             }, m_taskMode);
166         });
167     });
168 }
169
170 static inline Vector<CrossThreadRecordData> recordsDataFromRecords(const Vector<Record>& records)
171 {
172     Vector<CrossThreadRecordData> recordsData;
173     recordsData.reserveInitialCapacity(records.size());
174     for (const auto& record : records)
175         recordsData.uncheckedAppend(toCrossThreadRecordData(record));
176     return recordsData;
177 }
178
179 static inline Expected<Vector<CrossThreadRecordData>, Error> recordsDataOrErrorFromRecords(const RecordsOrError& result)
180 {
181     if (!result.hasValue())
182         return makeUnexpected(result.error());
183
184     return recordsDataFromRecords(result.value());
185 }
186
187 static inline Vector<Record> recordsFromRecordsData(Vector<CrossThreadRecordData>&& recordsData)
188 {
189     Vector<Record> records;
190     records.reserveInitialCapacity(recordsData.size());
191     for (auto& recordData : recordsData)
192         records.uncheckedAppend(fromCrossThreadRecordData(WTFMove(recordData)));
193     return records;
194 }
195
196 static inline RecordsOrError recordsOrErrorFromRecordsData(Expected<Vector<CrossThreadRecordData>, Error>&& recordsData)
197 {
198     if (!recordsData.hasValue())
199         return makeUnexpected(recordsData.error());
200     return recordsFromRecordsData(WTFMove(recordsData.value()));
201 }
202
203 void WorkerCacheStorageConnection::doRetrieveRecords(uint64_t requestIdentifier, uint64_t cacheIdentifier)
204 {
205     m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), requestIdentifier, cacheIdentifier](ScriptExecutionContext&) mutable {
206         ASSERT(isMainThread());
207         ASSERT(m_mainThreadConnection);
208
209         m_mainThreadConnection->retrieveRecords(cacheIdentifier, [this, protectedThis = WTFMove(protectedThis), requestIdentifier](RecordsOrError&& result) mutable {
210             m_proxy.postTaskForModeToWorkerGlobalScope([this, protectedThis = WTFMove(protectedThis), result = recordsDataOrErrorFromRecords(result), requestIdentifier](ScriptExecutionContext& context) mutable {
211                 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
212                 updateRecords(requestIdentifier, recordsOrErrorFromRecordsData(WTFMove(result)));
213             }, m_taskMode);
214         });
215     });
216 }
217
218 void WorkerCacheStorageConnection::doBatchDeleteOperation(uint64_t requestIdentifier, uint64_t cacheIdentifier, const ResourceRequest& request, CacheQueryOptions&& options)
219 {
220     m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), requestIdentifier, cacheIdentifier, request = request.isolatedCopy(), options = options.isolatedCopy()](ScriptExecutionContext&) mutable {
221         ASSERT(isMainThread());
222         ASSERT(m_mainThreadConnection);
223
224         m_mainThreadConnection->batchDeleteOperation(cacheIdentifier, request, WTFMove(options), [this, protectedThis = WTFMove(protectedThis), requestIdentifier](RecordIdentifiersOrError&& result) mutable {
225
226             m_proxy.postTaskForModeToWorkerGlobalScope([this, protectedThis = WTFMove(protectedThis), requestIdentifier, result = WTFMove(result)](ScriptExecutionContext& context) mutable {
227                 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
228                 deleteRecordsCompleted(requestIdentifier, WTFMove(result));
229             }, m_taskMode);
230         });
231     });
232 }
233
234 void WorkerCacheStorageConnection::doBatchPutOperation(uint64_t requestIdentifier, uint64_t cacheIdentifier, Vector<Record>&& records)
235 {
236     m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), requestIdentifier, cacheIdentifier, recordsData = recordsDataFromRecords(records)](ScriptExecutionContext&) mutable {
237         ASSERT(isMainThread());
238         ASSERT(m_mainThreadConnection);
239
240         m_mainThreadConnection->batchPutOperation(cacheIdentifier, recordsFromRecordsData(WTFMove(recordsData)), [this, protectedThis = WTFMove(protectedThis), requestIdentifier](RecordIdentifiersOrError&& result) mutable {
241
242             m_proxy.postTaskForModeToWorkerGlobalScope([this, protectedThis = WTFMove(protectedThis), requestIdentifier, result = WTFMove(result)](ScriptExecutionContext& context) mutable {
243                 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
244                 putRecordsCompleted(requestIdentifier, WTFMove(result));
245             }, m_taskMode);
246         });
247     });
248 }
249
250 } // namespace WebCore