1 /* 2 * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved. 3 * (C) 2007 Graham Dennis (graham.dennis (at) gmail.com) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "config.h" 31 #include "core/fetch/ResourceLoader.h" 32 33 #include "core/fetch/Resource.h" 34 #include "core/fetch/ResourceLoaderHost.h" 35 #include "core/fetch/ResourcePtr.h" 36 #include "platform/Logging.h" 37 #include "platform/SharedBuffer.h" 38 #include "platform/exported/WrappedResourceRequest.h" 39 #include "platform/exported/WrappedResourceResponse.h" 40 #include "platform/network/ResourceError.h" 41 #include "public/platform/Platform.h" 42 #include "public/platform/WebData.h" 43 #include "public/platform/WebURLError.h" 44 #include "public/platform/WebURLRequest.h" 45 #include "public/platform/WebURLResponse.h" 46 #include "wtf/Assertions.h" 47 #include "wtf/CurrentTime.h" 48 49 namespace WebCore { 50 51 ResourceLoader::RequestCountTracker::RequestCountTracker(ResourceLoaderHost* host, Resource* resource) 52 : m_host(host) 53 , m_resource(resource) 54 { 55 m_host->incrementRequestCount(m_resource); 56 } 57 58 ResourceLoader::RequestCountTracker::~RequestCountTracker() 59 { 60 m_host->decrementRequestCount(m_resource); 61 } 62 63 PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) 64 { 65 RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options))); 66 loader->init(request); 67 return loader.release(); 68 } 69 70 ResourceLoader::ResourceLoader(ResourceLoaderHost* host, Resource* resource, const ResourceLoaderOptions& options) 71 : m_host(host) 72 , m_notifiedLoadComplete(false) 73 , m_defersLoading(host->defersLoading()) 74 , m_options(options) 75 , m_resource(resource) 76 , m_state(Initialized) 77 , m_connectionState(ConnectionStateNew) 78 , m_requestCountTracker(adoptPtr(new RequestCountTracker(host, resource))) 79 { 80 } 81 82 ResourceLoader::~ResourceLoader() 83 { 84 ASSERT(m_state == Terminated); 85 } 86 87 void ResourceLoader::releaseResources() 88 { 89 ASSERT(m_state != Terminated); 90 m_requestCountTracker.clear(); 91 m_host->didLoadResource(m_resource); 92 if (m_state == Terminated) 93 return; 94 m_resource->clearLoader(); 95 m_host->willTerminateResourceLoader(this); 96 97 ASSERT(m_state != Terminated); 98 99 // It's possible that when we release the loader, it will be 100 // deallocated and release the last reference to this object. 101 // We need to retain to avoid accessing the object after it 102 // has been deallocated and also to avoid reentering this method. 103 RefPtr<ResourceLoader> protector(this); 104 105 m_host.clear(); 106 m_state = Terminated; 107 108 if (m_loader) { 109 m_loader->cancel(); 110 m_loader.clear(); 111 } 112 113 m_deferredRequest = ResourceRequest(); 114 } 115 116 void ResourceLoader::init(const ResourceRequest& passedRequest) 117 { 118 ResourceRequest request(passedRequest); 119 m_host->willSendRequest(m_resource->identifier(), request, ResourceResponse(), m_options); 120 request.setReportLoadTiming(true); 121 ASSERT(m_state != Terminated); 122 ASSERT(!request.isNull()); 123 m_originalRequest = m_request = request; 124 m_host->didInitializeResourceLoader(this); 125 } 126 127 void ResourceLoader::start() 128 { 129 ASSERT(!m_loader); 130 ASSERT(!m_request.isNull()); 131 ASSERT(m_deferredRequest.isNull()); 132 133 m_host->willStartLoadingResource(m_request); 134 135 if (m_options.synchronousPolicy == RequestSynchronously) { 136 requestSynchronously(); 137 return; 138 } 139 140 if (m_defersLoading) { 141 m_deferredRequest = m_request; 142 return; 143 } 144 145 if (m_state == Terminated) 146 return; 147 148 RELEASE_ASSERT(m_connectionState == ConnectionStateNew); 149 m_connectionState = ConnectionStateStarted; 150 151 m_loader = adoptPtr(blink::Platform::current()->createURLLoader()); 152 ASSERT(m_loader); 153 blink::WrappedResourceRequest wrappedRequest(m_request); 154 wrappedRequest.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials); 155 m_loader->loadAsynchronously(wrappedRequest, this); 156 } 157 158 void ResourceLoader::changeToSynchronous() 159 { 160 ASSERT(m_options.synchronousPolicy == RequestAsynchronously); 161 ASSERT(m_loader); 162 m_loader->cancel(); 163 m_loader.clear(); 164 m_request.setPriority(ResourceLoadPriorityHighest); 165 m_connectionState = ConnectionStateNew; 166 requestSynchronously(); 167 } 168 169 void ResourceLoader::setDefersLoading(bool defers) 170 { 171 m_defersLoading = defers; 172 if (m_loader) 173 m_loader->setDefersLoading(defers); 174 if (!defers && !m_deferredRequest.isNull()) { 175 m_request = m_deferredRequest; 176 m_deferredRequest = ResourceRequest(); 177 start(); 178 } 179 } 180 181 void ResourceLoader::didDownloadData(blink::WebURLLoader*, int length, int encodedDataLength) 182 { 183 RefPtr<ResourceLoader> protect(this); 184 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse); 185 m_host->didDownloadData(m_resource, length, encodedDataLength, m_options); 186 m_resource->didDownloadData(length); 187 } 188 189 void ResourceLoader::didFinishLoadingOnePart(double finishTime) 190 { 191 // If load has been cancelled after finishing (which could happen with a 192 // JavaScript that changes the window location), do nothing. 193 if (m_state == Terminated) 194 return; 195 196 if (m_notifiedLoadComplete) 197 return; 198 m_notifiedLoadComplete = true; 199 m_host->didFinishLoading(m_resource, finishTime, m_options); 200 } 201 202 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority) 203 { 204 if (m_loader) { 205 m_host->didChangeLoadingPriority(m_resource, loadPriority); 206 m_loader->didChangePriority(static_cast<blink::WebURLRequest::Priority>(loadPriority)); 207 } 208 } 209 210 void ResourceLoader::cancelIfNotFinishing() 211 { 212 if (m_state != Initialized) 213 return; 214 cancel(); 215 } 216 217 void ResourceLoader::cancel() 218 { 219 cancel(ResourceError()); 220 } 221 222 void ResourceLoader::cancel(const ResourceError& error) 223 { 224 // If the load has already completed - succeeded, failed, or previously cancelled - do nothing. 225 if (m_state == Terminated) 226 return; 227 if (m_state == Finishing) { 228 releaseResources(); 229 return; 230 } 231 232 ResourceError nonNullError = error.isNull() ? ResourceError::cancelledError(m_request.url()) : error; 233 234 // This function calls out to clients at several points that might do 235 // something that causes the last reference to this object to go away. 236 RefPtr<ResourceLoader> protector(this); 237 238 WTF_LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data()); 239 if (m_state == Initialized) 240 m_state = Finishing; 241 m_resource->setResourceError(nonNullError); 242 243 if (m_loader) { 244 m_connectionState = ConnectionStateCanceled; 245 m_loader->cancel(); 246 m_loader.clear(); 247 } 248 249 m_host->didFailLoading(m_resource, nonNullError, m_options); 250 251 if (m_state == Finishing) 252 m_resource->error(Resource::LoadError); 253 if (m_state != Terminated) 254 releaseResources(); 255 } 256 257 void ResourceLoader::willSendRequest(blink::WebURLLoader*, blink::WebURLRequest& passedRequest, const blink::WebURLResponse& passedRedirectResponse) 258 { 259 RefPtr<ResourceLoader> protect(this); 260 261 ResourceRequest& request(passedRequest.toMutableResourceRequest()); 262 ASSERT(!request.isNull()); 263 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceResponse()); 264 ASSERT(!redirectResponse.isNull()); 265 if (!m_host->shouldRequest(m_resource, request, m_options)) { 266 cancel(); 267 return; 268 } 269 m_host->redirectReceived(m_resource, redirectResponse); 270 m_resource->willSendRequest(request, redirectResponse); 271 if (request.isNull() || m_state == Terminated) 272 return; 273 274 m_host->willSendRequest(m_resource->identifier(), request, redirectResponse, m_options); 275 request.setReportLoadTiming(true); 276 ASSERT(!request.isNull()); 277 m_request = request; 278 } 279 280 void ResourceLoader::didReceiveCachedMetadata(blink::WebURLLoader*, const char* data, int length) 281 { 282 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 283 ASSERT(m_state == Initialized); 284 m_resource->setSerializedCachedMetadata(data, length); 285 } 286 287 void ResourceLoader::didSendData(blink::WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 288 { 289 ASSERT(m_state == Initialized); 290 RefPtr<ResourceLoader> protect(this); 291 m_resource->didSendData(bytesSent, totalBytesToBeSent); 292 } 293 294 void ResourceLoader::didReceiveResponse(blink::WebURLLoader*, const blink::WebURLResponse& response) 295 { 296 ASSERT(!response.isNull()); 297 ASSERT(m_state == Initialized); 298 299 bool isMultipartPayload = response.isMultipartPayload(); 300 bool isValidStateTransition = (m_connectionState == ConnectionStateStarted || m_connectionState == ConnectionStateReceivedResponse); 301 // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved. 302 RELEASE_ASSERT(isMultipartPayload || isValidStateTransition); 303 m_connectionState = ConnectionStateReceivedResponse; 304 305 // Reference the object in this method since the additional processing can do 306 // anything including removing the last reference to this object. 307 RefPtr<ResourceLoader> protect(this); 308 m_resource->responseReceived(response.toResourceResponse()); 309 if (m_state == Terminated) 310 return; 311 312 m_host->didReceiveResponse(m_resource, response.toResourceResponse(), m_options); 313 314 if (response.toResourceResponse().isMultipart()) { 315 // We don't count multiParts in a ResourceFetcher's request count 316 m_requestCountTracker.clear(); 317 if (!m_resource->isImage()) { 318 cancel(); 319 return; 320 } 321 } else if (isMultipartPayload) { 322 // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. 323 // After the first multipart section is complete, signal to delegates that this load is "finished" 324 m_host->subresourceLoaderFinishedLoadingOnePart(this); 325 didFinishLoadingOnePart(0); 326 } 327 328 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors()) 329 return; 330 m_state = Finishing; 331 m_resource->error(Resource::LoadError); 332 cancel(); 333 } 334 335 void ResourceLoader::didReceiveData(blink::WebURLLoader*, const char* data, int length, int encodedDataLength) 336 { 337 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 338 m_connectionState = ConnectionStateReceivingData; 339 340 // It is possible to receive data on uninitialized resources if it had an error status code, and we are running a nested message 341 // loop. When this occurs, ignoring the data is the correct action. 342 if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) 343 return; 344 ASSERT(m_state == Initialized); 345 346 // Reference the object in this method since the additional processing can do 347 // anything including removing the last reference to this object. 348 RefPtr<ResourceLoader> protect(this); 349 350 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. 351 // However, with today's computers and networking speeds, this won't happen in practice. 352 // Could be an issue with a giant local file. 353 m_host->didReceiveData(m_resource, data, length, encodedDataLength, m_options); 354 m_resource->appendData(data, length); 355 } 356 357 void ResourceLoader::didFinishLoading(blink::WebURLLoader*, double finishTime) 358 { 359 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 360 m_connectionState = ConnectionStateFinishedLoading; 361 if (m_state != Initialized) 362 return; 363 ASSERT(m_state != Terminated); 364 WTF_LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data()); 365 366 RefPtr<ResourceLoader> protect(this); 367 ResourcePtr<Resource> protectResource(m_resource); 368 m_state = Finishing; 369 m_resource->finish(finishTime); 370 didFinishLoadingOnePart(finishTime); 371 372 // If the load has been cancelled by a delegate in response to didFinishLoad(), do not release 373 // the resources a second time, they have been released by cancel. 374 if (m_state == Terminated) 375 return; 376 releaseResources(); 377 } 378 379 void ResourceLoader::didFail(blink::WebURLLoader*, const blink::WebURLError& error) 380 { 381 m_connectionState = ConnectionStateFailed; 382 ASSERT(m_state != Terminated); 383 WTF_LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data()); 384 385 RefPtr<ResourceLoader> protect(this); 386 RefPtr<ResourceLoaderHost> protectHost(m_host); 387 ResourcePtr<Resource> protectResource(m_resource); 388 m_state = Finishing; 389 m_resource->setResourceError(error); 390 m_resource->error(Resource::LoadError); 391 392 if (m_state == Terminated) 393 return; 394 395 if (!m_notifiedLoadComplete) { 396 m_notifiedLoadComplete = true; 397 m_host->didFailLoading(m_resource, error, m_options); 398 } 399 400 releaseResources(); 401 } 402 403 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const 404 { 405 return m_host->isLoadedBy(loader); 406 } 407 408 void ResourceLoader::requestSynchronously() 409 { 410 OwnPtr<blink::WebURLLoader> loader = adoptPtr(blink::Platform::current()->createURLLoader()); 411 ASSERT(loader); 412 413 RefPtr<ResourceLoader> protect(this); 414 RefPtr<ResourceLoaderHost> protectHost(m_host); 415 ResourcePtr<Resource> protectResource(m_resource); 416 417 RELEASE_ASSERT(m_connectionState == ConnectionStateNew); 418 m_connectionState = ConnectionStateStarted; 419 420 blink::WrappedResourceRequest requestIn(m_request); 421 requestIn.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials); 422 blink::WebURLResponse responseOut; 423 responseOut.initialize(); 424 blink::WebURLError errorOut; 425 blink::WebData dataOut; 426 loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut); 427 if (errorOut.reason) { 428 didFail(0, errorOut); 429 return; 430 } 431 didReceiveResponse(0, responseOut); 432 if (m_state == Terminated) 433 return; 434 RefPtr<ResourceLoadInfo> resourceLoadInfo = responseOut.toResourceResponse().resourceLoadInfo(); 435 m_host->didReceiveData(m_resource, dataOut.data(), dataOut.size(), resourceLoadInfo ? resourceLoadInfo->encodedDataLength : -1, m_options); 436 m_resource->setResourceBuffer(dataOut); 437 didFinishLoading(0, monotonicallyIncreasingTime()); 438 } 439 440 } 441