2 * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
3 * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include "core/fetch/ResourceLoader.h"
33 #include "core/fetch/CSSStyleSheetResource.h"
34 #include "core/fetch/Resource.h"
35 #include "core/fetch/ResourceFetcher.h"
36 #include "core/fetch/ResourcePtr.h"
37 #include "platform/Logging.h"
38 #include "platform/SharedBuffer.h"
39 #include "platform/ThreadedDataReceiver.h"
40 #include "platform/exported/WrappedResourceRequest.h"
41 #include "platform/exported/WrappedResourceResponse.h"
42 #include "platform/network/ResourceError.h"
43 #include "public/platform/Platform.h"
44 #include "public/platform/WebData.h"
45 #include "public/platform/WebThreadedDataReceiver.h"
46 #include "public/platform/WebURLError.h"
47 #include "public/platform/WebURLRequest.h"
48 #include "public/platform/WebURLResponse.h"
49 #include "wtf/Assertions.h"
50 #include "wtf/CurrentTime.h"
54 PassRefPtrWillBeRawPtr
<ResourceLoader
> ResourceLoader::create(ResourceFetcher
* fetcher
, Resource
* resource
, const ResourceRequest
& request
, const ResourceLoaderOptions
& options
)
56 RefPtrWillBeRawPtr
<ResourceLoader
> loader(adoptRefWillBeNoop(new ResourceLoader(fetcher
, resource
, options
)));
57 loader
->init(request
);
58 return loader
.release();
61 ResourceLoader::ResourceLoader(ResourceFetcher
* fetcher
, Resource
* resource
, const ResourceLoaderOptions
& options
)
63 , m_notifiedLoadComplete(false)
64 , m_defersLoading(fetcher
->defersLoading())
65 , m_loadingMultipartContent(false)
67 , m_resource(resource
)
68 , m_state(Initialized
)
69 , m_connectionState(ConnectionStateNew
)
73 ResourceLoader::~ResourceLoader()
75 ASSERT(m_state
== Terminated
);
78 DEFINE_TRACE(ResourceLoader
)
80 visitor
->trace(m_fetcher
);
81 visitor
->trace(m_resource
);
84 void ResourceLoader::releaseResources()
86 ASSERT(m_state
!= Terminated
);
87 ASSERT(m_notifiedLoadComplete
);
88 m_fetcher
->didLoadResource();
89 if (m_state
== Terminated
)
91 m_resource
->clearLoader();
92 m_resource
->deleteIfPossible();
95 ASSERT(m_state
!= Terminated
);
102 m_deferredRequest
= ResourceRequest();
106 void ResourceLoader::init(const ResourceRequest
& passedRequest
)
108 ASSERT(m_state
!= Terminated
);
109 ResourceRequest
request(passedRequest
);
110 m_fetcher
->willSendRequest(m_resource
->identifier(), request
, ResourceResponse(), m_options
.initiatorInfo
);
111 ASSERT(m_state
!= Terminated
);
112 ASSERT(!request
.isNull());
113 m_originalRequest
= m_request
= applyOptions(request
);
114 m_resource
->updateRequest(request
);
115 ASSERT(m_state
!= Terminated
);
116 m_fetcher
->didInitializeResourceLoader(this);
119 void ResourceLoader::start()
122 ASSERT(!m_request
.isNull());
123 ASSERT(m_deferredRequest
.isNull());
125 if (responseNeedsAccessControlCheck() && m_fetcher
->isControlledByServiceWorker()) {
126 m_fallbackRequestForServiceWorker
= adoptPtr(new ResourceRequest(m_request
));
127 m_fallbackRequestForServiceWorker
->setSkipServiceWorker(true);
130 m_fetcher
->willStartLoadingResource(m_resource
, m_request
);
132 if (m_options
.synchronousPolicy
== RequestSynchronously
) {
133 requestSynchronously();
137 if (m_defersLoading
) {
138 m_deferredRequest
= m_request
;
142 if (m_state
== Terminated
)
145 RELEASE_ASSERT(m_connectionState
== ConnectionStateNew
);
146 m_connectionState
= ConnectionStateStarted
;
148 m_loader
= adoptPtr(Platform::current()->createURLLoader());
150 WrappedResourceRequest
wrappedRequest(m_request
);
151 m_loader
->loadAsynchronously(wrappedRequest
, this);
154 void ResourceLoader::changeToSynchronous()
156 ASSERT(m_options
.synchronousPolicy
== RequestAsynchronously
);
160 m_request
.setPriority(ResourceLoadPriorityHighest
);
161 m_connectionState
= ConnectionStateNew
;
162 requestSynchronously();
165 void ResourceLoader::setDefersLoading(bool defers
)
167 m_defersLoading
= defers
;
169 m_loader
->setDefersLoading(defers
);
170 if (!defers
&& !m_deferredRequest
.isNull()) {
171 m_request
= applyOptions(m_deferredRequest
);
172 m_deferredRequest
= ResourceRequest();
177 void ResourceLoader::attachThreadedDataReceiver(PassRefPtrWillBeRawPtr
<ThreadedDataReceiver
> threadedDataReceiver
)
180 // The implementor of the WebURLLoader assumes ownership of the
181 // threaded data receiver if it signals that it got successfully
183 WebThreadedDataReceiver
* webDataReceiver
= new WebThreadedDataReceiver(threadedDataReceiver
);
184 if (!m_loader
->attachThreadedDataReceiver(webDataReceiver
))
185 delete webDataReceiver
;
189 void ResourceLoader::didDownloadData(WebURLLoader
*, int length
, int encodedDataLength
)
191 ASSERT(m_state
!= Terminated
);
192 RefPtrWillBeRawPtr
<ResourceLoader
> protect(this);
193 RELEASE_ASSERT(m_connectionState
== ConnectionStateReceivedResponse
);
194 m_fetcher
->didDownloadData(m_resource
, length
, encodedDataLength
);
195 if (m_state
== Terminated
)
197 m_resource
->didDownloadData(length
);
200 void ResourceLoader::didFinishLoadingOnePart(double finishTime
, int64_t encodedDataLength
)
202 // If load has been cancelled after finishing (which could happen with a
203 // JavaScript that changes the window location), do nothing.
204 if (m_state
== Terminated
)
207 if (m_notifiedLoadComplete
)
209 m_notifiedLoadComplete
= true;
210 m_fetcher
->didFinishLoading(m_resource
, finishTime
, encodedDataLength
);
213 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority
, int intraPriorityValue
)
216 m_fetcher
->didChangeLoadingPriority(m_resource
, loadPriority
, intraPriorityValue
);
217 ASSERT(m_state
!= Terminated
);
218 m_loader
->didChangePriority(static_cast<WebURLRequest::Priority
>(loadPriority
), intraPriorityValue
);
222 void ResourceLoader::cancelIfNotFinishing()
224 if (m_state
!= Initialized
)
229 void ResourceLoader::cancel()
231 cancel(ResourceError());
234 void ResourceLoader::cancel(const ResourceError
& error
)
236 // If the load has already completed - succeeded, failed, or previously cancelled - do nothing.
237 if (m_state
== Terminated
)
239 if (m_state
== Finishing
) {
244 ResourceError nonNullError
= error
.isNull() ? ResourceError::cancelledError(m_request
.url()) : error
;
246 // This function calls out to clients at several points that might do
247 // something that causes the last reference to this object to go away.
248 RefPtrWillBeRawPtr
<ResourceLoader
> protector(this);
250 WTF_LOG(ResourceLoading
, "Cancelled load of '%s'.\n", m_resource
->url().string().latin1().data());
251 if (m_state
== Initialized
)
253 m_resource
->setResourceError(nonNullError
);
256 m_connectionState
= ConnectionStateCanceled
;
261 if (!m_notifiedLoadComplete
) {
262 m_notifiedLoadComplete
= true;
263 m_fetcher
->didFailLoading(m_resource
, nonNullError
);
266 if (m_state
== Finishing
)
267 m_resource
->error(Resource::LoadError
);
268 if (m_state
!= Terminated
)
272 void ResourceLoader::willSendRequest(WebURLLoader
*, WebURLRequest
& passedNewRequest
, const WebURLResponse
& passedRedirectResponse
)
274 ASSERT(m_state
!= Terminated
);
275 RefPtrWillBeRawPtr
<ResourceLoader
> protect(this);
277 ResourceRequest
& newRequest(applyOptions(passedNewRequest
.toMutableResourceRequest()));
279 ASSERT(!newRequest
.isNull());
280 const ResourceResponse
& redirectResponse(passedRedirectResponse
.toResourceResponse());
281 ASSERT(!redirectResponse
.isNull());
282 newRequest
.setFollowedRedirect(true);
283 if (!m_fetcher
->canAccessRedirect(m_resource
, newRequest
, redirectResponse
, m_options
)) {
284 cancel(ResourceError::cancelledDueToAccessCheckError(newRequest
.url()));
287 ASSERT(m_state
!= Terminated
);
289 applyOptions(newRequest
); // canAccessRedirect() can modify m_options so we should re-apply it.
290 m_fetcher
->redirectReceived(m_resource
, redirectResponse
);
291 ASSERT(m_state
!= Terminated
);
292 m_resource
->willFollowRedirect(newRequest
, redirectResponse
);
293 if (newRequest
.isNull() || m_state
== Terminated
)
296 m_fetcher
->willSendRequest(m_resource
->identifier(), newRequest
, redirectResponse
, m_options
.initiatorInfo
);
297 ASSERT(m_state
!= Terminated
);
298 ASSERT(!newRequest
.isNull());
299 m_resource
->updateRequest(newRequest
);
300 m_request
= newRequest
;
303 void ResourceLoader::didReceiveCachedMetadata(WebURLLoader
*, const char* data
, int length
)
305 RELEASE_ASSERT(m_connectionState
== ConnectionStateReceivedResponse
|| m_connectionState
== ConnectionStateReceivingData
);
306 ASSERT(m_state
== Initialized
);
307 m_resource
->setSerializedCachedMetadata(data
, length
);
310 void ResourceLoader::didSendData(WebURLLoader
*, unsigned long long bytesSent
, unsigned long long totalBytesToBeSent
)
312 ASSERT(m_state
== Initialized
);
313 m_resource
->didSendData(bytesSent
, totalBytesToBeSent
);
316 bool ResourceLoader::responseNeedsAccessControlCheck() const
318 // If the fetch was (potentially) CORS enabled, an access control check of the response is required.
319 return m_options
.corsEnabled
== IsCORSEnabled
;
322 void ResourceLoader::didReceiveResponse(WebURLLoader
*, const WebURLResponse
& response
, WebDataConsumerHandle
* rawHandle
)
324 ASSERT(!response
.isNull());
325 ASSERT(m_state
== Initialized
);
326 // |rawHandle|'s ownership is transferred to the callee.
327 OwnPtr
<WebDataConsumerHandle
> handle
= adoptPtr(rawHandle
);
329 bool isMultipartPayload
= response
.isMultipartPayload();
330 bool isValidStateTransition
= (m_connectionState
== ConnectionStateStarted
|| m_connectionState
== ConnectionStateReceivedResponse
);
331 // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved.
332 RELEASE_ASSERT(isMultipartPayload
|| isValidStateTransition
);
333 m_connectionState
= ConnectionStateReceivedResponse
;
335 const ResourceResponse
& resourceResponse
= response
.toResourceResponse();
337 if (responseNeedsAccessControlCheck()) {
338 if (response
.wasFetchedViaServiceWorker()) {
339 if (response
.wasFallbackRequiredByServiceWorker()) {
340 ASSERT(m_fallbackRequestForServiceWorker
);
343 m_connectionState
= ConnectionStateStarted
;
344 m_request
= *m_fallbackRequestForServiceWorker
;
345 m_loader
= adoptPtr(Platform::current()->createURLLoader());
347 WrappedResourceRequest
wrappedRequest(m_request
);
348 m_loader
->loadAsynchronously(wrappedRequest
, this);
352 // If the response successfully validated a cached resource, perform
353 // the access control with respect to it. Need to do this right here
354 // before the resource switches clients over to that validated resource.
355 Resource
* resource
= m_resource
;
356 if (resource
->isCacheValidator() && resourceResponse
.httpStatusCode() == 304)
357 resource
= m_resource
->resourceToRevalidate();
359 m_resource
->setResponse(resourceResponse
);
360 if (!m_fetcher
->canAccessResource(resource
, m_options
.securityOrigin
.get(), response
.url(), ResourceFetcher::ShouldLogAccessControlErrors
)) {
361 m_fetcher
->didReceiveResponse(m_resource
, resourceResponse
);
362 cancel(ResourceError::cancelledDueToAccessCheckError(KURL(response
.url())));
368 // Reference the object in this method since the additional processing can do
369 // anything including removing the last reference to this object.
370 RefPtrWillBeRawPtr
<ResourceLoader
> protect(this);
371 m_resource
->responseReceived(resourceResponse
, handle
.release());
372 if (m_state
== Terminated
)
375 m_fetcher
->didReceiveResponse(m_resource
, resourceResponse
);
376 if (m_state
== Terminated
)
379 if (response
.toResourceResponse().isMultipart()) {
380 // We only support multipart for images, though the image may be loaded
381 // as a main resource that we end up displaying through an ImageDocument.
382 if (!m_resource
->isImage() && m_resource
->type() != Resource::MainResource
) {
386 m_loadingMultipartContent
= true;
387 } else if (isMultipartPayload
) {
388 // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
389 // After the first multipart section is complete, signal to delegates that this load is "finished"
390 m_fetcher
->subresourceLoaderFinishedLoadingOnePart(this);
391 didFinishLoadingOnePart(0, WebURLLoaderClient::kUnknownEncodedDataLength
);
393 if (m_state
== Terminated
)
396 if (m_resource
->response().httpStatusCode() < 400 || m_resource
->shouldIgnoreHTTPStatusCodeErrors())
400 if (!m_notifiedLoadComplete
) {
401 m_notifiedLoadComplete
= true;
402 m_fetcher
->didFailLoading(m_resource
, ResourceError::cancelledError(m_request
.url()));
405 ASSERT(m_state
!= Terminated
);
406 m_resource
->error(Resource::LoadError
);
410 void ResourceLoader::didReceiveResponse(WebURLLoader
* loader
, const WebURLResponse
& response
)
412 didReceiveResponse(loader
, response
, nullptr);
415 void ResourceLoader::didReceiveData(WebURLLoader
*, const char* data
, int length
, int encodedDataLength
)
417 ASSERT(m_state
!= Terminated
);
418 RELEASE_ASSERT(m_connectionState
== ConnectionStateReceivedResponse
|| m_connectionState
== ConnectionStateReceivingData
);
419 m_connectionState
= ConnectionStateReceivingData
;
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())
425 ASSERT(m_state
== Initialized
);
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);
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_fetcher
->didReceiveData(m_resource
, data
, length
, encodedDataLength
);
435 if (m_state
== Terminated
)
437 RELEASE_ASSERT(length
>= 0);
438 m_resource
->appendData(data
, length
);
441 void ResourceLoader::didFinishLoading(WebURLLoader
*, double finishTime
, int64_t encodedDataLength
)
443 RELEASE_ASSERT(m_connectionState
== ConnectionStateReceivedResponse
|| m_connectionState
== ConnectionStateReceivingData
);
444 m_connectionState
= ConnectionStateFinishedLoading
;
445 if (m_state
!= Initialized
)
447 ASSERT(m_state
!= Terminated
);
448 WTF_LOG(ResourceLoading
, "Received '%s'.", m_resource
->url().string().latin1().data());
450 RefPtrWillBeRawPtr
<ResourceLoader
> protect(this);
451 ResourcePtr
<Resource
> protectResource(m_resource
);
453 m_resource
->setLoadFinishTime(finishTime
);
454 didFinishLoadingOnePart(finishTime
, encodedDataLength
);
455 if (m_state
== Terminated
)
457 m_resource
->finish();
459 // If the load has been cancelled by a delegate in response to didFinishLoad(), do not release
460 // the resources a second time, they have been released by cancel.
461 if (m_state
== Terminated
)
466 void ResourceLoader::didFail(WebURLLoader
*, const WebURLError
& error
)
468 m_connectionState
= ConnectionStateFailed
;
469 ASSERT(m_state
!= Terminated
);
470 WTF_LOG(ResourceLoading
, "Failed to load '%s'.\n", m_resource
->url().string().latin1().data());
472 RefPtrWillBeRawPtr
<ResourceLoader
> protect(this);
473 RefPtrWillBeRawPtr
<ResourceFetcher
> protectFetcher(m_fetcher
.get());
474 ResourcePtr
<Resource
> protectResource(m_resource
);
476 m_resource
->setResourceError(error
);
478 if (!m_notifiedLoadComplete
) {
479 m_notifiedLoadComplete
= true;
480 m_fetcher
->didFailLoading(m_resource
, error
);
482 if (m_state
== Terminated
)
485 m_resource
->error(Resource::LoadError
);
487 if (m_state
== Terminated
)
493 bool ResourceLoader::isLoadedBy(ResourceFetcher
* loader
) const
495 return m_fetcher
->isLoadedBy(loader
);
498 void ResourceLoader::requestSynchronously()
500 OwnPtr
<WebURLLoader
> loader
= adoptPtr(Platform::current()->createURLLoader());
503 // downloadToFile is not supported for synchronous requests.
504 ASSERT(!m_request
.downloadToFile());
506 RefPtrWillBeRawPtr
<ResourceLoader
> protect(this);
507 RefPtrWillBeRawPtr
<ResourceFetcher
> protectFetcher(m_fetcher
.get());
508 ResourcePtr
<Resource
> protectResource(m_resource
);
510 RELEASE_ASSERT(m_connectionState
== ConnectionStateNew
);
511 m_connectionState
= ConnectionStateStarted
;
513 WrappedResourceRequest
requestIn(m_request
);
514 WebURLResponse responseOut
;
515 responseOut
.initialize();
516 WebURLError errorOut
;
518 loader
->loadSynchronously(requestIn
, responseOut
, errorOut
, dataOut
);
519 if (errorOut
.reason
) {
520 if (m_state
== Terminated
) {
521 // A message dispatched while synchronously fetching the resource
522 // can bring about the cancellation of this load.
526 didFail(0, errorOut
);
529 didReceiveResponse(0, responseOut
);
530 if (m_state
== Terminated
)
532 RefPtr
<ResourceLoadInfo
> resourceLoadInfo
= responseOut
.toResourceResponse().resourceLoadInfo();
533 int64_t encodedDataLength
= resourceLoadInfo
? resourceLoadInfo
->encodedDataLength
: WebURLLoaderClient::kUnknownEncodedDataLength
;
534 m_fetcher
->didReceiveData(m_resource
, dataOut
.data(), dataOut
.size(), encodedDataLength
);
535 m_resource
->setResourceBuffer(dataOut
);
536 didFinishLoading(0, monotonicallyIncreasingTime(), encodedDataLength
);
539 ResourceRequest
& ResourceLoader::applyOptions(ResourceRequest
& request
) const
541 request
.setAllowStoredCredentials(m_options
.allowCredentials
== AllowStoredCredentials
);