1 /* 2 Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de) 3 Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org) 5 Copyright (C) 2006 Samuel Weinig (sam.weinig (at) gmail.com) 6 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Library General Public 10 License as published by the Free Software Foundation; either 11 version 2 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Library General Public License for more details. 17 18 You should have received a copy of the GNU Library General Public License 19 along with this library; see the file COPYING.LIB. If not, write to 20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 Boston, MA 02110-1301, USA. 22 */ 23 24 #include "config.h" 25 #include "CachedResourceRequest.h" 26 27 #include "MemoryCache.h" 28 #include "CachedImage.h" 29 #include "CachedResource.h" 30 #include "CachedResourceLoader.h" 31 #include "Frame.h" 32 #include "FrameLoader.h" 33 #include "Logging.h" 34 #include "ResourceHandle.h" 35 #include "ResourceLoadScheduler.h" 36 #include "ResourceRequest.h" 37 #include "ResourceResponse.h" 38 #include "SharedBuffer.h" 39 #include <wtf/Assertions.h> 40 #include <wtf/Vector.h> 41 #include <wtf/text/CString.h> 42 43 namespace WebCore { 44 45 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type, ResourceLoadPriority priority) 46 { 47 #if !ENABLE(LINK_PREFETCH) 48 UNUSED_PARAM(priority); 49 #endif 50 switch (type) { 51 case CachedResource::CSSStyleSheet: 52 #if ENABLE(XSLT) 53 case CachedResource::XSLStyleSheet: 54 #endif 55 return ResourceRequest::TargetIsStyleSheet; 56 case CachedResource::Script: 57 return ResourceRequest::TargetIsScript; 58 case CachedResource::FontResource: 59 return ResourceRequest::TargetIsFontResource; 60 case CachedResource::ImageResource: 61 return ResourceRequest::TargetIsImage; 62 #if ENABLE(LINK_PREFETCH) 63 case CachedResource::LinkResource: 64 if (priority == ResourceLoadPriorityLowest) 65 return ResourceRequest::TargetIsPrefetch; 66 return ResourceRequest::TargetIsSubresource; 67 #endif 68 } 69 ASSERT_NOT_REACHED(); 70 return ResourceRequest::TargetIsSubresource; 71 } 72 73 CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental) 74 : m_cachedResourceLoader(cachedResourceLoader) 75 , m_resource(resource) 76 , m_incremental(incremental) 77 , m_multipart(false) 78 , m_finishing(false) 79 { 80 m_resource->setRequest(this); 81 } 82 83 CachedResourceRequest::~CachedResourceRequest() 84 { 85 m_resource->setRequest(0); 86 } 87 88 PassRefPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks) 89 { 90 RefPtr<CachedResourceRequest> request = adoptRef(new CachedResourceRequest(cachedResourceLoader, resource, incremental)); 91 92 ResourceRequest resourceRequest(resource->url()); 93 resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type(), resource->loadPriority())); 94 95 if (!resource->accept().isEmpty()) 96 resourceRequest.setHTTPAccept(resource->accept()); 97 98 if (resource->isCacheValidator()) { 99 CachedResource* resourceToRevalidate = resource->resourceToRevalidate(); 100 ASSERT(resourceToRevalidate->canUseCacheValidator()); 101 ASSERT(resourceToRevalidate->isLoaded()); 102 const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified"); 103 const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag"); 104 if (!lastModified.isEmpty() || !eTag.isEmpty()) { 105 ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload); 106 if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate) 107 resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); 108 if (!lastModified.isEmpty()) 109 resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); 110 if (!eTag.isEmpty()) 111 resourceRequest.setHTTPHeaderField("If-None-Match", eTag); 112 } 113 } 114 115 #if ENABLE(LINK_PREFETCH) 116 if (resource->type() == CachedResource::LinkResource) 117 resourceRequest.setHTTPHeaderField("Purpose", "prefetch"); 118 #endif 119 120 ResourceLoadPriority priority = resource->loadPriority(); 121 resourceRequest.setPriority(priority); 122 123 RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(), 124 request.get(), resourceRequest, priority, securityCheck, sendResourceLoadCallbacks); 125 if (!loader || loader->reachedTerminalState()) { 126 // FIXME: What if resources in other frames were waiting for this revalidation? 127 LOG(ResourceLoading, "Cannot start loading '%s'", resource->url().latin1().data()); 128 cachedResourceLoader->decrementRequestCount(resource); 129 cachedResourceLoader->loadFinishing(); 130 if (resource->resourceToRevalidate()) 131 memoryCache()->revalidationFailed(resource); 132 resource->error(CachedResource::LoadError); 133 cachedResourceLoader->loadDone(0); 134 return 0; 135 } 136 request->m_loader = loader; 137 return request.release(); 138 } 139 140 void CachedResourceRequest::willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse&) 141 { 142 m_resource->setRequestedFromNetworkingLayer(); 143 } 144 145 void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader, double) 146 { 147 if (m_finishing) 148 return; 149 150 ASSERT(loader == m_loader.get()); 151 ASSERT(!m_resource->resourceToRevalidate()); 152 LOG(ResourceLoading, "Received '%s'.", m_resource->url().latin1().data()); 153 154 // Prevent the document from being destroyed before we are done with 155 // the cachedResourceLoader that it will delete when the document gets deleted. 156 RefPtr<Document> protector(m_cachedResourceLoader->document()); 157 if (!m_multipart) 158 m_cachedResourceLoader->decrementRequestCount(m_resource); 159 m_finishing = true; 160 161 // If we got a 4xx response, we're pretending to have received a network 162 // error, so we can't send the successful data() and finish() callbacks. 163 if (!m_resource->errorOccurred()) { 164 m_cachedResourceLoader->loadFinishing(); 165 m_resource->data(loader->resourceData(), true); 166 if (!m_resource->errorOccurred()) 167 m_resource->finish(); 168 } 169 m_cachedResourceLoader->loadDone(this); 170 } 171 172 void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError&) 173 { 174 if (!m_loader) 175 return; 176 didFail(); 177 } 178 179 void CachedResourceRequest::didFail(bool cancelled) 180 { 181 if (m_finishing) 182 return; 183 184 LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().latin1().data(), cancelled); 185 186 // Prevent the document from being destroyed before we are done with 187 // the cachedResourceLoader that it will delete when the document gets deleted. 188 RefPtr<Document> protector(m_cachedResourceLoader->document()); 189 if (!m_multipart) 190 m_cachedResourceLoader->decrementRequestCount(m_resource); 191 m_finishing = true; 192 m_loader->clearClient(); 193 194 if (m_resource->resourceToRevalidate()) 195 memoryCache()->revalidationFailed(m_resource); 196 197 if (!cancelled) { 198 m_cachedResourceLoader->loadFinishing(); 199 m_resource->error(CachedResource::LoadError); 200 } 201 202 if (cancelled || !m_resource->isPreloaded()) 203 memoryCache()->remove(m_resource); 204 205 m_cachedResourceLoader->loadDone(this); 206 } 207 208 void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) 209 { 210 ASSERT(loader == m_loader.get()); 211 if (m_resource->isCacheValidator()) { 212 if (response.httpStatusCode() == 304) { 213 // 304 Not modified / Use local copy 214 loader->clearClient(); 215 RefPtr<Document> protector(m_cachedResourceLoader->document()); 216 m_cachedResourceLoader->decrementRequestCount(m_resource); 217 m_finishing = true; 218 219 // Existing resource is ok, just use it updating the expiration time. 220 memoryCache()->revalidationSucceeded(m_resource, response); 221 222 if (m_cachedResourceLoader->frame()) 223 m_cachedResourceLoader->frame()->loader()->checkCompleted(); 224 225 m_cachedResourceLoader->loadDone(this); 226 return; 227 } 228 // Did not get 304 response, continue as a regular resource load. 229 memoryCache()->revalidationFailed(m_resource); 230 } 231 232 m_resource->setResponse(response); 233 234 String encoding = response.textEncodingName(); 235 if (!encoding.isNull()) 236 m_resource->setEncoding(encoding); 237 238 if (m_multipart) { 239 ASSERT(m_resource->isImage()); 240 static_cast<CachedImage*>(m_resource)->clear(); 241 if (m_cachedResourceLoader->frame()) 242 m_cachedResourceLoader->frame()->loader()->checkCompleted(); 243 } else if (response.isMultipart()) { 244 m_multipart = true; 245 246 // We don't count multiParts in a CachedResourceLoader's request count 247 m_cachedResourceLoader->decrementRequestCount(m_resource); 248 249 // If we get a multipart response, we must have a handle 250 ASSERT(loader->handle()); 251 if (!m_resource->isImage()) 252 loader->handle()->cancel(); 253 } 254 } 255 256 void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size) 257 { 258 ASSERT(loader == m_loader.get()); 259 ASSERT(!m_resource->isCacheValidator()); 260 261 if (m_resource->errorOccurred()) 262 return; 263 264 if (m_resource->response().httpStatusCode() >= 400) { 265 if (!m_resource->shouldIgnoreHTTPStatusCodeErrors()) 266 m_resource->error(CachedResource::LoadError); 267 return; 268 } 269 270 // Set the data. 271 if (m_multipart) { 272 // The loader delivers the data in a multipart section all at once, send eof. 273 // The resource data will change as the next part is loaded, so we need to make a copy. 274 RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size); 275 m_resource->data(copiedData.release(), true); 276 } else if (m_incremental) 277 m_resource->data(loader->resourceData(), false); 278 } 279 280 void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size) 281 { 282 ASSERT(!m_resource->isCacheValidator()); 283 m_resource->setSerializedCachedMetadata(data, size); 284 } 285 286 } //namespace WebCore 287