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