| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved. | |
| 3 * (C) 2007 Graham Dennis (graham.dennis@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 "sky/engine/core/fetch/ResourceLoader.h" | |
| 31 | |
| 32 #include "sky/engine/core/fetch/Resource.h" | |
| 33 #include "sky/engine/core/fetch/ResourceLoaderHost.h" | |
| 34 #include "sky/engine/core/fetch/ResourcePtr.h" | |
| 35 #include "sky/engine/platform/Logging.h" | |
| 36 #include "sky/engine/platform/SharedBuffer.h" | |
| 37 #include "sky/engine/platform/exported/WrappedResourceRequest.h" | |
| 38 #include "sky/engine/platform/exported/WrappedResourceResponse.h" | |
| 39 #include "sky/engine/platform/network/ResourceError.h" | |
| 40 #include "sky/engine/public/platform/Platform.h" | |
| 41 #include "sky/engine/public/platform/WebData.h" | |
| 42 #include "sky/engine/public/platform/WebURLError.h" | |
| 43 #include "sky/engine/public/platform/WebURLRequest.h" | |
| 44 #include "sky/engine/public/platform/WebURLResponse.h" | |
| 45 #include "sky/engine/wtf/Assertions.h" | |
| 46 #include "sky/engine/wtf/CurrentTime.h" | |
| 47 | |
| 48 namespace blink { | |
| 49 | |
| 50 ResourceLoader::RequestCountTracker::RequestCountTracker(ResourceLoaderHost* hos
t, 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 ResourceLoader::RequestCountTracker::RequestCountTracker(const RequestCountTrack
er& other) | |
| 63 { | |
| 64 m_host = other.m_host; | |
| 65 m_resource = other.m_resource; | |
| 66 m_host->incrementRequestCount(m_resource); | |
| 67 } | |
| 68 | |
| 69 PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Reso
urce* resource, const ResourceRequest& request, const ResourceLoaderOptions& opt
ions) | |
| 70 { | |
| 71 RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, op
tions))); | |
| 72 loader->init(request); | |
| 73 return loader.release(); | |
| 74 } | |
| 75 | |
| 76 ResourceLoader::ResourceLoader(ResourceLoaderHost* host, Resource* resource, con
st ResourceLoaderOptions& options) | |
| 77 : m_host(host) | |
| 78 , m_notifiedLoadComplete(false) | |
| 79 , m_options(options) | |
| 80 , m_resource(resource) | |
| 81 , m_state(Initialized) | |
| 82 , m_connectionState(ConnectionStateNew) | |
| 83 , m_requestCountTracker(adoptPtr(new RequestCountTracker(host, resource))) | |
| 84 { | |
| 85 } | |
| 86 | |
| 87 ResourceLoader::~ResourceLoader() | |
| 88 { | |
| 89 ASSERT(m_state == Terminated); | |
| 90 } | |
| 91 | |
| 92 void ResourceLoader::releaseResources() | |
| 93 { | |
| 94 ASSERT(m_state != Terminated); | |
| 95 ASSERT(m_notifiedLoadComplete); | |
| 96 m_requestCountTracker.clear(); | |
| 97 m_host->didLoadResource(m_resource); | |
| 98 if (m_state == Terminated) | |
| 99 return; | |
| 100 m_resource->clearLoader(); | |
| 101 m_resource->deleteIfPossible(); | |
| 102 m_resource = nullptr; | |
| 103 m_host->willTerminateResourceLoader(this); | |
| 104 | |
| 105 ASSERT(m_state != Terminated); | |
| 106 | |
| 107 // It's possible that when we release the loader, it will be | |
| 108 // deallocated and release the last reference to this object. | |
| 109 // We need to retain to avoid accessing the object after it | |
| 110 // has been deallocated and also to avoid reentering this method. | |
| 111 RefPtr<ResourceLoader> protector(this); | |
| 112 | |
| 113 m_host.clear(); | |
| 114 m_state = Terminated; | |
| 115 | |
| 116 if (m_loader) { | |
| 117 m_loader->cancel(); | |
| 118 m_loader.clear(); | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 void ResourceLoader::init(const ResourceRequest& passedRequest) | |
| 123 { | |
| 124 ASSERT(m_state != Terminated); | |
| 125 ResourceRequest request(passedRequest); | |
| 126 m_host->willSendRequest(m_resource->identifier(), request, ResourceResponse(
), m_options.initiatorInfo); | |
| 127 ASSERT(m_state != Terminated); | |
| 128 ASSERT(!request.isNull()); | |
| 129 m_request = applyOptions(request); | |
| 130 m_resource->updateRequest(request); | |
| 131 ASSERT(m_state != Terminated); | |
| 132 m_host->didInitializeResourceLoader(this); | |
| 133 } | |
| 134 | |
| 135 void ResourceLoader::start() | |
| 136 { | |
| 137 ASSERT(!m_loader); | |
| 138 ASSERT(!m_request.isNull()); | |
| 139 | |
| 140 m_host->willStartLoadingResource(m_resource, m_request); | |
| 141 | |
| 142 if (m_state == Terminated) | |
| 143 return; | |
| 144 | |
| 145 RELEASE_ASSERT(m_connectionState == ConnectionStateNew); | |
| 146 m_connectionState = ConnectionStateStarted; | |
| 147 | |
| 148 m_loader = adoptPtr(blink::Platform::current()->createURLLoader()); | |
| 149 ASSERT(m_loader); | |
| 150 blink::WrappedResourceRequest wrappedRequest(m_request); | |
| 151 m_loader->loadAsynchronously(wrappedRequest, this); | |
| 152 } | |
| 153 | |
| 154 void ResourceLoader::didDownloadData(blink::WebURLLoader*, int length, int encod
edDataLength) | |
| 155 { | |
| 156 ASSERT(m_state != Terminated); | |
| 157 RefPtr<ResourceLoader> protect(this); | |
| 158 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse); | |
| 159 m_host->didDownloadData(m_resource, length, encodedDataLength); | |
| 160 if (m_state == Terminated) | |
| 161 return; | |
| 162 m_resource->didDownloadData(length); | |
| 163 } | |
| 164 | |
| 165 void ResourceLoader::didFinishLoadingOnePart(double finishTime, int64 encodedDat
aLength) | |
| 166 { | |
| 167 // If load has been cancelled after finishing (which could happen with a | |
| 168 // JavaScript that changes the window location), do nothing. | |
| 169 if (m_state == Terminated) | |
| 170 return; | |
| 171 | |
| 172 if (m_notifiedLoadComplete) | |
| 173 return; | |
| 174 m_notifiedLoadComplete = true; | |
| 175 m_host->didFinishLoading(m_resource, finishTime, encodedDataLength); | |
| 176 } | |
| 177 | |
| 178 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority, int in
traPriorityValue) | |
| 179 { | |
| 180 if (m_loader) { | |
| 181 m_host->didChangeLoadingPriority(m_resource, loadPriority, intraPriority
Value); | |
| 182 ASSERT(m_state != Terminated); | |
| 183 m_loader->didChangePriority(static_cast<blink::WebURLRequest::Priority>(
loadPriority), intraPriorityValue); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 void ResourceLoader::cancelIfNotFinishing() | |
| 188 { | |
| 189 if (m_state != Initialized) | |
| 190 return; | |
| 191 cancel(); | |
| 192 } | |
| 193 | |
| 194 void ResourceLoader::cancel() | |
| 195 { | |
| 196 cancel(ResourceError()); | |
| 197 } | |
| 198 | |
| 199 void ResourceLoader::cancel(const ResourceError& error) | |
| 200 { | |
| 201 // If the load has already completed - succeeded, failed, or previously canc
elled - do nothing. | |
| 202 if (m_state == Terminated) | |
| 203 return; | |
| 204 if (m_state == Finishing) { | |
| 205 releaseResources(); | |
| 206 return; | |
| 207 } | |
| 208 | |
| 209 ResourceError nonNullError = error.isNull() ? ResourceError::cancelledError(
m_request.url()) : error; | |
| 210 | |
| 211 // This function calls out to clients at several points that might do | |
| 212 // something that causes the last reference to this object to go away. | |
| 213 RefPtr<ResourceLoader> protector(this); | |
| 214 | |
| 215 WTF_LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().stri
ng().latin1().data()); | |
| 216 if (m_state == Initialized) | |
| 217 m_state = Finishing; | |
| 218 m_resource->setResourceError(nonNullError); | |
| 219 | |
| 220 if (m_loader) { | |
| 221 m_connectionState = ConnectionStateCanceled; | |
| 222 m_loader->cancel(); | |
| 223 m_loader.clear(); | |
| 224 } | |
| 225 | |
| 226 if (!m_notifiedLoadComplete) { | |
| 227 m_notifiedLoadComplete = true; | |
| 228 m_host->didFailLoading(m_resource, nonNullError); | |
| 229 } | |
| 230 | |
| 231 if (m_state == Finishing) | |
| 232 m_resource->error(Resource::LoadError); | |
| 233 if (m_state != Terminated) | |
| 234 releaseResources(); | |
| 235 } | |
| 236 | |
| 237 void ResourceLoader::willSendRequest(blink::WebURLLoader*, blink::WebURLRequest&
passedRequest, const blink::WebURLResponse& passedRedirectResponse) | |
| 238 { | |
| 239 ASSERT(m_state != Terminated); | |
| 240 RefPtr<ResourceLoader> protect(this); | |
| 241 | |
| 242 ResourceRequest& request(applyOptions(passedRequest.toMutableResourceRequest
())); | |
| 243 | |
| 244 // FIXME: We should have a real context for redirect requests. Currently, we
don't: see WebURLLoaderImpl::Context::OnReceivedRedirect in content/. | |
| 245 request.setRequestContext(blink::WebURLRequest::RequestContextInternal); | |
| 246 | |
| 247 ASSERT(!request.isNull()); | |
| 248 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceRe
sponse()); | |
| 249 ASSERT(!redirectResponse.isNull()); | |
| 250 ASSERT(m_state != Terminated); | |
| 251 | |
| 252 applyOptions(request); // canAccessRedirect() can modify m_options so we sho
uld re-apply it. | |
| 253 ASSERT(m_state != Terminated); | |
| 254 m_resource->willSendRequest(request, redirectResponse); | |
| 255 if (request.isNull() || m_state == Terminated) | |
| 256 return; | |
| 257 | |
| 258 m_host->willSendRequest(m_resource->identifier(), request, redirectResponse,
m_options.initiatorInfo); | |
| 259 ASSERT(m_state != Terminated); | |
| 260 ASSERT(!request.isNull()); | |
| 261 m_resource->updateRequest(request); | |
| 262 m_request = request; | |
| 263 } | |
| 264 | |
| 265 void ResourceLoader::didSendData(blink::WebURLLoader*, unsigned long long bytesS
ent, unsigned long long totalBytesToBeSent) | |
| 266 { | |
| 267 ASSERT(m_state == Initialized); | |
| 268 RefPtr<ResourceLoader> protect(this); | |
| 269 m_resource->didSendData(bytesSent, totalBytesToBeSent); | |
| 270 } | |
| 271 | |
| 272 void ResourceLoader::didReceiveResponse(blink::WebURLLoader*, const blink::WebUR
LResponse& response) | |
| 273 { | |
| 274 ASSERT(!response.isNull()); | |
| 275 ASSERT(m_state == Initialized); | |
| 276 | |
| 277 bool isMultipartPayload = response.isMultipartPayload(); | |
| 278 bool isValidStateTransition = (m_connectionState == ConnectionStateStarted |
| m_connectionState == ConnectionStateReceivedResponse); | |
| 279 // In the case of multipart loads, calls to didReceiveData & didReceiveRespo
nse can be interleaved. | |
| 280 RELEASE_ASSERT(isMultipartPayload || isValidStateTransition); | |
| 281 m_connectionState = ConnectionStateReceivedResponse; | |
| 282 | |
| 283 const ResourceResponse& resourceResponse = response.toResourceResponse(); | |
| 284 | |
| 285 // Reference the object in this method since the additional processing can d
o | |
| 286 // anything including removing the last reference to this object. | |
| 287 RefPtr<ResourceLoader> protect(this); | |
| 288 m_resource->responseReceived(resourceResponse); | |
| 289 if (m_state == Terminated) | |
| 290 return; | |
| 291 | |
| 292 m_host->didReceiveResponse(m_resource, resourceResponse); | |
| 293 if (m_state == Terminated) | |
| 294 return; | |
| 295 | |
| 296 if (response.toResourceResponse().isMultipart()) { | |
| 297 // We don't count multiParts in a ResourceFetcher's request count | |
| 298 m_requestCountTracker.clear(); | |
| 299 if (!m_resource->isImage()) { | |
| 300 cancel(); | |
| 301 return; | |
| 302 } | |
| 303 } else if (isMultipartPayload) { | |
| 304 // Since a subresource loader does not load multipart sections progressi
vely, data was delivered to the loader all at once. | |
| 305 // After the first multipart section is complete, signal to delegates th
at this load is "finished" | |
| 306 m_host->subresourceLoaderFinishedLoadingOnePart(this); | |
| 307 ASSERT(m_state != Terminated); | |
| 308 didFinishLoadingOnePart(0, blink::WebURLLoaderClient::kUnknownEncodedDat
aLength); | |
| 309 } | |
| 310 if (m_state == Terminated) | |
| 311 return; | |
| 312 | |
| 313 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnor
eHTTPStatusCodeErrors()) | |
| 314 return; | |
| 315 m_state = Finishing; | |
| 316 | |
| 317 if (!m_notifiedLoadComplete) { | |
| 318 m_notifiedLoadComplete = true; | |
| 319 m_host->didFailLoading(m_resource, ResourceError::cancelledError(m_reque
st.url())); | |
| 320 } | |
| 321 | |
| 322 ASSERT(m_state != Terminated); | |
| 323 m_resource->error(Resource::LoadError); | |
| 324 cancel(); | |
| 325 } | |
| 326 | |
| 327 void ResourceLoader::didReceiveData(blink::WebURLLoader*, const char* data, int
length, int encodedDataLength) | |
| 328 { | |
| 329 ASSERT(m_state != Terminated); | |
| 330 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_con
nectionState == ConnectionStateReceivingData); | |
| 331 m_connectionState = ConnectionStateReceivingData; | |
| 332 | |
| 333 // It is possible to receive data on uninitialized resources if it had an er
ror status code, and we are running a nested message | |
| 334 // loop. When this occurs, ignoring the data is the correct action. | |
| 335 if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgn
oreHTTPStatusCodeErrors()) | |
| 336 return; | |
| 337 ASSERT(m_state == Initialized); | |
| 338 | |
| 339 // Reference the object in this method since the additional processing can d
o | |
| 340 // anything including removing the last reference to this object. | |
| 341 RefPtr<ResourceLoader> protect(this); | |
| 342 | |
| 343 // FIXME: If we get a resource with more than 2B bytes, this code won't do t
he right thing. | |
| 344 // However, with today's computers and networking speeds, this won't happen
in practice. | |
| 345 // Could be an issue with a giant local file. | |
| 346 m_host->didReceiveData(m_resource, data, length, encodedDataLength); | |
| 347 if (m_state == Terminated) | |
| 348 return; | |
| 349 m_resource->appendData(data, length); | |
| 350 } | |
| 351 | |
| 352 void ResourceLoader::didFinishLoading(blink::WebURLLoader*, double finishTime, i
nt64 encodedDataLength) | |
| 353 { | |
| 354 RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_con
nectionState == ConnectionStateReceivingData); | |
| 355 m_connectionState = ConnectionStateFinishedLoading; | |
| 356 if (m_state != Initialized) | |
| 357 return; | |
| 358 ASSERT(m_state != Terminated); | |
| 359 WTF_LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1
().data()); | |
| 360 | |
| 361 RefPtr<ResourceLoader> protect(this); | |
| 362 ResourcePtr<Resource> protectResource(m_resource); | |
| 363 m_state = Finishing; | |
| 364 didFinishLoadingOnePart(finishTime, encodedDataLength); | |
| 365 if (m_state == Terminated) | |
| 366 return; | |
| 367 m_resource->finish(finishTime); | |
| 368 | |
| 369 // If the load has been cancelled by a delegate in response to didFinishLoad
(), do not release | |
| 370 // the resources a second time, they have been released by cancel. | |
| 371 if (m_state == Terminated) | |
| 372 return; | |
| 373 releaseResources(); | |
| 374 } | |
| 375 | |
| 376 void ResourceLoader::didFail(blink::WebURLLoader*, const blink::WebURLError& err
or) | |
| 377 { | |
| 378 m_connectionState = ConnectionStateFailed; | |
| 379 ASSERT(m_state != Terminated); | |
| 380 WTF_LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string(
).latin1().data()); | |
| 381 | |
| 382 RefPtr<ResourceLoader> protect(this); | |
| 383 RefPtr<ResourceLoaderHost> protectHost(m_host.get()); | |
| 384 ResourcePtr<Resource> protectResource(m_resource); | |
| 385 m_state = Finishing; | |
| 386 m_resource->setResourceError(error); | |
| 387 | |
| 388 if (!m_notifiedLoadComplete) { | |
| 389 m_notifiedLoadComplete = true; | |
| 390 m_host->didFailLoading(m_resource, error); | |
| 391 } | |
| 392 if (m_state == Terminated) | |
| 393 return; | |
| 394 | |
| 395 m_resource->error(Resource::LoadError); | |
| 396 | |
| 397 if (m_state == Terminated) | |
| 398 return; | |
| 399 | |
| 400 releaseResources(); | |
| 401 } | |
| 402 | |
| 403 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const | |
| 404 { | |
| 405 return m_host->isLoadedBy(loader); | |
| 406 } | |
| 407 | |
| 408 ResourceRequest& ResourceLoader::applyOptions(ResourceRequest& request) const | |
| 409 { | |
| 410 request.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredC
redentials); | |
| 411 return request; | |
| 412 } | |
| 413 | |
| 414 } | |
| OLD | NEW |