1 /*
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.
7
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.
12
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.
17
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.
22 */
23
24 #include "config.h"
25 #include "CachedResourceRequest.h"
26
27 #include "MemoryCache.h"
28 #include "CachedImage.h"
29 #include "CachedResource.h"
30 #include "CachedResourceLoader.h"
31 #include "Frame.h"
32 #include "FrameLoader.h"
33 #include "Logging.h"
34 #include "ResourceHandle.h"
35 #include "ResourceLoadScheduler.h"
36 #include "ResourceRequest.h"
37 #include "ResourceResponse.h"
38 #include "SharedBuffer.h"
39 #include <wtf/Assertions.h>
40 #include <wtf/Vector.h>
41 #include <wtf/text/CString.h>
42
43 namespace WebCore {
44
cachedResourceTypeToTargetType(CachedResource::Type type,ResourceLoadPriority priority)45 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type, ResourceLoadPriority priority)
46 {
47 #if !ENABLE(LINK_PREFETCH)
48 UNUSED_PARAM(priority);
49 #endif
50 switch (type) {
51 case CachedResource::CSSStyleSheet:
52 #if ENABLE(XSLT)
53 case CachedResource::XSLStyleSheet:
54 #endif
55 return ResourceRequest::TargetIsStyleSheet;
56 case CachedResource::Script:
57 return ResourceRequest::TargetIsScript;
58 case CachedResource::FontResource:
59 return ResourceRequest::TargetIsFontResource;
60 case CachedResource::ImageResource:
61 return ResourceRequest::TargetIsImage;
62 #if ENABLE(LINK_PREFETCH)
63 case CachedResource::LinkResource:
64 if (priority == ResourceLoadPriorityLowest)
65 return ResourceRequest::TargetIsPrefetch;
66 return ResourceRequest::TargetIsSubresource;
67 #endif
68 }
69 ASSERT_NOT_REACHED();
70 return ResourceRequest::TargetIsSubresource;
71 }
72
CachedResourceRequest(CachedResourceLoader * cachedResourceLoader,CachedResource * resource,bool incremental)73 CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental)
74 : m_cachedResourceLoader(cachedResourceLoader)
75 , m_resource(resource)
76 , m_incremental(incremental)
77 , m_multipart(false)
78 , m_finishing(false)
79 {
80 m_resource->setRequest(this);
81 }
82
~CachedResourceRequest()83 CachedResourceRequest::~CachedResourceRequest()
84 {
85 m_resource->setRequest(0);
86 }
87
load(CachedResourceLoader * cachedResourceLoader,CachedResource * resource,bool incremental,SecurityCheckPolicy securityCheck,bool sendResourceLoadCallbacks)88 PassRefPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
89 {
90 RefPtr<CachedResourceRequest> request = adoptRef(new CachedResourceRequest(cachedResourceLoader, resource, incremental));
91
92 ResourceRequest resourceRequest(resource->url());
93 resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type(), resource->loadPriority()));
94
95 if (!resource->accept().isEmpty())
96 resourceRequest.setHTTPAccept(resource->accept());
97
98 if (resource->isCacheValidator()) {
99 CachedResource* resourceToRevalidate = resource->resourceToRevalidate();
100 ASSERT(resourceToRevalidate->canUseCacheValidator());
101 ASSERT(resourceToRevalidate->isLoaded());
102 const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
103 const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
104 if (!lastModified.isEmpty() || !eTag.isEmpty()) {
105 ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
106 if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
107 resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
108 if (!lastModified.isEmpty())
109 resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
110 if (!eTag.isEmpty())
111 resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
112 }
113 }
114
115 #if ENABLE(LINK_PREFETCH)
116 if (resource->type() == CachedResource::LinkResource)
117 resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
118 #endif
119
120 ResourceLoadPriority priority = resource->loadPriority();
121 resourceRequest.setPriority(priority);
122
123 RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(),
124 request.get(), resourceRequest, priority, securityCheck, sendResourceLoadCallbacks);
125 if (!loader || loader->reachedTerminalState()) {
126 // FIXME: What if resources in other frames were waiting for this revalidation?
127 LOG(ResourceLoading, "Cannot start loading '%s'", resource->url().latin1().data());
128 cachedResourceLoader->decrementRequestCount(resource);
129 cachedResourceLoader->loadFinishing();
130 if (resource->resourceToRevalidate())
131 memoryCache()->revalidationFailed(resource);
132 resource->error(CachedResource::LoadError);
133 cachedResourceLoader->loadDone(0);
134 return 0;
135 }
136 request->m_loader = loader;
137 return request.release();
138 }
139
willSendRequest(SubresourceLoader *,ResourceRequest &,const ResourceResponse &)140 void CachedResourceRequest::willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse&)
141 {
142 m_resource->setRequestedFromNetworkingLayer();
143 }
144
didFinishLoading(SubresourceLoader * loader,double)145 void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader, double)
146 {
147 if (m_finishing)
148 return;
149
150 ASSERT(loader == m_loader.get());
151 ASSERT(!m_resource->resourceToRevalidate());
152 LOG(ResourceLoading, "Received '%s'.", m_resource->url().latin1().data());
153
154 // Prevent the document from being destroyed before we are done with
155 // the cachedResourceLoader that it will delete when the document gets deleted.
156 RefPtr<Document> protector(m_cachedResourceLoader->document());
157 if (!m_multipart)
158 m_cachedResourceLoader->decrementRequestCount(m_resource);
159 m_finishing = true;
160
161 // If we got a 4xx response, we're pretending to have received a network
162 // error, so we can't send the successful data() and finish() callbacks.
163 if (!m_resource->errorOccurred()) {
164 m_cachedResourceLoader->loadFinishing();
165 m_resource->data(loader->resourceData(), true);
166 if (!m_resource->errorOccurred())
167 m_resource->finish();
168 }
169 m_cachedResourceLoader->loadDone(this);
170 }
171
didFail(SubresourceLoader *,const ResourceError &)172 void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError&)
173 {
174 if (!m_loader)
175 return;
176 didFail();
177 }
178
didFail(bool cancelled)179 void CachedResourceRequest::didFail(bool cancelled)
180 {
181 if (m_finishing)
182 return;
183
184 LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().latin1().data(), cancelled);
185
186 // Prevent the document from being destroyed before we are done with
187 // the cachedResourceLoader that it will delete when the document gets deleted.
188 RefPtr<Document> protector(m_cachedResourceLoader->document());
189 if (!m_multipart)
190 m_cachedResourceLoader->decrementRequestCount(m_resource);
191 m_finishing = true;
192 m_loader->clearClient();
193
194 if (m_resource->resourceToRevalidate())
195 memoryCache()->revalidationFailed(m_resource);
196
197 if (!cancelled) {
198 m_cachedResourceLoader->loadFinishing();
199 m_resource->error(CachedResource::LoadError);
200 }
201
202 if (cancelled || !m_resource->isPreloaded())
203 memoryCache()->remove(m_resource);
204
205 m_cachedResourceLoader->loadDone(this);
206 }
207
didReceiveResponse(SubresourceLoader * loader,const ResourceResponse & response)208 void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
209 {
210 ASSERT(loader == m_loader.get());
211 if (m_resource->isCacheValidator()) {
212 if (response.httpStatusCode() == 304) {
213 // 304 Not modified / Use local copy
214 loader->clearClient();
215 RefPtr<Document> protector(m_cachedResourceLoader->document());
216 m_cachedResourceLoader->decrementRequestCount(m_resource);
217 m_finishing = true;
218
219 // Existing resource is ok, just use it updating the expiration time.
220 memoryCache()->revalidationSucceeded(m_resource, response);
221
222 if (m_cachedResourceLoader->frame())
223 m_cachedResourceLoader->frame()->loader()->checkCompleted();
224
225 m_cachedResourceLoader->loadDone(this);
226 return;
227 }
228 // Did not get 304 response, continue as a regular resource load.
229 memoryCache()->revalidationFailed(m_resource);
230 }
231
232 m_resource->setResponse(response);
233
234 String encoding = response.textEncodingName();
235 if (!encoding.isNull())
236 m_resource->setEncoding(encoding);
237
238 if (m_multipart) {
239 ASSERT(m_resource->isImage());
240 static_cast<CachedImage*>(m_resource)->clear();
241 if (m_cachedResourceLoader->frame())
242 m_cachedResourceLoader->frame()->loader()->checkCompleted();
243 } else if (response.isMultipart()) {
244 m_multipart = true;
245
246 // We don't count multiParts in a CachedResourceLoader's request count
247 m_cachedResourceLoader->decrementRequestCount(m_resource);
248
249 // If we get a multipart response, we must have a handle
250 ASSERT(loader->handle());
251 if (!m_resource->isImage())
252 loader->handle()->cancel();
253 }
254 }
255
didReceiveData(SubresourceLoader * loader,const char * data,int size)256 void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size)
257 {
258 ASSERT(loader == m_loader.get());
259 ASSERT(!m_resource->isCacheValidator());
260
261 if (m_resource->errorOccurred())
262 return;
263
264 if (m_resource->response().httpStatusCode() >= 400) {
265 if (!m_resource->shouldIgnoreHTTPStatusCodeErrors())
266 m_resource->error(CachedResource::LoadError);
267 return;
268 }
269
270 // Set the data.
271 if (m_multipart) {
272 // The loader delivers the data in a multipart section all at once, send eof.
273 // The resource data will change as the next part is loaded, so we need to make a copy.
274 RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size);
275 m_resource->data(copiedData.release(), true);
276 } else if (m_incremental)
277 m_resource->data(loader->resourceData(), false);
278 }
279
didReceiveCachedMetadata(SubresourceLoader *,const char * data,int size)280 void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size)
281 {
282 ASSERT(!m_resource->isCacheValidator());
283 m_resource->setSerializedCachedMetadata(data, size);
284 }
285
286 } //namespace WebCore
287