Reviewed by Mitz.
[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 "HTMLDocument.h"
38 #include "LoaderFunctions.h"
39 #include "Request.h"
40 #include "ResourceLoader.h"
41 #include <wtf/Assertions.h>
42 #include <wtf/Vector.h>
43
44 namespace WebCore {
45
46 Loader::Loader()
47 {
48     m_requestsPending.setAutoDelete(true);
49 }
50
51 Loader::~Loader()
52 {
53     deleteAllValues(m_requestsLoading);
54 }
55
56 void Loader::load(DocLoader* dl, CachedResource* object, bool incremental)
57 {
58     Request* req = new Request(dl, object, incremental);
59     m_requestsPending.append(req);
60     servePendingRequests();
61 }
62
63 void Loader::servePendingRequests()
64 {
65     if (m_requestsPending.count() == 0)
66         return;
67
68     // get the first pending request
69     Request* req = m_requestsPending.take(0);
70
71     KURL u(req->cachedObject()->url().deprecatedString());
72     RefPtr<ResourceLoader> loader = ResourceLoader::create(this, "GET", u);
73
74     if (!req->cachedObject()->accept().isEmpty())
75         loader->addMetaData("accept", req->cachedObject()->accept());
76     if (req->docLoader())  {
77         KURL r = req->docLoader()->doc()->URL();
78         if (r.protocol().startsWith("http") && r.path().isEmpty())
79             r.setPath("/");
80         loader->addMetaData("referrer", r.url());
81         DeprecatedString domain = r.host();
82         if (req->docLoader()->doc()->isHTMLDocument())
83             domain = static_cast<HTMLDocument*>(req->docLoader()->doc())->domain().deprecatedString();
84     }
85
86     if (loader->start(req->docLoader()))
87         m_requestsLoading.add(loader.get(), req);
88 }
89
90 void Loader::receivedAllData(ResourceLoader* job, PlatformData allData)
91 {
92     RequestMap::iterator i = m_requestsLoading.find(job);
93     if (i == m_requestsLoading.end())
94         return;
95
96     Request* req = i->second;
97     m_requestsLoading.remove(i);
98
99     CachedResource* object = req->cachedObject();
100     DocLoader* docLoader = req->docLoader();
101
102     if (job->error() || job->isErrorPage()) {
103         docLoader->setLoadInProgress(true);
104         object->error();
105         docLoader->setLoadInProgress(false);
106         Cache::remove(object);
107     } else {
108         docLoader->setLoadInProgress(true);
109         object->data(req->buffer(), true);
110         object->setAllData(allData);
111         docLoader->setLoadInProgress(false);
112         object->finish();
113     }
114
115     delete req;
116
117     servePendingRequests();
118 }
119
120 void Loader::receivedResponse(ResourceLoader* job, PlatformResponse response)
121 {
122     Request* req = m_requestsLoading.get(job);
123     ASSERT(req);
124 #if !PLATFORM(QT)
125     ASSERT(response);
126 #else
127     ASSERT(!response.isEmpty());
128 #endif
129
130     req->cachedObject()->setResponse(response);
131     req->cachedObject()->setExpireDate(CacheObjectExpiresTime(req->docLoader(), response), false);
132     
133     DeprecatedString chs = job->queryMetaData("charset").deprecatedString();
134     if (!chs.isNull())
135         req->cachedObject()->setCharset(chs);
136     
137     if (req->isMultipart()) {
138         ASSERT(req->cachedObject()->isImage());
139         static_cast<CachedImage*>(req->cachedObject())->clear();
140         if (req->docLoader()->frame())
141             req->docLoader()->frame()->checkCompleted();
142     } else if (ResponseIsMultipart(response)) {
143         req->setIsMultipart(true);
144         if (!req->cachedObject()->isImage())
145             job->cancel();
146     }
147 }
148
149 void Loader::receivedData(ResourceLoader* job, const char* data, int size)
150 {
151     Request* request = m_requestsLoading.get(job);
152     if (!request)
153         return;
154
155     CachedResource* object = request->cachedObject();    
156     Vector<char>& buffer = object->bufferData(data, size, request);
157
158     // Set the data.
159     if (request->isMultipart())
160         // The loader delivers the data in a multipart section all at once, send eof.
161         object->data(buffer, true);
162     else if (request->isIncremental())
163         object->data(buffer, false);
164 }
165
166 int Loader::numRequests(DocLoader* dl) const
167 {
168     // FIXME: Maybe we should keep a collection of requests by DocLoader, so we can do this instantly.
169
170     int res = 0;
171
172     DeprecatedPtrListIterator<Request> pIt(m_requestsPending);
173     for (; pIt.current(); ++pIt) {
174         if (pIt.current()->docLoader() == dl)
175             res++;
176     }
177
178     RequestMap::const_iterator end = m_requestsLoading.end();
179     for (RequestMap::const_iterator i = m_requestsLoading.begin(); !(i == end); ++i) {
180         Request* r = i->second;
181         res += (r->docLoader() == dl && !r->isMultipart());
182     }
183
184     DeprecatedPtrListIterator<Request> bdIt(m_requestsBackgroundDecoding);
185     for (; bdIt.current(); ++bdIt)
186         if (bdIt.current()->docLoader() == dl)
187             res++;
188
189     if (dl->loadInProgress())
190         res++;
191
192     return res;
193 }
194
195 void Loader::cancelRequests(DocLoader* dl)
196 {
197     DeprecatedPtrListIterator<Request> pIt(m_requestsPending);
198     while (pIt.current()) {
199         if (pIt.current()->docLoader() == dl) {
200             Cache::remove(pIt.current()->cachedObject());
201             m_requestsPending.remove(pIt);
202         } else
203             ++pIt;
204     }
205
206     Vector<ResourceLoader*, 256> jobsToCancel;
207
208     RequestMap::iterator end = m_requestsLoading.end();
209     for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) {
210         Request* r = i->second;
211         if (r->docLoader() == dl)
212             jobsToCancel.append(i->first);
213     }
214
215     for (unsigned i = 0; i < jobsToCancel.size(); ++i) {
216         ResourceLoader* job = jobsToCancel[i];
217         Request* r = m_requestsLoading.get(job);
218         m_requestsLoading.remove(job);
219         Cache::remove(r->cachedObject());
220         job->kill();
221     }
222
223     DeprecatedPtrListIterator<Request> bdIt(m_requestsBackgroundDecoding);
224     while (bdIt.current()) {
225         if (bdIt.current()->docLoader() == dl) {
226             Cache::remove(bdIt.current()->cachedObject());
227             m_requestsBackgroundDecoding.remove(bdIt);
228         } else
229             ++bdIt;
230     }
231 }
232
233 void Loader::removeBackgroundDecodingRequest(Request* r)
234 {
235     if (m_requestsBackgroundDecoding.containsRef(r))
236         m_requestsBackgroundDecoding.remove(r);
237 }
238
239 ResourceLoader* Loader::jobForRequest(const String& URL) const
240 {
241     RequestMap::const_iterator end = m_requestsLoading.end();
242     for (RequestMap::const_iterator i = m_requestsLoading.begin(); i != end; ++i) {
243         CachedResource* obj = i->second->cachedObject();
244         if (obj && obj->url() == URL)
245             return i->first;
246     }
247     return 0;
248 }
249
250 } //namespace WebCore