OSDN Git Service

Merge WebKit at r72274: Fix CodeGeneratorV8.pm
[android-x86/external-webkit.git] / WebCore / loader / loader.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 "loader.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 "Request.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>
43
44 namespace WebCore {
45
46 Loader::~Loader()
47 {    
48     ASSERT_NOT_REACHED();
49 }
50     
51 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
52 {
53     switch (type) {
54     case CachedResource::CSSStyleSheet:
55 #if ENABLE(XSLT)
56     case CachedResource::XSLStyleSheet:
57 #endif
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;
68 #endif
69     }
70     ASSERT_NOT_REACHED();
71     return ResourceRequest::TargetIsSubresource;
72 }
73
74 static ResourceLoadScheduler::Priority determinePriority(const CachedResource* resource)
75 {
76     switch (resource->type()) {
77         case CachedResource::CSSStyleSheet:
78 #if ENABLE(XSLT)
79         case CachedResource::XSLStyleSheet:
80 #endif
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;
90 #endif
91     }
92     ASSERT_NOT_REACHED();
93     return ResourceLoadScheduler::Low;
94 }
95
96 void Loader::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
97 {
98
99     ASSERT(cachedResourceLoader);
100     Request* request = new Request(cachedResourceLoader, resource, incremental, securityCheck, sendResourceLoadCallbacks);
101
102     cachedResourceLoader->incrementRequestCount(request->cachedResource());
103
104     ResourceRequest resourceRequest(request->cachedResource()->url());
105     resourceRequest.setTargetType(cachedResourceTypeToTargetType(request->cachedResource()->type()));
106
107     if (!request->cachedResource()->accept().isEmpty())
108         resourceRequest.setHTTPAccept(request->cachedResource()->accept());
109
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);
122             if (!eTag.isEmpty())
123                 resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
124         }
125     }
126     
127 #if ENABLE(LINK_PREFETCH)
128     if (request->cachedResource()->type() == CachedResource::LinkPrefetch)
129         resourceRequest.setHTTPHeaderField("X-Purpose", "prefetch");
130 #endif
131
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);
136     else {
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);
145         delete request;
146     }
147 }
148
149 void Loader::cancelRequests(CachedResourceLoader* cachedResourceLoader)
150 {
151     cachedResourceLoader->clearPendingPreloads();
152
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());
159     }
160
161     for (unsigned i = 0; i < loadersToCancel.size(); ++i) {
162         SubresourceLoader* loader = loadersToCancel[i];
163         didFail(loader, true);
164     }
165 }
166
167 void Loader::willSendRequest(SubresourceLoader* loader, ResourceRequest&, const ResourceResponse&)
168 {
169     RequestMap::iterator i = m_requestsLoading.find(loader);
170     if (i == m_requestsLoading.end())
171         return;
172     
173     Request* request = i->second;
174     request->cachedResource()->setRequestedFromNetworkingLayer();
175 }
176
177 void Loader::didFinishLoading(SubresourceLoader* loader)
178 {
179     RequestMap::iterator i = m_requestsLoading.find(loader);
180     if (i == m_requestsLoading.end())
181         return;
182     
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());
191
192     CachedResource* resource = request->cachedResource();
193     ASSERT(!resource->resourceToRevalidate());
194
195     LOG(ResourceLoading, "Received '%s'.", resource->url().latin1().data());
196
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())
203             resource->finish();
204     }
205
206     delete request;
207     cachedResourceLoader->setLoadInProgress(false);    
208     cachedResourceLoader->checkForPendingPreloads();
209 }
210
211 void Loader::didFail(SubresourceLoader* loader, const ResourceError&)
212 {
213     didFail(loader);
214 }
215
216 void Loader::didFail(SubresourceLoader* loader, bool cancelled)
217 {
218     loader->clearClient();
219
220     RequestMap::iterator i = m_requestsLoading.find(loader);
221     if (i == m_requestsLoading.end())
222         return;
223     
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());
232
233     CachedResource* resource = request->cachedResource();
234
235     LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", resource->url().latin1().data(), cancelled);
236
237     if (resource->resourceToRevalidate())
238         cache()->revalidationFailed(resource);
239
240     if (!cancelled) {
241         cachedResourceLoader->setLoadInProgress(true);
242         resource->error(CachedResource::LoadError);
243     }
244     
245     cachedResourceLoader->setLoadInProgress(false);
246     if (cancelled || !resource->isPreloaded())
247         cache()->remove(resource);
248     
249     delete request;
250     
251     cachedResourceLoader->checkForPendingPreloads();
252 }
253
254 void Loader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
255 {
256     Request* request = m_requestsLoading.get(loader);
257     
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. 
263     // ASSERT(request);
264     if (!request)
265         return;
266     
267     CachedResource* resource = request->cachedResource();
268     
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());
275
276             // Existing resource is ok, just use it updating the expiration time.
277             cache()->revalidationSucceeded(resource, response);
278             
279             if (request->cachedResourceLoader()->frame())
280                 request->cachedResourceLoader()->frame()->loader()->checkCompleted();
281
282             delete request;
283
284             return;
285         } 
286         // Did not get 304 response, continue as a regular resource load.
287         cache()->revalidationFailed(resource);
288     }
289
290     resource->setResponse(response);
291
292     String encoding = response.textEncodingName();
293     if (!encoding.isNull())
294         resource->setEncoding(encoding);
295     
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);
303         
304         // We don't count multiParts in a CachedResourceLoader's request count
305         request->cachedResourceLoader()->decrementRequestCount(request->cachedResource());
306
307         // If we get a multipart response, we must have a handle
308         ASSERT(loader->handle());
309         if (!resource->isImage())
310             loader->handle()->cancel();
311     }
312 }
313
314 void Loader::didReceiveData(SubresourceLoader* loader, const char* data, int size)
315 {
316     Request* request = m_requestsLoading.get(loader);
317     if (!request)
318         return;
319
320     CachedResource* resource = request->cachedResource();    
321     ASSERT(!resource->isCacheValidator());
322     
323     if (resource->errorOccurred())
324         return;
325
326     if (resource->response().httpStatusCode() >= 400) {
327         resource->httpStatusCodeError();
328         return;
329     }
330
331     // Set the data.
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);
339 }
340     
341 void Loader::didReceiveCachedMetadata(SubresourceLoader* loader, const char* data, int size)
342 {
343     Request* request = m_requestsLoading.get(loader);
344     if (!request)
345         return;
346
347     CachedResource* resource = request->cachedResource();    
348     ASSERT(!resource->isCacheValidator());
349
350     resource->setSerializedCachedMetadata(data, size);
351 }
352
353 } //namespace WebCore