2 * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "SubresourceLoader.h"
32 #include "CachedResourceLoader.h"
34 #include "DocumentLoader.h"
36 #include "FrameLoader.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>
47 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
49 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
50 : m_cachedResourceLoader(cachedResourceLoader)
51 , m_resource(resource)
53 m_cachedResourceLoader->incrementRequestCount(m_resource);
56 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
58 m_cachedResourceLoader->decrementRequestCount(m_resource);
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)))
70 subresourceLoaderCounter.increment();
74 SubresourceLoader::~SubresourceLoader()
76 ASSERT(m_state != Initialized);
78 ASSERT(reachedTerminalState());
80 subresourceLoaderCounter.decrement();
84 PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
89 FrameLoader* frameLoader = frame->loader();
90 if (options.securityCheck == DoSecurityCheck && (frameLoader->state() == FrameStateProvisional || !frameLoader->activeDocumentLoader() || frameLoader->activeDocumentLoader()->isStopping()))
93 ResourceRequest newRequest = request;
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.
99 String outgoingReferrer;
100 String outgoingOrigin;
101 if (request.httpReferrer().isNull()) {
102 outgoingReferrer = frameLoader->outgoingReferrer();
103 outgoingOrigin = frameLoader->outgoingOrigin();
105 outgoingReferrer = request.httpReferrer();
106 outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
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);
116 frameLoader->addExtraFieldsToSubresourceRequest(newRequest);
118 RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
119 if (!subloader->init(newRequest))
121 return subloader.release();
124 void SubresourceLoader::cancelIfNotFinishing()
126 if (m_state != Initialized)
129 ResourceLoader::cancel();
132 bool SubresourceLoader::init(const ResourceRequest& request)
134 if (!ResourceLoader::init(request))
137 ASSERT(!reachedTerminalState());
138 m_state = Initialized;
139 m_documentLoader->addSubresourceLoader(this);
143 void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
145 // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
146 KURL previousURL = request().url();
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())) {
154 m_resource->willSendRequest(newRequest, redirectResponse);
158 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
160 ASSERT(m_state == Initialized);
161 RefPtr<SubresourceLoader> protect(this);
162 m_resource->didSendData(bytesSent, totalBytesToBeSent);
165 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
167 ASSERT(!response.isNull());
168 ASSERT(m_state == Initialized);
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);
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);
183 // Did not get 304 response, continue as a regular resource load.
184 memoryCache()->revalidationFailed(m_resource);
187 m_resource->setResponse(response);
188 if (reachedTerminalState())
190 ResourceLoader::didReceiveResponse(response);
192 if (response.isMultipart()) {
193 m_loadingMultipartContent = true;
195 // We don't count multiParts in a CachedResourceLoader's request count
196 m_requestCountTracker.clear();
197 if (!m_resource->isImage()) {
203 RefPtr<SharedBuffer> buffer = resourceData();
204 if (m_loadingMultipartContent && buffer && buffer->size()) {
205 sendDataToResource(buffer->data(), buffer->size());
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);
214 void SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
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);
224 if (errorLoadingResource() || m_loadingMultipartContent)
227 sendDataToResource(data, length);
230 bool SubresourceLoader::errorLoadingResource()
232 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
235 m_resource->error(CachedResource::LoadError);
241 void SubresourceLoader::sendDataToResource(const char* data, int length)
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);
253 m_resource->data(resourceData(), false);
256 void SubresourceLoader::didReceiveCachedMetadata(const char* data, int length)
258 ASSERT(m_state == Initialized);
259 ASSERT(!m_resource->resourceToRevalidate());
260 m_resource->setSerializedCachedMetadata(data, length);
263 void SubresourceLoader::didFinishLoading(double finishTime)
265 if (m_state != Initialized)
267 ASSERT(!reachedTerminalState());
268 ASSERT(!m_resource->resourceToRevalidate());
269 ASSERT(!m_resource->errorOccurred());
270 LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
272 RefPtr<SubresourceLoader> protect(this);
273 CachedResourceHandle<CachedResource> protectResource(m_resource);
275 m_resource->setLoadFinishTime(finishTime);
276 m_resource->data(resourceData(), true);
277 m_resource->finish();
278 ResourceLoader::didFinishLoading(finishTime);
281 void SubresourceLoader::didFail(const ResourceError& error)
283 if (m_state != Initialized)
285 ASSERT(!reachedTerminalState());
286 ASSERT(!m_resource->resourceToRevalidate());
287 LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
289 RefPtr<SubresourceLoader> protect(this);
290 CachedResourceHandle<CachedResource> protectResource(m_resource);
292 m_resource->error(CachedResource::LoadError);
293 if (!m_resource->isPreloaded())
294 memoryCache()->remove(m_resource);
295 ResourceLoader::didFail(error);
298 void SubresourceLoader::willCancel(const ResourceError&)
300 if (m_state != Initialized)
302 ASSERT(!reachedTerminalState());
303 LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
305 RefPtr<SubresourceLoader> protect(this);
307 if (m_resource->resourceToRevalidate())
308 memoryCache()->revalidationFailed(m_resource);
309 memoryCache()->remove(m_resource);
312 void SubresourceLoader::releaseResources()
314 ASSERT(!reachedTerminalState());
315 if (m_state != Uninitialized) {
316 m_requestCountTracker.clear();
317 m_document->cachedResourceLoader()->loadDone();
318 if (reachedTerminalState())
320 m_resource->stopLoading();
321 m_documentLoader->removeSubresourceLoader(this);
324 ResourceLoader::releaseResources();