Reviewed by Darin, Geoff.
[WebKit-https.git] / WebCore / loader / loader.cpp
1 /*
2     This file is part of the KDE libraries
3
4     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
5     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
6     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
7     Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
8     Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
9
10     This library is free software; you can redistribute it and/or
11     modify it under the terms of the GNU Library General Public
12     License as published by the Free Software Foundation; either
13     version 2 of the License, or (at your option) any later version.
14
15     This library is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18     Library General Public License for more details.
19
20     You should have received a copy of the GNU Library General Public License
21     along with this library; see the file COPYING.LIB.  If not, write to
22     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23     Boston, MA 02111-1307, USA.
24
25     This class provides all functionality needed for loading images, style sheets and html
26     pages from the web. It has a memory cache for these objects.
27 */
28
29 #include "config.h"
30 #include "loader.h"
31
32 #include "Cache.h"
33 #include "CachedImage.h"
34 #include "CachedResource.h"
35 #include "DocLoader.h"
36 #include "Frame.h"
37 #include "FrameLoader.h"
38 #include "HTMLDocument.h"
39 #include "LoaderFunctions.h"
40 #include "Request.h"
41 #include "ResourceHandle.h"
42 #include "ResourceRequest.h"
43 #include "ResourceResponse.h"
44 #include <wtf/Assertions.h>
45 #include <wtf/Vector.h>
46
47 namespace WebCore {
48
49 Loader::Loader()
50 {
51     m_requestsPending.setAutoDelete(true);
52 }
53
54 Loader::~Loader()
55 {
56     deleteAllValues(m_requestsLoading);
57 }
58
59 void Loader::load(DocLoader* dl, CachedResource* object, bool incremental)
60 {
61     Request* req = new Request(dl, object, incremental);
62     m_requestsPending.append(req);
63     servePendingRequests();
64 }
65
66 void Loader::servePendingRequests()
67 {
68     if (m_requestsPending.count() == 0)
69         return;
70
71     // get the first pending request
72     Request* req = m_requestsPending.take(0);
73
74     ResourceRequest request(req->cachedResource()->url());
75
76     if (!req->cachedResource()->accept().isEmpty())
77         request.setHTTPAccept(req->cachedResource()->accept());
78     if (req->docLoader())  {
79         KURL r = req->docLoader()->doc()->URL();
80         if (r.protocol().startsWith("http") && r.path().isEmpty())
81             r.setPath("/");
82         request.setHTTPReferrer(r.url());
83         DeprecatedString domain = r.host();
84         if (req->docLoader()->doc()->isHTMLDocument())
85             domain = static_cast<HTMLDocument*>(req->docLoader()->doc())->domain().deprecatedString();
86     }
87     
88     RefPtr<ResourceHandle> loader = ResourceHandle::create(request, this, req->docLoader());
89
90     if (loader)
91         m_requestsLoading.add(loader.release(), req);
92 }
93
94 void Loader::receivedAllData(ResourceHandle* job, PlatformData allData)
95 {
96     RequestMap::iterator i = m_requestsLoading.find(job);
97     if (i == m_requestsLoading.end())
98         return;
99
100     Request* req = i->second;
101     m_requestsLoading.remove(i);
102
103     CachedResource* object = req->cachedResource();
104     DocLoader* docLoader = req->docLoader();
105
106     docLoader->setLoadInProgress(true);
107     object->data(req->buffer(), true);
108     object->setAllData(allData);
109     docLoader->setLoadInProgress(false);
110     object->finish();
111
112     delete req;
113
114     servePendingRequests();
115 }
116
117 void Loader::didFailWithError(ResourceHandle* handle, const ResourceError& error)
118 {
119     RequestMap::iterator i = m_requestsLoading.find(handle);
120     if (i == m_requestsLoading.end())
121         return;
122
123     Request* req = i->second;
124     m_requestsLoading.remove(i);
125
126     CachedResource* object = req->cachedResource();
127     DocLoader* docLoader = req->docLoader();
128
129     docLoader->setLoadInProgress(true);
130     object->error();
131     docLoader->setLoadInProgress(false);
132     cache()->remove(object);
133
134     delete req;
135
136     servePendingRequests();
137 }
138
139 void Loader::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
140 {
141     Request* req = m_requestsLoading.get(handle);
142     ASSERT(req);
143     req->cachedResource()->setResponse(response);
144     
145     String encoding = response.textEncodingName();
146     if (!encoding.isNull())
147         req->cachedResource()->setEncoding(encoding);
148     
149     if (req->isMultipart()) {
150         ASSERT(req->cachedResource()->isImage());
151         static_cast<CachedImage*>(req->cachedResource())->clear();
152         if (req->docLoader()->frame())
153             req->docLoader()->frame()->loader()->checkCompleted();
154      } else if (response.isMultipart()) {
155         req->setIsMultipart(true);
156         if (!req->cachedResource()->isImage())
157             handle->cancel();
158     }
159 }
160
161 void Loader::didReceiveData(ResourceHandle* job, const char* data, int size)
162 {
163     Request* request = m_requestsLoading.get(job);
164     if (!request)
165         return;
166
167     CachedResource* object = request->cachedResource();    
168     Vector<char>& buffer = object->bufferData(data, size, request);
169
170     // Set the data.
171     if (request->isMultipart())
172         // The loader delivers the data in a multipart section all at once, send eof.
173         object->data(buffer, true);
174     else if (request->isIncremental())
175         object->data(buffer, false);
176 }
177
178 int Loader::numRequests(DocLoader* dl) const
179 {
180     // FIXME: Maybe we should keep a collection of requests by DocLoader, so we can do this instantly.
181
182     int res = 0;
183
184     DeprecatedPtrListIterator<Request> pIt(m_requestsPending);
185     for (; pIt.current(); ++pIt) {
186         if (pIt.current()->docLoader() == dl)
187             res++;
188     }
189
190     RequestMap::const_iterator end = m_requestsLoading.end();
191     for (RequestMap::const_iterator i = m_requestsLoading.begin(); !(i == end); ++i) {
192         Request* r = i->second;
193         res += (r->docLoader() == dl && !r->isMultipart());
194     }
195
196     DeprecatedPtrListIterator<Request> bdIt(m_requestsBackgroundDecoding);
197     for (; bdIt.current(); ++bdIt)
198         if (bdIt.current()->docLoader() == dl)
199             res++;
200
201     if (dl->loadInProgress())
202         res++;
203
204     return res;
205 }
206
207 void Loader::cancelRequests(DocLoader* dl)
208 {
209     DeprecatedPtrListIterator<Request> pIt(m_requestsPending);
210     while (pIt.current()) {
211         if (pIt.current()->docLoader() == dl) {
212             cache()->remove(pIt.current()->cachedResource());
213             m_requestsPending.remove(pIt);
214         } else
215             ++pIt;
216     }
217
218     Vector<ResourceHandle*, 256> jobsToCancel;
219
220     RequestMap::iterator end = m_requestsLoading.end();
221     for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) {
222         Request* r = i->second;
223         if (r->docLoader() == dl)
224             jobsToCancel.append(i->first.get());
225     }
226
227     for (unsigned i = 0; i < jobsToCancel.size(); ++i) {
228         ResourceHandle* job = jobsToCancel[i];
229         Request* r = m_requestsLoading.get(job);
230         m_requestsLoading.remove(job);
231         cache()->remove(r->cachedResource());
232     }
233
234     DeprecatedPtrListIterator<Request> bdIt(m_requestsBackgroundDecoding);
235     while (bdIt.current()) {
236         if (bdIt.current()->docLoader() == dl) {
237             cache()->remove(bdIt.current()->cachedResource());
238             m_requestsBackgroundDecoding.remove(bdIt);
239         } else
240             ++bdIt;
241     }
242 }
243
244 void Loader::removeBackgroundDecodingRequest(Request* r)
245 {
246     if (m_requestsBackgroundDecoding.containsRef(r))
247         m_requestsBackgroundDecoding.remove(r);
248 }
249
250 ResourceHandle* Loader::jobForRequest(const String& URL) const
251 {
252     RequestMap::const_iterator end = m_requestsLoading.end();
253     for (RequestMap::const_iterator i = m_requestsLoading.begin(); i != end; ++i) {
254         CachedResource* obj = i->second->cachedResource();
255         if (obj && obj->url() == URL)
256             return i->first.get();
257     }
258     return 0;
259 }
260
261 } //namespace WebCore