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 "Document.h"
33 #include "DocumentLoader.h"
34 #include "Frame.h"
35 #include "FrameLoader.h"
36 #include "ResourceHandle.h"
37 #include "SecurityOrigin.h"
38 #include "SubresourceLoaderClient.h"
39 #include <wtf/RefCountedLeakCounter.h>
40 
41 namespace WebCore {
42 
43 #ifndef NDEBUG
44 static WTF::RefCountedLeakCounter subresourceLoaderCounter("SubresourceLoader");
45 #endif
46 
SubresourceLoader(Frame * frame,SubresourceLoaderClient * client,bool sendResourceLoadCallbacks,bool shouldContentSniff)47 SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* client, bool sendResourceLoadCallbacks, bool shouldContentSniff)
48     : ResourceLoader(frame, sendResourceLoadCallbacks, shouldContentSniff)
49     , m_client(client)
50     , m_loadingMultipartContent(false)
51 {
52 #ifndef NDEBUG
53     subresourceLoaderCounter.increment();
54 #endif
55 }
56 
~SubresourceLoader()57 SubresourceLoader::~SubresourceLoader()
58 {
59 #ifndef NDEBUG
60     subresourceLoaderCounter.decrement();
61 #endif
62 }
63 
create(Frame * frame,SubresourceLoaderClient * client,const ResourceRequest & request,SecurityCheckPolicy securityCheck,bool sendResourceLoadCallbacks,bool shouldContentSniff,const String & optionalOutgoingReferrer)64 PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks, bool shouldContentSniff, const String& optionalOutgoingReferrer)
65 {
66     if (!frame)
67         return 0;
68 
69     FrameLoader* fl = frame->loader();
70     if (securityCheck == DoSecurityCheck && (fl->state() == FrameStateProvisional || !fl->activeDocumentLoader() || fl->activeDocumentLoader()->isStopping()))
71         return 0;
72 
73     ResourceRequest newRequest = request;
74 
75     if (securityCheck == DoSecurityCheck && !frame->document()->securityOrigin()->canDisplay(request.url())) {
76         FrameLoader::reportLocalLoadFailed(frame, request.url().string());
77         return 0;
78     }
79 
80     // Note: We skip the Content-Security-Policy check here because we check
81     // the Content-Security-Policy at the CachedResourceLoader layer so we can
82     // handle different resource types differently.
83 
84     String outgoingReferrer;
85     String outgoingOrigin;
86     if (optionalOutgoingReferrer.isNull()) {
87         outgoingReferrer = fl->outgoingReferrer();
88         outgoingOrigin = fl->outgoingOrigin();
89     } else {
90         outgoingReferrer = optionalOutgoingReferrer;
91         outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
92     }
93 
94     if (SecurityOrigin::shouldHideReferrer(request.url(), outgoingReferrer))
95         newRequest.clearHTTPReferrer();
96     else if (!request.httpReferrer())
97         newRequest.setHTTPReferrer(outgoingReferrer);
98     FrameLoader::addHTTPOriginIfNeeded(newRequest, outgoingOrigin);
99 
100     fl->addExtraFieldsToSubresourceRequest(newRequest);
101 
102     RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff)));
103     subloader->documentLoader()->addSubresourceLoader(subloader.get());
104     if (!subloader->init(newRequest))
105         return 0;
106 
107     return subloader.release();
108 }
109 
willSendRequest(ResourceRequest & newRequest,const ResourceResponse & redirectResponse)110 void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
111 {
112     // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
113     KURL previousURL = request().url();
114 
115     ResourceLoader::willSendRequest(newRequest, redirectResponse);
116     if (!previousURL.isNull() && !newRequest.isNull() && previousURL != newRequest.url() && m_client)
117         m_client->willSendRequest(this, newRequest, redirectResponse);
118 }
119 
didSendData(unsigned long long bytesSent,unsigned long long totalBytesToBeSent)120 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
121 {
122     RefPtr<SubresourceLoader> protect(this);
123 
124     if (m_client)
125         m_client->didSendData(this, bytesSent, totalBytesToBeSent);
126 }
127 
didReceiveResponse(const ResourceResponse & r)128 void SubresourceLoader::didReceiveResponse(const ResourceResponse& r)
129 {
130     ASSERT(!r.isNull());
131 
132     if (r.isMultipart())
133         m_loadingMultipartContent = true;
134 
135     // Reference the object in this method since the additional processing can do
136     // anything including removing the last reference to this object; one example of this is 3266216.
137     RefPtr<SubresourceLoader> protect(this);
138 
139     if (m_client)
140         m_client->didReceiveResponse(this, r);
141 
142     // The loader can cancel a load if it receives a multipart response for a non-image
143     if (reachedTerminalState())
144         return;
145     ResourceLoader::didReceiveResponse(r);
146 
147     RefPtr<SharedBuffer> buffer = resourceData();
148     if (m_loadingMultipartContent && buffer && buffer->size()) {
149         // Since a subresource loader does not load multipart sections progressively,
150         // deliver the previously received data to the loader all at once now.
151         // Then clear the data to make way for the next multipart section.
152         if (m_client)
153             m_client->didReceiveData(this, buffer->data(), buffer->size());
154         clearResourceData();
155 
156         // After the first multipart section is complete, signal to delegates that this load is "finished"
157         m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
158         didFinishLoadingOnePart(0);
159     }
160 }
161 
didReceiveData(const char * data,int length,long long encodedDataLength,bool allAtOnce)162 void SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
163 {
164     // Reference the object in this method since the additional processing can do
165     // anything including removing the last reference to this object; one example of this is 3266216.
166     RefPtr<SubresourceLoader> protect(this);
167 
168     ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce);
169 
170     // A subresource loader does not load multipart sections progressively.
171     // So don't deliver any data to the loader yet.
172     if (!m_loadingMultipartContent && m_client)
173         m_client->didReceiveData(this, data, length);
174 }
175 
didReceiveCachedMetadata(const char * data,int length)176 void SubresourceLoader::didReceiveCachedMetadata(const char* data, int length)
177 {
178     // Reference the object in this method since the additional processing can do
179     // anything including removing the last reference to this object; one example of this is 3266216.
180     RefPtr<SubresourceLoader> protect(this);
181 
182     if (m_client)
183         m_client->didReceiveCachedMetadata(this, data, length);
184 }
185 
didFinishLoading(double finishTime)186 void SubresourceLoader::didFinishLoading(double finishTime)
187 {
188     if (cancelled())
189         return;
190     ASSERT(!reachedTerminalState());
191 
192     // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
193     RefPtr<SubresourceLoader> protect(this);
194 
195     if (m_client)
196         m_client->didFinishLoading(this, finishTime);
197 
198     m_handle = 0;
199 
200     if (cancelled())
201         return;
202     m_documentLoader->removeSubresourceLoader(this);
203     ResourceLoader::didFinishLoading(finishTime);
204 }
205 
didFail(const ResourceError & error)206 void SubresourceLoader::didFail(const ResourceError& error)
207 {
208     if (cancelled())
209         return;
210     ASSERT(!reachedTerminalState());
211 
212     // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
213     RefPtr<SubresourceLoader> protect(this);
214 
215     if (m_client)
216         m_client->didFail(this, error);
217 
218     m_handle = 0;
219 
220     if (cancelled())
221         return;
222     m_documentLoader->removeSubresourceLoader(this);
223     ResourceLoader::didFail(error);
224 }
225 
didCancel(const ResourceError & error)226 void SubresourceLoader::didCancel(const ResourceError& error)
227 {
228     ASSERT(!reachedTerminalState());
229 
230     // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
231     RefPtr<SubresourceLoader> protect(this);
232 
233     if (m_client)
234         m_client->didFail(this, error);
235 
236     if (cancelled())
237         return;
238 
239     // The only way the subresource loader can reach the terminal state here is if the run loop spins when calling
240     // m_client->didFail. This should in theory not happen which is why the assert is here.
241     ASSERT(!reachedTerminalState());
242     if (reachedTerminalState())
243         return;
244 
245     m_documentLoader->removeSubresourceLoader(this);
246     ResourceLoader::didCancel(error);
247 }
248 
shouldUseCredentialStorage()249 bool SubresourceLoader::shouldUseCredentialStorage()
250 {
251     RefPtr<SubresourceLoader> protect(this);
252 
253     bool shouldUse;
254     if (m_client && m_client->getShouldUseCredentialStorage(this, shouldUse))
255         return shouldUse;
256 
257     return ResourceLoader::shouldUseCredentialStorage();
258 }
259 
didReceiveAuthenticationChallenge(const AuthenticationChallenge & challenge)260 void SubresourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
261 {
262     RefPtr<SubresourceLoader> protect(this);
263 
264     ASSERT(handle()->hasAuthenticationChallenge());
265 
266     if (m_client)
267         m_client->didReceiveAuthenticationChallenge(this, challenge);
268 
269     // The SubResourceLoaderClient may have cancelled this ResourceLoader in response to the challenge.
270     // If that's the case, don't call didReceiveAuthenticationChallenge.
271     if (reachedTerminalState())
272         return;
273 
274     // It may have also handled authentication on its own.
275     if (!handle()->hasAuthenticationChallenge())
276         return;
277 
278     ResourceLoader::didReceiveAuthenticationChallenge(challenge);
279 }
280 
receivedCancellation(const AuthenticationChallenge & challenge)281 void SubresourceLoader::receivedCancellation(const AuthenticationChallenge& challenge)
282 {
283     ASSERT(!reachedTerminalState());
284 
285     RefPtr<SubresourceLoader> protect(this);
286 
287     if (m_client)
288         m_client->receivedCancellation(this, challenge);
289 
290     ResourceLoader::receivedCancellation(challenge);
291 }
292 
293 
294 }
295