tizen beta release
[framework/web/webkit-efl.git] / Source / WebCore / loader / cache / CachedResourceRequest.cpp
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 "CachedImage.h"
28 #include "CachedResource.h"
29 #include "CachedResourceLoader.h"
30 #include "Document.h"
31 #include "Frame.h"
32 #include "FrameLoader.h"
33 #include "Logging.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>
45
46 namespace WebCore {
47
48 #if PLATFORM(CHROMIUM)
49 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
50 {
51     switch (type) {
52     case CachedResource::CSSStyleSheet:
53 #if ENABLE(XSLT)
54     case CachedResource::XSLStyleSheet:
55 #endif
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;
72 #endif
73     }
74     ASSERT_NOT_REACHED();
75     return ResourceRequest::TargetIsSubresource;
76 }
77 #endif
78
79 CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
80     : m_cachedResourceLoader(cachedResourceLoader)
81     , m_resource(resource)
82     , m_multipart(false)
83     , m_finishing(false)
84 {
85 }
86
87 CachedResourceRequest::~CachedResourceRequest()
88 {
89 }
90
91 PassOwnPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, const ResourceLoaderOptions& options)
92 {
93     OwnPtr<CachedResourceRequest> request = adoptPtr(new CachedResourceRequest(cachedResourceLoader, resource));
94
95     ResourceRequest resourceRequest = resource->resourceRequest();
96 #if PLATFORM(CHROMIUM)
97     resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type()));
98 #endif
99
100     if (!resource->accept().isEmpty())
101         resourceRequest.setHTTPAccept(resource->accept());
102
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);
115             if (!eTag.isEmpty())
116                 resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
117         }
118     }
119     
120 #if ENABLE(LINK_PREFETCH)
121     if (resource->type() == CachedResource::LinkPrefetch || resource->type() == CachedResource::LinkPrerender || resource->type() == CachedResource::LinkSubresource)
122         resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
123 #endif
124
125     ResourceLoadPriority priority = resource->loadPriority();
126     resourceRequest.setPriority(priority);
127
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);
136     }
137     request->m_loader = loader;
138     return request.release();
139 }
140
141 void CachedResourceRequest::willSendRequest(SubresourceLoader* loader, ResourceRequest& req, const ResourceResponse&)
142 {
143     if (!m_cachedResourceLoader->canRequest(m_resource->type(), req.url())) {
144         loader->cancel();
145         return;
146     }
147     m_resource->setRequestedFromNetworkingLayer();
148 }
149
150 void CachedResourceRequest::cancel()
151 {
152     if (m_finishing)
153         return;
154     m_loader->cancel();
155 }
156
157 void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader, double)
158 {
159     if (m_finishing)
160         return;
161
162     ASSERT(loader == m_loader.get());
163     ASSERT(!m_resource->resourceToRevalidate());
164     LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
165
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());
169     if (!m_multipart)
170         m_cachedResourceLoader->decrementRequestCount(m_resource);
171     m_finishing = true;
172
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();
180     }
181     end();
182 }
183
184 void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError& error)
185 {
186     if (m_finishing || !m_loader)
187         return;
188
189     bool cancelled = error.isCancellation();
190     LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().string().latin1().data(), cancelled);
191
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());
195     if (!m_multipart)
196         m_cachedResourceLoader->decrementRequestCount(m_resource);
197     m_finishing = true;
198     m_loader->clearClient();
199
200     if (m_resource->resourceToRevalidate())
201         memoryCache()->revalidationFailed(m_resource);
202
203     if (!cancelled) {
204         m_cachedResourceLoader->loadFinishing();
205         m_resource->error(CachedResource::LoadError);
206     }
207
208     if (cancelled || !m_resource->isPreloaded())
209         memoryCache()->remove(m_resource);
210     
211     end();
212 }
213
214 void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
215 {
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);
223             m_finishing = true;
224
225             // Existing resource is ok, just use it updating the expiration time.
226             memoryCache()->revalidationSucceeded(m_resource, response);
227             
228             if (m_cachedResourceLoader->frame())
229                 m_cachedResourceLoader->frame()->loader()->checkCompleted();
230
231             end();
232             return;
233         } 
234         // Did not get 304 response, continue as a regular resource load.
235         memoryCache()->revalidationFailed(m_resource);
236     }
237
238     m_resource->setResponse(response);
239
240     String encoding = response.textEncodingName();
241     if (!encoding.isNull())
242         m_resource->setEncoding(encoding);
243     
244     if (m_multipart) {
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()) {
250         m_multipart = true;
251         
252         // We don't count multiParts in a CachedResourceLoader's request count
253         m_cachedResourceLoader->decrementRequestCount(m_resource);
254
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();
259     }
260 }
261
262 void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size)
263 {
264     ASSERT(loader == m_loader.get());
265     ASSERT(!m_resource->isCacheValidator());
266     
267     if (m_resource->errorOccurred())
268         return;
269
270     if (m_resource->response().httpStatusCode() >= 400) {
271         if (!m_resource->shouldIgnoreHTTPStatusCodeErrors())
272             m_resource->error(CachedResource::LoadError);
273         return;
274     }
275
276     // Set the data.
277     if (m_multipart) {
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);
282     } else
283         m_resource->data(loader->resourceData(), false);
284 }
285
286 void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size)
287 {
288     ASSERT(!m_resource->isCacheValidator());
289     m_resource->setSerializedCachedMetadata(data, size);
290 }
291     
292 void CachedResourceRequest::end()
293 {
294     m_cachedResourceLoader->loadDone();
295     m_resource->stopLoading();
296 }
297
298 } //namespace WebCore