WebCore:
[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
502 CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
503     : QObject(), CachedObject(url, Image, _cachePolicy, _expireDate)
504 #if APPLE_CHANGES
505     , m_dataSize(0)
506 #endif
507 {
508 #if !APPLE_CHANGES
509     static const QString &acceptHeader = KGlobal::staticQString( buildAcceptHeader() );
510 #endif
511
512     m = 0;
513     p = 0;
514     pixPart = 0;
515     bg = 0;
516 #if !APPLE_CHANGES
517     bgColor = qRgba( 0, 0, 0, 0xFF );
518     typeChecked = false;
519 #endif
520     isFullyTransparent = false;
521     errorOccured = false;
522     monochrome = false;
523     formatType = 0;
524     m_status = Unknown;
525     imgSource = 0;
526     m_loading = true;
527 #if !APPLE_CHANGES
528     setAccept( acceptHeader );
529 #endif
530     m_showAnimations = dl->showAnimations();
531 }
532
533 CachedImage::~CachedImage()
534 {
535     clear();
536 }
537
538 void CachedImage::ref( CachedObjectClient *c )
539 {
540 #ifdef CACHE_DEBUG
541     kdDebug( 6060 ) << this << " CachedImage::ref(" << c << ") " << endl;
542 #endif
543
544     CachedObject::ref(c);
545
546     if( m ) {
547         m->unpause();
548         if( m->finished() || m_clients.count() == 1 )
549             m->restart();
550     }
551
552     // for mouseovers, dynamic changes
553     if (!valid_rect().isNull())
554         c->setPixmap( pixmap(), valid_rect(), this);
555
556     if(!m_loading) c->notifyFinished(this);
557 }
558
559 void CachedImage::deref( CachedObjectClient *c )
560 {
561 #ifdef CACHE_DEBUG
562     kdDebug( 6060 ) << this << " CachedImage::deref(" << c << ") " << endl;
563 #endif
564     Cache::flush();
565     CachedObject::deref(c);
566     if(m && m_clients.isEmpty() && m->running())
567         m->pause();
568     if ( canDelete() && m_free )
569         delete this;
570 }
571
572 #define BGMINWIDTH      32
573 #define BGMINHEIGHT     32
574
575 const QPixmap &CachedImage::tiled_pixmap(const QColor& newc)
576 {
577 #if APPLE_CHANGES
578     return pixmap();
579 #else
580     static QRgb bgTransparant = qRgba( 0, 0, 0, 0xFF );
581     if ( (bgColor != bgTransparant) && (bgColor != newc.rgb()) ) {
582         delete bg; bg = 0;
583     }
584
585     if (bg)
586         return *bg;
587
588     const QPixmap &r = pixmap();
589
590     if (r.isNull()) return r;
591
592     // no error indication for background images
593     if(errorOccured) return *Cache::nullPixmap;
594
595     bool isvalid = newc.isValid();
596     QSize s(pixmap_size());
597     int w = r.width();
598     int h = r.height();
599     if ( w*h < 8192 )
600     {
601         if ( r.width() < BGMINWIDTH )
602             w = ((BGMINWIDTH  / s.width())+1) * s.width();
603         if ( r.height() < BGMINHEIGHT )
604             h = ((BGMINHEIGHT / s.height())+1) * s.height();
605     }
606     if ( (w != r.width()) || (h != r.height()) || (isvalid && r.mask()))
607     {
608         QPixmap pix = r;
609         if ( w != r.width() || (isvalid && pix.mask()))
610         {
611             bg = new QPixmap(w, r.height());
612             QPainter p(bg);
613             if(isvalid) p.fillRect(0, 0, w, r.height(), newc);
614             p.drawTiledPixmap(0, 0, w, r.height(), pix);
615             if(!isvalid && pix.mask())
616             {
617                 // unfortunately our anti-transparency trick doesn't work here
618                 // we need to create a mask.
619                 QBitmap newmask(w, r.height());
620                 QPainter pm(&newmask);
621                 pm.drawTiledPixmap(0, 0, w, r.height(), *pix.mask());
622                 bg->setMask(newmask);
623                 bgColor = bgTransparant;
624             }
625             else
626                 bgColor= newc.rgb();
627             pix = *bg;
628         }
629         if ( h != r.height() )
630         {
631             delete bg;
632             bg = new QPixmap(w, h);
633             QPainter p(bg);
634             if(isvalid) p.fillRect(0, 0, w, h, newc);
635             p.drawTiledPixmap(0, 0, w, h, pix);
636             if(!isvalid && pix.mask())
637             {
638                 // unfortunately our anti-transparency trick doesn't work here
639                 // we need to create a mask.
640                 QBitmap newmask(w, h);
641                 QPainter pm(&newmask);
642                 pm.drawTiledPixmap(0, 0, w, h, *pix.mask());
643                 bg->setMask(newmask);
644                 bgColor = bgTransparant;
645             }
646             else
647                 bgColor= newc.rgb();
648         }
649         return *bg;
650     }
651
652     return r;
653 #endif
654 }
655
656 const QPixmap &CachedImage::pixmap( ) const
657 {
658     if(errorOccured)
659         return *Cache::brokenPixmap;
660
661 #if APPLE_CHANGES
662     if (p)
663         return *p;
664 #else
665     if(m)
666     {
667         if(m->framePixmap().size() != m->getValidRect().size() && m->getValidRect().size().isValid())
668         {
669             // pixmap is not yet completely loaded, so we
670             // return a clipped version. asserting here
671             // that the valid rect is always from 0/0 to fullwidth/ someheight
672             if(!pixPart) pixPart = new QPixmap(m->getValidRect().size());
673
674             (*pixPart) = m->framePixmap();
675             pixPart->resize(m->getValidRect().size());
676             return *pixPart;
677         }
678         else
679             return m->framePixmap();
680     }
681     else if(p)
682         return *p;
683 #endif // APPLE_CHANGES
684
685     return *Cache::nullPixmap;
686 }
687
688
689 QSize CachedImage::pixmap_size() const
690 {
691     return (m ? m->framePixmap().size() : ( p ? p->size() : QSize()));
692 }
693
694
695 QRect CachedImage::valid_rect() const
696 {
697     return m ? m->getValidRect() : ( p ? p->rect() : QRect());
698 }
699
700
701 void CachedImage::do_notify(const QPixmap& p, const QRect& r)
702 {
703     CachedObjectClientWalker w(m_clients);
704     while (CachedObjectClient *c = w.next())
705         c->setPixmap(p, r, this);
706 }
707
708 #if !APPLE_CHANGES
709
710 void CachedImage::movieUpdated( const QRect& r )
711 {
712 #ifdef CACHE_DEBUG
713     qDebug("movie updated %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
714            m->framePixmap().size().width(), m->framePixmap().size().height());
715 #endif
716
717     do_notify(m->framePixmap(), r);
718 }
719
720 void CachedImage::movieStatus(int status)
721 {
722 #ifdef CACHE_DEBUG
723     qDebug("movieStatus(%d)", status);
724 #endif
725
726     // ### the html image objects are supposed to send the load event after every frame (according to
727     // netscape). We have a problem though where an image is present, and js code creates a new Image object,
728     // which uses the same CachedImage, the one in the document is not supposed to be notified
729
730     // just another Qt 2.2.0 bug. we cannot call
731     // QMovie::frameImage if we're after QMovie::EndOfMovie
732     if(status == QMovie::EndOfFrame)
733     {
734         const QImage& im = m->frameImage();
735         monochrome = ( ( im.depth() <= 8 ) && ( im.numColors() - int( im.hasAlphaBuffer() ) <= 2 ) );
736         for (int i = 0; monochrome && i < im.numColors(); ++i)
737             if (im.colorTable()[i] != qRgb(0xff, 0xff, 0xff) &&
738                 im.colorTable()[i] != qRgb(0x00, 0x00, 0x00))
739                 monochrome = false;
740         if((im.width() < 5 || im.height() < 5) && im.hasAlphaBuffer()) // only evaluate for small images
741         {
742             QImage am = im.createAlphaMask();
743             if(am.depth() == 1)
744             {
745                 bool solid = false;
746                 for(int y = 0; y < am.height(); y++)
747                     for(int x = 0; x < am.width(); x++)
748                         if(am.pixelIndex(x, y)) {
749                             solid = true;
750                             break;
751                         }
752                 isFullyTransparent = (!solid);
753             }
754         }
755
756         // we have to delete our tiled bg variant here
757         // because the frame has changed (in order to keep it in sync)
758         delete bg;
759         bg = 0;
760     }
761
762
763     if((status == QMovie::EndOfMovie && (!m || m->frameNumber() <= 1)) ||
764        ((status == QMovie::EndOfLoop) && (m_showAnimations == KHTMLSettings::KAnimationLoopOnce)) ||
765        ((status == QMovie::EndOfFrame) && (m_showAnimations == KHTMLSettings::KAnimationDisabled))
766       )
767     {
768         if(imgSource)
769         {
770             setShowAnimations( KHTMLSettings::KAnimationDisabled );
771
772             // monochrome alphamasked images are usually about 10000 times
773             // faster to draw, so this is worth the hack
774             if (p && monochrome && p->depth() > 1 )
775             {
776                 QPixmap* pix = new QPixmap;
777                 pix->convertFromImage( p->convertToImage().convertDepth( 1 ), MonoOnly|AvoidDither );
778                 if ( p->mask() )
779                     pix->setMask( *p->mask() );
780                 delete p;
781                 p = pix;
782                 monochrome = false;
783             }
784         }
785
786         CachedObjectClientWalker w(m_clients);
787         while (CachedObjectClient *c = w.next())
788             c->notifyFinished(this);
789     }
790
791     if((status == QMovie::EndOfFrame) || (status == QMovie::EndOfMovie))
792     {
793 #ifdef CACHE_DEBUG
794         QRect r(valid_rect());
795         qDebug("movie Status frame update %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
796                pixmap().size().width(), pixmap().size().height());
797 #endif
798             do_notify(pixmap(), valid_rect());
799     }
800 }
801
802 void CachedImage::movieResize(const QSize& /*s*/)
803 {
804 //    do_notify(m->framePixmap(), QRect());
805 }
806
807 #endif // APPLE_CHANGES
808
809 void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
810 {
811     m_showAnimations = showAnimations;
812 #if !APPLE_CHANGES
813     if ( (m_showAnimations == KHTMLSettings::KAnimationDisabled) && imgSource ) {
814         imgSource->cleanBuffer();
815         delete p;
816         p = new QPixmap(m->framePixmap());
817
818         m->disconnectUpdate( this, SLOT( movieUpdated( const QRect &) ));
819         m->disconnectStatus( this, SLOT( movieStatus( int ) ));
820         m->disconnectResize( this, SLOT( movieResize( const QSize& ) ) );
821         QTimer::singleShot(0, this, SLOT( deleteMovie()));
822         imgSource = 0;
823     }
824 #endif
825 }
826
827 #if !APPLE_CHANGES
828
829 void CachedImage::deleteMovie()
830 {
831     delete m; m = 0;
832 }
833
834 #endif // APPLE_CHANGES
835
836 void CachedImage::clear()
837 {
838     delete m;   m = 0;
839     delete p;   p = 0;
840     delete bg;  bg = 0;
841 #if !APPLE_CHANGES
842     bgColor = qRgba( 0, 0, 0, 0xff );
843 #endif
844     delete pixPart; pixPart = 0;
845
846     formatType = 0;
847
848 #if !APPLE_CHANGES
849     typeChecked = false;
850 #endif
851     setSize(0);
852
853     // No need to delete imageSource - QMovie does it for us
854     imgSource = 0;
855 }
856
857 void CachedImage::data ( QBuffer &_buffer, bool eof )
858 {
859 #ifdef CACHE_DEBUG
860     kdDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << endl;
861 #endif
862
863 #if !APPLE_CHANGES
864     if ( !typeChecked )
865     {
866         formatType = QImageDecoder::formatName( (const uchar*)_buffer.buffer().data(), _buffer.size());
867
868         typeChecked = true;
869
870         if ( formatType )  // movie format exists
871         {
872             imgSource = new ImageSource( _buffer.buffer());
873             m = new QMovie( imgSource, 8192 );
874             m->connectUpdate( this, SLOT( movieUpdated( const QRect &) ));
875             m->connectStatus( this, SLOT( movieStatus(int)));
876             m->connectResize( this, SLOT( movieResize( const QSize& ) ) );
877         }
878     }
879
880     if ( imgSource )
881     {
882         imgSource->setEOF(eof);
883         imgSource->maybeReady();
884     }
885
886     if(eof)
887     {
888         // QMovie currently doesn't support all kinds of image formats
889         // so we need to use a QPixmap here when we finished loading the complete
890         // picture and display it then all at once.
891         if(typeChecked && !formatType)
892         {
893 #ifdef CACHE_DEBUG
894             kdDebug(6060) << "CachedImage::data(): reloading as pixmap:" << endl;
895 #endif
896             p = new QPixmap( _buffer.buffer() );
897             // set size of image.
898 #ifdef CACHE_DEBUG
899             kdDebug(6060) << "CachedImage::data(): image is null: " << p->isNull() << endl;
900 #endif
901                 if(p->isNull())
902                 {
903                     errorOccured = true;
904                     do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
905                 }
906                 else
907                     do_notify(*p, p->rect());
908         }
909
910         QSize s = pixmap_size();
911         setSize(s.width() * s.height() * 2);
912     }
913 #else // APPLE_CHANGES
914     bool canDraw = false;
915     
916     m_dataSize = _buffer.size();
917         
918     // If we're at eof and don't have a pixmap yet, the data
919     // must have arrived in one chunk.  This avoids the attempt
920     // to perform incremental decoding.
921     if (eof && !p) {
922         p = new QPixmap(_buffer.buffer(), KWQResponseMIMEType(m_response));
923         canDraw = true;
924     } else {
925         // Always attempt to load the image incrementally.
926         // If the AppKit is unable to decode incrementally this pixmap
927         // will not be renderable until all the data has been received.
928         if (!p)
929             p = new QPixmap(KWQResponseMIMEType(m_response));
930         canDraw = p->receivedData(_buffer.buffer(), eof);
931     }
932     
933     if (canDraw || eof) {
934         if (p->isNull()) {
935             errorOccured = true;
936             QPixmap ep = pixmap();
937             do_notify (ep, ep.rect());
938             Cache::removeCacheEntry (this);
939         }
940         else
941             do_notify(*p, p->rect());
942
943         QSize s = pixmap_size();
944         setSize(s.width() * s.height() * 2);
945     }
946     if (eof) {
947         m_loading = false;
948         checkNotify();
949     }
950 #endif // APPLE_CHANGES
951 }
952
953 void CachedImage::error( int /*err*/, const char */*text*/ )
954 {
955 #ifdef CACHE_DEBUG
956     kdDebug(6060) << "CahcedImage::error" << endl;
957 #endif
958
959     clear();
960 #if !APPLE_CHANGES
961     typeChecked = true;
962 #endif
963     errorOccured = true;
964     do_notify(pixmap(), QRect(0, 0, 16, 16));
965 #if APPLE_CHANGES
966     m_loading = false;
967     checkNotify();
968 #endif
969 }
970
971 void CachedImage::checkNotify()
972 {
973     if(m_loading) return;
974
975     CachedObjectClientWalker w(m_clients);
976     while (CachedObjectClient *c = w.next())
977         c->notifyFinished(this);
978 }
979
980 // -------------------------------------------------------------------------------------------
981
982 #ifdef KHTML_XSLT
983
984 CachedXSLStyleSheet::CachedXSLStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
985 : CachedObject(url, XSLStyleSheet, _cachePolicy, _expireDate)
986 {
987     // It's XML we want.
988     setAccept(QString::fromLatin1("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml"));
989     
990     // load the file
991     Cache::loader()->load(dl, this, false);
992     m_loading = true;
993     m_codec = QTextCodec::codecForName("iso8859-1");
994 }
995
996 void CachedXSLStyleSheet::ref(CachedObjectClient *c)
997 {
998     CachedObject::ref(c);
999     
1000     if (!m_loading)
1001         c->setStyleSheet(m_url, m_sheet);
1002 }
1003
1004 void CachedXSLStyleSheet::deref(CachedObjectClient *c)
1005 {
1006     Cache::flush();
1007     CachedObject::deref(c);
1008     if (canDelete() && m_free)
1009         delete this;
1010 }
1011
1012 void CachedXSLStyleSheet::data(QBuffer &buffer, bool eof)
1013 {
1014     if(!eof) return;
1015     buffer.close();
1016     setSize(buffer.buffer().size());
1017     QString data = m_codec->toUnicode( buffer.buffer().data(), size() );
1018     m_sheet = DOMString(data);
1019     m_loading = false;
1020     
1021     checkNotify();
1022 }
1023
1024 void CachedXSLStyleSheet::checkNotify()
1025 {
1026     if (m_loading)
1027         return;
1028     
1029 #ifdef CACHE_DEBUG
1030     kdDebug( 6060 ) << "CachedCSSStyleSheet:: finishedLoading " << m_url.string() << endl;
1031 #endif
1032     
1033     CachedObjectClientWalker w(m_clients);
1034     while (CachedObjectClient *c = w.next())
1035         c->setStyleSheet(m_url, m_sheet);
1036 }
1037
1038
1039 void CachedXSLStyleSheet::error( int /*err*/, const char */*text*/ )
1040 {
1041     m_loading = false;
1042     checkNotify();
1043 }
1044
1045 #endif
1046
1047 #ifndef KHTML_NO_XBL
1048 CachedXBLDocument::CachedXBLDocument(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
1049 : CachedObject(url, XBL, _cachePolicy, _expireDate), m_document(0)
1050 {
1051     // It's XML we want.
1052     setAccept( QString::fromLatin1("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml") );
1053     
1054     // Load the file
1055     Cache::loader()->load(dl, this, false);
1056     m_loading = true;
1057     m_codec = QTextCodec::codecForName("iso8859-1");
1058 }
1059
1060 CachedXBLDocument::~CachedXBLDocument()
1061 {
1062     if (m_document)
1063         m_document->deref();
1064 }
1065
1066 void CachedXBLDocument::ref(CachedObjectClient *c)
1067 {
1068     CachedObject::ref(c);
1069     if (!m_loading)
1070         c->setXBLDocument(m_url, m_document);
1071 }
1072
1073 void CachedXBLDocument::deref(CachedObjectClient *c)
1074 {
1075     Cache::flush();
1076     CachedObject::deref(c);
1077     if (canDelete() && m_free)
1078         delete this;
1079 }
1080
1081 void CachedXBLDocument::data( QBuffer &buffer, bool eof )
1082 {
1083     if (!eof) return;
1084     
1085     assert(!m_document);
1086     
1087     m_document =  new XBL::XBLDocumentImpl();
1088     m_document->ref();
1089     m_document->open();
1090     
1091     QString data = m_codec->toUnicode(buffer.buffer().data(), buffer.buffer().size());
1092     m_document->write(data);
1093     setSize(buffer.buffer().size());
1094     buffer.close();
1095     
1096     m_document->finishParsing();
1097     m_document->close();
1098     m_loading = false;
1099     checkNotify();
1100 }
1101
1102 void CachedXBLDocument::checkNotify()
1103 {
1104     if(m_loading) return;
1105     
1106 #ifdef CACHE_DEBUG
1107     kdDebug( 6060 ) << "CachedXBLDocument:: finishedLoading " << m_url.string() << endl;
1108 #endif
1109     
1110     CachedObjectClientWalker w(m_clients);
1111     while (CachedObjectClient *c = w.next())
1112         c->setXBLDocument(m_url, m_document);
1113 }
1114
1115
1116 void CachedXBLDocument::error( int /*err*/, const char */*text*/ )
1117 {
1118     m_loading = false;
1119     checkNotify();
1120 }
1121 #endif
1122
1123 // ------------------------------------------------------------------------------------------
1124
1125 Request::Request(DocLoader* dl, CachedObject *_object, bool _incremental)
1126 {
1127     object = _object;
1128     object->setRequest(this);
1129     incremental = _incremental;
1130     m_docLoader = dl;
1131 }
1132
1133 Request::~Request()
1134 {
1135     object->setRequest(0);
1136 }
1137
1138 // ------------------------------------------------------------------------------------------
1139
1140 DocLoader::DocLoader(KHTMLPart* part, DocumentImpl* doc)
1141 {
1142     m_cachePolicy = KIO::CC_Verify;
1143     m_expireDate = 0;
1144     m_bautoloadImages = true;
1145     m_showAnimations = KHTMLSettings::KAnimationEnabled;
1146     m_part = part;
1147     m_doc = doc;
1148
1149 #if APPLE_CHANGES
1150     Cache::init();
1151 #endif
1152     Cache::docloader->append( this );
1153 }
1154
1155 DocLoader::~DocLoader()
1156 {
1157     Cache::docloader->remove( this );
1158 }
1159
1160 void DocLoader::setExpireDate(time_t _expireDate)
1161 {
1162     m_expireDate = _expireDate;
1163 }
1164
1165 bool DocLoader::needReload(const KURL &fullURL)
1166 {
1167     bool reload = false;
1168     if (m_cachePolicy == KIO::CC_Verify)
1169     {
1170        if (!m_reloadedURLs.contains(fullURL.url()))
1171        {
1172           CachedObject *existing = Cache::cache->find(fullURL.url());
1173           if (existing && existing->isExpired())
1174           {
1175              Cache::removeCacheEntry(existing);
1176              m_reloadedURLs.append(fullURL.url());
1177              reload = true;
1178           }
1179        }
1180     }
1181     else if ((m_cachePolicy == KIO::CC_Reload) || (m_cachePolicy == KIO::CC_Refresh))
1182     {
1183        if (!m_reloadedURLs.contains(fullURL.url()))
1184        {
1185           CachedObject *existing = Cache::cache->find(fullURL.url());
1186           if (existing)
1187           {
1188              Cache::removeCacheEntry(existing);
1189           }
1190           m_reloadedURLs.append(fullURL.url());
1191           reload = true;
1192        }
1193     }
1194     return reload;
1195 }
1196
1197 CachedImage *DocLoader::requestImage( const DOM::DOMString &url)
1198 {
1199     KURL fullURL = m_doc->completeURL( url.string() );
1200     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1201
1202 #if APPLE_CHANGES
1203     if (KWQCheckIfReloading(this)) {
1204         setCachePolicy(KIO::CC_Reload);
1205     }
1206 #endif
1207
1208     bool reload = needReload(fullURL);
1209
1210 #if APPLE_CHANGES
1211     CachedImage *cachedObject = Cache::requestImage(this, fullURL, reload, m_expireDate);
1212     KWQCheckCacheObjectStatus(this, cachedObject);
1213     return cachedObject;
1214 #else
1215     return Cache::requestImage(this, fullURL, reload, m_expireDate);
1216 #endif
1217 }
1218
1219 CachedCSSStyleSheet *DocLoader::requestStyleSheet( const DOM::DOMString &url, const QString& charset)
1220 {
1221     KURL fullURL = m_doc->completeURL( url.string() );
1222     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1223
1224 #if APPLE_CHANGES
1225     if (KWQCheckIfReloading(this)) {
1226         setCachePolicy(KIO::CC_Reload);
1227     }
1228 #endif
1229
1230     bool reload = needReload(fullURL);
1231
1232 #if APPLE_CHANGES
1233     CachedCSSStyleSheet *cachedObject = Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
1234     KWQCheckCacheObjectStatus(this, cachedObject);
1235     return cachedObject;
1236 #else
1237     return Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
1238 #endif
1239 }
1240
1241 CachedScript *DocLoader::requestScript( const DOM::DOMString &url, const QString& charset)
1242 {
1243     KURL fullURL = m_doc->completeURL( url.string() );
1244     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1245
1246 #if APPLE_CHANGES
1247     if (KWQCheckIfReloading(this)) {
1248         setCachePolicy(KIO::CC_Reload);
1249     }
1250 #endif
1251
1252     bool reload = needReload(fullURL);
1253
1254 #if APPLE_CHANGES
1255     CachedScript *cachedObject = Cache::requestScript(this, url, reload, m_expireDate, charset);
1256     KWQCheckCacheObjectStatus(this, cachedObject);
1257     return cachedObject;
1258 #else
1259     return Cache::requestScript(this, url, reload, m_expireDate, charset);
1260 #endif
1261 }
1262
1263 #ifdef KHTML_XSLT
1264 CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const DOM::DOMString &url)
1265 {
1266     KURL fullURL = m_doc->completeURL(url.string());
1267     
1268     if (m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1269     
1270 #if APPLE_CHANGES
1271     if (KWQCheckIfReloading(this))
1272         setCachePolicy(KIO::CC_Reload);
1273 #endif
1274     
1275     bool reload = needReload(fullURL);
1276     
1277 #if APPLE_CHANGES
1278     CachedXSLStyleSheet *cachedObject = Cache::requestXSLStyleSheet(this, url, reload, m_expireDate);
1279     KWQCheckCacheObjectStatus(this, cachedObject);
1280     return cachedObject;
1281 #else
1282     return Cache::requestXSLStyleSheet(this, url, reload, m_expireDate);
1283 #endif
1284 }
1285 #endif
1286
1287 #ifndef KHTML_NO_XBL
1288 CachedXBLDocument* DocLoader::requestXBLDocument(const DOM::DOMString &url)
1289 {
1290     KURL fullURL = m_doc->completeURL(url.string());
1291     
1292     // FIXME: Is this right for XBL?
1293     if (m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1294     
1295 #if APPLE_CHANGES
1296     if (KWQCheckIfReloading(this)) {
1297         setCachePolicy(KIO::CC_Reload);
1298     }
1299 #endif
1300     
1301     bool reload = needReload(fullURL);
1302     
1303 #if APPLE_CHANGES
1304     CachedXBLDocument *cachedObject = Cache::requestXBLDocument(this, url, reload, m_expireDate);
1305     KWQCheckCacheObjectStatus(this, cachedObject);
1306     return cachedObject;
1307 #else
1308     return Cache::requestXBLDocument(this, url, reload, m_expireDate);
1309 #endif
1310 }
1311 #endif
1312
1313 void DocLoader::setAutoloadImages( bool enable )
1314 {
1315     if ( enable == m_bautoloadImages )
1316         return;
1317
1318     m_bautoloadImages = enable;
1319
1320     if ( !m_bautoloadImages ) return;
1321
1322     for ( const CachedObject* co=m_docObjects.first(); co; co=m_docObjects.next() )
1323         if ( co->type() == CachedObject::Image )
1324         {
1325             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
1326
1327             CachedObject::Status status = img->status();
1328             if ( status != CachedObject::Unknown )
1329                 continue;
1330
1331             Cache::loader()->load(this, img, true);
1332         }
1333 }
1334
1335 void DocLoader::setCachePolicy( KIO::CacheControl cachePolicy )
1336 {
1337     m_cachePolicy = cachePolicy;
1338 }
1339
1340 void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
1341 {
1342     if ( showAnimations == m_showAnimations ) return;
1343     m_showAnimations = showAnimations;
1344
1345     const CachedObject* co;
1346     for ( co=m_docObjects.first(); co; co=m_docObjects.next() )
1347         if ( co->type() == CachedObject::Image )
1348         {
1349             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
1350
1351             img->setShowAnimations( showAnimations );
1352         }
1353 }
1354
1355 void DocLoader::removeCachedObject( CachedObject* o ) const
1356 {
1357     m_docObjects.removeRef( o );
1358 }
1359
1360 // ------------------------------------------------------------------------------------------
1361
1362 Loader::Loader() : QObject()
1363 {
1364     m_requestsPending.setAutoDelete( true );
1365     m_requestsLoading.setAutoDelete( true );
1366 #if APPLE_CHANGES
1367     kwq = new KWQLoader(this);
1368 #endif
1369 }
1370
1371 Loader::~Loader()
1372 {
1373 #if APPLE_CHANGES
1374     delete kwq;
1375 #endif
1376 }
1377
1378 void Loader::load(DocLoader* dl, CachedObject *object, bool incremental)
1379 {
1380     Request *req = new Request(dl, object, incremental);
1381     m_requestsPending.append(req);
1382
1383     emit requestStarted( req->m_docLoader, req->object );
1384
1385     servePendingRequests();
1386 }
1387
1388 void Loader::servePendingRequests()
1389 {
1390   if ( m_requestsPending.count() == 0 )
1391       return;
1392
1393   // get the first pending request
1394   Request *req = m_requestsPending.take(0);
1395
1396 #ifdef CACHE_DEBUG
1397   kdDebug( 6060 ) << "starting Loader url=" << req->object->url().string() << endl;
1398 #endif
1399
1400   KURL u(req->object->url().string());
1401   KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/);
1402
1403   job->addMetaData("cache", getCacheControlString(req->object->cachePolicy()));
1404   if (!req->object->accept().isEmpty())
1405       job->addMetaData("accept", req->object->accept());
1406   if ( req->m_docLoader )  {
1407       KURL r = req->m_docLoader->doc()->URL();
1408       if ( r.protocol().startsWith( "http" ) && r.path().isEmpty() )
1409           r.setPath( "/" );
1410
1411       job->addMetaData("referrer", r.url());
1412       QString domain = r.host();
1413       if (req->m_docLoader->doc()->isHTMLDocument())
1414          domain = static_cast<HTMLDocumentImpl*>(req->m_docLoader->doc())->domain().string();
1415       if (crossDomain(u.host(), domain))
1416          job->addMetaData("cross-domain", "true");
1417   }
1418
1419   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinished( KIO::Job * ) ) );
1420
1421 #if APPLE_CHANGES
1422   connect( job, SIGNAL( data( KIO::Job*, const char *, int)),
1423            SLOT( slotData( KIO::Job*, const char *, int)));
1424   connect( job, SIGNAL( receivedResponse( KIO::Job *, NSURLResponse *)), SLOT( slotReceivedResponse( KIO::Job *, NSURLResponse *)) );
1425   connect( job, SIGNAL( allData( KIO::Job *, NSData *)), SLOT( slotAllData( KIO::Job *, NSData *)) );
1426
1427   if (KWQServeRequest(this, req, job))
1428       m_requestsLoading.insert(job, req);
1429 #else
1430   connect( job, SIGNAL( data( KIO::Job*, const QByteArray &)),
1431            SLOT( slotData( KIO::Job*, const QByteArray &)));
1432
1433   if ( req->object->schedule() )
1434       KIO::Scheduler::scheduleJob( job );
1435
1436   m_requestsLoading.insert(job, req);
1437 #endif // APPLE_CHANGES
1438 }
1439
1440 void Loader::slotFinished( KIO::Job* job )
1441 {
1442   Request *r = m_requestsLoading.take( job );
1443   KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
1444
1445   if ( !r )
1446     return;
1447
1448   if (j->error() || j->isErrorPage())
1449   {
1450       kdDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage() << endl;
1451       r->object->error( job->error(), job->errorText().ascii() );
1452       emit requestFailed( r->m_docLoader, r->object );
1453   }
1454   else
1455   {
1456       r->object->data(r->m_buffer, true);
1457       emit requestDone( r->m_docLoader, r->object );
1458 #if !APPLE_CHANGES
1459       time_t expireDate = j->queryMetaData("expire-date").toLong();
1460 kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << " expires " << ctime(&expireDate) << endl;
1461       r->object->setExpireDate(expireDate, false);
1462 #endif
1463   }
1464
1465 #if APPLE_CHANGES
1466   // We don't want to ever finish the load in the case of an error because we don't want them cached.
1467   if (j->error())
1468       Cache::removeCacheEntry( r->object );
1469   else
1470 #endif
1471   r->object->finish();
1472
1473 #ifdef CACHE_DEBUG
1474   kdDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string() << endl;
1475 #endif
1476
1477   delete r;
1478   servePendingRequests();
1479 }
1480
1481 #if APPLE_CHANGES
1482
1483 void Loader::slotReceivedResponse(KIO::Job* job, NSURLResponse *response)
1484 {
1485     Request *r = m_requestsLoading[job];
1486     ASSERT(r);
1487     ASSERT(response);
1488     r->object->setResponse(response);
1489     r->object->setExpireDate(KWQCacheObjectExpiresTime(r->m_docLoader, response), false);
1490 }
1491
1492 void Loader::slotAllData(KIO::Job* job, NSData *data)
1493 {
1494     Request *r = m_requestsLoading[job];
1495     ASSERT(r);
1496     ASSERT(data);
1497     r->object->setAllData(data);
1498 }
1499
1500 #endif
1501
1502 #if APPLE_CHANGES
1503 void Loader::slotData( KIO::Job*job, const char *data, int size )
1504 #else
1505 void Loader::slotData( KIO::Job*job, const QByteArray &data )
1506 #endif
1507 {
1508     Request *r = m_requestsLoading[job];
1509     if(!r) {
1510         kdDebug( 6060 ) << "got data for unknown request!" << endl;
1511         return;
1512     }
1513
1514     if ( !r->m_buffer.isOpen() )
1515         r->m_buffer.open( IO_WriteOnly );
1516
1517 #if APPLE_CHANGES
1518     r->m_buffer.writeBlock( data, size );
1519 #else
1520     r->m_buffer.writeBlock( data.data(), data.size() );
1521 #endif
1522
1523     if(r->incremental)
1524         r->object->data( r->m_buffer, false );
1525 }
1526
1527 int Loader::numRequests( DocLoader* dl ) const
1528 {
1529     int res = 0;
1530
1531     QPtrListIterator<Request> pIt( m_requestsPending );
1532     for (; pIt.current(); ++pIt )
1533         if ( pIt.current()->m_docLoader == dl )
1534             res++;
1535
1536     QPtrDictIterator<Request> lIt( m_requestsLoading );
1537     for (; lIt.current(); ++lIt )
1538         if ( lIt.current()->m_docLoader == dl )
1539             res++;
1540
1541     return res;
1542 }
1543
1544 void Loader::cancelRequests( DocLoader* dl )
1545 {
1546     //kdDebug( 6060 ) << "void Loader::cancelRequests()" << endl;
1547     //kdDebug( 6060 ) << "got " << m_requestsPending.count() << " pending requests" << endl;
1548     QPtrListIterator<Request> pIt( m_requestsPending );
1549     while ( pIt.current() )
1550     {
1551         if ( pIt.current()->m_docLoader == dl )
1552         {
1553             kdDebug( 6060 ) << "cancelling pending request for " << pIt.current()->object->url().string() << endl;
1554             //emit requestFailed( dl, pIt.current()->object );
1555             Cache::removeCacheEntry( pIt.current()->object );
1556             m_requestsPending.remove( pIt );
1557         }
1558         else
1559             ++pIt;
1560     }
1561
1562     //kdDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests" << endl;
1563
1564     QPtrDictIterator<Request> lIt( m_requestsLoading );
1565     while ( lIt.current() )
1566     {
1567         if ( lIt.current()->m_docLoader == dl )
1568         {
1569             //kdDebug( 6060 ) << "cancelling loading request for " << lIt.current()->object->url().string() << endl;
1570             KIO::Job *job = static_cast<KIO::Job *>( lIt.currentKey() );
1571             Cache::removeCacheEntry( lIt.current()->object );
1572             m_requestsLoading.remove( lIt.currentKey() );
1573             job->kill();
1574             //emit requestFailed( dl, pIt.current()->object );
1575         }
1576         else
1577             ++lIt;
1578     }
1579 }
1580
1581 KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
1582 {
1583     QPtrDictIterator<Request> it( m_requestsLoading );
1584
1585     for (; it.current(); ++it )
1586     {
1587         CachedObject *obj = it.current()->object;
1588
1589         if ( obj && obj->url() == url )
1590             return static_cast<KIO::Job *>( it.currentKey() );
1591     }
1592
1593     return 0;
1594 }
1595
1596 // ----------------------------------------------------------------------------
1597
1598
1599 LRUList::LRUList()
1600 :m_head(0), m_tail(0) 
1601 {}
1602
1603 LRUList::~LRUList()
1604 {}
1605
1606 QDict<CachedObject> *Cache::cache = 0;
1607 QPtrList<DocLoader>* Cache::docloader = 0;
1608 Loader *Cache::m_loader = 0;
1609
1610 int Cache::maxSize = DEFCACHESIZE;
1611 int Cache::maxCacheable = MAXCACHEABLE;
1612 int Cache::flushCount = 0;
1613
1614 QPixmap *Cache::nullPixmap = 0;
1615 QPixmap *Cache::brokenPixmap = 0;
1616
1617 CachedObject *Cache::m_headOfUncacheableList = 0;
1618 int Cache::m_totalSizeOfLRULists = 0;
1619 int Cache::m_countOfLRUAndUncacheableLists;
1620 LRUList *Cache::m_LRULists = 0;
1621
1622 void Cache::init()
1623 {
1624     if ( !cache )
1625         cache = new QDict<CachedObject>(401, true);
1626
1627     if ( !docloader )
1628         docloader = new QPtrList<DocLoader>;
1629
1630     if ( !nullPixmap )
1631         nullPixmap = new QPixmap;
1632
1633     if ( !brokenPixmap )
1634 #if APPLE_CHANGES
1635         brokenPixmap = KWQLoadPixmap("missing_image");
1636 #else
1637         brokenPixmap = new QPixmap(KHTMLFactory::instance()->iconLoader()->loadIcon("file_broken", KIcon::Desktop, 16, KIcon::DisabledState));
1638 #endif
1639
1640     if ( !m_loader )
1641         m_loader = new Loader();
1642 }
1643
1644 void Cache::clear()
1645 {
1646     if ( !cache ) return;
1647 #ifdef CACHE_DEBUG
1648     kdDebug( 6060 ) << "Cache: CLEAR!" << endl;
1649     statistics();
1650 #endif
1651     cache->setAutoDelete( true );
1652     delete cache; cache = 0;
1653     delete nullPixmap; nullPixmap = 0;
1654     delete brokenPixmap; brokenPixmap = 0;
1655     delete m_loader;   m_loader = 0;
1656     delete docloader; docloader = 0;
1657 }
1658
1659 CachedImage *Cache::requestImage( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate )
1660 {
1661     // this brings the _url to a standard form...
1662     KURL kurl;
1663     if (dl)
1664         kurl = dl->m_doc->completeURL( url.string() );
1665     else
1666         kurl = url.string();
1667     return requestImage(dl, kurl, reload, _expireDate);
1668 }
1669
1670 CachedImage *Cache::requestImage( DocLoader* dl, const KURL & url, bool reload, time_t _expireDate )
1671 {
1672     KIO::CacheControl cachePolicy;
1673     if (dl)
1674         cachePolicy = dl->cachePolicy();
1675     else
1676         cachePolicy = KIO::CC_Verify;
1677
1678 #if APPLE_CHANGES
1679     // Checking if the URL is malformed is lots of extra work for little benefit.
1680 #else
1681     if( kurl.isMalformed() )
1682     {
1683 #ifdef CACHE_DEBUG
1684       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1685 #endif
1686       return 0;
1687     }
1688 #endif
1689
1690 #if APPLE_CHANGES
1691     if (!dl->doc()->shouldCreateRenderers()){
1692         return 0;
1693     }
1694 #endif
1695
1696     CachedObject *o = 0;
1697     if (!reload)
1698         o = cache->find(url.url());
1699     if(!o)
1700     {
1701 #ifdef CACHE_DEBUG
1702         kdDebug( 6060 ) << "Cache: new: " << url.url() << endl;
1703 #endif
1704         CachedImage *im = new CachedImage(dl, url.url(), cachePolicy, _expireDate);
1705         if ( dl && dl->autoloadImages() ) Cache::loader()->load(dl, im, true);
1706 #if APPLE_CHANGES
1707         if (cacheDisabled)
1708             im->setFree(true);
1709         else {
1710 #endif
1711         cache->insert( url.url(), im );
1712         moveToHeadOfLRUList(im);
1713 #if APPLE_CHANGES
1714         }
1715 #endif
1716         o = im;
1717     }
1718
1719 #if !APPLE_CHANGES
1720     o->setExpireDate(_expireDate, true);
1721 #endif
1722     
1723     if(o->type() != CachedObject::Image)
1724     {
1725 #ifdef CACHE_DEBUG
1726         kdDebug( 6060 ) << "Cache::Internal Error in requestImage url=" << kurl.url() << "!" << endl;
1727 #endif
1728         return 0;
1729     }
1730
1731 #ifdef CACHE_DEBUG
1732     if( o->status() == CachedObject::Pending )
1733         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
1734     else
1735         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << ", status " << o->status() << endl;
1736 #endif
1737
1738     moveToHeadOfLRUList(o);
1739     if ( dl ) {
1740         dl->m_docObjects.remove( o );
1741 #if APPLE_CHANGES
1742         if (!cacheDisabled)
1743 #endif
1744         dl->m_docObjects.append( o );
1745     }
1746     return static_cast<CachedImage *>(o);
1747 }
1748
1749 CachedCSSStyleSheet *Cache::requestStyleSheet( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate, const QString& charset)
1750 {
1751     // this brings the _url to a standard form...
1752     KURL kurl;
1753     KIO::CacheControl cachePolicy;
1754     if ( dl )
1755     {
1756         kurl = dl->m_doc->completeURL( url.string() );
1757         cachePolicy = dl->cachePolicy();
1758     }
1759     else
1760     {
1761         kurl = url.string();
1762         cachePolicy = KIO::CC_Verify;
1763     }
1764
1765 #if APPLE_CHANGES
1766     // Checking if the URL is malformed is lots of extra work for little benefit.
1767 #else
1768     if( kurl.isMalformed() )
1769     {
1770       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1771       return 0;
1772     }
1773 #endif
1774
1775     CachedObject *o = cache->find(kurl.url());
1776     if(!o)
1777     {
1778 #ifdef CACHE_DEBUG
1779         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
1780 #endif
1781         CachedCSSStyleSheet *sheet = new CachedCSSStyleSheet(dl, kurl.url(), cachePolicy, _expireDate, charset);
1782 #if APPLE_CHANGES
1783         if (cacheDisabled)
1784             sheet->setFree(true);
1785         else {
1786 #endif
1787         cache->insert( kurl.url(), sheet );
1788         moveToHeadOfLRUList(sheet);
1789 #if APPLE_CHANGES
1790         }
1791 #endif
1792         o = sheet;
1793     }
1794
1795 #if !APPLE_CHANGES
1796     o->setExpireDate(_expireDate, true);
1797 #endif
1798     
1799     if(o->type() != CachedObject::CSSStyleSheet)
1800     {
1801 #ifdef CACHE_DEBUG
1802         kdDebug( 6060 ) << "Cache::Internal Error in requestStyleSheet url=" << kurl.url() << "!" << endl;
1803 #endif
1804         return 0;
1805     }
1806
1807 #ifdef CACHE_DEBUG
1808     if( o->status() == CachedObject::Pending )
1809         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
1810     else
1811         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
1812 #endif
1813
1814     moveToHeadOfLRUList(o);
1815     if ( dl ) {
1816         dl->m_docObjects.remove( o );
1817 #if APPLE_CHANGES
1818         if (!cacheDisabled)
1819 #endif
1820         dl->m_docObjects.append( o );
1821     }
1822     return static_cast<CachedCSSStyleSheet *>(o);
1823 }
1824
1825 void Cache::preloadStyleSheet( const QString &url, const QString &stylesheet_data)
1826 {
1827     CachedObject *o = cache->find(url);
1828     if(o)
1829         removeCacheEntry(o);
1830
1831     CachedCSSStyleSheet *stylesheet = new CachedCSSStyleSheet(url, stylesheet_data);
1832     cache->insert( url, stylesheet );
1833 }
1834
1835 CachedScript *Cache::requestScript( DocLoader* dl, const DOM::DOMString &url, bool reload, time_t _expireDate, const QString& charset)
1836 {
1837     // this brings the _url to a standard form...
1838     KURL kurl;
1839     KIO::CacheControl cachePolicy;
1840     if ( dl )
1841     {
1842         kurl = dl->m_doc->completeURL( url.string() );
1843         cachePolicy = dl->cachePolicy();
1844     }
1845     else
1846     {
1847         kurl = url.string();
1848         cachePolicy = KIO::CC_Verify;
1849     }
1850
1851 #if APPLE_CHANGES
1852     // Checking if the URL is malformed is lots of extra work for little benefit.
1853 #else
1854     if( kurl.isMalformed() )
1855     {
1856       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1857       return 0;
1858     }
1859 #endif
1860
1861     CachedObject *o = cache->find(kurl.url());
1862     if(!o)
1863     {
1864 #ifdef CACHE_DEBUG
1865         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
1866 #endif
1867         CachedScript *script = new CachedScript(dl, kurl.url(), cachePolicy, _expireDate, charset);
1868 #if APPLE_CHANGES
1869         if (cacheDisabled)
1870             script->setFree(true);
1871         else {
1872 #endif
1873         cache->insert( kurl.url(), script );
1874         moveToHeadOfLRUList(script);
1875 #if APPLE_CHANGES
1876         }
1877 #endif
1878         o = script;
1879     }
1880
1881 #if !APPLE_CHANGES
1882     o->setExpireDate(_expireDate, true);
1883 #endif
1884     
1885     if(!(o->type() == CachedObject::Script))
1886     {
1887 #ifdef CACHE_DEBUG
1888         kdDebug( 6060 ) << "Cache::Internal Error in requestScript url=" << kurl.url() << "!" << endl;
1889 #endif
1890         return 0;
1891     }
1892     
1893     
1894 #ifdef CACHE_DEBUG
1895     if( o->status() == CachedObject::Pending )
1896         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
1897     else
1898         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
1899 #endif
1900
1901     moveToHeadOfLRUList(o);
1902     if ( dl ) {
1903         dl->m_docObjects.remove( o );
1904 #if APPLE_CHANGES
1905         if (!cacheDisabled)
1906 #endif
1907         dl->m_docObjects.append( o );
1908     }
1909     return static_cast<CachedScript *>(o);
1910 }
1911
1912 void Cache::preloadScript( const QString &url, const QString &script_data)
1913 {
1914     CachedObject *o = cache->find(url);
1915     if(o)
1916         removeCacheEntry(o);
1917
1918     CachedScript *script = new CachedScript(url, script_data);
1919     cache->insert( url, script );
1920 }
1921
1922 #ifdef KHTML_XSLT
1923 CachedXSLStyleSheet* Cache::requestXSLStyleSheet(DocLoader* dl, const DOMString & url, bool reload, 
1924                                                  time_t _expireDate)
1925 {
1926     // this brings the _url to a standard form...
1927     KURL kurl;
1928     KIO::CacheControl cachePolicy;
1929     if (dl) {
1930         kurl = dl->m_doc->completeURL(url.string());
1931         cachePolicy = dl->cachePolicy();
1932     }
1933     else {
1934         kurl = url.string();
1935         cachePolicy = KIO::CC_Verify;
1936     }
1937     
1938 #if APPLE_CHANGES
1939     // Checking if the URL is malformed is lots of extra work for little benefit.
1940 #else
1941     if(kurl.isMalformed()) {
1942         kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1943         return 0;
1944     }
1945 #endif
1946     
1947     CachedObject *o = cache->find(kurl.url());
1948     if (!o) {
1949 #ifdef CACHE_DEBUG
1950         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
1951 #endif
1952         CachedXSLStyleSheet* doc = new CachedXSLStyleSheet(dl, kurl.url(), cachePolicy, _expireDate);
1953 #if APPLE_CHANGES
1954         if (cacheDisabled)
1955             doc->setFree(true);
1956         else {
1957 #endif
1958             cache->insert(kurl.url(), doc);
1959             moveToHeadOfLRUList(doc);
1960 #if APPLE_CHANGES
1961         }
1962 #endif
1963         o = doc;
1964     }
1965     
1966 #if !APPLE_CHANGES
1967     o->setExpireDate(_expireDate, true);
1968 #endif
1969     
1970     if (o->type() != CachedObject::XSLStyleSheet) {
1971 #ifdef CACHE_DEBUG
1972         kdDebug( 6060 ) << "Cache::Internal Error in requestXSLStyleSheet url=" << kurl.url() << "!" << endl;
1973 #endif
1974         return 0;
1975     }
1976     
1977 #ifdef CACHE_DEBUG
1978     if (o->status() == CachedObject::Pending)
1979         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
1980     else
1981         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
1982 #endif
1983     
1984     moveToHeadOfLRUList(o);
1985     if (dl) {
1986         dl->m_docObjects.remove( o );
1987 #if APPLE_CHANGES
1988         if (!cacheDisabled)
1989 #endif
1990             dl->m_docObjects.append( o );
1991     }
1992     return static_cast<CachedXSLStyleSheet*>(o);
1993 }
1994 #endif
1995
1996 #ifndef KHTML_NO_XBL
1997 CachedXBLDocument* Cache::requestXBLDocument(DocLoader* dl, const DOMString & url, bool reload, 
1998                                              time_t _expireDate)
1999 {
2000     // this brings the _url to a standard form...
2001     KURL kurl;
2002     KIO::CacheControl cachePolicy;
2003     if (dl) {
2004         kurl = dl->m_doc->completeURL(url.string());
2005         cachePolicy = dl->cachePolicy();
2006     }
2007     else {
2008         kurl = url.string();
2009         cachePolicy = KIO::CC_Verify;
2010     }
2011     
2012 #if APPLE_CHANGES
2013     // Checking if the URL is malformed is lots of extra work for little benefit.
2014 #else
2015     if( kurl.isMalformed() )
2016     {
2017         kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2018         return 0;
2019     }
2020 #endif
2021     
2022     CachedObject *o = cache->find(kurl.url());
2023     if(!o)
2024     {
2025 #ifdef CACHE_DEBUG
2026         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2027 #endif
2028         CachedXBLDocument* doc = new CachedXBLDocument(dl, kurl.url(), cachePolicy, _expireDate);
2029 #if APPLE_CHANGES
2030         if (cacheDisabled)
2031             doc->setFree(true);
2032         else {
2033 #endif
2034             cache->insert(kurl.url(), doc);
2035             moveToHeadOfLRUList(doc);
2036 #if APPLE_CHANGES
2037         }
2038 #endif
2039         o = doc;
2040     }
2041     
2042 #if !APPLE_CHANGES
2043     o->setExpireDate(_expireDate, true);
2044 #endif
2045     
2046     if(o->type() != CachedObject::XBL)
2047     {
2048 #ifdef CACHE_DEBUG
2049         kdDebug( 6060 ) << "Cache::Internal Error in requestXBLDocument url=" << kurl.url() << "!" << endl;
2050 #endif
2051         return 0;
2052     }
2053     
2054 #ifdef CACHE_DEBUG
2055     if( o->status() == CachedObject::Pending )
2056         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2057     else
2058         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2059 #endif
2060     
2061     moveToHeadOfLRUList(o);
2062     if ( dl ) {
2063         dl->m_docObjects.remove( o );
2064 #if APPLE_CHANGES
2065         if (!cacheDisabled)
2066 #endif
2067             dl->m_docObjects.append( o );
2068     }
2069     return static_cast<CachedXBLDocument*>(o);
2070 }
2071 #endif
2072
2073 void Cache::flush(bool force)
2074 {
2075     if (force)
2076        flushCount = 0;
2077     // Don't flush for every image.
2078     if (m_countOfLRUAndUncacheableLists < flushCount)
2079        return;
2080
2081     init();
2082
2083 #ifdef CACHE_DEBUG
2084     statistics();
2085     kdDebug( 6060 ) << "Cache: flush()" << endl;
2086 #endif
2087
2088     while (m_headOfUncacheableList)
2089         removeCacheEntry(m_headOfUncacheableList);
2090
2091     for (int i = MAX_LRU_LISTS-1; i>=0; i--) {
2092         if (m_totalSizeOfLRULists <= maxSize)
2093             break;
2094             
2095         while (m_totalSizeOfLRULists > maxSize && m_LRULists[i].m_tail)
2096             removeCacheEntry(m_LRULists[i].m_tail);
2097     }
2098
2099     flushCount = m_countOfLRUAndUncacheableLists+10; // Flush again when the cache has grown.
2100 #ifdef CACHE_DEBUG
2101     //statistics();
2102 #endif
2103 }
2104
2105 #if 0
2106
2107 void Cache::checkLRUAndUncacheableListIntegrity()
2108 {
2109     int count = 0;
2110     
2111     {
2112         int size = 0;
2113         CachedObject *prev = 0;
2114         for (CachedObject *o = m_headOfLRUList; o; o = o->m_nextInLRUList) {
2115             ASSERT(o->allowInLRUList());
2116             ASSERT(o->status() != CachedObject::Uncacheable);
2117             ASSERT(o->m_prevInLRUList == prev);
2118             size += o->size();
2119             prev = o;
2120             ++count;
2121         }
2122         ASSERT(m_tailOfLRUList == prev);
2123         ASSERT(m_totalSizeOfLRUList == size);
2124     }
2125     
2126     {
2127         CachedObject *prev = 0;
2128         for (CachedObject *o = m_headOfUncacheableList; o; o = o->m_nextInLRUList) {
2129             ASSERT(o->allowInLRUList());
2130             ASSERT(o->status() == CachedObject::Uncacheable);
2131             ASSERT(o->m_prevInLRUList == prev);
2132             prev = o;
2133             ++count;
2134         }
2135     }
2136     
2137     ASSERT(m_countOfLRUAndUncacheableLists == count);
2138 }
2139
2140 #endif
2141
2142 void Cache::setSize( int bytes )
2143 {
2144     maxSize = bytes;
2145     maxCacheable = maxSize / 128;
2146
2147     // may be we need to clear parts of the cache
2148     flushCount = 0;
2149     flush(true);
2150 }
2151
2152 void Cache::statistics()
2153 {
2154     CachedObject *o;
2155     // this function is for debugging purposes only
2156     init();
2157
2158     int size = 0;
2159     int msize = 0;
2160     int movie = 0;
2161     int stylesheets = 0;
2162     QDictIterator<CachedObject> it(*cache);
2163     for(it.toFirst(); it.current(); ++it)
2164     {
2165         o = it.current();
2166         if(o->type() == CachedObject::Image)
2167         {
2168             CachedImage *im = static_cast<CachedImage *>(o);
2169             if(im->m != 0)
2170             {
2171                 movie++;
2172                 msize += im->size();
2173             }
2174         }
2175         else
2176         {
2177             if(o->type() == CachedObject::CSSStyleSheet)
2178                 stylesheets++;
2179
2180         }
2181         size += o->size();
2182     }
2183     size /= 1024;
2184
2185     kdDebug( 6060 ) << "------------------------- image cache statistics -------------------" << endl;
2186     kdDebug( 6060 ) << "Number of items in cache: " << cache->count() << endl;
2187     kdDebug( 6060 ) << "Number of items in lru  : " << m_countOfLRUAndUncacheableLists << endl;
2188     kdDebug( 6060 ) << "Number of cached images: " << cache->count()-movie << endl;
2189     kdDebug( 6060 ) << "Number of cached movies: " << movie << endl;
2190     kdDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets << endl;
2191     kdDebug( 6060 ) << "pixmaps:   allocated space approx. " << size << " kB" << endl;
2192     kdDebug( 6060 ) << "movies :   allocated space approx. " << msize/1024 << " kB" << endl;
2193     kdDebug( 6060 ) << "--------------------------------------------------------------------" << endl;
2194 }
2195
2196 void Cache::removeCacheEntry( CachedObject *object )
2197 {
2198   QString key = object->url().string();
2199
2200   // this indicates the deref() method of CachedObject to delete itself when the reference counter
2201   // drops down to zero
2202   object->setFree( true );
2203
2204   cache->remove( key );
2205   removeFromLRUList(object);
2206
2207   const DocLoader* dl;
2208   for ( dl=docloader->first(); dl; dl=docloader->next() )
2209       dl->removeCachedObject( object );
2210
2211   if ( object->canDelete() )
2212      delete object;
2213 }
2214
2215 #define FAST_LOG2(_log2,_n)   \
2216       unsigned int j_ = (unsigned int)(_n);   \
2217       (_log2) = 0;                    \
2218       if ((j_) & ((j_)-1))            \
2219       (_log2) += 1;               \
2220       if ((j_) >> 16)                 \
2221       (_log2) += 16, (j_) >>= 16; \
2222       if ((j_) >> 8)                  \
2223       (_log2) += 8, (j_) >>= 8;   \
2224       if ((j_) >> 4)                  \
2225       (_log2) += 4, (j_) >>= 4;   \
2226       if ((j_) >> 2)                  \
2227       (_log2) += 2, (j_) >>= 2;   \
2228       if ((j_) >> 1)                  \
2229       (_log2) += 1;
2230
2231 int FastLog2(unsigned int i) {
2232     int log2;
2233     FAST_LOG2(log2,i);
2234     return log2;
2235 }
2236
2237 LRUList* Cache::getLRUListFor(CachedObject* o)
2238 {
2239     int accessCount = o->accessCount();
2240     int queueIndex;
2241     if (accessCount == 0) {
2242         queueIndex = 0;
2243     } else {
2244         int sizeLog = FastLog2(o->size());
2245         queueIndex = sizeLog/o->accessCount() - 1;
2246         if (queueIndex < 0)
2247             queueIndex = 0;
2248         if (queueIndex >= MAX_LRU_LISTS)
2249             queueIndex = MAX_LRU_LISTS-1;
2250     }
2251     if (m_LRULists == 0) {
2252         m_LRULists = new LRUList [MAX_LRU_LISTS];
2253     }
2254     return &m_LRULists[queueIndex];
2255 }
2256
2257 void Cache::removeFromLRUList(CachedObject *object)
2258 {
2259     CachedObject *next = object->m_nextInLRUList;
2260     CachedObject *prev = object->m_prevInLRUList;
2261     bool uncacheable = object->status() == CachedObject::Uncacheable;
2262     
2263     LRUList* list = uncacheable ? 0 : getLRUListFor(object);
2264     CachedObject *&head = uncacheable ? m_headOfUncacheableList : list->m_head;
2265     
2266     if (next == 0 && prev == 0 && head != object) {
2267         return;
2268     }
2269     
2270     object->m_nextInLRUList = 0;
2271     object->m_prevInLRUList = 0;
2272     
2273     if (next)
2274         next->m_prevInLRUList = prev;
2275     else if (!uncacheable && list->m_tail == object)
2276         list->m_tail = prev;
2277
2278     if (prev)
2279         prev->m_nextInLRUList = next;
2280     else if (head == object)
2281         head = next;
2282     
2283     --m_countOfLRUAndUncacheableLists;
2284     
2285     if (!uncacheable)
2286         m_totalSizeOfLRULists -= object->size();
2287 }
2288
2289 void Cache::moveToHeadOfLRUList(CachedObject *object)
2290 {
2291     insertInLRUList(object);
2292 }
2293
2294 void Cache::insertInLRUList(CachedObject *object)
2295 {
2296     removeFromLRUList(object);
2297     
2298     if (!object->allowInLRUList())
2299         return;
2300     
2301     LRUList* list = getLRUListFor(object);
2302     
2303     bool uncacheable = object->status() == CachedObject::Uncacheable;
2304     CachedObject *&head = uncacheable ? m_headOfUncacheableList : list->m_head;
2305
2306     object->m_nextInLRUList = head;
2307     if (head)
2308         head->m_prevInLRUList = object;
2309     head = object;
2310     
2311     if (object->m_nextInLRUList == 0 && !uncacheable)
2312         list->m_tail = object;
2313     
2314     ++m_countOfLRUAndUncacheableLists;
2315     
2316     if (!uncacheable)
2317         m_totalSizeOfLRULists += object->size();
2318 }
2319
2320 bool Cache::adjustSize(CachedObject *object, int delta)
2321 {
2322     if (object->status() == CachedObject::Uncacheable)
2323         return false;
2324
2325     if (object->m_nextInLRUList == 0 && object->m_prevInLRUList == 0 &&
2326         getLRUListFor(object)->m_head != object)
2327         return false;
2328     
2329     m_totalSizeOfLRULists += delta;
2330     return delta != 0;
2331 }
2332
2333 // --------------------------------------
2334
2335 CachedObjectClient *CachedObjectClientWalker::next()
2336 {
2337     // Only advance if we already returned this item.
2338     // This handles cases where the current item is removed, and prevents us from skipping the next item.
2339     // The iterator automatically gets advanced to the next item, and we make sure we return it.
2340     if (_current == _iterator.current())
2341         ++_iterator;
2342     _current = _iterator.current();
2343     return _current;
2344 }
2345
2346 // --------------------------------------
2347
2348 void CachedObjectClient::setPixmap(const QPixmap &, const QRect&, CachedImage *) {}
2349 void CachedObjectClient::setStyleSheet(const DOM::DOMString &/*url*/, const DOM::DOMString &/*sheet*/) {}
2350 #ifndef KHTML_NO_XBL
2351 void CachedObjectClient::setXBLDocument(const DOM::DOMString& url, XBL::XBLDocumentImpl* doc) {}
2352 #endif
2353 void CachedObjectClient::notifyFinished(CachedObject * /*finishedObj*/) {}
2354
2355 #include "loader.moc"
2356
2357
2358 #if APPLE_CHANGES
2359
2360 Cache::Statistics Cache::getStatistics()
2361 {
2362     Statistics stats;
2363
2364     if (!cache)
2365         return stats;
2366
2367     QDictIterator<CachedObject> i(*cache);
2368     for (i.toFirst(); i.current(); ++i) {
2369         CachedObject *o = i.current();
2370         switch (o->type()) {
2371             case CachedObject::Image:
2372                 if (static_cast<CachedImage *>(o)->m) {
2373                     stats.movies.count++;
2374                     stats.movies.size += o->size();
2375                 } else {
2376                     stats.images.count++;
2377                     stats.images.size += o->size();
2378                 }
2379                 break;
2380
2381             case CachedObject::CSSStyleSheet:
2382                 stats.styleSheets.count++;
2383                 stats.styleSheets.size += o->size();
2384                 break;
2385
2386             case CachedObject::Script:
2387                 stats.scripts.count++;
2388                 stats.scripts.size += o->size();
2389                 break;
2390 #ifdef KHTML_XSLT
2391             case CachedObject::XSLStyleSheet:
2392                 stats.xslStyleSheets.count++;
2393                 stats.xslStyleSheets.size += o->size();
2394                 break;
2395 #endif
2396 #ifndef KHTML_NO_XBL
2397             case CachedObject::XBL:
2398                 stats.xblDocs.count++;
2399                 stats.xblDocs.size += o->size();
2400                 break;
2401 #endif
2402             default:
2403                 stats.other.count++;
2404                 stats.other.size += o->size();
2405         }
2406     }
2407     
2408     return stats;
2409 }
2410
2411 void Cache::flushAll()
2412 {
2413     if (!cache)
2414         return;
2415
2416     for (;;) {
2417         QDictIterator<CachedObject> i(*cache);
2418         CachedObject *o = i.toFirst();
2419         if (!o)
2420             break;
2421         removeCacheEntry(o);
2422     }
2423 }
2424
2425 void Cache::setCacheDisabled(bool disabled)
2426 {
2427     cacheDisabled = disabled;
2428     if (disabled)
2429         flushAll();
2430 }
2431
2432 #endif // APPLE_CHANGES