Shrink various loading-related enums to shrink CachedResource
[WebKit-https.git] / Source / WebCore / loader / cache / CachedRawResource.cpp
1 /*
2  * Copyright (C) 2011 Google 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 AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CachedRawResource.h"
28
29 #include "CachedRawResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceLoader.h"
32 #include "HTTPHeaderNames.h"
33 #include "SharedBuffer.h"
34 #include "SubresourceLoader.h"
35 #include <wtf/CompletionHandler.h>
36 #include <wtf/SetForScope.h>
37 #include <wtf/text/StringView.h>
38
39 namespace WebCore {
40
41 CachedRawResource::CachedRawResource(CachedResourceRequest&& request, Type type, PAL::SessionID sessionID)
42     : CachedResource(WTFMove(request), type, sessionID)
43     , m_identifier(0)
44     , m_allowEncodedDataReplacement(true)
45 {
46     ASSERT(isMainOrMediaOrIconOrRawResource());
47 }
48
49 std::optional<SharedBufferDataView> CachedRawResource::calculateIncrementalDataChunk(const SharedBuffer* data) const
50 {
51     size_t previousDataLength = encodedSize();
52     if (!data || data->size() <= previousDataLength)
53         return std::nullopt;
54     return data->getSomeData(previousDataLength);
55 }
56
57 void CachedRawResource::updateBuffer(SharedBuffer& data)
58 {
59     // Skip any updateBuffers triggered from nested runloops. We'll have the complete buffer in finishLoading.
60     if (m_inIncrementalDataNotify)
61         return;
62
63     CachedResourceHandle<CachedRawResource> protectedThis(this);
64     ASSERT(dataBufferingPolicy() == DataBufferingPolicy::BufferData);
65     m_data = &data;
66
67     auto previousDataSize = encodedSize();
68     while (data.size() > previousDataSize) {
69         auto incrementalData = data.getSomeData(previousDataSize);
70         previousDataSize += incrementalData.size();
71
72         SetForScope<bool> notifyScope(m_inIncrementalDataNotify, true);
73         notifyClientsDataWasReceived(incrementalData.data(), incrementalData.size());
74     }
75     setEncodedSize(data.size());
76
77     if (dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData) {
78         if (m_loader)
79             m_loader->setDataBufferingPolicy(DataBufferingPolicy::DoNotBufferData);
80         clear();
81     } else
82         CachedResource::updateBuffer(data);
83
84     if (m_delayedFinishLoading) {
85         auto delayedFinishLoading = std::exchange(m_delayedFinishLoading, std::nullopt);
86         finishLoading(delayedFinishLoading->buffer.get());
87     }
88 }
89
90 void CachedRawResource::updateData(const char* data, unsigned length)
91 {
92     ASSERT(dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData);
93     notifyClientsDataWasReceived(data, length);
94     CachedResource::updateData(data, length);
95 }
96
97 void CachedRawResource::finishLoading(SharedBuffer* data)
98 {
99     if (m_inIncrementalDataNotify) {
100         // We may get here synchronously from updateBuffer() if the callback there ends up spinning a runloop.
101         // In that case delay the call.
102         m_delayedFinishLoading = std::make_optional(DelayedFinishLoading { data });
103         return;
104     };
105     CachedResourceHandle<CachedRawResource> protectedThis(this);
106     DataBufferingPolicy dataBufferingPolicy = this->dataBufferingPolicy();
107     if (dataBufferingPolicy == DataBufferingPolicy::BufferData) {
108         m_data = data;
109
110         if (auto incrementalData = calculateIncrementalDataChunk(data)) {
111             setEncodedSize(data->size());
112             notifyClientsDataWasReceived(incrementalData->data(), incrementalData->size());
113         }
114     }
115
116 #if USE(QUICK_LOOK)
117     m_allowEncodedDataReplacement = m_loader && !m_loader->isQuickLookResource();
118 #endif
119
120     CachedResource::finishLoading(data);
121     if (dataBufferingPolicy == DataBufferingPolicy::BufferData && this->dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData) {
122         if (m_loader)
123             m_loader->setDataBufferingPolicy(DataBufferingPolicy::DoNotBufferData);
124         clear();
125     }
126 }
127
128 void CachedRawResource::notifyClientsDataWasReceived(const char* data, unsigned length)
129 {
130     if (!length)
131         return;
132
133     CachedResourceHandle<CachedRawResource> protectedThis(this);
134     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
135     while (CachedRawResourceClient* c = w.next())
136         c->dataReceived(*this, data, length);
137 }
138
139 static void iterateRedirects(CachedResourceHandle<CachedRawResource>&& handle, CachedRawResourceClient& client, Vector<std::pair<ResourceRequest, ResourceResponse>>&& redirectsInReverseOrder, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
140 {
141     if (!handle->hasClient(client) || redirectsInReverseOrder.isEmpty())
142         return completionHandler({ });
143     auto redirectPair = redirectsInReverseOrder.takeLast();
144     client.redirectReceived(*handle, WTFMove(redirectPair.first), WTFMove(redirectPair.second), [handle = WTFMove(handle), client, redirectsInReverseOrder = WTFMove(redirectsInReverseOrder), completionHandler = WTFMove(completionHandler)] (ResourceRequest&&) mutable {
145         // Ignore the new request because we can't do anything with it.
146         // We're just replying a redirect chain that has already happened.
147         iterateRedirects(WTFMove(handle), client, WTFMove(redirectsInReverseOrder), WTFMove(completionHandler));
148     });
149 }
150
151 void CachedRawResource::didAddClient(CachedResourceClient& c)
152 {
153     CachedRawResourceClient& client = static_cast<CachedRawResourceClient&>(c);
154     size_t redirectCount = m_redirectChain.size();
155     Vector<std::pair<ResourceRequest, ResourceResponse>> redirectsInReverseOrder;
156     redirectsInReverseOrder.reserveInitialCapacity(redirectCount);
157     for (size_t i = 0; i < redirectCount; ++i) {
158         const auto& pair = m_redirectChain[redirectCount - i - 1];
159         redirectsInReverseOrder.uncheckedAppend(std::make_pair(pair.m_request, pair.m_redirectResponse));
160     }
161     iterateRedirects(CachedResourceHandle<CachedRawResource>(this), client, WTFMove(redirectsInReverseOrder), [this, protectedThis = CachedResourceHandle<CachedRawResource>(this), client = &client] (ResourceRequest&&) mutable {
162         if (!hasClient(*client))
163             return;
164         auto responseProcessedHandler = [this, protectedThis = WTFMove(protectedThis), client] {
165             if (!hasClient(*client))
166                 return;
167             if (m_data)
168                 client->dataReceived(*this, m_data->data(), m_data->size());
169             if (!hasClient(*client))
170                 return;
171             CachedResource::didAddClient(*client);
172         };
173
174         if (!m_response.isNull()) {
175             ResourceResponse response(m_response);
176             if (validationCompleting())
177                 response.setSource(ResourceResponse::Source::MemoryCacheAfterValidation);
178             else {
179                 ASSERT(!validationInProgress());
180                 response.setSource(ResourceResponse::Source::MemoryCache);
181             }
182             client->responseReceived(*this, response, WTFMove(responseProcessedHandler));
183         } else
184             responseProcessedHandler();
185     });
186 }
187
188 void CachedRawResource::allClientsRemoved()
189 {
190     if (m_loader)
191         m_loader->cancelIfNotFinishing();
192 }
193
194 static void iterateClients(CachedResourceClientWalker<CachedRawResourceClient>&& walker, CachedResourceHandle<CachedRawResource>&& handle, ResourceRequest&& request, std::unique_ptr<ResourceResponse>&& response, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
195 {
196     auto client = walker.next();
197     if (!client)
198         return completionHandler(WTFMove(request));
199     const ResourceResponse& responseReference = *response;
200     client->redirectReceived(*handle, WTFMove(request), responseReference, [walker = WTFMove(walker), handle = WTFMove(handle), response = WTFMove(response), completionHandler = WTFMove(completionHandler)] (ResourceRequest&& request) mutable {
201         iterateClients(WTFMove(walker), WTFMove(handle), WTFMove(request), WTFMove(response), WTFMove(completionHandler));
202     });
203 }
204
205 void CachedRawResource::redirectReceived(ResourceRequest&& request, const ResourceResponse& response, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
206 {
207     if (response.isNull())
208         CachedResource::redirectReceived(WTFMove(request), response, WTFMove(completionHandler));
209     else {
210         m_redirectChain.append(RedirectPair(request, response));
211         iterateClients(CachedResourceClientWalker<CachedRawResourceClient>(m_clients), CachedResourceHandle<CachedRawResource>(this), WTFMove(request), std::make_unique<ResourceResponse>(response), [this, protectedThis = CachedResourceHandle<CachedRawResource>(this), completionHandler = WTFMove(completionHandler), response] (ResourceRequest&& request) mutable {
212             CachedResource::redirectReceived(WTFMove(request), response, WTFMove(completionHandler));
213         });
214     }
215 }
216
217 void CachedRawResource::responseReceived(const ResourceResponse& response)
218 {
219     CachedResourceHandle<CachedRawResource> protectedThis(this);
220     if (!m_identifier)
221         m_identifier = m_loader->identifier();
222     CachedResource::responseReceived(response);
223     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
224     while (CachedRawResourceClient* c = w.next())
225         c->responseReceived(*this, m_response, nullptr);
226 }
227
228 bool CachedRawResource::shouldCacheResponse(const ResourceResponse& response)
229 {
230     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
231     while (CachedRawResourceClient* c = w.next()) {
232         if (!c->shouldCacheResponse(*this, response))
233             return false;
234     }
235     return true;
236 }
237
238 void CachedRawResource::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
239 {
240     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
241     while (CachedRawResourceClient* c = w.next())
242         c->dataSent(*this, bytesSent, totalBytesToBeSent);
243 }
244
245 void CachedRawResource::finishedTimingForWorkerLoad(ResourceTiming&& resourceTiming)
246 {
247     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
248     while (CachedRawResourceClient* c = w.next())
249         c->finishedTimingForWorkerLoad(*this, resourceTiming);
250 }
251
252 void CachedRawResource::switchClientsToRevalidatedResource()
253 {
254     ASSERT(m_loader);
255     // If we're in the middle of a successful revalidation, responseReceived() hasn't been called, so we haven't set m_identifier.
256     ASSERT(!m_identifier);
257     downcast<CachedRawResource>(*resourceToRevalidate()).m_identifier = m_loader->identifier();
258     CachedResource::switchClientsToRevalidatedResource();
259 }
260
261 void CachedRawResource::setDefersLoading(bool defers)
262 {
263     if (m_loader)
264         m_loader->setDefersLoading(defers);
265 }
266
267 void CachedRawResource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy)
268 {
269     m_options.dataBufferingPolicy = dataBufferingPolicy;
270 }
271
272 static bool shouldIgnoreHeaderForCacheReuse(HTTPHeaderName name)
273 {
274     switch (name) {
275     // FIXME: This list of headers that don't affect cache policy almost certainly isn't complete.
276     case HTTPHeaderName::Accept:
277     case HTTPHeaderName::CacheControl:
278     case HTTPHeaderName::Pragma:
279     case HTTPHeaderName::Purpose:
280     case HTTPHeaderName::Referer:
281     case HTTPHeaderName::UserAgent:
282         return true;
283
284     default:
285         return false;
286     }
287 }
288
289 bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
290 {
291     if (dataBufferingPolicy() == DataBufferingPolicy::DoNotBufferData)
292         return false;
293
294     if (m_resourceRequest.httpMethod() != newRequest.httpMethod())
295         return false;
296
297     if (m_resourceRequest.httpBody() != newRequest.httpBody())
298         return false;
299
300     if (m_resourceRequest.allowCookies() != newRequest.allowCookies())
301         return false;
302
303     if (newRequest.isConditional())
304         return false;
305
306     // Ensure most headers match the existing headers before continuing.
307     // Note that the list of ignored headers includes some headers explicitly related to caching.
308     // A more detailed check of caching policy will be performed later, this is simply a list of
309     // headers that we might permit to be different and still reuse the existing CachedResource.
310     const HTTPHeaderMap& newHeaders = newRequest.httpHeaderFields();
311     const HTTPHeaderMap& oldHeaders = m_resourceRequest.httpHeaderFields();
312
313     for (const auto& header : newHeaders) {
314         if (header.keyAsHTTPHeaderName) {
315             if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
316                 && header.value != oldHeaders.get(header.keyAsHTTPHeaderName.value()))
317                 return false;
318         } else if (header.value != oldHeaders.get(header.key))
319             return false;
320     }
321
322     // For this second loop, we don't actually need to compare values, checking that the
323     // key is contained in newHeaders is sufficient due to the previous loop.
324     for (const auto& header : oldHeaders) {
325         if (header.keyAsHTTPHeaderName) {
326             if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
327                 && !newHeaders.contains(header.keyAsHTTPHeaderName.value()))
328                 return false;
329         } else if (!newHeaders.contains(header.key))
330             return false;
331     }
332
333     return true;
334 }
335
336 void CachedRawResource::clear()
337 {
338     m_data = nullptr;
339     setEncodedSize(0);
340     if (m_loader)
341         m_loader->clearResourceData();
342 }
343
344 } // namespace WebCore