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