2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
25 #include "CachedResourceRequest.h"
27 #include "CachedImage.h"
28 #include "CachedResource.h"
29 #include "CachedResourceLoader.h"
32 #include "FrameLoader.h"
34 #include "MemoryCache.h"
35 #include "ResourceHandle.h"
36 #include "ResourceLoadScheduler.h"
37 #include "ResourceRequest.h"
38 #include "ResourceResponse.h"
39 #include "SharedBuffer.h"
40 #include "SubresourceLoader.h"
41 #include <wtf/Assertions.h>
42 #include <wtf/UnusedParam.h>
43 #include <wtf/Vector.h>
44 #include <wtf/text/CString.h>
48 #if PLATFORM(CHROMIUM)
49 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
52 case CachedResource::CSSStyleSheet:
54 case CachedResource::XSLStyleSheet:
56 return ResourceRequest::TargetIsStyleSheet;
57 case CachedResource::Script:
58 return ResourceRequest::TargetIsScript;
59 case CachedResource::FontResource:
60 return ResourceRequest::TargetIsFontResource;
61 case CachedResource::ImageResource:
62 return ResourceRequest::TargetIsImage;
63 case CachedResource::RawResource:
64 return ResourceRequest::TargetIsSubresource;
65 #if ENABLE(LINK_PREFETCH)
66 case CachedResource::LinkPrefetch:
67 return ResourceRequest::TargetIsPrefetch;
68 case CachedResource::LinkPrerender:
69 return ResourceRequest::TargetIsPrerender;
70 case CachedResource::LinkSubresource:
71 return ResourceRequest::TargetIsSubresource;
75 return ResourceRequest::TargetIsSubresource;
79 CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
80 : m_cachedResourceLoader(cachedResourceLoader)
81 , m_resource(resource)
87 CachedResourceRequest::~CachedResourceRequest()
91 PassOwnPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, const ResourceLoaderOptions& options)
93 OwnPtr<CachedResourceRequest> request = adoptPtr(new CachedResourceRequest(cachedResourceLoader, resource));
95 ResourceRequest resourceRequest = resource->resourceRequest();
96 #if PLATFORM(CHROMIUM)
97 resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type()));
100 if (!resource->accept().isEmpty())
101 resourceRequest.setHTTPAccept(resource->accept());
103 if (resource->isCacheValidator()) {
104 CachedResource* resourceToRevalidate = resource->resourceToRevalidate();
105 ASSERT(resourceToRevalidate->canUseCacheValidator());
106 ASSERT(resourceToRevalidate->isLoaded());
107 const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
108 const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
109 if (!lastModified.isEmpty() || !eTag.isEmpty()) {
110 ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
111 if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
112 resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
113 if (!lastModified.isEmpty())
114 resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
116 resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
120 #if ENABLE(LINK_PREFETCH)
121 if (resource->type() == CachedResource::LinkPrefetch || resource->type() == CachedResource::LinkPrerender || resource->type() == CachedResource::LinkSubresource)
122 resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
125 ResourceLoadPriority priority = resource->loadPriority();
126 resourceRequest.setPriority(priority);
128 RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(), request.get(), resourceRequest, priority, options);
129 if (!loader || loader->reachedTerminalState()) {
130 // FIXME: What if resources in other frames were waiting for this revalidation?
131 LOG(ResourceLoading, "Cannot start loading '%s'", resource->url().string().latin1().data());
132 if (resource->resourceToRevalidate())
133 memoryCache()->revalidationFailed(resource);
134 resource->error(CachedResource::LoadError);
135 return PassOwnPtr<CachedResourceRequest>(nullptr);
137 request->m_loader = loader;
138 return request.release();
141 void CachedResourceRequest::willSendRequest(SubresourceLoader* loader, ResourceRequest& req, const ResourceResponse&)
143 if (!m_cachedResourceLoader->canRequest(m_resource->type(), req.url())) {
147 m_resource->setRequestedFromNetworkingLayer();
150 void CachedResourceRequest::cancel()
157 void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader, double)
162 ASSERT(loader == m_loader.get());
163 ASSERT(!m_resource->resourceToRevalidate());
164 LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
166 // Prevent the document from being destroyed before we are done with
167 // the cachedResourceLoader that it will delete when the document gets deleted.
168 RefPtr<Document> protector(m_cachedResourceLoader->document());
170 m_cachedResourceLoader->decrementRequestCount(m_resource);
173 // If we got a 4xx response, we're pretending to have received a network
174 // error, so we can't send the successful data() and finish() callbacks.
175 if (!m_resource->errorOccurred()) {
176 m_cachedResourceLoader->loadFinishing();
177 m_resource->data(loader->resourceData(), true);
178 if (!m_resource->errorOccurred())
179 m_resource->finish();
184 void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError& error)
186 if (m_finishing || !m_loader)
189 bool cancelled = error.isCancellation();
190 LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().string().latin1().data(), cancelled);
192 // Prevent the document from being destroyed before we are done with
193 // the cachedResourceLoader that it will delete when the document gets deleted.
194 RefPtr<Document> protector(m_cachedResourceLoader->document());
196 m_cachedResourceLoader->decrementRequestCount(m_resource);
198 m_loader->clearClient();
200 if (m_resource->resourceToRevalidate())
201 memoryCache()->revalidationFailed(m_resource);
204 m_cachedResourceLoader->loadFinishing();
205 m_resource->error(CachedResource::LoadError);
208 if (cancelled || !m_resource->isPreloaded())
209 memoryCache()->remove(m_resource);
214 void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
216 ASSERT(loader == m_loader.get());
217 if (m_resource->isCacheValidator()) {
218 if (response.httpStatusCode() == 304) {
219 // 304 Not modified / Use local copy
220 loader->clearClient();
221 RefPtr<Document> protector(m_cachedResourceLoader->document());
222 m_cachedResourceLoader->decrementRequestCount(m_resource);
225 // Existing resource is ok, just use it updating the expiration time.
226 memoryCache()->revalidationSucceeded(m_resource, response);
228 if (m_cachedResourceLoader->frame())
229 m_cachedResourceLoader->frame()->loader()->checkCompleted();
234 // Did not get 304 response, continue as a regular resource load.
235 memoryCache()->revalidationFailed(m_resource);
238 m_resource->setResponse(response);
240 String encoding = response.textEncodingName();
241 if (!encoding.isNull())
242 m_resource->setEncoding(encoding);
245 ASSERT(m_resource->isImage());
246 static_cast<CachedImage*>(m_resource)->clear();
247 if (m_cachedResourceLoader->frame())
248 m_cachedResourceLoader->frame()->loader()->checkCompleted();
249 } else if (response.isMultipart()) {
252 // We don't count multiParts in a CachedResourceLoader's request count
253 m_cachedResourceLoader->decrementRequestCount(m_resource);
255 // If we get a multipart response, we must have a handle
256 ASSERT(loader->handle());
257 if (!m_resource->isImage())
258 loader->handle()->cancel();
262 void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size)
264 ASSERT(loader == m_loader.get());
265 ASSERT(!m_resource->isCacheValidator());
267 if (m_resource->errorOccurred())
270 if (m_resource->response().httpStatusCode() >= 400) {
271 if (!m_resource->shouldIgnoreHTTPStatusCodeErrors())
272 m_resource->error(CachedResource::LoadError);
278 // The loader delivers the data in a multipart section all at once, send eof.
279 // The resource data will change as the next part is loaded, so we need to make a copy.
280 RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size);
281 m_resource->data(copiedData.release(), true);
283 m_resource->data(loader->resourceData(), false);
286 void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size)
288 ASSERT(!m_resource->isCacheValidator());
289 m_resource->setSerializedCachedMetadata(data, size);
292 void CachedResourceRequest::end()
294 m_cachedResourceLoader->loadDone();
295 m_resource->stopLoading();
298 } //namespace WebCore