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/WebThreadedDataReceiver.h" 44 #include "public/platform/WebURLError.h" 45 #include "public/platform/WebURLRequest.h" 46 #include "public/platform/WebURLResponse.h" 47 #include "wtf/Assertions.h" 48 #include "wtf/CurrentTime.h" 49 50 namespace blink { 51 52 ResourceLoader::RequestCountTracker::RequestCountTracker(ResourceLoaderHost* host, Resource* resource) 53 : m_host(host) 54 , m_resource(resource) 55 { 56 m_host->incrementRequestCount(m_resource); 57 } 58 59 ResourceLoader::RequestCountTracker::~RequestCountTracker() 60 { 61 m_host->decrementRequestCount(m_resource); 62 } 63 64 ResourceLoader::RequestCountTracker::RequestCountTracker(const RequestCountTracker& other) 65 { 66 m_host = other.m_host; 67 m_resource = other.m_resource; 68 m_host->incrementRequestCount(m_resource); 69 } 70 71 PassRefPtrWillBeRawPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) 72 { 73 RefPtrWillBeRawPtr<ResourceLoader> loader(adoptRefWillBeNoop(new ResourceLoader(host, resource, options))); 74 loader->init(request); 75 return loader.release(); 76 } 77 78 ResourceLoader::ResourceLoader(ResourceLoaderHost* host, Resource* resource, const ResourceLoaderOptions& options) 79 : m_host(host) 80 , m_notifiedLoadComplete(false) 81 , m_defersLoading(host->defersLoading()) 82 , m_options(options) 83 , m_resource(resource) 84 , m_state(Initialized) 85 , m_connectionState(ConnectionStateNew) 86 , m_requestCountTracker(adoptPtr(new RequestCountTracker(host, resource))) 87 { 88 } 89 90 ResourceLoader::~ResourceLoader() 91 { 92 ASSERT(m_state == Terminated); 93 } 94 95 void ResourceLoader::trace(Visitor* visitor) 96 { 97 visitor->trace(m_host); 98 visitor->trace(m_resource); 99 } 100 101 void ResourceLoader::releaseResources() 102 { 103 ASSERT(m_state != Terminated); 104 ASSERT(m_notifiedLoadComplete); 105 m_requestCountTracker.clear(); 106 m_host->didLoadResource(m_resource); 107 if (m_state == Terminated) 108 return; 109 m_resource->clearLoader(); 110 m_resource->deleteIfPossible(); 111 m_resource = nullptr; 112 m_host->willTerminateResourceLoader(this); 113 114 ASSERT(m_state != Terminated); 115 116 // It's possible that when we release the loader, it will be 117 // deallocated and release the last reference to this object. 118 // We need to retain to avoid accessing the object after it 119 // has been deallocated and also to avoid reentering this method. 120 RefPtrWillBeRawPtr<ResourceLoader> protector(this); 121 122 m_host.clear(); 123 m_state = Terminated; 124 125 if (m_loader) { 126 m_loader->cancel(); 127 m_loader.clear(); 128 } 129 130 m_deferredRequest = ResourceRequest(); 131 } 132 133 void ResourceLoader::init(const ResourceRequest& passedRequest) 134 { 135 ASSERT(m_state != Terminated); 136 ResourceRequest request(passedRequest); 137 m_host->willSendRequest(m_resource->identifier(), request, ResourceResponse(), m_options.initiatorInfo); 138 ASSERT(m_state != Terminated); 139 ASSERT(!request.isNull()); 140 m_originalRequest = m_request = applyOptions(request); 141 m_resource->updateRequest(request); 142 ASSERT(m_state != Terminated); 143 m_host->didInitializeResourceLoader(this); 144 } 145 146 void ResourceLoader::start() 147 { 148 ASSERT(!m_loader); 149 ASSERT(!m_request.isNull()); 150 ASSERT(m_deferredRequest.isNull()); 151 152 m_host->willStartLoadingResource(m_resource, m_request); 153 154 if (m_options.synchronousPolicy == RequestSynchronously) { 155 requestSynchronously(); 156 return; 157 } 158 159 if (m_defersLoading) { 160 m_deferredRequest = m_request; 161 return; 162 } 163 164 if (m_state == Terminated) 165 return; 166 167 RELEASE_ASSERT(m_connectionState == ConnectionStateNew); 168 m_connectionState = ConnectionStateStarted; 169 170 m_loader = adoptPtr(blink::Platform::current()->createURLLoader()); 171 ASSERT(m_loader); 172 blink::WrappedResourceRequest wrappedRequest(m_request); 173 m_loader->loadAsynchronously(wrappedRequest, this); 174 } 175 176 void ResourceLoader::changeToSynchronous() 177 { 178 ASSERT(m_options.synchronousPolicy == RequestAsynchronously); 179 ASSERT(m_loader); 180 m_loader->cancel(); 181 m_loader.clear(); 182 m_request.setPriority(ResourceLoadPriorityHighest); 183 m_connectionState = ConnectionStateNew; 184 requestSynchronously(); 185 } 186 187 void ResourceLoader::setDefersLoading(bool defers) 188 { 189 m_defersLoading = defers; 190 if (m_loader) 191 m_loader->setDefersLoading(defers); 192 if (!defers && !m_deferredRequest.isNull()) { 193 m_request = applyOptions(m_deferredRequest); 194 m_deferredRequest = ResourceRequest(); 195 start(); 196 } 197 } 198 199 void ResourceLoader::attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver) 200 { 201 if (m_loader) { 202 // The implementor of the WebURLLoader assumes ownership of the 203 // threaded data receiver if it signals that it got successfully 204 // attached. 205 blink::WebThreadedDataReceiver* rawThreadedDataReceiver = threadedDataReceiver.leakPtr(); 206 if (!m_loader->attachThreadedDataReceiver(rawThreadedDataReceiver)) 207 delete rawThreadedDataReceiver; 208 } 209 } 210 211 void ResourceLoader::didDownloadData(blink::WebURLLoader*, int length, int encodedDataLength) 212 { 213 ASSERT(m_state != Terminated); 214 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 215 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse); 216 m_host->didDownloadData(m_resource, length, encodedDataLength); 217 if (m_state == Terminated) 218 return; 219 m_resource->didDownloadData(length); 220 } 221 222 void ResourceLoader::didFinishLoadingOnePart(double finishTime, int64 encodedDataLength) 223 { 224 // If load has been cancelled after finishing (which could happen with a 225 // JavaScript that changes the window location), do nothing. 226 if (m_state == Terminated) 227 return; 228 229 if (m_notifiedLoadComplete) 230 return; 231 m_notifiedLoadComplete = true; 232 m_host->didFinishLoading(m_resource, finishTime, encodedDataLength); 233 } 234 235 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority, int intraPriorityValue) 236 { 237 if (m_loader) { 238 m_host->didChangeLoadingPriority(m_resource, loadPriority, intraPriorityValue); 239 ASSERT(m_state != Terminated); 240 m_loader->didChangePriority(static_cast<blink::WebURLRequest::Priority>(loadPriority), intraPriorityValue); 241 } 242 } 243 244 void ResourceLoader::cancelIfNotFinishing() 245 { 246 if (m_state != Initialized) 247 return; 248 cancel(); 249 } 250 251 void ResourceLoader::cancel() 252 { 253 cancel(ResourceError()); 254 } 255 256 void ResourceLoader::cancel(const ResourceError& error) 257 { 258 // If the load has already completed - succeeded, failed, or previously cancelled - do nothing. 259 if (m_state == Terminated) 260 return; 261 if (m_state == Finishing) { 262 releaseResources(); 263 return; 264 } 265 266 ResourceError nonNullError = error.isNull() ? ResourceError::cancelledError(m_request.url()) : error; 267 268 // This function calls out to clients at several points that might do 269 // something that causes the last reference to this object to go away. 270 RefPtrWillBeRawPtr<ResourceLoader> protector(this); 271 272 WTF_LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data()); 273 if (m_state == Initialized) 274 m_state = Finishing; 275 m_resource->setResourceError(nonNullError); 276 277 if (m_loader) { 278 m_connectionState = ConnectionStateCanceled; 279 m_loader->cancel(); 280 m_loader.clear(); 281 } 282 283 if (!m_notifiedLoadComplete) { 284 m_notifiedLoadComplete = true; 285 m_host->didFailLoading(m_resource, nonNullError); 286 } 287 288 if (m_state == Finishing) 289 m_resource->error(Resource::LoadError); 290 if (m_state != Terminated) 291 releaseResources(); 292 } 293 294 void ResourceLoader::willSendRequest(blink::WebURLLoader*, blink::WebURLRequest& passedRequest, const blink::WebURLResponse& passedRedirectResponse) 295 { 296 ASSERT(m_state != Terminated); 297 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 298 299 ResourceRequest& request(applyOptions(passedRequest.toMutableResourceRequest())); 300 301 ASSERT(!request.isNull()); 302 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceResponse()); 303 ASSERT(!redirectResponse.isNull()); 304 if (!m_host->canAccessRedirect(m_resource, request, redirectResponse, m_options)) { 305 cancel(); 306 return; 307 } 308 ASSERT(m_state != Terminated); 309 310 applyOptions(request); // canAccessRedirect() can modify m_options so we should re-apply it. 311 m_host->redirectReceived(m_resource, redirectResponse); 312 ASSERT(m_state != Terminated); 313 m_resource->willSendRequest(request, redirectResponse); 314 if (request.isNull() || m_state == Terminated) 315 return; 316 317 m_host->willSendRequest(m_resource->identifier(), request, redirectResponse, m_options.initiatorInfo); 318 ASSERT(m_state != Terminated); 319 ASSERT(!request.isNull()); 320 m_resource->updateRequest(request); 321 m_request = request; 322 } 323 324 void ResourceLoader::didReceiveCachedMetadata(blink::WebURLLoader*, const char* data, int length) 325 { 326 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 327 ASSERT(m_state == Initialized); 328 m_resource->setSerializedCachedMetadata(data, length); 329 } 330 331 void ResourceLoader::didSendData(blink::WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 332 { 333 ASSERT(m_state == Initialized); 334 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 335 m_resource->didSendData(bytesSent, totalBytesToBeSent); 336 } 337 338 bool ResourceLoader::responseNeedsAccessControlCheck() const 339 { 340 // If the fetch was (potentially) CORS enabled, an access control check of the response is required. 341 return m_options.corsEnabled == IsCORSEnabled; 342 } 343 344 void ResourceLoader::didReceiveResponse(blink::WebURLLoader*, const blink::WebURLResponse& response) 345 { 346 ASSERT(!response.isNull()); 347 ASSERT(m_state == Initialized); 348 349 bool isMultipartPayload = response.isMultipartPayload(); 350 bool isValidStateTransition = (m_connectionState == ConnectionStateStarted || m_connectionState == ConnectionStateReceivedResponse); 351 // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved. 352 RELEASE_ASSERT(isMultipartPayload || isValidStateTransition); 353 m_connectionState = ConnectionStateReceivedResponse; 354 355 const ResourceResponse& resourceResponse = response.toResourceResponse(); 356 357 if (responseNeedsAccessControlCheck()) { 358 // If the response successfully validated a cached resource, perform 359 // the access control with respect to it. Need to do this right here 360 // before the resource switches clients over to that validated resource. 361 Resource* resource = m_resource; 362 if (resource->isCacheValidator() && resourceResponse.httpStatusCode() == 304) 363 resource = m_resource->resourceToRevalidate(); 364 else 365 m_resource->setResponse(resourceResponse); 366 if (!m_host->canAccessResource(resource, m_options.securityOrigin.get(), response.url())) { 367 m_host->didReceiveResponse(m_resource, resourceResponse); 368 cancel(); 369 return; 370 } 371 } 372 373 // Reference the object in this method since the additional processing can do 374 // anything including removing the last reference to this object. 375 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 376 m_resource->responseReceived(resourceResponse); 377 if (m_state == Terminated) 378 return; 379 380 m_host->didReceiveResponse(m_resource, resourceResponse); 381 if (m_state == Terminated) 382 return; 383 384 if (response.toResourceResponse().isMultipart()) { 385 // We don't count multiParts in a ResourceFetcher's request count 386 m_requestCountTracker.clear(); 387 if (!m_resource->isImage()) { 388 cancel(); 389 return; 390 } 391 } else if (isMultipartPayload) { 392 // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. 393 // After the first multipart section is complete, signal to delegates that this load is "finished" 394 m_host->subresourceLoaderFinishedLoadingOnePart(this); 395 ASSERT(m_state != Terminated); 396 didFinishLoadingOnePart(0, blink::WebURLLoaderClient::kUnknownEncodedDataLength); 397 } 398 if (m_state == Terminated) 399 return; 400 401 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors()) 402 return; 403 m_state = Finishing; 404 405 if (!m_notifiedLoadComplete) { 406 m_notifiedLoadComplete = true; 407 m_host->didFailLoading(m_resource, ResourceError::cancelledError(m_request.url())); 408 } 409 410 ASSERT(m_state != Terminated); 411 m_resource->error(Resource::LoadError); 412 cancel(); 413 } 414 415 void ResourceLoader::didReceiveData(blink::WebURLLoader*, const char* data, int length, int encodedDataLength) 416 { 417 ASSERT(m_state != Terminated); 418 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 419 m_connectionState = ConnectionStateReceivingData; 420 421 // It is possible to receive data on uninitialized resources if it had an error status code, and we are running a nested message 422 // loop. When this occurs, ignoring the data is the correct action. 423 if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) 424 return; 425 ASSERT(m_state == Initialized); 426 427 // Reference the object in this method since the additional processing can do 428 // anything including removing the last reference to this object. 429 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 430 431 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. 432 // However, with today's computers and networking speeds, this won't happen in practice. 433 // Could be an issue with a giant local file. 434 m_host->didReceiveData(m_resource, data, length, encodedDataLength); 435 if (m_state == Terminated) 436 return; 437 m_resource->appendData(data, length); 438 } 439 440 void ResourceLoader::didFinishLoading(blink::WebURLLoader*, double finishTime, int64 encodedDataLength) 441 { 442 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 443 m_connectionState = ConnectionStateFinishedLoading; 444 if (m_state != Initialized) 445 return; 446 ASSERT(m_state != Terminated); 447 WTF_LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data()); 448 449 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 450 ResourcePtr<Resource> protectResource(m_resource); 451 m_state = Finishing; 452 didFinishLoadingOnePart(finishTime, encodedDataLength); 453 if (m_state == Terminated) 454 return; 455 m_resource->finish(finishTime); 456 457 // If the load has been cancelled by a delegate in response to didFinishLoad(), do not release 458 // the resources a second time, they have been released by cancel. 459 if (m_state == Terminated) 460 return; 461 releaseResources(); 462 } 463 464 void ResourceLoader::didFail(blink::WebURLLoader*, const blink::WebURLError& error) 465 { 466 m_connectionState = ConnectionStateFailed; 467 ASSERT(m_state != Terminated); 468 WTF_LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data()); 469 470 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 471 RefPtrWillBeRawPtr<ResourceLoaderHost> protectHost(m_host.get()); 472 ResourcePtr<Resource> protectResource(m_resource); 473 m_state = Finishing; 474 m_resource->setResourceError(error); 475 476 if (!m_notifiedLoadComplete) { 477 m_notifiedLoadComplete = true; 478 m_host->didFailLoading(m_resource, error); 479 } 480 if (m_state == Terminated) 481 return; 482 483 m_resource->error(Resource::LoadError); 484 485 if (m_state == Terminated) 486 return; 487 488 releaseResources(); 489 } 490 491 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const 492 { 493 return m_host->isLoadedBy(loader); 494 } 495 496 void ResourceLoader::requestSynchronously() 497 { 498 OwnPtr<blink::WebURLLoader> loader = adoptPtr(blink::Platform::current()->createURLLoader()); 499 ASSERT(loader); 500 501 // downloadToFile is not supported for synchronous requests. 502 ASSERT(!m_request.downloadToFile()); 503 504 RefPtrWillBeRawPtr<ResourceLoader> protect(this); 505 RefPtrWillBeRawPtr<ResourceLoaderHost> protectHost(m_host.get()); 506 ResourcePtr<Resource> protectResource(m_resource); 507 508 RELEASE_ASSERT(m_connectionState == ConnectionStateNew); 509 m_connectionState = ConnectionStateStarted; 510 511 blink::WrappedResourceRequest requestIn(m_request); 512 blink::WebURLResponse responseOut; 513 responseOut.initialize(); 514 blink::WebURLError errorOut; 515 blink::WebData dataOut; 516 loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut); 517 if (errorOut.reason) { 518 didFail(0, errorOut); 519 return; 520 } 521 didReceiveResponse(0, responseOut); 522 if (m_state == Terminated) 523 return; 524 RefPtr<ResourceLoadInfo> resourceLoadInfo = responseOut.toResourceResponse().resourceLoadInfo(); 525 int64 encodedDataLength = resourceLoadInfo ? resourceLoadInfo->encodedDataLength : blink::WebURLLoaderClient::kUnknownEncodedDataLength; 526 m_host->didReceiveData(m_resource, dataOut.data(), dataOut.size(), encodedDataLength); 527 m_resource->setResourceBuffer(dataOut); 528 didFinishLoading(0, monotonicallyIncreasingTime(), encodedDataLength); 529 } 530 531 ResourceRequest& ResourceLoader::applyOptions(ResourceRequest& request) const 532 { 533 request.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials); 534 return request; 535 } 536 537 } 538