[Content Filtering] Determine navigation and content policy before continuing to...
[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/PassRefPtr.h>
36 #include <wtf/text/StringView.h>
37
38 namespace WebCore {
39
40 CachedRawResource::CachedRawResource(ResourceRequest& resourceRequest, Type type, SessionID sessionID)
41     : CachedResource(resourceRequest, type, sessionID)
42     , m_identifier(0)
43     , m_allowEncodedDataReplacement(true)
44 {
45     ASSERT(isMainOrRawResource());
46 }
47
48 const char* CachedRawResource::calculateIncrementalDataChunk(SharedBuffer* data, unsigned& incrementalDataLength)
49 {
50     incrementalDataLength = 0;
51     if (!data)
52         return 0;
53
54     unsigned previousDataLength = encodedSize();
55     ASSERT(data->size() >= previousDataLength);
56     incrementalDataLength = data->size() - previousDataLength;
57     return data->data() + previousDataLength;
58 }
59
60 void CachedRawResource::addDataBuffer(SharedBuffer& data)
61 {
62     CachedResourceHandle<CachedRawResource> protect(this);
63     ASSERT(dataBufferingPolicy() == BufferData);
64     m_data = &data;
65
66     unsigned incrementalDataLength;
67     const char* incrementalData = calculateIncrementalDataChunk(&data, incrementalDataLength);
68     setEncodedSize(data.size());
69     notifyClientsDataWasReceived(incrementalData, incrementalDataLength);
70     if (dataBufferingPolicy() == DoNotBufferData) {
71         if (m_loader)
72             m_loader->setDataBufferingPolicy(DoNotBufferData);
73         clear();
74         return;
75     }
76
77     CachedResource::addDataBuffer(data);
78 }
79
80 void CachedRawResource::addData(const char* data, unsigned length)
81 {
82     ASSERT(dataBufferingPolicy() == DoNotBufferData);
83     notifyClientsDataWasReceived(data, length);
84     CachedResource::addData(data, length);
85 }
86
87 void CachedRawResource::finishLoading(SharedBuffer* data)
88 {
89     CachedResourceHandle<CachedRawResource> protect(this);
90     DataBufferingPolicy dataBufferingPolicy = this->dataBufferingPolicy();
91     if (dataBufferingPolicy == BufferData) {
92         m_data = data;
93
94         unsigned incrementalDataLength;
95         const char* incrementalData = calculateIncrementalDataChunk(data, incrementalDataLength);
96         if (data)
97             setEncodedSize(data->size());
98         notifyClientsDataWasReceived(incrementalData, incrementalDataLength);
99     }
100
101     m_allowEncodedDataReplacement = !m_loader->isQuickLookResource();
102
103     CachedResource::finishLoading(data);
104     if (dataBufferingPolicy == BufferData && this->dataBufferingPolicy() == DoNotBufferData) {
105         if (m_loader)
106             m_loader->setDataBufferingPolicy(DoNotBufferData);
107         clear();
108     }
109 }
110
111 void CachedRawResource::notifyClientsDataWasReceived(const char* data, unsigned length)
112 {
113     if (!length)
114         return;
115
116     CachedResourceHandle<CachedRawResource> protect(this);
117     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
118     while (CachedRawResourceClient* c = w.next())
119         c->dataReceived(this, data, length);
120 }
121
122 void CachedRawResource::didAddClient(CachedResourceClient* c)
123 {
124     if (!hasClient(c))
125         return;
126     // The calls to the client can result in events running, potentially causing
127     // this resource to be evicted from the cache and all clients to be removed,
128     // so a protector is necessary.
129     CachedResourceHandle<CachedRawResource> protect(this);
130     CachedRawResourceClient* client = static_cast<CachedRawResourceClient*>(c);
131     size_t redirectCount = m_redirectChain.size();
132     for (size_t i = 0; i < redirectCount; i++) {
133         RedirectPair redirect = m_redirectChain[i];
134         ResourceRequest request(redirect.m_request);
135         client->redirectReceived(this, request, redirect.m_redirectResponse);
136         if (!hasClient(c))
137             return;
138     }
139     ASSERT(redirectCount == m_redirectChain.size());
140
141     if (!m_response.isNull()) {
142         ResourceResponse response(m_response);
143         if (validationCompleting())
144             response.setSource(ResourceResponse::Source::MemoryCacheAfterValidation);
145         else {
146             ASSERT(!validationInProgress());
147             response.setSource(ResourceResponse::Source::MemoryCache);
148         }
149         client->responseReceived(this, response);
150     }
151     if (!hasClient(c))
152         return;
153     if (m_data)
154         client->dataReceived(this, m_data->data(), m_data->size());
155     if (!hasClient(c))
156        return;
157     CachedResource::didAddClient(client);
158 }
159
160 void CachedRawResource::allClientsRemoved()
161 {
162     if (m_loader)
163         m_loader->cancelIfNotFinishing();
164 }
165
166 void CachedRawResource::redirectReceived(ResourceRequest& request, const ResourceResponse& response)
167 {
168     CachedResourceHandle<CachedRawResource> protect(this);
169     if (!response.isNull()) {
170         CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
171         while (CachedRawResourceClient* c = w.next())
172             c->redirectReceived(this, request, response);
173         m_redirectChain.append(RedirectPair(request, response));
174     }
175     CachedResource::redirectReceived(request, response);
176 }
177
178 void CachedRawResource::responseReceived(const ResourceResponse& response)
179 {
180     CachedResourceHandle<CachedRawResource> protect(this);
181     if (!m_identifier)
182         m_identifier = m_loader->identifier();
183     CachedResource::responseReceived(response);
184     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
185     while (CachedRawResourceClient* c = w.next())
186         c->responseReceived(this, m_response);
187 }
188
189 void CachedRawResource::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
190 {
191     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
192     while (CachedRawResourceClient* c = w.next())
193         c->dataSent(this, bytesSent, totalBytesToBeSent);
194 }
195
196 void CachedRawResource::switchClientsToRevalidatedResource()
197 {
198     ASSERT(m_loader);
199     // If we're in the middle of a successful revalidation, responseReceived() hasn't been called, so we haven't set m_identifier.
200     ASSERT(!m_identifier);
201     downcast<CachedRawResource>(*resourceToRevalidate()).m_identifier = m_loader->identifier();
202     CachedResource::switchClientsToRevalidatedResource();
203 }
204
205 void CachedRawResource::setDefersLoading(bool defers)
206 {
207     if (m_loader)
208         m_loader->setDefersLoading(defers);
209 }
210
211 void CachedRawResource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy)
212 {
213     m_options.setDataBufferingPolicy(dataBufferingPolicy);
214 }
215
216 static bool shouldIgnoreHeaderForCacheReuse(HTTPHeaderName name)
217 {
218     switch (name) {
219     // FIXME: This list of headers that don't affect cache policy almost certainly isn't complete.
220     case HTTPHeaderName::Accept:
221     case HTTPHeaderName::CacheControl:
222     case HTTPHeaderName::Pragma:
223     case HTTPHeaderName::Purpose:
224     case HTTPHeaderName::Referer:
225     case HTTPHeaderName::UserAgent:
226         return true;
227
228     default:
229         return false;
230     }
231 }
232
233 bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
234 {
235     if (dataBufferingPolicy() == DoNotBufferData)
236         return false;
237
238     if (m_resourceRequest.httpMethod() != newRequest.httpMethod())
239         return false;
240
241     if (m_resourceRequest.httpBody() != newRequest.httpBody())
242         return false;
243
244     if (m_resourceRequest.allowCookies() != newRequest.allowCookies())
245         return false;
246
247     // Ensure most headers match the existing headers before continuing.
248     // Note that the list of ignored headers includes some headers explicitly related to caching.
249     // A more detailed check of caching policy will be performed later, this is simply a list of
250     // headers that we might permit to be different and still reuse the existing CachedResource.
251     const HTTPHeaderMap& newHeaders = newRequest.httpHeaderFields();
252     const HTTPHeaderMap& oldHeaders = m_resourceRequest.httpHeaderFields();
253
254     for (const auto& header : newHeaders) {
255         if (header.keyAsHTTPHeaderName) {
256             if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
257                 && header.value != oldHeaders.commonHeaders().get(header.keyAsHTTPHeaderName.value()))
258                 return false;
259         } else if (header.value != oldHeaders.uncommonHeaders().get(header.key))
260             return false;
261     }
262
263     // For this second loop, we don't actually need to compare values, checking that the
264     // key is contained in newHeaders is sufficient due to the previous loop.
265     for (const auto& header : oldHeaders) {
266         if (header.keyAsHTTPHeaderName) {
267             if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
268                 && !newHeaders.commonHeaders().contains(header.keyAsHTTPHeaderName.value()))
269                 return false;
270         } else if (!newHeaders.uncommonHeaders().contains(header.key))
271             return false;
272     }
273
274     return true;
275 }
276
277 void CachedRawResource::clear()
278 {
279     m_data = nullptr;
280     setEncodedSize(0);
281     if (m_loader)
282         m_loader->clearResourceData();
283 }
284
285 } // namespace WebCore