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