8a9890c2ba383dae890a066d40e48a711957b593
[WebKit-https.git] / WebCore / khtml / misc / 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) 2004 Apple Computer, Inc.
8
9     This library is free software; you can redistribute it and/or
10     modify it under the terms of the GNU Library General Public
11     License as published by the Free Software Foundation; either
12     version 2 of the License, or (at your option) any later version.
13
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     Library General Public License for more details.
18
19     You should have received a copy of the GNU Library General Public License
20     along with this library; see the file COPYING.LIB.  If not, write to
21     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22     Boston, MA 02111-1307, USA.
23
24     This class provides all functionality needed for loading images, style sheets and html
25     pages from the web. It has a memory cache for these objects.
26 */
27
28 #undef CACHE_DEBUG
29 //#define CACHE_DEBUG
30 #include <assert.h>
31
32 #include "loader.h"
33
34 // up to which size is a picture for sure cacheable
35 #define MAXCACHEABLE 40*1024
36 // default cache size
37 #define DEFCACHESIZE 4096*1024
38
39 #include <qasyncio.h>
40 #include <qasyncimageio.h>
41 #include <qpainter.h>
42 #include <qbitmap.h>
43 #include <qmovie.h>
44
45 #include <kio/job.h>
46 #include <kio/jobclasses.h>
47 #include <kglobal.h>
48 #include <kimageio.h>
49 #include <kcharsets.h>
50 #include <kiconloader.h>
51 #include <scheduler.h>
52 #include <kdebug.h>
53 #include "khtml_factory.h"
54 #include "khtml_part.h"
55
56 #include "html/html_documentimpl.h"
57 #include "css/css_stylesheetimpl.h"
58
59 #ifndef KHTML_NO_XBL
60 #include "xbl/xbl_docimpl.h"
61 #endif
62
63 #if APPLE_CHANGES
64 #include "KWQAssertions.h"
65 #include "KWQLoader.h"
66 #endif
67
68 using namespace khtml;
69 using namespace DOM;
70
71 #if APPLE_CHANGES
72 static bool cacheDisabled;
73 #endif
74
75 // Call this "walker" instead of iterator so people won't expect Qt or STL-style iterator interface.
76 // Just keep calling next() on this. It's safe from deletions of the current item
77 class CachedObjectClientWalker {
78 public:
79     CachedObjectClientWalker(const QPtrDict<CachedObjectClient> &clients) : _current(0), _iterator(clients) { }
80     CachedObjectClient *next();
81 private:
82     CachedObjectClient *_current;
83     QPtrDictIterator<CachedObjectClient> _iterator;
84 };
85
86 CachedObject::~CachedObject()
87 {
88     if(m_deleted) abort();
89     Cache::removeFromLRUList(this);
90     m_deleted = true;
91 #if APPLE_CHANGES
92     setResponse(0);
93     setAllData(0);
94 #endif
95 }
96
97 void CachedObject::finish()
98 {
99     if (m_size > Cache::maxCacheableObjectSize())
100         m_status = Uncacheable;
101     else
102         m_status = Cached;
103     KURL url(m_url.string());
104     if (m_expireDateChanged && url.protocol().startsWith("http"))
105     {
106         m_expireDateChanged = false;
107         KIO::http_update_cache(url, false, m_expireDate);
108 #ifdef CACHE_DEBUG
109         kdDebug(6060) << " Setting expire date for image "<<m_url.string()<<" to " << m_expireDate << endl;
110 #endif
111     }
112 #ifdef CACHE_DEBUG
113     else kdDebug(6060) << " No expire date for image "<<m_url.string()<<endl;
114 #endif
115 }
116
117 void CachedObject::setExpireDate(time_t _expireDate, bool changeHttpCache)
118 {
119     if ( _expireDate == m_expireDate)
120         return;
121
122     if (m_status == Uncacheable || m_status == Cached)
123     {
124         finish();
125     }
126     m_expireDate = _expireDate;
127     if (changeHttpCache && m_expireDate)
128        m_expireDateChanged = true;
129 }
130
131 bool CachedObject::isExpired() const
132 {
133     if (!m_expireDate) return false;
134     time_t now = time(0);
135     return (difftime(now, m_expireDate) >= 0);
136 }
137
138 void CachedObject::setRequest(Request *_request)
139 {
140     if ( _request && !m_request )
141         m_status = Pending;
142     m_request = _request;
143     if (canDelete() && m_free)
144         delete this;
145     else if (allowInLRUList())
146         Cache::insertInLRUList(this);
147 }
148
149 void CachedObject::ref(CachedObjectClient *c)
150 {
151     m_clients.insert(c, c);
152     Cache::removeFromLRUList(this);
153     increaseAccessCount();
154 }
155
156 void CachedObject::deref(CachedObjectClient *c)
157 {
158     m_clients.remove(c);
159     if (allowInLRUList())
160         Cache::insertInLRUList(this);
161 }
162
163 void CachedObject::setSize(int size)
164 {
165     bool sizeChanged = Cache::adjustSize(this, size - m_size);
166
167     // The object must now be moved to a different queue, since its size has been changed.
168     if (sizeChanged && allowInLRUList())
169         Cache::removeFromLRUList(this);
170
171     m_size = size;
172     
173     if (sizeChanged && allowInLRUList())
174         Cache::insertInLRUList(this);
175 }
176
177 // -------------------------------------------------------------------------------------------
178
179 CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate, const QString& charset)
180     : CachedObject(url, CSSStyleSheet, _cachePolicy, _expireDate)
181 {
182     // It's css we want.
183     setAccept( QString::fromLatin1("text/css") );
184     // load the file
185     Cache::loader()->load(dl, this, false);
186     m_loading = true;
187     bool b;
188     if(!charset.isEmpty())
189         m_codec = KGlobal::charsets()->codecForName(charset, b);
190     else
191         m_codec = QTextCodec::codecForName("iso8859-1");
192 }
193
194 CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString &url, const QString &stylesheet_data)
195     : CachedObject(url, CSSStyleSheet, KIO::CC_Verify, 0, stylesheet_data.length())
196 {
197     m_loading = false;
198     m_status = Persistent;
199     m_codec = 0;
200     m_sheet = DOMString(stylesheet_data);
201 }
202
203
204 CachedCSSStyleSheet::~CachedCSSStyleSheet()
205 {
206 }
207
208 void CachedCSSStyleSheet::ref(CachedObjectClient *c)
209 {
210     CachedObject::ref(c);
211
212     if(!m_loading) c->setStyleSheet( m_url, m_sheet );
213 }
214
215 void CachedCSSStyleSheet::deref(CachedObjectClient *c)
216 {
217     Cache::flush();
218     CachedObject::deref(c);
219     if ( canDelete() && m_free )
220       delete this;
221 }
222
223 void CachedCSSStyleSheet::data( QBuffer &buffer, bool eof )
224 {
225     if(!eof) return;
226     buffer.close();
227     setSize(buffer.buffer().size());
228     QString data = m_codec->toUnicode( buffer.buffer().data(), size() );
229     m_sheet = DOMString(data);
230     m_loading = false;
231
232     checkNotify();
233 }
234
235 void CachedCSSStyleSheet::checkNotify()
236 {
237     if(m_loading) return;
238
239 #ifdef CACHE_DEBUG
240     kdDebug( 6060 ) << "CachedCSSStyleSheet:: finishedLoading " << m_url.string() << endl;
241 #endif
242
243     CachedObjectClientWalker w(m_clients);
244     while (CachedObjectClient *c = w.next()) {
245         if (m_response && !KWQIsResponseURLEqualToURL(m_response, m_url))
246             c->setStyleSheet(DOMString(KWQResponseURL(m_response)), m_sheet);
247         else
248             c->setStyleSheet(m_url, m_sheet);
249     }
250 }
251
252
253 void CachedCSSStyleSheet::error( int /*err*/, const char */*text*/ )
254 {
255     m_loading = false;
256     checkNotify();
257 }
258
259 // -------------------------------------------------------------------------------------------
260
261 CachedScript::CachedScript(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate, const QString& charset)
262     : CachedObject(url, Script, _cachePolicy, _expireDate)
263 {
264     // It's javascript we want.
265     // But some websites think their scripts are <some wrong mimetype here>
266     // and refuse to serve them if we only accept application/x-javascript.
267     setAccept( QString::fromLatin1("*/*") );
268     // load the file
269     Cache::loader()->load(dl, this, false);
270     m_loading = true;
271     bool b;
272     if(!charset.isEmpty())
273         m_codec = KGlobal::charsets()->codecForName(charset, b);
274     else
275         m_codec = QTextCodec::codecForName("iso8859-1");
276 }
277
278 CachedScript::CachedScript(const DOMString &url, const QString &script_data)
279     : CachedObject(url, Script, KIO::CC_Verify, 0, script_data.length())
280 {
281     m_loading = false;
282     m_status = Persistent;
283     m_codec = 0;
284     m_script = DOMString(script_data);
285 }
286
287 CachedScript::~CachedScript()
288 {
289 }
290
291 void CachedScript::ref(CachedObjectClient *c)
292 {
293     CachedObject::ref(c);
294
295     if(!m_loading) c->notifyFinished(this);
296 }
297
298 void CachedScript::deref(CachedObjectClient *c)
299 {
300     Cache::flush();
301     CachedObject::deref(c);
302     if ( canDelete() && m_free )
303       delete this;
304 }
305
306 void CachedScript::data( QBuffer &buffer, bool eof )
307 {
308     if(!eof) return;
309     buffer.close();
310     setSize(buffer.buffer().size());
311     QString data = m_codec->toUnicode( buffer.buffer().data(), size() );
312     m_script = DOMString(data);
313     m_loading = false;
314     checkNotify();
315 }
316
317 void CachedScript::checkNotify()
318 {
319     if(m_loading) return;
320
321     CachedObjectClientWalker w(m_clients);
322     while (CachedObjectClient *c = w.next())
323         c->notifyFinished(this);
324 }
325
326
327 void CachedScript::error( int /*err*/, const char */*text*/ )
328 {
329     m_loading = false;
330     checkNotify();
331 }
332
333 // ------------------------------------------------------------------------------------------
334
335 #if !APPLE_CHANGES
336
337 namespace khtml
338 {
339
340     class ImageSource : public QDataSource
341     {
342     public:
343         ImageSource(QByteArray buf);
344
345         /**
346          * Overload QDataSource::readyToSend() and returns the number
347          * of bytes ready to send if not eof instead of returning -1.
348          */
349         int readyToSend();
350
351         /*!
352           Reads and sends a block of data.
353         */
354         void sendTo(QDataSink*, int count);
355
356         /**
357          * Sets the EOF state.
358          */
359         void setEOF( bool state );
360
361         /*!
362           KHTMLImageSource's is rewindable.
363         */
364         bool rewindable() const;
365
366         /*!
367           Enables rewinding.  No special action is taken.
368         */
369         void enableRewind(bool on);
370
371         /*
372           Calls reset() on the QIODevice.
373         */
374         void rewind();
375
376         /*
377           Indicates that the buffered data is no longer
378           needed.
379         */
380         void cleanBuffer();
381
382         QByteArray buffer;
383         unsigned int pos;
384     private:
385         bool eof     : 1;
386         bool rew     : 1;
387         bool rewable : 1;
388     };
389 }
390
391
392 ImageSource::ImageSource(QByteArray buf)
393 {
394   buffer = buf;
395   rew = false;
396   pos = 0;
397   eof = false;
398   rewable = true;
399 }
400
401 int ImageSource::readyToSend()
402 {
403     if(eof && pos == buffer.size())
404         return -1;
405
406     return  buffer.size() - pos;
407 }
408
409 void ImageSource::sendTo(QDataSink* sink, int n)
410 {
411     sink->receive((const uchar*)&buffer.at(pos), n);
412
413     pos += n;
414
415     // buffer is no longer needed
416     if(eof && pos == buffer.size() && !rewable)
417     {
418         buffer.resize(0);
419         pos = 0;
420     }
421 }
422
423 void ImageSource::setEOF( bool state )
424 {
425     eof = state;
426 }
427
428 // ImageSource's is rewindable.
429 bool ImageSource::rewindable() const
430 {
431     return rewable;
432 }
433
434 // Enables rewinding.  No special action is taken.
435 void ImageSource::enableRewind(bool on)
436 {
437     rew = on;
438 }
439
440 // Calls reset() on the QIODevice.
441 void ImageSource::rewind()
442 {
443     pos = 0;
444     if (!rew) {
445         QDataSource::rewind();
446     } else
447         ready();
448 }
449
450 void ImageSource::cleanBuffer()
451 {
452     // if we need to be able to rewind, buffer is needed
453     if(rew)
454         return;
455
456     rewable = false;
457
458     // buffer is no longer needed
459     if(eof && pos == buffer.size())
460     {
461         buffer.resize(0);
462         pos = 0;
463     }
464 }
465
466 static QString buildAcceptHeader()
467 {
468     QString result = KImageIO::mimeTypes( KImageIO::Reading ).join(", ");
469     if (result.right(2) == ", ")
470         result = result.left(result.length()-2);
471     return result;
472 }
473
474 #endif // APPLE_CHANGES
475
476 static bool crossDomain(const QString &a, const QString &b)
477 {
478     if (a == b) return false;
479
480     QStringList l1 = QStringList::split('.', a);
481     QStringList l2 = QStringList::split('.', b);
482
483     while(l1.count() > l2.count())
484         l1.pop_front();
485
486     while(l2.count() > l1.count())
487         l2.pop_front();
488
489     while(l2.count() >= 2)
490     {
491         if (l1 == l2)
492            return false;
493
494         l1.pop_front();
495         l2.pop_front();
496     }
497     return true;
498 }
499
500 // -------------------------------------------------------------------------------------
501 #if APPLE_CHANGES
502 void CachedImageCallback::notifyUpdate() 
503
504     if (cachedImage) {
505         cachedImage->do_notify (cachedImage->pixmap(), cachedImage->pixmap().rect()); 
506         QSize s = cachedImage->pixmap_size();
507         cachedImage->setSize(s.width() * s.height() * 2);
508
509         // After receiving the image header we are guaranteed to know
510         // the image size.  Although all of the data may not have arrived or
511         // been decoded we can consider the image loaded for purposed of
512         // layout and dispatching the image's onload handler.  Removing the request from
513         // the list of background decoding requests will ensure that Loader::numRequests() 
514         // does not count this background request.  Further numRequests() can
515         // be correctly used by the part to determine if loading is sufficiently
516         // complete to dispatch the page's onload handler.
517         Request *r = cachedImage->m_request;
518         DocLoader *dl = r->m_docLoader;
519
520         khtml::Cache::loader()->removeBackgroundDecodingRequest(r);
521
522         // Poke the part to get it to do a checkCompleted().  Only do this for
523         // the first update to minimize work.  Note that we are guaranteed to have
524         // read the header when we received this first update, which is triggered
525         // by the first kCGImageStatusIncomplete status from CG. kCGImageStatusIncomplete
526         // really means that the CG decoder is waiting for more data, but has already
527         // read the header.
528         if (!headerReceived) {
529             emit khtml::Cache::loader()->requestDone( dl, cachedImage );
530             headerReceived = true;
531         }
532     }
533 }
534
535 void CachedImageCallback::notifyFinished()
536 {
537     if (cachedImage) {
538         cachedImage->do_notify (cachedImage->pixmap(), cachedImage->pixmap().rect()); 
539         cachedImage->m_loading = false;
540         cachedImage->checkNotify();
541         QSize s = cachedImage->pixmap_size();
542         cachedImage->setSize(s.width() * s.height() * 2);
543         
544         Request *r = cachedImage->m_request;
545         DocLoader *dl = r->m_docLoader;
546
547         khtml::Cache::loader()->removeBackgroundDecodingRequest(r);
548
549         // Poke the part to get it to do a checkCompleted().
550         emit khtml::Cache::loader()->requestDone( dl, cachedImage );
551         
552         delete r;
553     }
554 }
555
556 void CachedImageCallback::notifyDecodingError()
557 {
558     if (cachedImage) {
559         handleError();
560     }
561 }
562
563 void CachedImageCallback::handleError()
564 {
565     if (cachedImage) {
566         cachedImage->errorOccured = true;
567         QPixmap ep = cachedImage->pixmap();
568         cachedImage->do_notify (ep, ep.rect());
569         Cache::removeCacheEntry (cachedImage);
570
571         clear();
572     }
573 }
574
575 void CachedImageCallback::clear() 
576 {
577     if (cachedImage && cachedImage->m_request) {
578         Request *r = cachedImage->m_request;
579         DocLoader *dl = r->m_docLoader;
580
581         khtml::Cache::loader()->removeBackgroundDecodingRequest(r);
582
583         // Poke the part to get it to do a checkCompleted().
584         emit khtml::Cache::loader()->requestFailed( dl, cachedImage );
585
586         delete r;
587     }
588     cachedImage = 0;
589 }
590 #endif
591
592 CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
593     : QObject(), CachedObject(url, Image, _cachePolicy, _expireDate)
594 #if APPLE_CHANGES
595     , m_dataSize(0)
596 #endif
597 {
598 #if !APPLE_CHANGES
599     static const QString &acceptHeader = KGlobal::staticQString( buildAcceptHeader() );
600 #endif
601
602     m = 0;
603     p = 0;
604     pixPart = 0;
605     bg = 0;
606 #if !APPLE_CHANGES
607     bgColor = qRgba( 0, 0, 0, 0xFF );
608     typeChecked = false;
609 #endif
610     isFullyTransparent = false;
611     errorOccured = false;
612     monochrome = false;
613     formatType = 0;
614     m_status = Unknown;
615     imgSource = 0;
616     m_loading = true;
617 #if !APPLE_CHANGES
618     setAccept( acceptHeader );
619 #endif
620     m_showAnimations = dl->showAnimations();
621 #if APPLE_CHANGES
622 #if BUILDING_ON_PANTHER
623     m_decoderCallback = 0;
624 #else
625     if (QPixmap::shouldUseThreadedDecoding())
626         m_decoderCallback = new CachedImageCallback(this);
627     else
628         m_decoderCallback = 0;
629 #endif
630 #endif
631 }
632
633 CachedImage::~CachedImage()
634 {
635     clear();
636 }
637
638 void CachedImage::ref( CachedObjectClient *c )
639 {
640 #ifdef CACHE_DEBUG
641     kdDebug( 6060 ) << this << " CachedImage::ref(" << c << ") " << endl;
642 #endif
643
644     CachedObject::ref(c);
645
646     if( m ) {
647         m->unpause();
648         if( m->finished() || m_clients.count() == 1 )
649             m->restart();
650     }
651
652     // for mouseovers, dynamic changes
653     if (!valid_rect().isNull())
654         c->setPixmap( pixmap(), valid_rect(), this);
655
656     if(!m_loading) c->notifyFinished(this);
657 }
658
659 void CachedImage::deref( CachedObjectClient *c )
660 {
661 #ifdef CACHE_DEBUG
662     kdDebug( 6060 ) << this << " CachedImage::deref(" << c << ") " << endl;
663 #endif
664     Cache::flush();
665     CachedObject::deref(c);
666     if(m && m_clients.isEmpty() && m->running())
667         m->pause();
668     if ( canDelete() && m_free )
669         delete this;
670 }
671
672 #define BGMINWIDTH      32
673 #define BGMINHEIGHT     32
674
675 const QPixmap &CachedImage::tiled_pixmap(const QColor& newc)
676 {
677 #if APPLE_CHANGES
678     return pixmap();
679 #else
680     static QRgb bgTransparant = qRgba( 0, 0, 0, 0xFF );
681     if ( (bgColor != bgTransparant) && (bgColor != newc.rgb()) ) {
682         delete bg; bg = 0;
683     }
684
685     if (bg)
686         return *bg;
687
688     const QPixmap &r = pixmap();
689
690     if (r.isNull()) return r;
691
692     // no error indication for background images
693     if(errorOccured) return *Cache::nullPixmap;
694
695     bool isvalid = newc.isValid();
696     QSize s(pixmap_size());
697     int w = r.width();
698     int h = r.height();
699     if ( w*h < 8192 )
700     {
701         if ( r.width() < BGMINWIDTH )
702             w = ((BGMINWIDTH  / s.width())+1) * s.width();
703         if ( r.height() < BGMINHEIGHT )
704             h = ((BGMINHEIGHT / s.height())+1) * s.height();
705     }
706     if ( (w != r.width()) || (h != r.height()) || (isvalid && r.mask()))
707     {
708         QPixmap pix = r;
709         if ( w != r.width() || (isvalid && pix.mask()))
710         {
711             bg = new QPixmap(w, r.height());
712             QPainter p(bg);
713             if(isvalid) p.fillRect(0, 0, w, r.height(), newc);
714             p.drawTiledPixmap(0, 0, w, r.height(), pix);
715             if(!isvalid && pix.mask())
716             {
717                 // unfortunately our anti-transparency trick doesn't work here
718                 // we need to create a mask.
719                 QBitmap newmask(w, r.height());
720                 QPainter pm(&newmask);
721                 pm.drawTiledPixmap(0, 0, w, r.height(), *pix.mask());
722                 bg->setMask(newmask);
723                 bgColor = bgTransparant;
724             }
725             else
726                 bgColor= newc.rgb();
727             pix = *bg;
728         }
729         if ( h != r.height() )
730         {
731             delete bg;
732             bg = new QPixmap(w, h);
733             QPainter p(bg);
734             if(isvalid) p.fillRect(0, 0, w, h, newc);
735             p.drawTiledPixmap(0, 0, w, h, pix);
736             if(!isvalid && pix.mask())
737             {
738                 // unfortunately our anti-transparency trick doesn't work here
739                 // we need to create a mask.
740                 QBitmap newmask(w, h);
741                 QPainter pm(&newmask);
742                 pm.drawTiledPixmap(0, 0, w, h, *pix.mask());
743                 bg->setMask(newmask);
744                 bgColor = bgTransparant;
745             }
746             else
747                 bgColor= newc.rgb();
748         }
749         return *bg;
750     }
751
752     return r;
753 #endif
754 }
755
756 const QPixmap &CachedImage::pixmap( ) const
757 {
758     if(errorOccured)
759         return *Cache::brokenPixmap;
760
761 #if APPLE_CHANGES
762     if (p)
763         return *p;
764 #else
765     if(m)
766     {
767         if(m->framePixmap().size() != m->getValidRect().size() && m->getValidRect().size().isValid())
768         {
769             // pixmap is not yet completely loaded, so we
770             // return a clipped version. asserting here
771             // that the valid rect is always from 0/0 to fullwidth/ someheight
772             if(!pixPart) pixPart = new QPixmap(m->getValidRect().size());
773
774             (*pixPart) = m->framePixmap();
775             pixPart->resize(m->getValidRect().size());
776             return *pixPart;
777         }
778         else
779             return m->framePixmap();
780     }
781     else if(p)
782         return *p;
783 #endif // APPLE_CHANGES
784
785     return *Cache::nullPixmap;
786 }
787
788
789 QSize CachedImage::pixmap_size() const
790 {
791     return (m ? m->framePixmap().size() : ( p ? p->size() : QSize()));
792 }
793
794
795 QRect CachedImage::valid_rect() const
796 {
797     return m ? m->getValidRect() : ( p ? p->rect() : QRect());
798 }
799
800
801 void CachedImage::do_notify(const QPixmap& p, const QRect& r)
802 {
803     CachedObjectClientWalker w(m_clients);
804     while (CachedObjectClient *c = w.next())
805         c->setPixmap(p, r, this);
806 }
807
808 #if !APPLE_CHANGES
809
810 void CachedImage::movieUpdated( const QRect& r )
811 {
812 #ifdef CACHE_DEBUG
813     qDebug("movie updated %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
814            m->framePixmap().size().width(), m->framePixmap().size().height());
815 #endif
816
817     do_notify(m->framePixmap(), r);
818 }
819
820 void CachedImage::movieStatus(int status)
821 {
822 #ifdef CACHE_DEBUG
823     qDebug("movieStatus(%d)", status);
824 #endif
825
826     // ### the html image objects are supposed to send the load event after every frame (according to
827     // netscape). We have a problem though where an image is present, and js code creates a new Image object,
828     // which uses the same CachedImage, the one in the document is not supposed to be notified
829
830     // just another Qt 2.2.0 bug. we cannot call
831     // QMovie::frameImage if we're after QMovie::EndOfMovie
832     if(status == QMovie::EndOfFrame)
833     {
834         const QImage& im = m->frameImage();
835         monochrome = ( ( im.depth() <= 8 ) && ( im.numColors() - int( im.hasAlphaBuffer() ) <= 2 ) );
836         for (int i = 0; monochrome && i < im.numColors(); ++i)
837             if (im.colorTable()[i] != qRgb(0xff, 0xff, 0xff) &&
838                 im.colorTable()[i] != qRgb(0x00, 0x00, 0x00))
839                 monochrome = false;
840         if((im.width() < 5 || im.height() < 5) && im.hasAlphaBuffer()) // only evaluate for small images
841         {
842             QImage am = im.createAlphaMask();
843             if(am.depth() == 1)
844             {
845                 bool solid = false;
846                 for(int y = 0; y < am.height(); y++)
847                     for(int x = 0; x < am.width(); x++)
848                         if(am.pixelIndex(x, y)) {
849                             solid = true;
850                             break;
851                         }
852                 isFullyTransparent = (!solid);
853             }
854         }
855
856         // we have to delete our tiled bg variant here
857         // because the frame has changed (in order to keep it in sync)
858         delete bg;
859         bg = 0;
860     }
861
862
863     if((status == QMovie::EndOfMovie && (!m || m->frameNumber() <= 1)) ||
864        ((status == QMovie::EndOfLoop) && (m_showAnimations == KHTMLSettings::KAnimationLoopOnce)) ||
865        ((status == QMovie::EndOfFrame) && (m_showAnimations == KHTMLSettings::KAnimationDisabled))
866       )
867     {
868         if(imgSource)
869         {
870             setShowAnimations( KHTMLSettings::KAnimationDisabled );
871
872             // monochrome alphamasked images are usually about 10000 times
873             // faster to draw, so this is worth the hack
874             if (p && monochrome && p->depth() > 1 )
875             {
876                 QPixmap* pix = new QPixmap;
877                 pix->convertFromImage( p->convertToImage().convertDepth( 1 ), MonoOnly|AvoidDither );
878                 if ( p->mask() )
879                     pix->setMask( *p->mask() );
880                 delete p;
881                 p = pix;
882                 monochrome = false;
883             }
884         }
885
886         CachedObjectClientWalker w(m_clients);
887         while (CachedObjectClient *c = w.next())
888             c->notifyFinished(this);
889     }
890
891     if((status == QMovie::EndOfFrame) || (status == QMovie::EndOfMovie))
892     {
893 #ifdef CACHE_DEBUG
894         QRect r(valid_rect());
895         qDebug("movie Status frame update %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
896                pixmap().size().width(), pixmap().size().height());
897 #endif
898             do_notify(pixmap(), valid_rect());
899     }
900 }
901
902 void CachedImage::movieResize(const QSize& /*s*/)
903 {
904 //    do_notify(m->framePixmap(), QRect());
905 }
906
907 #endif // APPLE_CHANGES
908
909 void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
910 {
911     m_showAnimations = showAnimations;
912 #if !APPLE_CHANGES
913     if ( (m_showAnimations == KHTMLSettings::KAnimationDisabled) && imgSource ) {
914         imgSource->cleanBuffer();
915         delete p;
916         p = new QPixmap(m->framePixmap());
917
918         m->disconnectUpdate( this, SLOT( movieUpdated( const QRect &) ));
919         m->disconnectStatus( this, SLOT( movieStatus( int ) ));
920         m->disconnectResize( this, SLOT( movieResize( const QSize& ) ) );
921         QTimer::singleShot(0, this, SLOT( deleteMovie()));
922         imgSource = 0;
923     }
924 #endif
925 }
926
927 #if !APPLE_CHANGES
928
929 void CachedImage::deleteMovie()
930 {
931     delete m; m = 0;
932 }
933
934 #endif // APPLE_CHANGES
935
936 void CachedImage::clear()
937 {
938     delete m;   m = 0;
939     delete p;   p = 0;
940     delete bg;  bg = 0;
941 #if !APPLE_CHANGES
942     bgColor = qRgba( 0, 0, 0, 0xff );
943 #endif
944     delete pixPart; pixPart = 0;
945
946     formatType = 0;
947
948 #if !APPLE_CHANGES
949     typeChecked = false;
950 #endif
951     setSize(0);
952
953     // No need to delete imageSource - QMovie does it for us
954     imgSource = 0;
955
956 #if APPLE_CHANGES
957     if (m_decoderCallback) {
958         m_decoderCallback->clear();
959         m_decoderCallback->deref();
960         m_decoderCallback = 0;
961     }
962 #endif
963 }
964
965 void CachedImage::data ( QBuffer &_buffer, bool eof )
966 {
967 #ifdef CACHE_DEBUG
968     kdDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << endl;
969 #endif
970
971 #if !APPLE_CHANGES
972     if ( !typeChecked )
973     {
974         formatType = QImageDecoder::formatName( (const uchar*)_buffer.buffer().data(), _buffer.size());
975
976         typeChecked = true;
977
978         if ( formatType )  // movie format exists
979         {
980             imgSource = new ImageSource( _buffer.buffer());
981             m = new QMovie( imgSource, 8192 );
982             m->connectUpdate( this, SLOT( movieUpdated( const QRect &) ));
983             m->connectStatus( this, SLOT( movieStatus(int)));
984             m->connectResize( this, SLOT( movieResize( const QSize& ) ) );
985         }
986     }
987
988     if ( imgSource )
989     {
990         imgSource->setEOF(eof);
991         imgSource->maybeReady();
992     }
993
994     if(eof)
995     {
996         // QMovie currently doesn't support all kinds of image formats
997         // so we need to use a QPixmap here when we finished loading the complete
998         // picture and display it then all at once.
999         if(typeChecked && !formatType)
1000         {
1001 #ifdef CACHE_DEBUG
1002             kdDebug(6060) << "CachedImage::data(): reloading as pixmap:" << endl;
1003 #endif
1004             p = new QPixmap( _buffer.buffer() );
1005             // set size of image.
1006 #ifdef CACHE_DEBUG
1007             kdDebug(6060) << "CachedImage::data(): image is null: " << p->isNull() << endl;
1008 #endif
1009                 if(p->isNull())
1010                 {
1011                     errorOccured = true;
1012                     do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
1013                 }
1014                 else
1015                     do_notify(*p, p->rect());
1016         }
1017
1018         QSize s = pixmap_size();
1019         setSize(s.width() * s.height() * 2);
1020     }
1021 #else // APPLE_CHANGES
1022     bool canDraw = false;
1023     
1024     m_dataSize = _buffer.size();
1025         
1026     // If we're at eof and don't have a pixmap yet, the data
1027     // must have arrived in one chunk.  This avoids the attempt
1028     // to perform incremental decoding.
1029     if (eof && !p) {
1030         p = new QPixmap(_buffer.buffer(), KWQResponseMIMEType(m_response));
1031         canDraw = true;
1032     } else {
1033         // Always attempt to load the image incrementally.
1034         if (!p)
1035             p = new QPixmap(KWQResponseMIMEType(m_response));
1036         canDraw = p->receivedData(_buffer.buffer(), eof, m_decoderCallback);
1037     }
1038     
1039     // If we have a decoder, we'll be notified when decoding has completed.
1040     if (!m_decoderCallback) {
1041         if (canDraw || eof) {
1042             if (p->isNull()) {
1043                 errorOccured = true;
1044                 QPixmap ep = pixmap();
1045                 do_notify (ep, ep.rect());
1046                 Cache::removeCacheEntry (this);
1047             }
1048             else
1049                 do_notify(*p, p->rect());
1050
1051             QSize s = pixmap_size();
1052             setSize(s.width() * s.height() * 2);
1053         }
1054         if (eof) {
1055             m_loading = false;
1056             checkNotify();
1057         }
1058     }
1059 #endif // APPLE_CHANGES
1060 }
1061
1062 void CachedImage::error( int /*err*/, const char */*text*/ )
1063 {
1064 #ifdef CACHE_DEBUG
1065     kdDebug(6060) << "CahcedImage::error" << endl;
1066 #endif
1067
1068     clear();
1069 #if !APPLE_CHANGES
1070     typeChecked = true;
1071 #endif
1072     errorOccured = true;
1073     do_notify(pixmap(), QRect(0, 0, 16, 16));
1074 #if APPLE_CHANGES
1075     m_loading = false;
1076     checkNotify();
1077 #endif
1078 }
1079
1080 void CachedImage::checkNotify()
1081 {
1082     if(m_loading) return;
1083
1084     CachedObjectClientWalker w(m_clients);
1085     while (CachedObjectClient *c = w.next()) {
1086         c->notifyFinished(this);
1087     }
1088 }
1089
1090 // -------------------------------------------------------------------------------------------
1091
1092 #ifdef KHTML_XSLT
1093
1094 CachedXSLStyleSheet::CachedXSLStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
1095 : CachedObject(url, XSLStyleSheet, _cachePolicy, _expireDate)
1096 {
1097     // It's XML we want.
1098     setAccept(QString::fromLatin1("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml"));
1099     
1100     // load the file
1101     Cache::loader()->load(dl, this, false);
1102     m_loading = true;
1103     m_codec = QTextCodec::codecForName("iso8859-1");
1104 }
1105
1106 void CachedXSLStyleSheet::ref(CachedObjectClient *c)
1107 {
1108     CachedObject::ref(c);
1109     
1110     if (!m_loading)
1111         c->setStyleSheet(m_url, m_sheet);
1112 }
1113
1114 void CachedXSLStyleSheet::deref(CachedObjectClient *c)
1115 {
1116     Cache::flush();
1117     CachedObject::deref(c);
1118     if (canDelete() && m_free)
1119         delete this;
1120 }
1121
1122 void CachedXSLStyleSheet::data(QBuffer &buffer, bool eof)
1123 {
1124     if(!eof) return;
1125     buffer.close();
1126     setSize(buffer.buffer().size());
1127     QString data = m_codec->toUnicode( buffer.buffer().data(), size() );
1128     m_sheet = DOMString(data);
1129     m_loading = false;
1130     
1131     checkNotify();
1132 }
1133
1134 void CachedXSLStyleSheet::checkNotify()
1135 {
1136     if (m_loading)
1137         return;
1138     
1139 #ifdef CACHE_DEBUG
1140     kdDebug( 6060 ) << "CachedCSSStyleSheet:: finishedLoading " << m_url.string() << endl;
1141 #endif
1142     
1143     CachedObjectClientWalker w(m_clients);
1144     while (CachedObjectClient *c = w.next())
1145         c->setStyleSheet(m_url, m_sheet);
1146 }
1147
1148
1149 void CachedXSLStyleSheet::error( int /*err*/, const char */*text*/ )
1150 {
1151     m_loading = false;
1152     checkNotify();
1153 }
1154
1155 #endif
1156
1157 #ifndef KHTML_NO_XBL
1158 CachedXBLDocument::CachedXBLDocument(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
1159 : CachedObject(url, XBL, _cachePolicy, _expireDate), m_document(0)
1160 {
1161     // It's XML we want.
1162     setAccept( QString::fromLatin1("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml") );
1163     
1164     // Load the file
1165     Cache::loader()->load(dl, this, false);
1166     m_loading = true;
1167     m_codec = QTextCodec::codecForName("iso8859-1");
1168 }
1169
1170 CachedXBLDocument::~CachedXBLDocument()
1171 {
1172     if (m_document)
1173         m_document->deref();
1174 }
1175
1176 void CachedXBLDocument::ref(CachedObjectClient *c)
1177 {
1178     CachedObject::ref(c);
1179     if (!m_loading)
1180         c->setXBLDocument(m_url, m_document);
1181 }
1182
1183 void CachedXBLDocument::deref(CachedObjectClient *c)
1184 {
1185     Cache::flush();
1186     CachedObject::deref(c);
1187     if (canDelete() && m_free)
1188         delete this;
1189 }
1190
1191 void CachedXBLDocument::data( QBuffer &buffer, bool eof )
1192 {
1193     if (!eof) return;
1194     
1195     assert(!m_document);
1196     
1197     m_document =  new XBL::XBLDocumentImpl();
1198     m_document->ref();
1199     m_document->open();
1200     
1201     QString data = m_codec->toUnicode(buffer.buffer().data(), buffer.buffer().size());
1202     m_document->write(data);
1203     setSize(buffer.buffer().size());
1204     buffer.close();
1205     
1206     m_document->finishParsing();
1207     m_document->close();
1208     m_loading = false;
1209     checkNotify();
1210 }
1211
1212 void CachedXBLDocument::checkNotify()
1213 {
1214     if(m_loading) return;
1215     
1216 #ifdef CACHE_DEBUG
1217     kdDebug( 6060 ) << "CachedXBLDocument:: finishedLoading " << m_url.string() << endl;
1218 #endif
1219     
1220     CachedObjectClientWalker w(m_clients);
1221     while (CachedObjectClient *c = w.next())
1222         c->setXBLDocument(m_url, m_document);
1223 }
1224
1225
1226 void CachedXBLDocument::error( int /*err*/, const char */*text*/ )
1227 {
1228     m_loading = false;
1229     checkNotify();
1230 }
1231 #endif
1232
1233 // ------------------------------------------------------------------------------------------
1234
1235 Request::Request(DocLoader* dl, CachedObject *_object, bool _incremental)
1236 {
1237     object = _object;
1238     object->setRequest(this);
1239     incremental = _incremental;
1240     m_docLoader = dl;
1241 }
1242
1243 Request::~Request()
1244 {
1245     object->setRequest(0);
1246 }
1247
1248 // ------------------------------------------------------------------------------------------
1249
1250 DocLoader::DocLoader(KHTMLPart* part, DocumentImpl* doc)
1251 {
1252     m_cachePolicy = KIO::CC_Verify;
1253     m_expireDate = 0;
1254     m_bautoloadImages = true;
1255     m_showAnimations = KHTMLSettings::KAnimationEnabled;
1256     m_part = part;
1257     m_doc = doc;
1258
1259 #if APPLE_CHANGES
1260     Cache::init();
1261 #endif
1262     Cache::docloader->append( this );
1263 }
1264
1265 DocLoader::~DocLoader()
1266 {
1267     Cache::docloader->remove( this );
1268 }
1269
1270 void DocLoader::setExpireDate(time_t _expireDate)
1271 {
1272     m_expireDate = _expireDate;
1273 }
1274
1275 bool DocLoader::needReload(const KURL &fullURL)
1276 {
1277     bool reload = false;
1278     if (m_cachePolicy == KIO::CC_Verify)
1279     {
1280        if (!m_reloadedURLs.contains(fullURL.url()))
1281        {
1282           CachedObject *existing = Cache::cache->find(fullURL.url());
1283           if (existing && existing->isExpired())
1284           {
1285              Cache::removeCacheEntry(existing);
1286              m_reloadedURLs.append(fullURL.url());
1287              reload = true;
1288           }
1289        }
1290     }
1291     else if ((m_cachePolicy == KIO::CC_Reload) || (m_cachePolicy == KIO::CC_Refresh))
1292     {
1293        if (!m_reloadedURLs.contains(fullURL.url()))
1294        {
1295           CachedObject *existing = Cache::cache->find(fullURL.url());
1296           if (existing)
1297           {
1298              Cache::removeCacheEntry(existing);
1299           }
1300           m_reloadedURLs.append(fullURL.url());
1301           reload = true;
1302        }
1303     }
1304     return reload;
1305 }
1306
1307 CachedImage *DocLoader::requestImage( const DOM::DOMString &url)
1308 {
1309     KURL fullURL = m_doc->completeURL( url.string() );
1310     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1311
1312 #if APPLE_CHANGES
1313     if (KWQCheckIfReloading(this)) {
1314         setCachePolicy(KIO::CC_Reload);
1315     }
1316 #endif
1317
1318     bool reload = needReload(fullURL);
1319
1320 #if APPLE_CHANGES
1321     CachedImage *cachedObject = Cache::requestImage(this, fullURL, reload, m_expireDate);
1322     KWQCheckCacheObjectStatus(this, cachedObject);
1323     return cachedObject;
1324 #else
1325     return Cache::requestImage(this, fullURL, reload, m_expireDate);
1326 #endif
1327 }
1328
1329 CachedCSSStyleSheet *DocLoader::requestStyleSheet( const DOM::DOMString &url, const QString& charset)
1330 {
1331     KURL fullURL = m_doc->completeURL( url.string() );
1332     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1333
1334 #if APPLE_CHANGES
1335     if (KWQCheckIfReloading(this)) {
1336         setCachePolicy(KIO::CC_Reload);
1337     }
1338 #endif
1339
1340     bool reload = needReload(fullURL);
1341
1342 #if APPLE_CHANGES
1343     CachedCSSStyleSheet *cachedObject = Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
1344     KWQCheckCacheObjectStatus(this, cachedObject);
1345     return cachedObject;
1346 #else
1347     return Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
1348 #endif
1349 }
1350
1351 CachedScript *DocLoader::requestScript( const DOM::DOMString &url, const QString& charset)
1352 {
1353     KURL fullURL = m_doc->completeURL( url.string() );
1354     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1355
1356 #if APPLE_CHANGES
1357     if (KWQCheckIfReloading(this)) {
1358         setCachePolicy(KIO::CC_Reload);
1359     }
1360 #endif
1361
1362     bool reload = needReload(fullURL);
1363
1364 #if APPLE_CHANGES
1365     CachedScript *cachedObject = Cache::requestScript(this, url, reload, m_expireDate, charset);
1366     KWQCheckCacheObjectStatus(this, cachedObject);
1367     return cachedObject;
1368 #else
1369     return Cache::requestScript(this, url, reload, m_expireDate, charset);
1370 #endif
1371 }
1372
1373 #ifdef KHTML_XSLT
1374 CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const DOM::DOMString &url)
1375 {
1376     KURL fullURL = m_doc->completeURL(url.string());
1377     
1378     if (m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1379     
1380 #if APPLE_CHANGES
1381     if (KWQCheckIfReloading(this))
1382         setCachePolicy(KIO::CC_Reload);
1383 #endif
1384     
1385     bool reload = needReload(fullURL);
1386     
1387 #if APPLE_CHANGES
1388     CachedXSLStyleSheet *cachedObject = Cache::requestXSLStyleSheet(this, url, reload, m_expireDate);
1389     KWQCheckCacheObjectStatus(this, cachedObject);
1390     return cachedObject;
1391 #else
1392     return Cache::requestXSLStyleSheet(this, url, reload, m_expireDate);
1393 #endif
1394 }
1395 #endif
1396
1397 #ifndef KHTML_NO_XBL
1398 CachedXBLDocument* DocLoader::requestXBLDocument(const DOM::DOMString &url)
1399 {
1400     KURL fullURL = m_doc->completeURL(url.string());
1401     
1402     // FIXME: Is this right for XBL?
1403     if (m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1404     
1405 #if APPLE_CHANGES
1406     if (KWQCheckIfReloading(this)) {
1407         setCachePolicy(KIO::CC_Reload);
1408     }
1409 #endif
1410     
1411     bool reload = needReload(fullURL);
1412     
1413 #if APPLE_CHANGES
1414     CachedXBLDocument *cachedObject = Cache::requestXBLDocument(this, url, reload, m_expireDate);
1415     KWQCheckCacheObjectStatus(this, cachedObject);
1416     return cachedObject;
1417 #else
1418     return Cache::requestXBLDocument(this, url, reload, m_expireDate);
1419 #endif
1420 }
1421 #endif
1422
1423 void DocLoader::setAutoloadImages( bool enable )
1424 {
1425     if ( enable == m_bautoloadImages )
1426         return;
1427
1428     m_bautoloadImages = enable;
1429
1430     if ( !m_bautoloadImages ) return;
1431
1432     for ( const CachedObject* co=m_docObjects.first(); co; co=m_docObjects.next() )
1433         if ( co->type() == CachedObject::Image )
1434         {
1435             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
1436
1437             CachedObject::Status status = img->status();
1438             if ( status != CachedObject::Unknown )
1439                 continue;
1440
1441             Cache::loader()->load(this, img, true);
1442         }
1443 }
1444
1445 void DocLoader::setCachePolicy( KIO::CacheControl cachePolicy )
1446 {
1447     m_cachePolicy = cachePolicy;
1448 }
1449
1450 void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
1451 {
1452     if ( showAnimations == m_showAnimations ) return;
1453     m_showAnimations = showAnimations;
1454
1455     const CachedObject* co;
1456     for ( co=m_docObjects.first(); co; co=m_docObjects.next() )
1457         if ( co->type() == CachedObject::Image )
1458         {
1459             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
1460
1461             img->setShowAnimations( showAnimations );
1462         }
1463 }
1464
1465 void DocLoader::removeCachedObject( CachedObject* o ) const
1466 {
1467     m_docObjects.removeRef( o );
1468 }
1469
1470 // ------------------------------------------------------------------------------------------
1471
1472 Loader::Loader() : QObject()
1473 {
1474     m_requestsPending.setAutoDelete( true );
1475     m_requestsLoading.setAutoDelete( true );
1476 #if APPLE_CHANGES
1477     kwq = new KWQLoader(this);
1478 #endif
1479 }
1480
1481 Loader::~Loader()
1482 {
1483 #if APPLE_CHANGES
1484     delete kwq;
1485 #endif
1486 }
1487
1488 void Loader::load(DocLoader* dl, CachedObject *object, bool incremental)
1489 {
1490     Request *req = new Request(dl, object, incremental);
1491     m_requestsPending.append(req);
1492
1493     emit requestStarted( req->m_docLoader, req->object );
1494
1495     servePendingRequests();
1496 }
1497
1498 void Loader::servePendingRequests()
1499 {
1500   if ( m_requestsPending.count() == 0 )
1501       return;
1502
1503   // get the first pending request
1504   Request *req = m_requestsPending.take(0);
1505
1506 #ifdef CACHE_DEBUG
1507   kdDebug( 6060 ) << "starting Loader url=" << req->object->url().string() << endl;
1508 #endif
1509
1510   KURL u(req->object->url().string());
1511 #if APPLE_CHANGES
1512   KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/, true);
1513 #else
1514   KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/);
1515 #endif
1516   
1517   job->addMetaData("cache", getCacheControlString(req->object->cachePolicy()));
1518   if (!req->object->accept().isEmpty())
1519       job->addMetaData("accept", req->object->accept());
1520   if ( req->m_docLoader )  {
1521       KURL r = req->m_docLoader->doc()->URL();
1522       if ( r.protocol().startsWith( "http" ) && r.path().isEmpty() )
1523           r.setPath( "/" );
1524
1525       job->addMetaData("referrer", r.url());
1526       QString domain = r.host();
1527       if (req->m_docLoader->doc()->isHTMLDocument())
1528          domain = static_cast<HTMLDocumentImpl*>(req->m_docLoader->doc())->domain().string();
1529       if (crossDomain(u.host(), domain))
1530          job->addMetaData("cross-domain", "true");
1531   }
1532
1533 #if APPLE_CHANGES
1534   connect( job, SIGNAL( result( KIO::Job *, NSData *) ), this, SLOT( slotFinished( KIO::Job *, NSData *) ) );
1535 #else
1536   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinished( KIO::Job * ) ) );
1537 #endif
1538   
1539 #if APPLE_CHANGES
1540   connect( job, SIGNAL( data( KIO::Job*, const char *, int)),
1541            SLOT( slotData( KIO::Job*, const char *, int)));
1542   connect( job, SIGNAL( receivedResponse( KIO::Job *, NSURLResponse *)), SLOT( slotReceivedResponse( KIO::Job *, NSURLResponse *)) );
1543
1544   if (KWQServeRequest(this, req, job)) {
1545       if (req->object->type() == CachedObject::Image) {
1546         CachedImage *ci = static_cast<CachedImage*>(req->object);
1547         if (ci->decoderCallback()) {
1548             m_requestsBackgroundDecoding.append(req);
1549         }
1550       }
1551       m_requestsLoading.insert(job, req);
1552   }
1553 #else
1554   connect( job, SIGNAL( data( KIO::Job*, const QByteArray &)),
1555            SLOT( slotData( KIO::Job*, const QByteArray &)));
1556
1557   if ( req->object->schedule() )
1558       KIO::Scheduler::scheduleJob( job );
1559
1560   m_requestsLoading.insert(job, req);
1561 #endif // APPLE_CHANGES
1562 }
1563
1564 #if !APPLE_CHANGES
1565 void Loader::slotFinished( KIO::Job* job)
1566 {
1567   Request *r = m_requestsLoading.take( job );
1568   KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
1569
1570   if ( !r )
1571     return;
1572
1573   if (j->error() || j->isErrorPage())
1574   {
1575       kdDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage() << endl;
1576       r->object->error( job->error(), job->errorText().ascii() );
1577       emit requestFailed( r->m_docLoader, r->object );
1578   }
1579   else
1580   {
1581       r->object->data(r->m_buffer, true);
1582
1583       emit requestDone( r->m_docLoader, r->object );
1584       time_t expireDate = j->queryMetaData("expire-date").toLong();
1585 kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << " expires " << ctime(&expireDate) << endl;
1586       r->object->setExpireDate(expireDate, false);
1587   }
1588
1589   r->object->finish();
1590
1591 #ifdef CACHE_DEBUG
1592   kdDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string() << endl;
1593 #endif
1594
1595   delete r;
1596
1597   servePendingRequests();
1598 }
1599 #else // APPLE_CHANGES
1600 void Loader::slotFinished( KIO::Job* job, NSData *allData)
1601 {
1602     Request *r = m_requestsLoading.take( job );
1603     KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
1604
1605     if ( !r )
1606         return;
1607
1608     CachedObject *object = r->object;
1609     DocLoader *docLoader = r->m_docLoader;
1610     
1611     bool backgroundImageDecoding = (object->type() == CachedObject::Image && 
1612         static_cast<CachedImage*>(object)->decoderCallback());
1613         
1614     if (j->error() || j->isErrorPage()) {
1615         // Use the background image decoder's callback to handle the error.
1616         if (backgroundImageDecoding) {
1617             CachedImageCallback *callback = static_cast<CachedImage*>(object)->decoderCallback();
1618             callback->handleError();
1619         }
1620         else {
1621             r->object->error( job->error(), job->errorText().ascii() );
1622             emit requestFailed( docLoader, object );
1623             Cache::removeCacheEntry( object );
1624         }
1625     }
1626     else {
1627         object->data(r->m_buffer, true);
1628
1629         r->object->setAllData(allData);
1630
1631         // Let the background image decoder trigger the done signal.
1632         if (!backgroundImageDecoding)
1633             emit requestDone( docLoader, object );
1634
1635         object->finish();
1636     }
1637
1638     // Let the background image decoder release the request when it is
1639     // finished.
1640     if (!backgroundImageDecoding) {
1641         delete r;
1642     }
1643
1644     servePendingRequests();
1645 }
1646 #endif
1647
1648 #if APPLE_CHANGES
1649
1650 void Loader::slotReceivedResponse(KIO::Job* job, NSURLResponse *response)
1651 {
1652     Request *r = m_requestsLoading[job];
1653     ASSERT(r);
1654     ASSERT(response);
1655     r->object->setResponse(response);
1656     r->object->setExpireDate(KWQCacheObjectExpiresTime(r->m_docLoader, response), false);
1657 }
1658
1659 #endif
1660
1661 #if APPLE_CHANGES
1662 void Loader::slotData( KIO::Job*job, const char *data, int size )
1663 #else
1664 void Loader::slotData( KIO::Job*job, const QByteArray &data )
1665 #endif
1666 {
1667     Request *r = m_requestsLoading[job];
1668     if(!r) {
1669         kdDebug( 6060 ) << "got data for unknown request!" << endl;
1670         return;
1671     }
1672
1673     if ( !r->m_buffer.isOpen() )
1674         r->m_buffer.open( IO_WriteOnly );
1675
1676 #if APPLE_CHANGES
1677     r->m_buffer.writeBlock( data, size );
1678 #else
1679     r->m_buffer.writeBlock( data.data(), data.size() );
1680 #endif
1681
1682     if(r->incremental)
1683         r->object->data( r->m_buffer, false );
1684 }
1685
1686 int Loader::numRequests( DocLoader* dl ) const
1687 {
1688     int res = 0;
1689
1690     QPtrListIterator<Request> pIt( m_requestsPending );
1691     for (; pIt.current(); ++pIt )
1692         if ( pIt.current()->m_docLoader == dl )
1693             res++;
1694
1695     QPtrDictIterator<Request> lIt( m_requestsLoading );
1696     for (; lIt.current(); ++lIt )
1697         if ( lIt.current()->m_docLoader == dl )
1698             res++;
1699
1700 #if APPLE_CHANGES
1701     QPtrListIterator<Request> bdIt( m_requestsBackgroundDecoding );
1702     for (; bdIt.current(); ++bdIt )
1703         if ( bdIt.current()->m_docLoader == dl )
1704             res++;
1705 #endif
1706
1707     return res;
1708 }
1709
1710 void Loader::cancelRequests( DocLoader* dl )
1711 {
1712     //kdDebug( 6060 ) << "void Loader::cancelRequests()" << endl;
1713     //kdDebug( 6060 ) << "got " << m_requestsPending.count() << " pending requests" << endl;
1714     QPtrListIterator<Request> pIt( m_requestsPending );
1715     while ( pIt.current() )
1716     {
1717         if ( pIt.current()->m_docLoader == dl )
1718         {
1719             kdDebug( 6060 ) << "cancelling pending request for " << pIt.current()->object->url().string() << endl;
1720             //emit requestFailed( dl, pIt.current()->object );
1721             Cache::removeCacheEntry( pIt.current()->object );
1722             m_requestsPending.remove( pIt );
1723         }
1724         else
1725             ++pIt;
1726     }
1727
1728     //kdDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests" << endl;
1729
1730     QPtrDictIterator<Request> lIt( m_requestsLoading );
1731     while ( lIt.current() )
1732     {
1733         if ( lIt.current()->m_docLoader == dl )
1734         {
1735             //kdDebug( 6060 ) << "cancelling loading request for " << lIt.current()->object->url().string() << endl;
1736             KIO::Job *job = static_cast<KIO::Job *>( lIt.currentKey() );
1737             Cache::removeCacheEntry( lIt.current()->object );
1738             m_requestsLoading.remove( lIt.currentKey() );
1739             job->kill();
1740             //emit requestFailed( dl, pIt.current()->object );
1741         }
1742         else
1743             ++lIt;
1744     }
1745
1746 #if APPLE_CHANGES
1747     QPtrListIterator<Request> bdIt( m_requestsBackgroundDecoding );
1748     while ( bdIt.current() )
1749     {
1750         if ( bdIt.current()->m_docLoader == dl )
1751         {
1752             kdDebug( 6060 ) << "cancelling pending request for " << bdIt.current()->object->url().string() << endl;
1753             //emit requestFailed( dl, bdIt.current()->object );
1754             Cache::removeCacheEntry( bdIt.current()->object );
1755             m_requestsBackgroundDecoding.remove( bdIt );
1756         }
1757         else
1758             ++bdIt;
1759     }
1760 #endif
1761 }
1762
1763 #if APPLE_CHANGES
1764 void Loader::removeBackgroundDecodingRequest (Request *r)
1765 {
1766     bool present = m_requestsBackgroundDecoding.containsRef(r);
1767     if (present) {
1768         m_requestsBackgroundDecoding.remove (r);
1769     }
1770 }
1771 #endif
1772
1773 KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
1774 {
1775     QPtrDictIterator<Request> it( m_requestsLoading );
1776
1777     for (; it.current(); ++it )
1778     {
1779         CachedObject *obj = it.current()->object;
1780
1781         if ( obj && obj->url() == url )
1782             return static_cast<KIO::Job *>( it.currentKey() );
1783     }
1784
1785     return 0;
1786 }
1787
1788 // ----------------------------------------------------------------------------
1789
1790
1791 LRUList::LRUList()
1792 :m_head(0), m_tail(0) 
1793 {}
1794
1795 LRUList::~LRUList()
1796 {}
1797
1798 QDict<CachedObject> *Cache::cache = 0;
1799 QPtrList<DocLoader>* Cache::docloader = 0;
1800 Loader *Cache::m_loader = 0;
1801
1802 int Cache::maxSize = DEFCACHESIZE;
1803 int Cache::maxCacheable = MAXCACHEABLE;
1804 int Cache::flushCount = 0;
1805
1806 QPixmap *Cache::nullPixmap = 0;
1807 QPixmap *Cache::brokenPixmap = 0;
1808
1809 CachedObject *Cache::m_headOfUncacheableList = 0;
1810 int Cache::m_totalSizeOfLRULists = 0;
1811 int Cache::m_countOfLRUAndUncacheableLists;
1812 LRUList *Cache::m_LRULists = 0;
1813
1814 void Cache::init()
1815 {
1816     if ( !cache )
1817         cache = new QDict<CachedObject>(401, true);
1818
1819     if ( !docloader )
1820         docloader = new QPtrList<DocLoader>;
1821
1822     if ( !nullPixmap )
1823         nullPixmap = new QPixmap;
1824
1825     if ( !brokenPixmap )
1826 #if APPLE_CHANGES
1827         brokenPixmap = KWQLoadPixmap("missing_image");
1828 #else
1829         brokenPixmap = new QPixmap(KHTMLFactory::instance()->iconLoader()->loadIcon("file_broken", KIcon::Desktop, 16, KIcon::DisabledState));
1830 #endif
1831
1832     if ( !m_loader )
1833         m_loader = new Loader();
1834 }
1835
1836 void Cache::clear()
1837 {
1838     if ( !cache ) return;
1839 #ifdef CACHE_DEBUG
1840     kdDebug( 6060 ) << "Cache: CLEAR!" << endl;
1841     statistics();
1842 #endif
1843     cache->setAutoDelete( true );
1844     delete cache; cache = 0;
1845     delete nullPixmap; nullPixmap = 0;
1846     delete brokenPixmap; brokenPixmap = 0;
1847     delete m_loader;   m_loader = 0;
1848     delete docloader; docloader = 0;
1849 }
1850
1851 CachedImage *Cache::requestImage( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate )
1852 {
1853     // this brings the _url to a standard form...
1854     KURL kurl;
1855     if (dl)
1856         kurl = dl->m_doc->completeURL( url.string() );
1857     else
1858         kurl = url.string();
1859     return requestImage(dl, kurl, reload, _expireDate);
1860 }
1861
1862 CachedImage *Cache::requestImage( DocLoader* dl, const KURL & url, bool reload, time_t _expireDate )
1863 {
1864     KIO::CacheControl cachePolicy;
1865     if (dl)
1866         cachePolicy = dl->cachePolicy();
1867     else
1868         cachePolicy = KIO::CC_Verify;
1869
1870 #if APPLE_CHANGES
1871     // Checking if the URL is malformed is lots of extra work for little benefit.
1872 #else
1873     if( kurl.isMalformed() )
1874     {
1875 #ifdef CACHE_DEBUG
1876       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1877 #endif
1878       return 0;
1879     }
1880 #endif
1881
1882 #if APPLE_CHANGES
1883     if (!dl->doc()->shouldCreateRenderers()){
1884         return 0;
1885     }
1886 #endif
1887
1888     CachedObject *o = 0;
1889     if (!reload)
1890         o = cache->find(url.url());
1891     if(!o)
1892     {
1893 #ifdef CACHE_DEBUG
1894         kdDebug( 6060 ) << "Cache: new: " << url.url() << endl;
1895 #endif
1896         CachedImage *im = new CachedImage(dl, url.url(), cachePolicy, _expireDate);
1897         if ( dl && dl->autoloadImages() ) Cache::loader()->load(dl, im, true);
1898 #if APPLE_CHANGES
1899         if (cacheDisabled)
1900             im->setFree(true);
1901         else {
1902 #endif
1903         cache->insert( url.url(), im );
1904         moveToHeadOfLRUList(im);
1905 #if APPLE_CHANGES
1906         }
1907 #endif
1908         o = im;
1909     }
1910
1911 #if !APPLE_CHANGES
1912     o->setExpireDate(_expireDate, true);
1913 #endif
1914     
1915     if(o->type() != CachedObject::Image)
1916     {
1917 #ifdef CACHE_DEBUG
1918         kdDebug( 6060 ) << "Cache::Internal Error in requestImage url=" << kurl.url() << "!" << endl;
1919 #endif
1920         return 0;
1921     }
1922
1923 #ifdef CACHE_DEBUG
1924     if( o->status() == CachedObject::Pending )
1925         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
1926     else
1927         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << ", status " << o->status() << endl;
1928 #endif
1929
1930     moveToHeadOfLRUList(o);
1931     if ( dl ) {
1932         dl->m_docObjects.remove( o );
1933 #if APPLE_CHANGES
1934         if (!cacheDisabled)
1935 #endif
1936         dl->m_docObjects.append( o );
1937     }
1938     return static_cast<CachedImage *>(o);
1939 }
1940
1941 CachedCSSStyleSheet *Cache::requestStyleSheet( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate, const QString& charset)
1942 {
1943     // this brings the _url to a standard form...
1944     KURL kurl;
1945     KIO::CacheControl cachePolicy;
1946     if ( dl )
1947     {
1948         kurl = dl->m_doc->completeURL( url.string() );
1949         cachePolicy = dl->cachePolicy();
1950     }
1951     else
1952     {
1953         kurl = url.string();
1954         cachePolicy = KIO::CC_Verify;
1955     }
1956
1957 #if APPLE_CHANGES
1958     // Checking if the URL is malformed is lots of extra work for little benefit.
1959 #else
1960     if( kurl.isMalformed() )
1961     {
1962       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1963       return 0;
1964     }
1965 #endif
1966
1967     CachedObject *o = cache->find(kurl.url());
1968     if(!o)
1969     {
1970 #ifdef CACHE_DEBUG
1971         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
1972 #endif
1973         CachedCSSStyleSheet *sheet = new CachedCSSStyleSheet(dl, kurl.url(), cachePolicy, _expireDate, charset);
1974 #if APPLE_CHANGES
1975         if (cacheDisabled)
1976             sheet->setFree(true);
1977         else {
1978 #endif
1979         cache->insert( kurl.url(), sheet );
1980         moveToHeadOfLRUList(sheet);
1981 #if APPLE_CHANGES
1982         }
1983 #endif
1984         o = sheet;
1985     }
1986
1987 #if !APPLE_CHANGES
1988     o->setExpireDate(_expireDate, true);
1989 #endif
1990     
1991     if(o->type() != CachedObject::CSSStyleSheet)
1992     {
1993 #ifdef CACHE_DEBUG
1994         kdDebug( 6060 ) << "Cache::Internal Error in requestStyleSheet url=" << kurl.url() << "!" << endl;
1995 #endif
1996         return 0;
1997     }
1998
1999 #ifdef CACHE_DEBUG
2000     if( o->status() == CachedObject::Pending )
2001         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2002     else
2003         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2004 #endif
2005
2006     moveToHeadOfLRUList(o);
2007     if ( dl ) {
2008         dl->m_docObjects.remove( o );
2009 #if APPLE_CHANGES
2010         if (!cacheDisabled)
2011 #endif
2012         dl->m_docObjects.append( o );
2013     }
2014     return static_cast<CachedCSSStyleSheet *>(o);
2015 }
2016
2017 void Cache::preloadStyleSheet( const QString &url, const QString &stylesheet_data)
2018 {
2019     CachedObject *o = cache->find(url);
2020     if(o)
2021         removeCacheEntry(o);
2022
2023     CachedCSSStyleSheet *stylesheet = new CachedCSSStyleSheet(url, stylesheet_data);
2024     cache->insert( url, stylesheet );
2025 }
2026
2027 CachedScript *Cache::requestScript( DocLoader* dl, const DOM::DOMString &url, bool reload, time_t _expireDate, const QString& charset)
2028 {
2029     // this brings the _url to a standard form...
2030     KURL kurl;
2031     KIO::CacheControl cachePolicy;
2032     if ( dl )
2033     {
2034         kurl = dl->m_doc->completeURL( url.string() );
2035         cachePolicy = dl->cachePolicy();
2036     }
2037     else
2038     {
2039         kurl = url.string();
2040         cachePolicy = KIO::CC_Verify;
2041     }
2042
2043 #if APPLE_CHANGES
2044     // Checking if the URL is malformed is lots of extra work for little benefit.
2045 #else
2046     if( kurl.isMalformed() )
2047     {
2048       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2049       return 0;
2050     }
2051 #endif
2052
2053     CachedObject *o = cache->find(kurl.url());
2054     if(!o)
2055     {
2056 #ifdef CACHE_DEBUG
2057         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2058 #endif
2059         CachedScript *script = new CachedScript(dl, kurl.url(), cachePolicy, _expireDate, charset);
2060 #if APPLE_CHANGES
2061         if (cacheDisabled)
2062             script->setFree(true);
2063         else {
2064 #endif
2065         cache->insert( kurl.url(), script );
2066         moveToHeadOfLRUList(script);
2067 #if APPLE_CHANGES
2068         }
2069 #endif
2070         o = script;
2071     }
2072
2073 #if !APPLE_CHANGES
2074     o->setExpireDate(_expireDate, true);
2075 #endif
2076     
2077     if(!(o->type() == CachedObject::Script))
2078     {
2079 #ifdef CACHE_DEBUG
2080         kdDebug( 6060 ) << "Cache::Internal Error in requestScript url=" << kurl.url() << "!" << endl;
2081 #endif
2082         return 0;
2083     }
2084     
2085     
2086 #ifdef CACHE_DEBUG
2087     if( o->status() == CachedObject::Pending )
2088         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2089     else
2090         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2091 #endif
2092
2093     moveToHeadOfLRUList(o);
2094     if ( dl ) {
2095         dl->m_docObjects.remove( o );
2096 #if APPLE_CHANGES
2097         if (!cacheDisabled)
2098 #endif
2099         dl->m_docObjects.append( o );
2100     }
2101     return static_cast<CachedScript *>(o);
2102 }
2103
2104 void Cache::preloadScript( const QString &url, const QString &script_data)
2105 {
2106     CachedObject *o = cache->find(url);
2107     if(o)
2108         removeCacheEntry(o);
2109
2110     CachedScript *script = new CachedScript(url, script_data);
2111     cache->insert( url, script );
2112 }
2113
2114 #ifdef KHTML_XSLT
2115 CachedXSLStyleSheet* Cache::requestXSLStyleSheet(DocLoader* dl, const DOMString & url, bool reload, 
2116                                                  time_t _expireDate)
2117 {
2118     // this brings the _url to a standard form...
2119     KURL kurl;
2120     KIO::CacheControl cachePolicy;
2121     if (dl) {
2122         kurl = dl->m_doc->completeURL(url.string());
2123         cachePolicy = dl->cachePolicy();
2124     }
2125     else {
2126         kurl = url.string();
2127         cachePolicy = KIO::CC_Verify;
2128     }
2129     
2130 #if APPLE_CHANGES
2131     // Checking if the URL is malformed is lots of extra work for little benefit.
2132 #else
2133     if(kurl.isMalformed()) {
2134         kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2135         return 0;
2136     }
2137 #endif
2138     
2139     CachedObject *o = cache->find(kurl.url());
2140     if (!o) {
2141 #ifdef CACHE_DEBUG
2142         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2143 #endif
2144         CachedXSLStyleSheet* doc = new CachedXSLStyleSheet(dl, kurl.url(), cachePolicy, _expireDate);
2145 #if APPLE_CHANGES
2146         if (cacheDisabled)
2147             doc->setFree(true);
2148         else {
2149 #endif
2150             cache->insert(kurl.url(), doc);
2151             moveToHeadOfLRUList(doc);
2152 #if APPLE_CHANGES
2153         }
2154 #endif
2155         o = doc;
2156     }
2157     
2158 #if !APPLE_CHANGES
2159     o->setExpireDate(_expireDate, true);
2160 #endif
2161     
2162     if (o->type() != CachedObject::XSLStyleSheet) {
2163 #ifdef CACHE_DEBUG
2164         kdDebug( 6060 ) << "Cache::Internal Error in requestXSLStyleSheet url=" << kurl.url() << "!" << endl;
2165 #endif
2166         return 0;
2167     }
2168     
2169 #ifdef CACHE_DEBUG
2170     if (o->status() == CachedObject::Pending)
2171         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2172     else
2173         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2174 #endif
2175     
2176     moveToHeadOfLRUList(o);
2177     if (dl) {
2178         dl->m_docObjects.remove( o );
2179 #if APPLE_CHANGES
2180         if (!cacheDisabled)
2181 #endif
2182             dl->m_docObjects.append( o );
2183     }
2184     return static_cast<CachedXSLStyleSheet*>(o);
2185 }
2186 #endif
2187
2188 #ifndef KHTML_NO_XBL
2189 CachedXBLDocument* Cache::requestXBLDocument(DocLoader* dl, const DOMString & url, bool reload, 
2190                                              time_t _expireDate)
2191 {
2192     // this brings the _url to a standard form...
2193     KURL kurl;
2194     KIO::CacheControl cachePolicy;
2195     if (dl) {
2196         kurl = dl->m_doc->completeURL(url.string());
2197         cachePolicy = dl->cachePolicy();
2198     }
2199     else {
2200         kurl = url.string();
2201         cachePolicy = KIO::CC_Verify;
2202     }
2203     
2204 #if APPLE_CHANGES
2205     // Checking if the URL is malformed is lots of extra work for little benefit.
2206 #else
2207     if( kurl.isMalformed() )
2208     {
2209         kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2210         return 0;
2211     }
2212 #endif
2213     
2214     CachedObject *o = cache->find(kurl.url());
2215     if(!o)
2216     {
2217 #ifdef CACHE_DEBUG
2218         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2219 #endif
2220         CachedXBLDocument* doc = new CachedXBLDocument(dl, kurl.url(), cachePolicy, _expireDate);
2221 #if APPLE_CHANGES
2222         if (cacheDisabled)
2223             doc->setFree(true);
2224         else {
2225 #endif
2226             cache->insert(kurl.url(), doc);
2227             moveToHeadOfLRUList(doc);
2228 #if APPLE_CHANGES
2229         }
2230 #endif
2231         o = doc;
2232     }
2233     
2234 #if !APPLE_CHANGES
2235     o->setExpireDate(_expireDate, true);
2236 #endif
2237     
2238     if(o->type() != CachedObject::XBL)
2239     {
2240 #ifdef CACHE_DEBUG
2241         kdDebug( 6060 ) << "Cache::Internal Error in requestXBLDocument url=" << kurl.url() << "!" << endl;
2242 #endif
2243         return 0;
2244     }
2245     
2246 #ifdef CACHE_DEBUG
2247     if( o->status() == CachedObject::Pending )
2248         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2249     else
2250         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2251 #endif
2252     
2253     moveToHeadOfLRUList(o);
2254     if ( dl ) {
2255         dl->m_docObjects.remove( o );
2256 #if APPLE_CHANGES
2257         if (!cacheDisabled)
2258 #endif
2259             dl->m_docObjects.append( o );
2260     }
2261     return static_cast<CachedXBLDocument*>(o);
2262 }
2263 #endif
2264
2265 void Cache::flush(bool force)
2266 {
2267     if (force)
2268        flushCount = 0;
2269     // Don't flush for every image.
2270     if (m_countOfLRUAndUncacheableLists < flushCount)
2271        return;
2272
2273     init();
2274
2275 #ifdef CACHE_DEBUG
2276     statistics();
2277     kdDebug( 6060 ) << "Cache: flush()" << endl;
2278 #endif
2279
2280     while (m_headOfUncacheableList)
2281         removeCacheEntry(m_headOfUncacheableList);
2282
2283     for (int i = MAX_LRU_LISTS-1; i>=0; i--) {
2284         if (m_totalSizeOfLRULists <= maxSize)
2285             break;
2286             
2287         while (m_totalSizeOfLRULists > maxSize && m_LRULists[i].m_tail)
2288             removeCacheEntry(m_LRULists[i].m_tail);
2289     }
2290
2291     flushCount = m_countOfLRUAndUncacheableLists+10; // Flush again when the cache has grown.
2292 #ifdef CACHE_DEBUG
2293     //statistics();
2294 #endif
2295 }
2296
2297 #if 0
2298
2299 void Cache::checkLRUAndUncacheableListIntegrity()
2300 {
2301     int count = 0;
2302     
2303     {
2304         int size = 0;
2305         CachedObject *prev = 0;
2306         for (CachedObject *o = m_headOfLRUList; o; o = o->m_nextInLRUList) {
2307             ASSERT(o->allowInLRUList());
2308             ASSERT(o->status() != CachedObject::Uncacheable);
2309             ASSERT(o->m_prevInLRUList == prev);
2310             size += o->size();
2311             prev = o;
2312             ++count;
2313         }
2314         ASSERT(m_tailOfLRUList == prev);
2315         ASSERT(m_totalSizeOfLRUList == size);
2316     }
2317     
2318     {
2319         CachedObject *prev = 0;
2320         for (CachedObject *o = m_headOfUncacheableList; o; o = o->m_nextInLRUList) {
2321             ASSERT(o->allowInLRUList());
2322             ASSERT(o->status() == CachedObject::Uncacheable);
2323             ASSERT(o->m_prevInLRUList == prev);
2324             prev = o;
2325             ++count;
2326         }
2327     }
2328     
2329     ASSERT(m_countOfLRUAndUncacheableLists == count);
2330 }
2331
2332 #endif
2333
2334 void Cache::setSize( int bytes )
2335 {
2336     maxSize = bytes;
2337     maxCacheable = maxSize / 128;
2338
2339     // may be we need to clear parts of the cache
2340     flushCount = 0;
2341     flush(true);
2342 }
2343
2344 void Cache::statistics()
2345 {
2346     CachedObject *o;
2347     // this function is for debugging purposes only
2348     init();
2349
2350     int size = 0;
2351     int msize = 0;
2352     int movie = 0;
2353     int stylesheets = 0;
2354     QDictIterator<CachedObject> it(*cache);
2355     for(it.toFirst(); it.current(); ++it)
2356     {
2357         o = it.current();
2358         if(o->type() == CachedObject::Image)
2359         {
2360             CachedImage *im = static_cast<CachedImage *>(o);
2361             if(im->m != 0)
2362             {
2363                 movie++;
2364                 msize += im->size();
2365             }
2366         }
2367         else
2368         {
2369             if(o->type() == CachedObject::CSSStyleSheet)
2370                 stylesheets++;
2371
2372         }
2373         size += o->size();
2374     }
2375     size /= 1024;
2376
2377     kdDebug( 6060 ) << "------------------------- image cache statistics -------------------" << endl;
2378     kdDebug( 6060 ) << "Number of items in cache: " << cache->count() << endl;
2379     kdDebug( 6060 ) << "Number of items in lru  : " << m_countOfLRUAndUncacheableLists << endl;
2380     kdDebug( 6060 ) << "Number of cached images: " << cache->count()-movie << endl;
2381     kdDebug( 6060 ) << "Number of cached movies: " << movie << endl;
2382     kdDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets << endl;
2383     kdDebug( 6060 ) << "pixmaps:   allocated space approx. " << size << " kB" << endl;
2384     kdDebug( 6060 ) << "movies :   allocated space approx. " << msize/1024 << " kB" << endl;
2385     kdDebug( 6060 ) << "--------------------------------------------------------------------" << endl;
2386 }
2387
2388 void Cache::removeCacheEntry( CachedObject *object )
2389 {
2390   QString key = object->url().string();
2391
2392   // this indicates the deref() method of CachedObject to delete itself when the reference counter
2393   // drops down to zero
2394   object->setFree( true );
2395
2396   cache->remove( key );
2397   removeFromLRUList(object);
2398
2399   const DocLoader* dl;
2400   for ( dl=docloader->first(); dl; dl=docloader->next() )
2401       dl->removeCachedObject( object );
2402
2403   if ( object->canDelete() )
2404      delete object;
2405 }
2406
2407 #define FAST_LOG2(_log2,_n)   \
2408       unsigned int j_ = (unsigned int)(_n);   \
2409       (_log2) = 0;                    \
2410       if ((j_) & ((j_)-1))            \
2411       (_log2) += 1;               \
2412       if ((j_) >> 16)                 \
2413       (_log2) += 16, (j_) >>= 16; \
2414       if ((j_) >> 8)                  \
2415       (_log2) += 8, (j_) >>= 8;   \
2416       if ((j_) >> 4)                  \
2417       (_log2) += 4, (j_) >>= 4;   \
2418       if ((j_) >> 2)                  \
2419       (_log2) += 2, (j_) >>= 2;   \
2420       if ((j_) >> 1)                  \
2421       (_log2) += 1;
2422
2423 int FastLog2(unsigned int i) {
2424     int log2;
2425     FAST_LOG2(log2,i);
2426     return log2;
2427 }
2428
2429 LRUList* Cache::getLRUListFor(CachedObject* o)
2430 {
2431     int accessCount = o->accessCount();
2432     int queueIndex;
2433     if (accessCount == 0) {
2434         queueIndex = 0;
2435     } else {
2436         int sizeLog = FastLog2(o->size());
2437         queueIndex = sizeLog/o->accessCount() - 1;
2438         if (queueIndex < 0)
2439             queueIndex = 0;
2440         if (queueIndex >= MAX_LRU_LISTS)
2441             queueIndex = MAX_LRU_LISTS-1;
2442     }
2443     if (m_LRULists == 0) {
2444         m_LRULists = new LRUList [MAX_LRU_LISTS];
2445     }
2446     return &m_LRULists[queueIndex];
2447 }
2448
2449 void Cache::removeFromLRUList(CachedObject *object)
2450 {
2451     CachedObject *next = object->m_nextInLRUList;
2452     CachedObject *prev = object->m_prevInLRUList;
2453     bool uncacheable = object->status() == CachedObject::Uncacheable;
2454     
2455     LRUList* list = uncacheable ? 0 : getLRUListFor(object);
2456     CachedObject *&head = uncacheable ? m_headOfUncacheableList : list->m_head;
2457     
2458     if (next == 0 && prev == 0 && head != object) {
2459         return;
2460     }
2461     
2462     object->m_nextInLRUList = 0;
2463     object->m_prevInLRUList = 0;
2464     
2465     if (next)
2466         next->m_prevInLRUList = prev;
2467     else if (!uncacheable && list->m_tail == object)
2468         list->m_tail = prev;
2469
2470     if (prev)
2471         prev->m_nextInLRUList = next;
2472     else if (head == object)
2473         head = next;
2474     
2475     --m_countOfLRUAndUncacheableLists;
2476     
2477     if (!uncacheable)
2478         m_totalSizeOfLRULists -= object->size();
2479 }
2480
2481 void Cache::moveToHeadOfLRUList(CachedObject *object)
2482 {
2483     insertInLRUList(object);
2484 }
2485
2486 void Cache::insertInLRUList(CachedObject *object)
2487 {
2488     removeFromLRUList(object);
2489     
2490     if (!object->allowInLRUList())
2491         return;
2492     
2493     LRUList* list = getLRUListFor(object);
2494     
2495     bool uncacheable = object->status() == CachedObject::Uncacheable;
2496     CachedObject *&head = uncacheable ? m_headOfUncacheableList : list->m_head;
2497
2498     object->m_nextInLRUList = head;
2499     if (head)
2500         head->m_prevInLRUList = object;
2501     head = object;
2502     
2503     if (object->m_nextInLRUList == 0 && !uncacheable)
2504         list->m_tail = object;
2505     
2506     ++m_countOfLRUAndUncacheableLists;
2507     
2508     if (!uncacheable)
2509         m_totalSizeOfLRULists += object->size();
2510 }
2511
2512 bool Cache::adjustSize(CachedObject *object, int delta)
2513 {
2514     if (object->status() == CachedObject::Uncacheable)
2515         return false;
2516
2517     if (object->m_nextInLRUList == 0 && object->m_prevInLRUList == 0 &&
2518         getLRUListFor(object)->m_head != object)
2519         return false;
2520     
2521     m_totalSizeOfLRULists += delta;
2522     return delta != 0;
2523 }
2524
2525 // --------------------------------------
2526
2527 CachedObjectClient *CachedObjectClientWalker::next()
2528 {
2529     // Only advance if we already returned this item.
2530     // This handles cases where the current item is removed, and prevents us from skipping the next item.
2531     // The iterator automatically gets advanced to the next item, and we make sure we return it.
2532     if (_current == _iterator.current())
2533         ++_iterator;
2534     _current = _iterator.current();
2535     return _current;
2536 }
2537
2538 // --------------------------------------
2539
2540 void CachedObjectClient::setPixmap(const QPixmap &, const QRect&, CachedImage *) {}
2541 void CachedObjectClient::setStyleSheet(const DOM::DOMString &/*url*/, const DOM::DOMString &/*sheet*/) {}
2542 #ifndef KHTML_NO_XBL
2543 void CachedObjectClient::setXBLDocument(const DOM::DOMString& url, XBL::XBLDocumentImpl* doc) {}
2544 #endif
2545 void CachedObjectClient::notifyFinished(CachedObject * /*finishedObj*/) {}
2546
2547 #include "loader.moc"
2548
2549
2550 #if APPLE_CHANGES
2551
2552 Cache::Statistics Cache::getStatistics()
2553 {
2554     Statistics stats;
2555
2556     if (!cache)
2557         return stats;
2558
2559     QDictIterator<CachedObject> i(*cache);
2560     for (i.toFirst(); i.current(); ++i) {
2561         CachedObject *o = i.current();
2562         switch (o->type()) {
2563             case CachedObject::Image:
2564                 if (static_cast<CachedImage *>(o)->m) {
2565                     stats.movies.count++;
2566                     stats.movies.size += o->size();
2567                 } else {
2568                     stats.images.count++;
2569                     stats.images.size += o->size();
2570                 }
2571                 break;
2572
2573             case CachedObject::CSSStyleSheet:
2574                 stats.styleSheets.count++;
2575                 stats.styleSheets.size += o->size();
2576                 break;
2577
2578             case CachedObject::Script:
2579                 stats.scripts.count++;
2580                 stats.scripts.size += o->size();
2581                 break;
2582 #ifdef KHTML_XSLT
2583             case CachedObject::XSLStyleSheet:
2584                 stats.xslStyleSheets.count++;
2585                 stats.xslStyleSheets.size += o->size();
2586                 break;
2587 #endif
2588 #ifndef KHTML_NO_XBL
2589             case CachedObject::XBL:
2590                 stats.xblDocs.count++;
2591                 stats.xblDocs.size += o->size();
2592                 break;
2593 #endif
2594             default:
2595                 stats.other.count++;
2596                 stats.other.size += o->size();
2597         }
2598     }
2599     
2600     return stats;
2601 }
2602
2603 void Cache::flushAll()
2604 {
2605     if (!cache)
2606         return;
2607
2608     for (;;) {
2609         QDictIterator<CachedObject> i(*cache);
2610         CachedObject *o = i.toFirst();
2611         if (!o)
2612             break;
2613         removeCacheEntry(o);
2614     }
2615 }
2616
2617 void Cache::setCacheDisabled(bool disabled)
2618 {
2619     cacheDisabled = disabled;
2620     if (disabled)
2621         flushAll();
2622 }
2623
2624 #endif // APPLE_CHANGES