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