• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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