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