Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / fetch / ResourceLoader.cpp
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 WebCore {
51
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
59 ResourceLoader::RequestCountTracker::~RequestCountTracker()
60 {
61     m_host->decrementRequestCount(m_resource);
62 }
63
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
71 PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
72 {
73     RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options)));
74     loader->init(request);
75     return loader.release();
76 }
77
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
90 ResourceLoader::~ResourceLoader()
91 {
92     ASSERT(m_state == Terminated);
93 }
94
95 void ResourceLoader::releaseResources()
96 {
97     ASSERT(m_state != Terminated);
98     ASSERT(m_notifiedLoadComplete);
99     m_requestCountTracker.clear();
100     m_host->didLoadResource(m_resource);
101     if (m_state == Terminated)
102         return;
103     m_resource->clearLoader();
104     m_host->willTerminateResourceLoader(this);
105
106     ASSERT(m_state != Terminated);
107
108     // It's possible that when we release the loader, it will be
109     // deallocated and release the last reference to this object.
110     // We need to retain to avoid accessing the object after it
111     // has been deallocated and also to avoid reentering this method.
112     RefPtr<ResourceLoader> protector(this);
113
114     m_host.clear();
115     m_state = Terminated;
116
117     if (m_loader) {
118         m_loader->cancel();
119         m_loader.clear();
120     }
121
122     m_deferredRequest = ResourceRequest();
123 }
124
125 void ResourceLoader::init(const ResourceRequest& passedRequest)
126 {
127     ResourceRequest request(passedRequest);
128     m_host->willSendRequest(m_resource->identifier(), request, ResourceResponse(), m_options.initiatorInfo);
129     request.setReportLoadTiming(true);
130     ASSERT(m_state != Terminated);
131     ASSERT(!request.isNull());
132     m_originalRequest = m_request = applyOptions(request);
133     m_resource->updateRequest(request);
134     m_host->didInitializeResourceLoader(this);
135 }
136
137 void ResourceLoader::start()
138 {
139     ASSERT(!m_loader);
140     ASSERT(!m_request.isNull());
141     ASSERT(m_deferredRequest.isNull());
142
143     m_host->willStartLoadingResource(m_resource, m_request);
144
145     if (m_options.synchronousPolicy == RequestSynchronously) {
146         requestSynchronously();
147         return;
148     }
149
150     if (m_defersLoading) {
151         m_deferredRequest = m_request;
152         return;
153     }
154
155     if (m_state == Terminated)
156         return;
157
158     RELEASE_ASSERT(m_connectionState == ConnectionStateNew);
159     m_connectionState = ConnectionStateStarted;
160
161     m_loader = adoptPtr(blink::Platform::current()->createURLLoader());
162     ASSERT(m_loader);
163     blink::WrappedResourceRequest wrappedRequest(m_request);
164     m_loader->loadAsynchronously(wrappedRequest, this);
165 }
166
167 void ResourceLoader::changeToSynchronous()
168 {
169     ASSERT(m_options.synchronousPolicy == RequestAsynchronously);
170     ASSERT(m_loader);
171     m_loader->cancel();
172     m_loader.clear();
173     m_request.setPriority(ResourceLoadPriorityHighest);
174     m_connectionState = ConnectionStateNew;
175     requestSynchronously();
176 }
177
178 void ResourceLoader::setDefersLoading(bool defers)
179 {
180     m_defersLoading = defers;
181     if (m_loader)
182         m_loader->setDefersLoading(defers);
183     if (!defers && !m_deferredRequest.isNull()) {
184         m_request = applyOptions(m_deferredRequest);
185         m_deferredRequest = ResourceRequest();
186         start();
187     }
188 }
189
190 void ResourceLoader::attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver)
191 {
192     if (m_loader) {
193         // The implementor of the WebURLLoader assumes ownership of the
194         // threaded data receiver if it signals that it got successfully
195         // attached.
196         blink::WebThreadedDataReceiver* rawThreadedDataReceiver = threadedDataReceiver.leakPtr();
197         if (!m_loader->attachThreadedDataReceiver(rawThreadedDataReceiver))
198             delete rawThreadedDataReceiver;
199     }
200 }
201
202 void ResourceLoader::didDownloadData(blink::WebURLLoader*, int length, int encodedDataLength)
203 {
204     RefPtr<ResourceLoader> protect(this);
205     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse);
206     m_host->didDownloadData(m_resource, length, encodedDataLength);
207     m_resource->didDownloadData(length);
208 }
209
210 void ResourceLoader::didFinishLoadingOnePart(double finishTime, int64 encodedDataLength)
211 {
212     // If load has been cancelled after finishing (which could happen with a
213     // JavaScript that changes the window location), do nothing.
214     if (m_state == Terminated)
215         return;
216
217     if (m_notifiedLoadComplete)
218         return;
219     m_notifiedLoadComplete = true;
220     m_host->didFinishLoading(m_resource, finishTime, encodedDataLength);
221 }
222
223 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority, int intraPriorityValue)
224 {
225     if (m_loader) {
226         m_host->didChangeLoadingPriority(m_resource, loadPriority, intraPriorityValue);
227         m_loader->didChangePriority(static_cast<blink::WebURLRequest::Priority>(loadPriority), intraPriorityValue);
228     }
229 }
230
231 void ResourceLoader::cancelIfNotFinishing()
232 {
233     if (m_state != Initialized)
234         return;
235     cancel();
236 }
237
238 void ResourceLoader::cancel()
239 {
240     cancel(ResourceError());
241 }
242
243 void ResourceLoader::cancel(const ResourceError& error)
244 {
245     // If the load has already completed - succeeded, failed, or previously cancelled - do nothing.
246     if (m_state == Terminated)
247         return;
248     if (m_state == Finishing) {
249         releaseResources();
250         return;
251     }
252
253     ResourceError nonNullError = error.isNull() ? ResourceError::cancelledError(m_request.url()) : error;
254
255     // This function calls out to clients at several points that might do
256     // something that causes the last reference to this object to go away.
257     RefPtr<ResourceLoader> protector(this);
258
259     WTF_LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
260     if (m_state == Initialized)
261         m_state = Finishing;
262     m_resource->setResourceError(nonNullError);
263
264     if (m_loader) {
265         m_connectionState = ConnectionStateCanceled;
266         m_loader->cancel();
267         m_loader.clear();
268     }
269
270     if (!m_notifiedLoadComplete) {
271         m_notifiedLoadComplete = true;
272         m_host->didFailLoading(m_resource, nonNullError);
273     }
274
275     if (m_state == Finishing)
276         m_resource->error(Resource::LoadError);
277     if (m_state != Terminated)
278         releaseResources();
279 }
280
281 void ResourceLoader::willSendRequest(blink::WebURLLoader*, blink::WebURLRequest& passedRequest, const blink::WebURLResponse& passedRedirectResponse)
282 {
283     RefPtr<ResourceLoader> protect(this);
284
285     ResourceRequest& request(applyOptions(passedRequest.toMutableResourceRequest()));
286     ASSERT(!request.isNull());
287     const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceResponse());
288     ASSERT(!redirectResponse.isNull());
289     if (!m_host->canAccessRedirect(m_resource, request, redirectResponse, m_options)) {
290         cancel();
291         return;
292     }
293
294     applyOptions(request); // canAccessRedirect() can modify m_options so we should re-apply it.
295     m_host->redirectReceived(m_resource, redirectResponse);
296     m_resource->willSendRequest(request, redirectResponse);
297     if (request.isNull() || m_state == Terminated)
298         return;
299
300     m_host->willSendRequest(m_resource->identifier(), request, redirectResponse, m_options.initiatorInfo);
301     request.setReportLoadTiming(true);
302     ASSERT(!request.isNull());
303     m_resource->updateRequest(request);
304     m_request = request;
305 }
306
307 void ResourceLoader::didReceiveCachedMetadata(blink::WebURLLoader*, const char* data, int length)
308 {
309     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData);
310     ASSERT(m_state == Initialized);
311     m_resource->setSerializedCachedMetadata(data, length);
312 }
313
314 void ResourceLoader::didSendData(blink::WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
315 {
316     ASSERT(m_state == Initialized);
317     RefPtr<ResourceLoader> protect(this);
318     m_resource->didSendData(bytesSent, totalBytesToBeSent);
319 }
320
321 bool ResourceLoader::responseNeedsAccessControlCheck() const
322 {
323     // If the fetch was (potentially) CORS enabled, an access control check of the response is required.
324     return m_options.corsEnabled == IsCORSEnabled;
325 }
326
327 void ResourceLoader::didReceiveResponse(blink::WebURLLoader*, const blink::WebURLResponse& response)
328 {
329     ASSERT(!response.isNull());
330     ASSERT(m_state == Initialized);
331
332     bool isMultipartPayload = response.isMultipartPayload();
333     bool isValidStateTransition = (m_connectionState == ConnectionStateStarted || m_connectionState == ConnectionStateReceivedResponse);
334     // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved.
335     RELEASE_ASSERT(isMultipartPayload || isValidStateTransition);
336     m_connectionState = ConnectionStateReceivedResponse;
337
338     const ResourceResponse& resourceResponse = response.toResourceResponse();
339
340     if (responseNeedsAccessControlCheck()) {
341         // If the response successfully validated a cached resource, perform
342         // the access control with respect to it. Need to do this right here
343         // before the resource switches clients over to that validated resource.
344         Resource* resource = m_resource;
345         if (resource->isCacheValidator() && resourceResponse.httpStatusCode() == 304)
346             resource = m_resource->resourceToRevalidate();
347         else
348             m_resource->setResponse(resourceResponse);
349         if (!m_host->canAccessResource(resource, m_options.securityOrigin.get(), response.url())) {
350             m_host->didReceiveResponse(m_resource, resourceResponse);
351             cancel();
352             return;
353         }
354     }
355
356     // Reference the object in this method since the additional processing can do
357     // anything including removing the last reference to this object.
358     RefPtr<ResourceLoader> protect(this);
359     m_resource->responseReceived(resourceResponse);
360     if (m_state == Terminated)
361         return;
362
363     m_host->didReceiveResponse(m_resource, resourceResponse);
364
365     if (response.toResourceResponse().isMultipart()) {
366         // We don't count multiParts in a ResourceFetcher's request count
367         m_requestCountTracker.clear();
368         if (!m_resource->isImage()) {
369             cancel();
370             return;
371         }
372     } else if (isMultipartPayload) {
373         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
374         // After the first multipart section is complete, signal to delegates that this load is "finished"
375         m_host->subresourceLoaderFinishedLoadingOnePart(this);
376         didFinishLoadingOnePart(0, blink::WebURLLoaderClient::kUnknownEncodedDataLength);
377     }
378
379     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
380         return;
381     m_state = Finishing;
382
383     if (!m_notifiedLoadComplete) {
384         m_notifiedLoadComplete = true;
385         m_host->didFailLoading(m_resource, ResourceError::cancelledError(m_request.url()));
386     }
387
388     m_resource->error(Resource::LoadError);
389     cancel();
390 }
391
392 void ResourceLoader::didReceiveData(blink::WebURLLoader*, const char* data, int length, int encodedDataLength)
393 {
394     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData);
395     m_connectionState = ConnectionStateReceivingData;
396
397     // It is possible to receive data on uninitialized resources if it had an error status code, and we are running a nested message
398     // loop. When this occurs, ignoring the data is the correct action.
399     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
400         return;
401     ASSERT(m_state == Initialized);
402
403     // Reference the object in this method since the additional processing can do
404     // anything including removing the last reference to this object.
405     RefPtr<ResourceLoader> protect(this);
406
407     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
408     // However, with today's computers and networking speeds, this won't happen in practice.
409     // Could be an issue with a giant local file.
410     m_host->didReceiveData(m_resource, data, length, encodedDataLength);
411     m_resource->appendData(data, length);
412 }
413
414 void ResourceLoader::didFinishLoading(blink::WebURLLoader*, double finishTime, int64 encodedDataLength)
415 {
416     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData);
417     m_connectionState = ConnectionStateFinishedLoading;
418     if (m_state != Initialized)
419         return;
420     ASSERT(m_state != Terminated);
421     WTF_LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
422
423     RefPtr<ResourceLoader> protect(this);
424     ResourcePtr<Resource> protectResource(m_resource);
425     m_state = Finishing;
426     didFinishLoadingOnePart(finishTime, encodedDataLength);
427     m_resource->finish(finishTime);
428
429     // If the load has been cancelled by a delegate in response to didFinishLoad(), do not release
430     // the resources a second time, they have been released by cancel.
431     if (m_state == Terminated)
432         return;
433     releaseResources();
434 }
435
436 void ResourceLoader::didFail(blink::WebURLLoader*, const blink::WebURLError& error)
437 {
438     m_connectionState = ConnectionStateFailed;
439     ASSERT(m_state != Terminated);
440     WTF_LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
441
442     RefPtr<ResourceLoader> protect(this);
443     RefPtrWillBeRawPtr<ResourceLoaderHost> protectHost(m_host.get());
444     ResourcePtr<Resource> protectResource(m_resource);
445     m_state = Finishing;
446     m_resource->setResourceError(error);
447
448     if (!m_notifiedLoadComplete) {
449         m_notifiedLoadComplete = true;
450         m_host->didFailLoading(m_resource, error);
451     }
452
453     m_resource->error(Resource::LoadError);
454
455     if (m_state == Terminated)
456         return;
457
458     releaseResources();
459 }
460
461 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const
462 {
463     return m_host->isLoadedBy(loader);
464 }
465
466 void ResourceLoader::requestSynchronously()
467 {
468     OwnPtr<blink::WebURLLoader> loader = adoptPtr(blink::Platform::current()->createURLLoader());
469     ASSERT(loader);
470
471     // downloadToFile is not supported for synchronous requests.
472     ASSERT(!m_request.downloadToFile());
473
474     RefPtr<ResourceLoader> protect(this);
475     RefPtrWillBeRawPtr<ResourceLoaderHost> protectHost(m_host.get());
476     ResourcePtr<Resource> protectResource(m_resource);
477
478     RELEASE_ASSERT(m_connectionState == ConnectionStateNew);
479     m_connectionState = ConnectionStateStarted;
480
481     blink::WrappedResourceRequest requestIn(m_request);
482     blink::WebURLResponse responseOut;
483     responseOut.initialize();
484     blink::WebURLError errorOut;
485     blink::WebData dataOut;
486     loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut);
487     if (errorOut.reason) {
488         didFail(0, errorOut);
489         return;
490     }
491     didReceiveResponse(0, responseOut);
492     if (m_state == Terminated)
493         return;
494     RefPtr<ResourceLoadInfo> resourceLoadInfo = responseOut.toResourceResponse().resourceLoadInfo();
495     int64 encodedDataLength = resourceLoadInfo ? resourceLoadInfo->encodedDataLength : blink::WebURLLoaderClient::kUnknownEncodedDataLength;
496     m_host->didReceiveData(m_resource, dataOut.data(), dataOut.size(), encodedDataLength);
497     m_resource->setResourceBuffer(dataOut);
498     didFinishLoading(0, monotonicallyIncreasingTime(), encodedDataLength);
499 }
500
501 ResourceRequest& ResourceLoader::applyOptions(ResourceRequest& request) const
502 {
503     request.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials);
504     return request;
505 }
506
507 }