76583e8d9c799adc64106271bd23a576b69d953e
[WebKit-https.git] / Source / WebCore / loader / SubresourceLoader.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "SubresourceLoader.h"
31
32 #include "CachedResourceLoader.h"
33 #include "Document.h"
34 #include "DocumentLoader.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "Logging.h"
38 #include "MemoryCache.h"
39 #include "SecurityOrigin.h"
40 #include "SecurityPolicy.h"
41 #include <wtf/RefCountedLeakCounter.h>
42 #include <wtf/StdLibExtras.h>
43 #include <wtf/text/CString.h>
44
45 namespace WebCore {
46
47 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
48
49 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
50     : m_cachedResourceLoader(cachedResourceLoader)
51     , m_resource(resource)
52 {
53     m_cachedResourceLoader->incrementRequestCount(m_resource);
54 }
55
56 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
57 {
58     m_cachedResourceLoader->decrementRequestCount(m_resource);
59 }
60
61 SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options)
62     : ResourceLoader(frame, options)
63     , m_resource(resource)
64     , m_document(frame->document())
65     , m_loadingMultipartContent(false)
66     , m_state(Uninitialized)
67     , m_requestCountTracker(adoptPtr(new RequestCountTracker(frame->document()->cachedResourceLoader(), resource)))
68 {
69 #ifndef NDEBUG
70     subresourceLoaderCounter.increment();
71 #endif
72 }
73
74 SubresourceLoader::~SubresourceLoader()
75 {
76     ASSERT(m_state != Initialized);
77     ASSERT(!m_document);
78     ASSERT(reachedTerminalState());
79 #ifndef NDEBUG
80     subresourceLoaderCounter.decrement();
81 #endif
82 }
83
84 PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
85 {
86     if (!frame)
87         return 0;
88
89     FrameLoader* frameLoader = frame->loader();
90     if (options.securityCheck == DoSecurityCheck && (frameLoader->state() == FrameStateProvisional || !frameLoader->activeDocumentLoader() || frameLoader->activeDocumentLoader()->isStopping()))
91         return 0;
92
93     ResourceRequest newRequest = request;
94
95     // Note: We skip the Content-Security-Policy check here because we check
96     // the Content-Security-Policy at the CachedResourceLoader layer so we can
97     // handle different resource types differently.
98
99     String outgoingReferrer;
100     String outgoingOrigin;
101     if (request.httpReferrer().isNull()) {
102         outgoingReferrer = frameLoader->outgoingReferrer();
103         outgoingOrigin = frameLoader->outgoingOrigin();
104     } else {
105         outgoingReferrer = request.httpReferrer();
106         outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
107     }
108
109     outgoingReferrer = SecurityPolicy::generateReferrerHeader(frame->document()->referrerPolicy(), request.url(), outgoingReferrer);
110     if (outgoingReferrer.isEmpty())
111         newRequest.clearHTTPReferrer();
112     else if (!request.httpReferrer())
113         newRequest.setHTTPReferrer(outgoingReferrer);
114     FrameLoader::addHTTPOriginIfNeeded(newRequest, outgoingOrigin);
115
116     frameLoader->addExtraFieldsToSubresourceRequest(newRequest);
117
118     RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
119     if (!subloader->init(newRequest))
120         return 0;
121     return subloader.release();
122 }
123
124 void SubresourceLoader::cancelIfNotFinishing()
125 {
126     if (m_state != Initialized)
127         return;
128
129     ResourceLoader::cancel();
130 }
131
132 bool SubresourceLoader::init(const ResourceRequest& request)
133 {
134     if (!ResourceLoader::init(request))
135         return false;
136
137     ASSERT(!reachedTerminalState());
138     m_state = Initialized;
139     m_documentLoader->addSubresourceLoader(this);
140     return true;
141 }
142
143 void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
144 {
145     // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
146     KURL previousURL = request().url();
147     
148     ResourceLoader::willSendRequest(newRequest, redirectResponse);
149     if (!previousURL.isNull() && !newRequest.isNull() && previousURL != newRequest.url()) {
150         if (!m_document->cachedResourceLoader()->canRequest(m_resource->type(), newRequest.url())) {
151             cancel();
152             return;
153         }
154         m_resource->willSendRequest(newRequest, redirectResponse);
155     }
156 }
157
158 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
159 {
160     ASSERT(m_state == Initialized);
161     RefPtr<SubresourceLoader> protect(this);
162     m_resource->didSendData(bytesSent, totalBytesToBeSent);
163 }
164
165 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
166 {
167     ASSERT(!response.isNull());
168     ASSERT(m_state == Initialized);
169
170     // Reference the object in this method since the additional processing can do
171     // anything including removing the last reference to this object; one example of this is 3266216.
172     RefPtr<SubresourceLoader> protect(this);
173
174     if (m_resource->resourceToRevalidate()) {
175         if (response.httpStatusCode() == 304) {
176             // 304 Not modified / Use local copy
177             // Existing resource is ok, just use it updating the expiration time.
178             memoryCache()->revalidationSucceeded(m_resource, response);
179             if (!reachedTerminalState())
180                 ResourceLoader::didReceiveResponse(response);
181             return;
182         }
183         // Did not get 304 response, continue as a regular resource load.
184         memoryCache()->revalidationFailed(m_resource);
185     }
186
187     m_resource->setResponse(response);
188     if (reachedTerminalState())
189         return;
190     ResourceLoader::didReceiveResponse(response);
191
192     if (response.isMultipart()) {
193         m_loadingMultipartContent = true;
194
195         // We don't count multiParts in a CachedResourceLoader's request count
196         m_requestCountTracker.clear();
197         if (!m_resource->isImage()) {
198             cancel();
199             return;
200         }
201     }
202
203     RefPtr<SharedBuffer> buffer = resourceData();
204     if (m_loadingMultipartContent && buffer && buffer->size()) {
205         sendDataToResource(buffer->data(), buffer->size());
206         clearResourceData();
207         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.        
208         // After the first multipart section is complete, signal to delegates that this load is "finished" 
209         m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
210         didFinishLoadingOnePart(0);
211     }
212 }
213
214 void SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
215 {
216     ASSERT(!m_resource->resourceToRevalidate());
217     ASSERT(!m_resource->errorOccurred());
218     ASSERT(m_state == Initialized);
219     // Reference the object in this method since the additional processing can do
220     // anything including removing the last reference to this object; one example of this is 3266216.
221     RefPtr<SubresourceLoader> protect(this);
222     ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce);
223
224     if (errorLoadingResource() || m_loadingMultipartContent)
225         return;
226
227     sendDataToResource(data, length);
228 }
229
230 bool SubresourceLoader::errorLoadingResource()
231 {
232     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
233         return false;
234
235     m_resource->error(CachedResource::LoadError);
236     m_state = Finishing;
237     cancel();
238     return true;
239 }
240
241 void SubresourceLoader::sendDataToResource(const char* data, int length)
242 {
243     // There are two cases where we might need to create our own SharedBuffer instead of copying the one in ResourceLoader. 
244     // (1) Multipart content: The loader delivers the data in a multipart section all at once, then sends eof. 
245     //     The resource data will change as the next part is loaded, so we need to make a copy. 
246     // (2) Our client requested that the data not be buffered at the ResourceLoader level via ResourceLoaderOptions. In this case, 
247     //     ResourceLoader::resourceData() will be null. However, unlike the multipart case, we don't want to tell the CachedResource 
248     //     that all data has been received yet. 
249     if (m_loadingMultipartContent || !resourceData()) { 
250         RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, length); 
251         m_resource->data(copiedData.release(), m_loadingMultipartContent); 
252     } else 
253         m_resource->data(resourceData(), false);
254 }
255
256 void SubresourceLoader::didReceiveCachedMetadata(const char* data, int length)
257 {
258     ASSERT(m_state == Initialized);
259     ASSERT(!m_resource->resourceToRevalidate());
260     m_resource->setSerializedCachedMetadata(data, length);
261 }
262
263 void SubresourceLoader::didFinishLoading(double finishTime)
264 {
265     if (m_state != Initialized)
266         return;
267     ASSERT(!reachedTerminalState());
268     ASSERT(!m_resource->resourceToRevalidate());
269     ASSERT(!m_resource->errorOccurred());
270     LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
271
272     RefPtr<SubresourceLoader> protect(this);
273     CachedResourceHandle<CachedResource> protectResource(m_resource);
274     m_state = Finishing;
275     m_resource->setLoadFinishTime(finishTime);
276     m_resource->data(resourceData(), true);
277     m_resource->finish();
278     ResourceLoader::didFinishLoading(finishTime);
279 }
280
281 void SubresourceLoader::didFail(const ResourceError& error)
282 {
283     if (m_state != Initialized)
284         return;
285     ASSERT(!reachedTerminalState());
286     ASSERT(!m_resource->resourceToRevalidate());
287     LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
288
289     RefPtr<SubresourceLoader> protect(this);
290     CachedResourceHandle<CachedResource> protectResource(m_resource);
291     m_state = Finishing;
292     m_resource->error(CachedResource::LoadError);
293     if (!m_resource->isPreloaded())
294         memoryCache()->remove(m_resource);
295     ResourceLoader::didFail(error);
296 }
297
298 void SubresourceLoader::willCancel(const ResourceError&)
299 {
300     if (m_state != Initialized)
301         return;
302     ASSERT(!reachedTerminalState());
303     LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
304
305     RefPtr<SubresourceLoader> protect(this);
306     m_state = Finishing;
307     if (m_resource->resourceToRevalidate())
308         memoryCache()->revalidationFailed(m_resource);
309     memoryCache()->remove(m_resource);
310 }
311
312 void SubresourceLoader::releaseResources()
313 {
314     ASSERT(!reachedTerminalState());
315     if (m_state != Uninitialized) {
316         m_requestCountTracker.clear();
317         m_document->cachedResourceLoader()->loadDone();
318         if (reachedTerminalState())
319             return;
320         m_resource->stopLoading();
321         m_documentLoader->removeSubresourceLoader(this);
322     }
323     m_document = 0;
324     ResourceLoader::releaseResources();
325 }
326
327 }