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/loader/ResourceLoader.h" 32 33 #include "core/loader/ResourceLoaderHost.h" 34 #include "core/loader/cache/Resource.h" 35 #include "core/loader/cache/ResourcePtr.h" 36 #include "core/platform/Logging.h" 37 #include "core/platform/SharedBuffer.h" 38 #include "core/platform/chromium/support/WrappedResourceRequest.h" 39 #include "core/platform/chromium/support/WrappedResourceResponse.h" 40 #include "core/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 48 namespace WebCore { 49 50 ResourceLoader::RequestCountTracker::RequestCountTracker(ResourceLoaderHost* host, Resource* resource) 51 : m_host(host) 52 , m_resource(resource) 53 { 54 m_host->incrementRequestCount(m_resource); 55 } 56 57 ResourceLoader::RequestCountTracker::~RequestCountTracker() 58 { 59 m_host->decrementRequestCount(m_resource); 60 } 61 62 PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) 63 { 64 RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options))); 65 loader->init(request); 66 loader->start(); 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, 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_defersLoading) { 136 m_deferredRequest = m_request; 137 return; 138 } 139 140 if (m_state == Terminated) 141 return; 142 143 RELEASE_ASSERT(m_connectionState == ConnectionStateNew); 144 m_connectionState = ConnectionStateStarted; 145 146 m_loader = adoptPtr(WebKit::Platform::current()->createURLLoader()); 147 ASSERT(m_loader); 148 WebKit::WrappedResourceRequest wrappedRequest(m_request); 149 wrappedRequest.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials); 150 m_loader->loadAsynchronously(wrappedRequest, this); 151 } 152 153 void ResourceLoader::setDefersLoading(bool defers) 154 { 155 m_defersLoading = defers; 156 if (m_loader) 157 m_loader->setDefersLoading(defers); 158 if (!defers && !m_deferredRequest.isNull()) { 159 m_request = m_deferredRequest; 160 m_deferredRequest = ResourceRequest(); 161 start(); 162 } 163 } 164 165 void ResourceLoader::didDownloadData(WebKit::WebURLLoader*, int length) 166 { 167 RefPtr<ResourceLoader> protect(this); 168 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse); 169 m_resource->didDownloadData(length); 170 } 171 172 void ResourceLoader::didFinishLoadingOnePart(double finishTime) 173 { 174 // If load has been cancelled after finishing (which could happen with a 175 // JavaScript that changes the window location), do nothing. 176 if (m_state == Terminated) 177 return; 178 179 if (m_notifiedLoadComplete) 180 return; 181 m_notifiedLoadComplete = true; 182 m_host->didFinishLoading(m_resource, finishTime, m_options); 183 } 184 185 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority) 186 { 187 if (m_loader) { 188 m_host->didChangeLoadingPriority(m_resource, loadPriority); 189 m_loader->didChangePriority(static_cast<WebKit::WebURLRequest::Priority>(loadPriority)); 190 } 191 } 192 193 void ResourceLoader::cancelIfNotFinishing() 194 { 195 if (m_state != Initialized) 196 return; 197 cancel(); 198 } 199 200 void ResourceLoader::cancel() 201 { 202 cancel(ResourceError()); 203 } 204 205 void ResourceLoader::cancel(const ResourceError& error) 206 { 207 // If the load has already completed - succeeded, failed, or previously cancelled - do nothing. 208 if (m_state == Terminated) 209 return; 210 if (m_state == Finishing) { 211 releaseResources(); 212 return; 213 } 214 215 ResourceError nonNullError = error.isNull() ? ResourceError::cancelledError(m_request.url()) : error; 216 217 // This function calls out to clients at several points that might do 218 // something that causes the last reference to this object to go away. 219 RefPtr<ResourceLoader> protector(this); 220 221 LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data()); 222 if (m_state == Initialized) 223 m_state = Finishing; 224 m_resource->setResourceError(nonNullError); 225 226 if (m_loader) { 227 m_connectionState = ConnectionStateCanceled; 228 m_loader->cancel(); 229 m_loader.clear(); 230 } 231 232 m_host->didFailLoading(m_resource, nonNullError, m_options); 233 234 if (m_state == Finishing) 235 m_resource->error(Resource::LoadError); 236 if (m_state != Terminated) 237 releaseResources(); 238 } 239 240 void ResourceLoader::willSendRequest(WebKit::WebURLLoader*, WebKit::WebURLRequest& passedRequest, const WebKit::WebURLResponse& passedRedirectResponse) 241 { 242 RefPtr<ResourceLoader> protect(this); 243 244 ResourceRequest& request(passedRequest.toMutableResourceRequest()); 245 ASSERT(!request.isNull()); 246 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceResponse()); 247 ASSERT(!redirectResponse.isNull()); 248 if (!m_host->shouldRequest(m_resource, request, m_options)) { 249 cancel(); 250 return; 251 } 252 m_host->redirectReceived(m_resource, redirectResponse); 253 m_resource->willSendRequest(request, redirectResponse); 254 if (request.isNull() || m_state == Terminated) 255 return; 256 257 m_host->willSendRequest(m_resource, request, redirectResponse, m_options); 258 request.setReportLoadTiming(true); 259 ASSERT(!request.isNull()); 260 m_request = request; 261 } 262 263 void ResourceLoader::didReceiveCachedMetadata(WebKit::WebURLLoader*, const char* data, int length) 264 { 265 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 266 ASSERT(m_state == Initialized); 267 m_resource->setSerializedCachedMetadata(data, length); 268 } 269 270 void ResourceLoader::didSendData(WebKit::WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 271 { 272 ASSERT(m_state == Initialized); 273 RefPtr<ResourceLoader> protect(this); 274 m_resource->didSendData(bytesSent, totalBytesToBeSent); 275 } 276 277 void ResourceLoader::didReceiveResponse(WebKit::WebURLLoader*, const WebKit::WebURLResponse& response) 278 { 279 ASSERT(!response.isNull()); 280 ASSERT(m_state == Initialized); 281 282 bool isMultipartPayload = response.isMultipartPayload(); 283 bool isValidStateTransition = (m_connectionState == ConnectionStateStarted || m_connectionState == ConnectionStateReceivedResponse); 284 // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved. 285 RELEASE_ASSERT(isMultipartPayload || isValidStateTransition); 286 m_connectionState = ConnectionStateReceivedResponse; 287 288 // Reference the object in this method since the additional processing can do 289 // anything including removing the last reference to this object. 290 RefPtr<ResourceLoader> protect(this); 291 m_resource->responseReceived(response.toResourceResponse()); 292 if (m_state == Terminated) 293 return; 294 295 m_host->didReceiveResponse(m_resource, response.toResourceResponse(), m_options); 296 297 if (response.toResourceResponse().isMultipart()) { 298 // We don't count multiParts in a ResourceFetcher's request count 299 m_requestCountTracker.clear(); 300 if (!m_resource->isImage()) { 301 cancel(); 302 return; 303 } 304 } else if (isMultipartPayload) { 305 // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once. 306 // After the first multipart section is complete, signal to delegates that this load is "finished" 307 m_host->subresourceLoaderFinishedLoadingOnePart(this); 308 didFinishLoadingOnePart(0); 309 } 310 311 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors()) 312 return; 313 m_state = Finishing; 314 m_resource->error(Resource::LoadError); 315 cancel(); 316 } 317 318 void ResourceLoader::didReceiveData(WebKit::WebURLLoader*, const char* data, int length, int encodedDataLength) 319 { 320 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 321 m_connectionState = ConnectionStateReceivingData; 322 323 // It is possible to receive data on uninitialized resources if it had an error status code, and we are running a nested message 324 // loop. When this occurs, ignoring the data is the correct action. 325 if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) 326 return; 327 ASSERT(m_state == Initialized); 328 329 // Reference the object in this method since the additional processing can do 330 // anything including removing the last reference to this object. 331 RefPtr<ResourceLoader> protect(this); 332 333 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. 334 // However, with today's computers and networking speeds, this won't happen in practice. 335 // Could be an issue with a giant local file. 336 m_host->didReceiveData(m_resource, data, length, encodedDataLength, m_options); 337 m_resource->appendData(data, length); 338 } 339 340 void ResourceLoader::didFinishLoading(WebKit::WebURLLoader*, double finishTime) 341 { 342 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData); 343 m_connectionState = ConnectionStateFinishedLoading; 344 if (m_state != Initialized) 345 return; 346 ASSERT(m_state != Terminated); 347 LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data()); 348 349 RefPtr<ResourceLoader> protect(this); 350 ResourcePtr<Resource> protectResource(m_resource); 351 m_state = Finishing; 352 m_resource->finish(finishTime); 353 didFinishLoadingOnePart(finishTime); 354 355 // If the load has been cancelled by a delegate in response to didFinishLoad(), do not release 356 // the resources a second time, they have been released by cancel. 357 if (m_state == Terminated) 358 return; 359 releaseResources(); 360 } 361 362 void ResourceLoader::didFail(WebKit::WebURLLoader*, const WebKit::WebURLError& error) 363 { 364 m_connectionState = ConnectionStateFailed; 365 ASSERT(m_state != Terminated); 366 LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data()); 367 368 RefPtr<ResourceLoader> protect(this); 369 RefPtr<ResourceLoaderHost> protectHost(m_host); 370 ResourcePtr<Resource> protectResource(m_resource); 371 m_state = Finishing; 372 m_resource->setResourceError(error); 373 m_resource->error(Resource::LoadError); 374 375 if (m_state == Terminated) 376 return; 377 378 if (!m_notifiedLoadComplete) { 379 m_notifiedLoadComplete = true; 380 m_host->didFailLoading(m_resource, error, m_options); 381 } 382 383 releaseResources(); 384 } 385 386 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const 387 { 388 return m_host->isLoadedBy(loader); 389 } 390 391 void ResourceLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) 392 { 393 OwnPtr<WebKit::WebURLLoader> loader = adoptPtr(WebKit::Platform::current()->createURLLoader()); 394 ASSERT(loader); 395 396 WebKit::WrappedResourceRequest requestIn(request); 397 requestIn.setAllowStoredCredentials(storedCredentials == AllowStoredCredentials); 398 WebKit::WrappedResourceResponse responseOut(response); 399 WebKit::WebURLError errorOut; 400 WebKit::WebData dataOut; 401 402 loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut); 403 404 error = errorOut; 405 data.clear(); 406 RefPtr<SharedBuffer> buffer = dataOut; 407 if (buffer) 408 buffer->moveTo(data); 409 } 410 411 } 412