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.
27 #include "MemoryCache.h"
28 #include "CachedImage.h"
29 #include "CachedResource.h"
30 #include "CachedResourceLoader.h"
32 #include "FrameLoader.h"
35 #include "ResourceHandle.h"
36 #include "ResourceLoadScheduler.h"
37 #include "ResourceRequest.h"
38 #include "ResourceResponse.h"
39 #include "SharedBuffer.h"
40 #include <wtf/Assertions.h>
41 #include <wtf/Vector.h>
42 #include <wtf/text/CString.h>
51 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
54 case CachedResource::CSSStyleSheet:
56 case CachedResource::XSLStyleSheet:
58 return ResourceRequest::TargetIsStyleSheet;
59 case CachedResource::Script:
60 return ResourceRequest::TargetIsScript;
61 case CachedResource::FontResource:
62 return ResourceRequest::TargetIsFontResource;
63 case CachedResource::ImageResource:
64 return ResourceRequest::TargetIsImage;
65 #if ENABLE(LINK_PREFETCH)
66 case CachedResource::LinkPrefetch:
67 return ResourceRequest::TargetIsPrefetch;
71 return ResourceRequest::TargetIsSubresource;
74 static ResourceLoadScheduler::Priority determinePriority(const CachedResource* resource)
76 switch (resource->type()) {
77 case CachedResource::CSSStyleSheet:
79 case CachedResource::XSLStyleSheet:
81 return ResourceLoadScheduler::High;
82 case CachedResource::Script:
83 case CachedResource::FontResource:
84 return ResourceLoadScheduler::Medium;
85 case CachedResource::ImageResource:
86 return ResourceLoadScheduler::Low;
87 #if ENABLE(LINK_PREFETCH)
88 case CachedResource::LinkPrefetch:
89 return ResourceLoadScheduler::VeryLow;
93 return ResourceLoadScheduler::Low;
96 void Loader::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
99 ASSERT(cachedResourceLoader);
100 Request* request = new Request(cachedResourceLoader, resource, incremental, securityCheck, sendResourceLoadCallbacks);
102 cachedResourceLoader->incrementRequestCount(request->cachedResource());
104 ResourceRequest resourceRequest(request->cachedResource()->url());
105 resourceRequest.setTargetType(cachedResourceTypeToTargetType(request->cachedResource()->type()));
107 if (!request->cachedResource()->accept().isEmpty())
108 resourceRequest.setHTTPAccept(request->cachedResource()->accept());
110 if (request->cachedResource()->isCacheValidator()) {
111 CachedResource* resourceToRevalidate = request->cachedResource()->resourceToRevalidate();
112 ASSERT(resourceToRevalidate->canUseCacheValidator());
113 ASSERT(resourceToRevalidate->isLoaded());
114 const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
115 const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
116 if (!lastModified.isEmpty() || !eTag.isEmpty()) {
117 ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
118 if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
119 resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
120 if (!lastModified.isEmpty())
121 resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
123 resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
127 #if ENABLE(LINK_PREFETCH)
128 if (request->cachedResource()->type() == CachedResource::LinkPrefetch)
129 resourceRequest.setHTTPHeaderField("X-Purpose", "prefetch");
132 RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(),
133 this, resourceRequest, determinePriority(resource), request->shouldDoSecurityCheck(), request->sendResourceLoadCallbacks());
134 if (loader && !loader->reachedTerminalState())
135 m_requestsLoading.add(loader.release(), request);
137 // FIXME: What if resources in other frames were waiting for this revalidation?
138 LOG(ResourceLoading, "Cannot start loading '%s'", request->cachedResource()->url().latin1().data());
139 cachedResourceLoader->decrementRequestCount(request->cachedResource());
140 cachedResourceLoader->setLoadInProgress(true);
141 if (resource->resourceToRevalidate())
142 cache()->revalidationFailed(resource);
143 resource->error(CachedResource::LoadError);
144 cachedResourceLoader->setLoadInProgress(false);
149 void Loader::cancelRequests(CachedResourceLoader* cachedResourceLoader)
151 cachedResourceLoader->clearPendingPreloads();
153 Vector<SubresourceLoader*, 256> loadersToCancel;
154 RequestMap::iterator end = m_requestsLoading.end();
155 for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) {
156 Request* r = i->second;
157 if (r->cachedResourceLoader() == cachedResourceLoader)
158 loadersToCancel.append(i->first.get());
161 for (unsigned i = 0; i < loadersToCancel.size(); ++i) {
162 SubresourceLoader* loader = loadersToCancel[i];
163 didFail(loader, true);
167 void Loader::willSendRequest(SubresourceLoader* loader, ResourceRequest&, const ResourceResponse&)
169 RequestMap::iterator i = m_requestsLoading.find(loader);
170 if (i == m_requestsLoading.end())
173 Request* request = i->second;
174 request->cachedResource()->setRequestedFromNetworkingLayer();
177 void Loader::didFinishLoading(SubresourceLoader* loader)
179 RequestMap::iterator i = m_requestsLoading.find(loader);
180 if (i == m_requestsLoading.end())
183 Request* request = i->second;
184 m_requestsLoading.remove(i);
185 CachedResourceLoader* cachedResourceLoader = request->cachedResourceLoader();
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(cachedResourceLoader->document());
189 if (!request->isMultipart())
190 cachedResourceLoader->decrementRequestCount(request->cachedResource());
192 CachedResource* resource = request->cachedResource();
193 ASSERT(!resource->resourceToRevalidate());
195 LOG(ResourceLoading, "Received '%s'.", resource->url().latin1().data());
197 // If we got a 4xx response, we're pretending to have received a network
198 // error, so we can't send the successful data() and finish() callbacks.
199 if (!resource->errorOccurred()) {
200 cachedResourceLoader->setLoadInProgress(true);
201 resource->data(loader->resourceData(), true);
202 if (!resource->errorOccurred())
207 cachedResourceLoader->setLoadInProgress(false);
208 cachedResourceLoader->checkForPendingPreloads();
211 void Loader::didFail(SubresourceLoader* loader, const ResourceError&)
216 void Loader::didFail(SubresourceLoader* loader, bool cancelled)
218 loader->clearClient();
220 RequestMap::iterator i = m_requestsLoading.find(loader);
221 if (i == m_requestsLoading.end())
224 Request* request = i->second;
225 m_requestsLoading.remove(i);
226 CachedResourceLoader* cachedResourceLoader = request->cachedResourceLoader();
227 // Prevent the document from being destroyed before we are done with
228 // the cachedResourceLoader that it will delete when the document gets deleted.
229 RefPtr<Document> protector(cachedResourceLoader->document());
230 if (!request->isMultipart())
231 cachedResourceLoader->decrementRequestCount(request->cachedResource());
233 CachedResource* resource = request->cachedResource();
235 LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", resource->url().latin1().data(), cancelled);
237 if (resource->resourceToRevalidate())
238 cache()->revalidationFailed(resource);
241 cachedResourceLoader->setLoadInProgress(true);
242 resource->error(CachedResource::LoadError);
245 cachedResourceLoader->setLoadInProgress(false);
246 if (cancelled || !resource->isPreloaded())
247 cache()->remove(resource);
251 cachedResourceLoader->checkForPendingPreloads();
254 void Loader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
256 Request* request = m_requestsLoading.get(loader);
258 // FIXME: This is a workaround for <rdar://problem/5236843>
259 // If a load starts while the frame is still in the provisional state
260 // (this can be the case when loading the user style sheet), committing the load then causes all
261 // requests to be removed from the m_requestsLoading map. This means that request might be null here.
262 // In that case we just return early.
267 CachedResource* resource = request->cachedResource();
269 if (resource->isCacheValidator()) {
270 if (response.httpStatusCode() == 304) {
271 // 304 Not modified / Use local copy
272 m_requestsLoading.remove(loader);
273 loader->clearClient();
274 request->cachedResourceLoader()->decrementRequestCount(request->cachedResource());
276 // Existing resource is ok, just use it updating the expiration time.
277 cache()->revalidationSucceeded(resource, response);
279 if (request->cachedResourceLoader()->frame())
280 request->cachedResourceLoader()->frame()->loader()->checkCompleted();
286 // Did not get 304 response, continue as a regular resource load.
287 cache()->revalidationFailed(resource);
290 resource->setResponse(response);
292 String encoding = response.textEncodingName();
293 if (!encoding.isNull())
294 resource->setEncoding(encoding);
296 if (request->isMultipart()) {
297 ASSERT(resource->isImage());
298 static_cast<CachedImage*>(resource)->clear();
299 if (request->cachedResourceLoader()->frame())
300 request->cachedResourceLoader()->frame()->loader()->checkCompleted();
301 } else if (response.isMultipart()) {
302 request->setIsMultipart(true);
304 // We don't count multiParts in a CachedResourceLoader's request count
305 request->cachedResourceLoader()->decrementRequestCount(request->cachedResource());
307 // If we get a multipart response, we must have a handle
308 ASSERT(loader->handle());
309 if (!resource->isImage())
310 loader->handle()->cancel();
314 void Loader::didReceiveData(SubresourceLoader* loader, const char* data, int size)
316 Request* request = m_requestsLoading.get(loader);
320 CachedResource* resource = request->cachedResource();
321 ASSERT(!resource->isCacheValidator());
323 if (resource->errorOccurred())
326 if (resource->response().httpStatusCode() >= 400) {
327 resource->httpStatusCodeError();
332 if (request->isMultipart()) {
333 // The loader delivers the data in a multipart section all at once, send eof.
334 // The resource data will change as the next part is loaded, so we need to make a copy.
335 RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size);
336 resource->data(copiedData.release(), true);
337 } else if (request->isIncremental())
338 resource->data(loader->resourceData(), false);
341 void Loader::didReceiveCachedMetadata(SubresourceLoader* loader, const char* data, int size)
343 Request* request = m_requestsLoading.get(loader);
347 CachedResource* resource = request->cachedResource();
348 ASSERT(!resource->isCacheValidator());
350 resource->setSerializedCachedMetadata(data, size);
353 } //namespace WebCore