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