• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
RequestCountTracker(ResourceLoaderHost * host,Resource * resource)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 
~RequestCountTracker()59 ResourceLoader::RequestCountTracker::~RequestCountTracker()
60 {
61     m_host->decrementRequestCount(m_resource);
62 }
63 
RequestCountTracker(const RequestCountTracker & other)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 
create(ResourceLoaderHost * host,Resource * resource,const ResourceRequest & request,const ResourceLoaderOptions & options)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 
ResourceLoader(ResourceLoaderHost * host,Resource * resource,const ResourceLoaderOptions & options)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 
~ResourceLoader()90 ResourceLoader::~ResourceLoader()
91 {
92     ASSERT(m_state == Terminated);
93 }
94 
trace(Visitor * visitor)95 void ResourceLoader::trace(Visitor* visitor)
96 {
97     visitor->trace(m_host);
98     visitor->trace(m_resource);
99 }
100 
releaseResources()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 
init(const ResourceRequest & passedRequest)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 
start()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 
changeToSynchronous()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 
setDefersLoading(bool defers)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 
attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver)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 
didDownloadData(blink::WebURLLoader *,int length,int encodedDataLength)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 
didFinishLoadingOnePart(double finishTime,int64 encodedDataLength)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 
didChangePriority(ResourceLoadPriority loadPriority,int intraPriorityValue)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 
cancelIfNotFinishing()244 void ResourceLoader::cancelIfNotFinishing()
245 {
246     if (m_state != Initialized)
247         return;
248     cancel();
249 }
250 
cancel()251 void ResourceLoader::cancel()
252 {
253     cancel(ResourceError());
254 }
255 
cancel(const ResourceError & error)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 
willSendRequest(blink::WebURLLoader *,blink::WebURLRequest & passedRequest,const blink::WebURLResponse & passedRedirectResponse)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 
didReceiveCachedMetadata(blink::WebURLLoader *,const char * data,int length)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 
didSendData(blink::WebURLLoader *,unsigned long long bytesSent,unsigned long long totalBytesToBeSent)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 
responseNeedsAccessControlCheck() const338 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 
didReceiveResponse(blink::WebURLLoader *,const blink::WebURLResponse & response)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 
didReceiveData(blink::WebURLLoader *,const char * data,int length,int encodedDataLength)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 
didFinishLoading(blink::WebURLLoader *,double finishTime,int64 encodedDataLength)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 
didFail(blink::WebURLLoader *,const blink::WebURLError & error)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 
isLoadedBy(ResourceLoaderHost * loader) const491 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const
492 {
493     return m_host->isLoadedBy(loader);
494 }
495 
requestSynchronously()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 
applyOptions(ResourceRequest & request) const531 ResourceRequest& ResourceLoader::applyOptions(ResourceRequest& request) const
532 {
533     request.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials);
534     return request;
535 }
536 
537 }
538