1/* 2 * Copyright (C) 2006, 2007, 2009 Apple 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 * 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 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "SubresourceLoader.h" 31 32#include "CachedResourceLoader.h" 33#include "Document.h" 34#include "DocumentLoader.h" 35#include "Frame.h" 36#include "FrameLoader.h" 37#include "Logging.h" 38#include "MemoryCache.h" 39#include "Page.h" 40#include "PageActivityAssertionToken.h" 41#include "ResourceBuffer.h" 42#include <wtf/RefCountedLeakCounter.h> 43#include <wtf/StdLibExtras.h> 44#include <wtf/text/CString.h> 45 46namespace WebCore { 47 48DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader")); 49 50SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource) 51 : m_cachedResourceLoader(cachedResourceLoader) 52 , m_resource(resource) 53{ 54 m_cachedResourceLoader->incrementRequestCount(m_resource); 55} 56 57SubresourceLoader::RequestCountTracker::~RequestCountTracker() 58{ 59 m_cachedResourceLoader->decrementRequestCount(m_resource); 60} 61 62SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options) 63 : ResourceLoader(frame, options) 64 , m_resource(resource) 65 , m_loadingMultipartContent(false) 66 , m_state(Uninitialized) 67 , m_requestCountTracker(adoptPtr(new RequestCountTracker(frame->document()->cachedResourceLoader(), resource))) 68{ 69#ifndef NDEBUG 70 subresourceLoaderCounter.increment(); 71#endif 72} 73 74SubresourceLoader::~SubresourceLoader() 75{ 76 ASSERT(m_state != Initialized); 77 ASSERT(reachedTerminalState()); 78#ifndef NDEBUG 79 subresourceLoaderCounter.decrement(); 80#endif 81} 82 83PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) 84{ 85 RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options))); 86 if (!subloader->init(request)) 87 return 0; 88 return subloader.release(); 89} 90 91CachedResource* SubresourceLoader::cachedResource() 92{ 93 return m_resource; 94} 95 96void SubresourceLoader::cancelIfNotFinishing() 97{ 98 if (m_state != Initialized) 99 return; 100 101 ResourceLoader::cancel(); 102} 103 104bool SubresourceLoader::init(const ResourceRequest& request) 105{ 106 if (!ResourceLoader::init(request)) 107 return false; 108 109 ASSERT(!reachedTerminalState()); 110 m_state = Initialized; 111 if (m_frame && m_frame->page() && !m_activityAssertion) 112 m_activityAssertion = m_frame->page()->createActivityToken(); 113 m_documentLoader->addSubresourceLoader(this); 114 return true; 115} 116 117bool SubresourceLoader::isSubresourceLoader() 118{ 119 return true; 120} 121 122void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) 123{ 124 // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it. 125 KURL previousURL = request().url(); 126 RefPtr<SubresourceLoader> protect(this); 127 128 ASSERT(!newRequest.isNull()); 129 if (!redirectResponse.isNull()) { 130 // CachedResources are keyed off their original request URL. 131 // Requesting the same original URL a second time can redirect to a unique second resource. 132 // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource. 133 // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds. 134 if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) { 135 newRequest.makeUnconditional(); 136 memoryCache()->revalidationFailed(m_resource); 137 } 138 139 if (!m_documentLoader->cachedResourceLoader()->canRequest(m_resource->type(), newRequest.url())) { 140 cancel(); 141 return; 142 } 143 if (m_resource->type() == CachedResource::ImageResource && m_documentLoader->cachedResourceLoader()->shouldDeferImageLoad(newRequest.url())) { 144 cancel(); 145 return; 146 } 147 m_resource->willSendRequest(newRequest, redirectResponse); 148 } 149 150 if (newRequest.isNull() || reachedTerminalState()) 151 return; 152 153 ResourceLoader::willSendRequest(newRequest, redirectResponse); 154 if (newRequest.isNull()) 155 cancel(); 156} 157 158void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 159{ 160 ASSERT(m_state == Initialized); 161 RefPtr<SubresourceLoader> protect(this); 162 m_resource->didSendData(bytesSent, totalBytesToBeSent); 163} 164 165void SubresourceLoader::didReceiveResponse(const ResourceResponse& response) 166{ 167 ASSERT(!response.isNull()); 168 ASSERT(m_state == Initialized); 169 170 // Reference the object in this method since the additional processing can do 171 // anything including removing the last reference to this object; one example of this is 3266216. 172 RefPtr<SubresourceLoader> protect(this); 173 174 if (m_resource->resourceToRevalidate()) { 175 if (response.httpStatusCode() == 304) { 176 // 304 Not modified / Use local copy 177 // Existing resource is ok, just use it updating the expiration time. 178 m_resource->setResponse(response); 179 memoryCache()->revalidationSucceeded(m_resource, response); 180 if (!reachedTerminalState()) 181 ResourceLoader::didReceiveResponse(response); 182 return; 183 } 184 // Did not get 304 response, continue as a regular resource load. 185 memoryCache()->revalidationFailed(m_resource); 186 } 187 188 m_resource->responseReceived(response); 189 if (reachedTerminalState()) 190 return; 191 192 ResourceLoader::didReceiveResponse(response); 193 if (reachedTerminalState()) 194 return; 195 196 // FIXME: Main resources have a different set of rules for multipart than images do. 197 // Hopefully we can merge those 2 paths. 198 if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) { 199 m_loadingMultipartContent = true; 200 201 // We don't count multiParts in a CachedResourceLoader's request count 202 m_requestCountTracker.clear(); 203 if (!m_resource->isImage()) { 204 cancel(); 205 return; 206 } 207 } 208 209 RefPtr<ResourceBuffer> buffer = resourceData(); 210 if (m_loadingMultipartContent && buffer && buffer->size()) { 211 // The resource data will change as the next part is loaded, so we need to make a copy. 212 RefPtr<ResourceBuffer> copiedData = ResourceBuffer::create(buffer->data(), buffer->size()); 213 m_resource->finishLoading(copiedData.get()); 214 clearResourceData(); 215 // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. 216 // After the first multipart section is complete, signal to delegates that this load is "finished" 217 m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this); 218 didFinishLoadingOnePart(0); 219 } 220 221 checkForHTTPStatusCodeError(); 222} 223 224void SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, DataPayloadType dataPayloadType) 225{ 226 didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType); 227} 228 229void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType) 230{ 231 didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType); 232} 233 234void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType) 235{ 236 if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) 237 return; 238 ASSERT(!m_resource->resourceToRevalidate()); 239 ASSERT(!m_resource->errorOccurred()); 240 ASSERT(m_state == Initialized); 241 // Reference the object in this method since the additional processing can do 242 // anything including removing the last reference to this object; one example of this is 3266216. 243 RefPtr<SubresourceLoader> protect(this); 244 RefPtr<SharedBuffer> buffer = prpBuffer; 245 246 ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType); 247 248 if (!m_loadingMultipartContent) { 249 if (ResourceBuffer* resourceData = this->resourceData()) 250 m_resource->addDataBuffer(resourceData); 251 else 252 m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length); 253 } 254} 255 256bool SubresourceLoader::checkForHTTPStatusCodeError() 257{ 258 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors()) 259 return false; 260 261 m_state = Finishing; 262 m_activityAssertion.clear(); 263 m_resource->error(CachedResource::LoadError); 264 cancel(); 265 return true; 266} 267 268void SubresourceLoader::didFinishLoading(double finishTime) 269{ 270 if (m_state != Initialized) 271 return; 272 ASSERT(!reachedTerminalState()); 273 ASSERT(!m_resource->resourceToRevalidate()); 274 ASSERT(!m_resource->errorOccurred()); 275 LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data()); 276 277 RefPtr<SubresourceLoader> protect(this); 278 CachedResourceHandle<CachedResource> protectResource(m_resource); 279 m_state = Finishing; 280 m_activityAssertion.clear(); 281 m_resource->setLoadFinishTime(finishTime); 282 m_resource->finishLoading(resourceData()); 283 284 if (wasCancelled()) 285 return; 286 m_resource->finish(); 287 ASSERT(!reachedTerminalState()); 288 didFinishLoadingOnePart(finishTime); 289 notifyDone(); 290 if (reachedTerminalState()) 291 return; 292 releaseResources(); 293} 294 295void SubresourceLoader::didFail(const ResourceError& error) 296{ 297 if (m_state != Initialized) 298 return; 299 ASSERT(!reachedTerminalState()); 300 LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data()); 301 302 RefPtr<SubresourceLoader> protect(this); 303 CachedResourceHandle<CachedResource> protectResource(m_resource); 304 m_state = Finishing; 305 m_activityAssertion.clear(); 306 if (m_resource->resourceToRevalidate()) 307 memoryCache()->revalidationFailed(m_resource); 308 m_resource->setResourceError(error); 309 if (!m_resource->isPreloaded()) 310 memoryCache()->remove(m_resource); 311 m_resource->error(CachedResource::LoadError); 312 cleanupForError(error); 313 notifyDone(); 314 if (reachedTerminalState()) 315 return; 316 releaseResources(); 317} 318 319void SubresourceLoader::willCancel(const ResourceError& error) 320{ 321 if (m_state != Initialized) 322 return; 323 ASSERT(!reachedTerminalState()); 324 LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data()); 325 326 RefPtr<SubresourceLoader> protect(this); 327 m_state = Finishing; 328 m_activityAssertion.clear(); 329 if (m_resource->resourceToRevalidate()) 330 memoryCache()->revalidationFailed(m_resource); 331 m_resource->setResourceError(error); 332 memoryCache()->remove(m_resource); 333} 334 335void SubresourceLoader::didCancel(const ResourceError&) 336{ 337 if (m_state == Uninitialized) 338 return; 339 340 m_resource->cancelLoad(); 341 notifyDone(); 342} 343 344void SubresourceLoader::notifyDone() 345{ 346 if (reachedTerminalState()) 347 return; 348 349 m_requestCountTracker.clear(); 350 m_documentLoader->cachedResourceLoader()->loadDone(m_resource); 351 if (reachedTerminalState()) 352 return; 353 m_documentLoader->removeSubresourceLoader(this); 354} 355 356void SubresourceLoader::releaseResources() 357{ 358 ASSERT(!reachedTerminalState()); 359 if (m_state != Uninitialized) 360 m_resource->clearLoader(); 361 m_resource = 0; 362 ResourceLoader::releaseResources(); 363} 364 365} 366