Change by Richard, reviewed by me.
[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
1261 #if APPLE_CHANGES
1262     Cache::init();
1263 #endif
1264     Cache::docloader->append( this );
1265 }
1266
1267 DocLoader::~DocLoader()
1268 {
1269     Cache::docloader->remove( this );
1270 }
1271
1272 void DocLoader::setExpireDate(time_t _expireDate)
1273 {
1274     m_expireDate = _expireDate;
1275 }
1276
1277 bool DocLoader::needReload(const KURL &fullURL)
1278 {
1279     bool reload = false;
1280     if (m_cachePolicy == KIO::CC_Verify)
1281     {
1282        if (!m_reloadedURLs.contains(fullURL.url()))
1283        {
1284           CachedObject *existing = Cache::cache->find(fullURL.url());
1285           if (existing && existing->isExpired())
1286           {
1287              Cache::removeCacheEntry(existing);
1288              m_reloadedURLs.append(fullURL.url());
1289              reload = true;
1290           }
1291        }
1292     }
1293     else if ((m_cachePolicy == KIO::CC_Reload) || (m_cachePolicy == KIO::CC_Refresh))
1294     {
1295        if (!m_reloadedURLs.contains(fullURL.url()))
1296        {
1297           CachedObject *existing = Cache::cache->find(fullURL.url());
1298           if (existing)
1299           {
1300              Cache::removeCacheEntry(existing);
1301           }
1302           m_reloadedURLs.append(fullURL.url());
1303           reload = true;
1304        }
1305     }
1306     return reload;
1307 }
1308
1309 CachedImage *DocLoader::requestImage( const DOM::DOMString &url)
1310 {
1311     KURL fullURL = m_doc->completeURL( url.string() );
1312     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1313
1314 #if APPLE_CHANGES
1315     if (KWQCheckIfReloading(this)) {
1316         setCachePolicy(KIO::CC_Reload);
1317     }
1318 #endif
1319
1320     bool reload = needReload(fullURL);
1321
1322 #if APPLE_CHANGES
1323     CachedImage *cachedObject = Cache::requestImage(this, fullURL, reload, m_expireDate);
1324     KWQCheckCacheObjectStatus(this, cachedObject);
1325     return cachedObject;
1326 #else
1327     return Cache::requestImage(this, fullURL, reload, m_expireDate);
1328 #endif
1329 }
1330
1331 CachedCSSStyleSheet *DocLoader::requestStyleSheet( const DOM::DOMString &url, const QString& charset)
1332 {
1333     KURL fullURL = m_doc->completeURL( url.string() );
1334     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1335
1336 #if APPLE_CHANGES
1337     if (KWQCheckIfReloading(this)) {
1338         setCachePolicy(KIO::CC_Reload);
1339     }
1340 #endif
1341
1342     bool reload = needReload(fullURL);
1343
1344 #if APPLE_CHANGES
1345     CachedCSSStyleSheet *cachedObject = Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
1346     KWQCheckCacheObjectStatus(this, cachedObject);
1347     return cachedObject;
1348 #else
1349     return Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
1350 #endif
1351 }
1352
1353 CachedScript *DocLoader::requestScript( const DOM::DOMString &url, const QString& charset)
1354 {
1355     KURL fullURL = m_doc->completeURL( url.string() );
1356     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1357
1358 #if APPLE_CHANGES
1359     if (KWQCheckIfReloading(this)) {
1360         setCachePolicy(KIO::CC_Reload);
1361     }
1362 #endif
1363
1364     bool reload = needReload(fullURL);
1365
1366 #if APPLE_CHANGES
1367     CachedScript *cachedObject = Cache::requestScript(this, url, reload, m_expireDate, charset);
1368     KWQCheckCacheObjectStatus(this, cachedObject);
1369     return cachedObject;
1370 #else
1371     return Cache::requestScript(this, url, reload, m_expireDate, charset);
1372 #endif
1373 }
1374
1375 #ifdef KHTML_XSLT
1376 CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const DOM::DOMString &url)
1377 {
1378     KURL fullURL = m_doc->completeURL(url.string());
1379     
1380     if (m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1381     
1382 #if APPLE_CHANGES
1383     if (KWQCheckIfReloading(this))
1384         setCachePolicy(KIO::CC_Reload);
1385 #endif
1386     
1387     bool reload = needReload(fullURL);
1388     
1389 #if APPLE_CHANGES
1390     CachedXSLStyleSheet *cachedObject = Cache::requestXSLStyleSheet(this, url, reload, m_expireDate);
1391     KWQCheckCacheObjectStatus(this, cachedObject);
1392     return cachedObject;
1393 #else
1394     return Cache::requestXSLStyleSheet(this, url, reload, m_expireDate);
1395 #endif
1396 }
1397 #endif
1398
1399 #ifndef KHTML_NO_XBL
1400 CachedXBLDocument* DocLoader::requestXBLDocument(const DOM::DOMString &url)
1401 {
1402     KURL fullURL = m_doc->completeURL(url.string());
1403     
1404     // FIXME: Is this right for XBL?
1405     if (m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
1406     
1407 #if APPLE_CHANGES
1408     if (KWQCheckIfReloading(this)) {
1409         setCachePolicy(KIO::CC_Reload);
1410     }
1411 #endif
1412     
1413     bool reload = needReload(fullURL);
1414     
1415 #if APPLE_CHANGES
1416     CachedXBLDocument *cachedObject = Cache::requestXBLDocument(this, url, reload, m_expireDate);
1417     KWQCheckCacheObjectStatus(this, cachedObject);
1418     return cachedObject;
1419 #else
1420     return Cache::requestXBLDocument(this, url, reload, m_expireDate);
1421 #endif
1422 }
1423 #endif
1424
1425 void DocLoader::setAutoloadImages( bool enable )
1426 {
1427     if ( enable == m_bautoloadImages )
1428         return;
1429
1430     m_bautoloadImages = enable;
1431
1432     if ( !m_bautoloadImages ) return;
1433
1434     for ( const CachedObject* co=m_docObjects.first(); co; co=m_docObjects.next() )
1435         if ( co->type() == CachedObject::Image )
1436         {
1437             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
1438
1439             CachedObject::Status status = img->status();
1440             if ( status != CachedObject::Unknown )
1441                 continue;
1442
1443             Cache::loader()->load(this, img, true);
1444         }
1445 }
1446
1447 void DocLoader::setCachePolicy( KIO::CacheControl cachePolicy )
1448 {
1449     m_cachePolicy = cachePolicy;
1450 }
1451
1452 void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
1453 {
1454     if ( showAnimations == m_showAnimations ) return;
1455     m_showAnimations = showAnimations;
1456
1457     const CachedObject* co;
1458     for ( co=m_docObjects.first(); co; co=m_docObjects.next() )
1459         if ( co->type() == CachedObject::Image )
1460         {
1461             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
1462
1463             img->setShowAnimations( showAnimations );
1464         }
1465 }
1466
1467 void DocLoader::removeCachedObject( CachedObject* o ) const
1468 {
1469     m_docObjects.removeRef( o );
1470 }
1471
1472 // ------------------------------------------------------------------------------------------
1473
1474 Loader::Loader() : QObject()
1475 {
1476     m_requestsPending.setAutoDelete( true );
1477     m_requestsLoading.setAutoDelete( true );
1478 #if APPLE_CHANGES
1479     kwq = new KWQLoader(this);
1480 #endif
1481 }
1482
1483 Loader::~Loader()
1484 {
1485 #if APPLE_CHANGES
1486     delete kwq;
1487 #endif
1488 }
1489
1490 void Loader::load(DocLoader* dl, CachedObject *object, bool incremental)
1491 {
1492     Request *req = new Request(dl, object, incremental);
1493     m_requestsPending.append(req);
1494
1495     emit requestStarted( req->m_docLoader, req->object );
1496
1497     servePendingRequests();
1498 }
1499
1500 void Loader::servePendingRequests()
1501 {
1502   if ( m_requestsPending.count() == 0 )
1503       return;
1504
1505   // get the first pending request
1506   Request *req = m_requestsPending.take(0);
1507
1508 #ifdef CACHE_DEBUG
1509   kdDebug( 6060 ) << "starting Loader url=" << req->object->url().string() << endl;
1510 #endif
1511
1512   KURL u(req->object->url().string());
1513 #if APPLE_CHANGES
1514   KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/, true);
1515 #else
1516   KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/);
1517 #endif
1518   
1519   job->addMetaData("cache", getCacheControlString(req->object->cachePolicy()));
1520   if (!req->object->accept().isEmpty())
1521       job->addMetaData("accept", req->object->accept());
1522   if ( req->m_docLoader )  {
1523       KURL r = req->m_docLoader->doc()->URL();
1524       if ( r.protocol().startsWith( "http" ) && r.path().isEmpty() )
1525           r.setPath( "/" );
1526
1527       job->addMetaData("referrer", r.url());
1528       QString domain = r.host();
1529       if (req->m_docLoader->doc()->isHTMLDocument())
1530          domain = static_cast<HTMLDocumentImpl*>(req->m_docLoader->doc())->domain().string();
1531       if (crossDomain(u.host(), domain))
1532          job->addMetaData("cross-domain", "true");
1533   }
1534
1535 #if APPLE_CHANGES
1536   connect( job, SIGNAL( result( KIO::Job *, NSData *) ), this, SLOT( slotFinished( KIO::Job *, NSData *) ) );
1537 #else
1538   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinished( KIO::Job * ) ) );
1539 #endif
1540   
1541 #if APPLE_CHANGES
1542   connect( job, SIGNAL( data( KIO::Job*, const char *, int)),
1543            SLOT( slotData( KIO::Job*, const char *, int)));
1544   connect( job, SIGNAL( receivedResponse( KIO::Job *, NSURLResponse *)), SLOT( slotReceivedResponse( KIO::Job *, NSURLResponse *)) );
1545
1546   if (KWQServeRequest(this, req, job)) {
1547       if (req->object->type() == CachedObject::Image) {
1548         CachedImage *ci = static_cast<CachedImage*>(req->object);
1549         if (ci->decoderCallback()) {
1550             m_requestsBackgroundDecoding.append(req);
1551         }
1552       }
1553       m_requestsLoading.insert(job, req);
1554   }
1555 #else
1556   connect( job, SIGNAL( data( KIO::Job*, const QByteArray &)),
1557            SLOT( slotData( KIO::Job*, const QByteArray &)));
1558
1559   if ( req->object->schedule() )
1560       KIO::Scheduler::scheduleJob( job );
1561
1562   m_requestsLoading.insert(job, req);
1563 #endif // APPLE_CHANGES
1564 }
1565
1566 #if !APPLE_CHANGES
1567 void Loader::slotFinished( KIO::Job* job)
1568 {
1569   Request *r = m_requestsLoading.take( job );
1570   KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
1571
1572   if ( !r )
1573     return;
1574
1575   if (j->error() || j->isErrorPage())
1576   {
1577       kdDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage() << endl;
1578       r->object->error( job->error(), job->errorText().ascii() );
1579       emit requestFailed( r->m_docLoader, r->object );
1580   }
1581   else
1582   {
1583       r->object->data(r->m_buffer, true);
1584
1585       emit requestDone( r->m_docLoader, r->object );
1586       time_t expireDate = j->queryMetaData("expire-date").toLong();
1587 kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << " expires " << ctime(&expireDate) << endl;
1588       r->object->setExpireDate(expireDate, false);
1589   }
1590
1591   r->object->finish();
1592
1593 #ifdef CACHE_DEBUG
1594   kdDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string() << endl;
1595 #endif
1596
1597   delete r;
1598
1599   servePendingRequests();
1600 }
1601 #else // APPLE_CHANGES
1602 void Loader::slotFinished( KIO::Job* job, NSData *allData)
1603 {
1604     Request *r = m_requestsLoading.take( job );
1605     KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
1606
1607     if ( !r )
1608         return;
1609
1610     CachedObject *object = r->object;
1611     DocLoader *docLoader = r->m_docLoader;
1612     
1613     bool backgroundImageDecoding = (object->type() == CachedObject::Image && 
1614         static_cast<CachedImage*>(object)->decoderCallback());
1615         
1616     if (j->error() || j->isErrorPage()) {
1617         // Use the background image decoder's callback to handle the error.
1618         if (backgroundImageDecoding) {
1619             CachedImageCallback *callback = static_cast<CachedImage*>(object)->decoderCallback();
1620             callback->handleError();
1621         }
1622         else {
1623             r->object->error( job->error(), job->errorText().ascii() );
1624             emit requestFailed( docLoader, object );
1625             Cache::removeCacheEntry( object );
1626         }
1627     }
1628     else {
1629         object->data(r->m_buffer, true);
1630
1631         r->object->setAllData(allData);
1632
1633         // Let the background image decoder trigger the done signal.
1634         if (!backgroundImageDecoding)
1635             emit requestDone( docLoader, object );
1636
1637         object->finish();
1638     }
1639
1640     // Let the background image decoder release the request when it is
1641     // finished.
1642     if (!backgroundImageDecoding) {
1643         delete r;
1644     }
1645
1646     servePendingRequests();
1647 }
1648 #endif
1649
1650 #if APPLE_CHANGES
1651
1652 void Loader::slotReceivedResponse(KIO::Job* job, NSURLResponse *response)
1653 {
1654     Request *r = m_requestsLoading[job];
1655     ASSERT(r);
1656     ASSERT(response);
1657     r->object->setResponse(response);
1658     r->object->setExpireDate(KWQCacheObjectExpiresTime(r->m_docLoader, response), false);
1659 }
1660
1661 #endif
1662
1663 #if APPLE_CHANGES
1664 void Loader::slotData( KIO::Job*job, const char *data, int size )
1665 #else
1666 void Loader::slotData( KIO::Job*job, const QByteArray &data )
1667 #endif
1668 {
1669     Request *r = m_requestsLoading[job];
1670     if(!r) {
1671         kdDebug( 6060 ) << "got data for unknown request!" << endl;
1672         return;
1673     }
1674
1675     if ( !r->m_buffer.isOpen() )
1676         r->m_buffer.open( IO_WriteOnly );
1677
1678 #if APPLE_CHANGES
1679     r->m_buffer.writeBlock( data, size );
1680 #else
1681     r->m_buffer.writeBlock( data.data(), data.size() );
1682 #endif
1683
1684     if(r->incremental)
1685         r->object->data( r->m_buffer, false );
1686 }
1687
1688 int Loader::numRequests( DocLoader* dl ) const
1689 {
1690     int res = 0;
1691
1692     QPtrListIterator<Request> pIt( m_requestsPending );
1693     for (; pIt.current(); ++pIt )
1694         if ( pIt.current()->m_docLoader == dl )
1695             res++;
1696
1697     QPtrDictIterator<Request> lIt( m_requestsLoading );
1698     for (; lIt.current(); ++lIt )
1699         if ( lIt.current()->m_docLoader == dl )
1700             res++;
1701
1702 #if APPLE_CHANGES
1703     QPtrListIterator<Request> bdIt( m_requestsBackgroundDecoding );
1704     for (; bdIt.current(); ++bdIt )
1705         if ( bdIt.current()->m_docLoader == dl )
1706             res++;
1707 #endif
1708
1709     return res;
1710 }
1711
1712 void Loader::cancelRequests( DocLoader* dl )
1713 {
1714     //kdDebug( 6060 ) << "void Loader::cancelRequests()" << endl;
1715     //kdDebug( 6060 ) << "got " << m_requestsPending.count() << " pending requests" << endl;
1716     QPtrListIterator<Request> pIt( m_requestsPending );
1717     while ( pIt.current() )
1718     {
1719         if ( pIt.current()->m_docLoader == dl )
1720         {
1721             kdDebug( 6060 ) << "cancelling pending request for " << pIt.current()->object->url().string() << endl;
1722             //emit requestFailed( dl, pIt.current()->object );
1723             Cache::removeCacheEntry( pIt.current()->object );
1724             m_requestsPending.remove( pIt );
1725         }
1726         else
1727             ++pIt;
1728     }
1729
1730     //kdDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests" << endl;
1731
1732     QPtrDictIterator<Request> lIt( m_requestsLoading );
1733     while ( lIt.current() )
1734     {
1735         if ( lIt.current()->m_docLoader == dl )
1736         {
1737             //kdDebug( 6060 ) << "cancelling loading request for " << lIt.current()->object->url().string() << endl;
1738             KIO::Job *job = static_cast<KIO::Job *>( lIt.currentKey() );
1739             Cache::removeCacheEntry( lIt.current()->object );
1740             m_requestsLoading.remove( lIt.currentKey() );
1741             job->kill();
1742             //emit requestFailed( dl, pIt.current()->object );
1743         }
1744         else
1745             ++lIt;
1746     }
1747
1748 #if APPLE_CHANGES
1749     QPtrListIterator<Request> bdIt( m_requestsBackgroundDecoding );
1750     while ( bdIt.current() )
1751     {
1752         if ( bdIt.current()->m_docLoader == dl )
1753         {
1754             kdDebug( 6060 ) << "cancelling pending request for " << bdIt.current()->object->url().string() << endl;
1755             //emit requestFailed( dl, bdIt.current()->object );
1756             Cache::removeCacheEntry( bdIt.current()->object );
1757             m_requestsBackgroundDecoding.remove( bdIt );
1758         }
1759         else
1760             ++bdIt;
1761     }
1762 #endif
1763 }
1764
1765 #if APPLE_CHANGES
1766 void Loader::removeBackgroundDecodingRequest (Request *r)
1767 {
1768     bool present = m_requestsBackgroundDecoding.containsRef(r);
1769     if (present) {
1770         m_requestsBackgroundDecoding.remove (r);
1771     }
1772 }
1773 #endif
1774
1775 KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
1776 {
1777     QPtrDictIterator<Request> it( m_requestsLoading );
1778
1779     for (; it.current(); ++it )
1780     {
1781         CachedObject *obj = it.current()->object;
1782
1783         if ( obj && obj->url() == url )
1784             return static_cast<KIO::Job *>( it.currentKey() );
1785     }
1786
1787     return 0;
1788 }
1789
1790 // ----------------------------------------------------------------------------
1791
1792
1793 LRUList::LRUList()
1794 :m_head(0), m_tail(0) 
1795 {}
1796
1797 LRUList::~LRUList()
1798 {}
1799
1800 QDict<CachedObject> *Cache::cache = 0;
1801 QPtrList<DocLoader>* Cache::docloader = 0;
1802 Loader *Cache::m_loader = 0;
1803
1804 int Cache::maxSize = DEFCACHESIZE;
1805 int Cache::maxCacheable = MAXCACHEABLE;
1806 int Cache::flushCount = 0;
1807
1808 QPixmap *Cache::nullPixmap = 0;
1809 QPixmap *Cache::brokenPixmap = 0;
1810
1811 CachedObject *Cache::m_headOfUncacheableList = 0;
1812 int Cache::m_totalSizeOfLRULists = 0;
1813 int Cache::m_countOfLRUAndUncacheableLists;
1814 LRUList *Cache::m_LRULists = 0;
1815
1816 void Cache::init()
1817 {
1818     if ( !cache )
1819         cache = new QDict<CachedObject>(401, true);
1820
1821     if ( !docloader )
1822         docloader = new QPtrList<DocLoader>;
1823
1824     if ( !nullPixmap )
1825         nullPixmap = new QPixmap;
1826
1827     if ( !brokenPixmap )
1828 #if APPLE_CHANGES
1829         brokenPixmap = KWQLoadPixmap("missing_image");
1830 #else
1831         brokenPixmap = new QPixmap(KHTMLFactory::instance()->iconLoader()->loadIcon("file_broken", KIcon::Desktop, 16, KIcon::DisabledState));
1832 #endif
1833
1834     if ( !m_loader )
1835         m_loader = new Loader();
1836 }
1837
1838 void Cache::clear()
1839 {
1840     if ( !cache ) return;
1841 #ifdef CACHE_DEBUG
1842     kdDebug( 6060 ) << "Cache: CLEAR!" << endl;
1843     statistics();
1844 #endif
1845     cache->setAutoDelete( true );
1846     delete cache; cache = 0;
1847     delete nullPixmap; nullPixmap = 0;
1848     delete brokenPixmap; brokenPixmap = 0;
1849     delete m_loader;   m_loader = 0;
1850     delete docloader; docloader = 0;
1851 }
1852
1853 CachedImage *Cache::requestImage( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate )
1854 {
1855     // this brings the _url to a standard form...
1856     KURL kurl;
1857     if (dl)
1858         kurl = dl->m_doc->completeURL( url.string() );
1859     else
1860         kurl = url.string();
1861     return requestImage(dl, kurl, reload, _expireDate);
1862 }
1863
1864 CachedImage *Cache::requestImage( DocLoader* dl, const KURL & url, bool reload, time_t _expireDate )
1865 {
1866     KIO::CacheControl cachePolicy;
1867     if (dl)
1868         cachePolicy = dl->cachePolicy();
1869     else
1870         cachePolicy = KIO::CC_Verify;
1871
1872 #if APPLE_CHANGES
1873     // Checking if the URL is malformed is lots of extra work for little benefit.
1874 #else
1875     if( kurl.isMalformed() )
1876     {
1877 #ifdef CACHE_DEBUG
1878       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1879 #endif
1880       return 0;
1881     }
1882 #endif
1883
1884 #if APPLE_CHANGES
1885     if (!dl->doc()->shouldCreateRenderers()){
1886         return 0;
1887     }
1888 #endif
1889
1890     CachedObject *o = 0;
1891     if (!reload)
1892         o = cache->find(url.url());
1893     if(!o)
1894     {
1895 #ifdef CACHE_DEBUG
1896         kdDebug( 6060 ) << "Cache: new: " << url.url() << endl;
1897 #endif
1898         CachedImage *im = new CachedImage(dl, url.url(), cachePolicy, _expireDate);
1899         if ( dl && dl->autoloadImages() ) Cache::loader()->load(dl, im, true);
1900 #if APPLE_CHANGES
1901         if (cacheDisabled)
1902             im->setFree(true);
1903         else {
1904 #endif
1905         cache->insert( url.url(), im );
1906         moveToHeadOfLRUList(im);
1907 #if APPLE_CHANGES
1908         }
1909 #endif
1910         o = im;
1911     }
1912
1913 #if !APPLE_CHANGES
1914     o->setExpireDate(_expireDate, true);
1915 #endif
1916     
1917     if(o->type() != CachedObject::Image)
1918     {
1919 #ifdef CACHE_DEBUG
1920         kdDebug( 6060 ) << "Cache::Internal Error in requestImage url=" << kurl.url() << "!" << endl;
1921 #endif
1922         return 0;
1923     }
1924
1925 #ifdef CACHE_DEBUG
1926     if( o->status() == CachedObject::Pending )
1927         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
1928     else
1929         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << ", status " << o->status() << endl;
1930 #endif
1931
1932     moveToHeadOfLRUList(o);
1933     if ( dl ) {
1934         dl->m_docObjects.remove( o );
1935 #if APPLE_CHANGES
1936         if (!cacheDisabled)
1937 #endif
1938         dl->m_docObjects.append( o );
1939     }
1940     return static_cast<CachedImage *>(o);
1941 }
1942
1943 CachedCSSStyleSheet *Cache::requestStyleSheet( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate, const QString& charset)
1944 {
1945     // this brings the _url to a standard form...
1946     KURL kurl;
1947     KIO::CacheControl cachePolicy;
1948     if ( dl )
1949     {
1950         kurl = dl->m_doc->completeURL( url.string() );
1951         cachePolicy = dl->cachePolicy();
1952     }
1953     else
1954     {
1955         kurl = url.string();
1956         cachePolicy = KIO::CC_Verify;
1957     }
1958
1959 #if APPLE_CHANGES
1960     // Checking if the URL is malformed is lots of extra work for little benefit.
1961 #else
1962     if( kurl.isMalformed() )
1963     {
1964       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
1965       return 0;
1966     }
1967 #endif
1968
1969     CachedObject *o = cache->find(kurl.url());
1970     if(!o)
1971     {
1972 #ifdef CACHE_DEBUG
1973         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
1974 #endif
1975         CachedCSSStyleSheet *sheet = new CachedCSSStyleSheet(dl, kurl.url(), cachePolicy, _expireDate, charset);
1976 #if APPLE_CHANGES
1977         if (cacheDisabled)
1978             sheet->setFree(true);
1979         else {
1980 #endif
1981         cache->insert( kurl.url(), sheet );
1982         moveToHeadOfLRUList(sheet);
1983 #if APPLE_CHANGES
1984         }
1985 #endif
1986         o = sheet;
1987     }
1988
1989 #if !APPLE_CHANGES
1990     o->setExpireDate(_expireDate, true);
1991 #endif
1992     
1993     if(o->type() != CachedObject::CSSStyleSheet)
1994     {
1995 #ifdef CACHE_DEBUG
1996         kdDebug( 6060 ) << "Cache::Internal Error in requestStyleSheet url=" << kurl.url() << "!" << endl;
1997 #endif
1998         return 0;
1999     }
2000
2001 #ifdef CACHE_DEBUG
2002     if( o->status() == CachedObject::Pending )
2003         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2004     else
2005         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2006 #endif
2007
2008     moveToHeadOfLRUList(o);
2009     if ( dl ) {
2010         dl->m_docObjects.remove( o );
2011 #if APPLE_CHANGES
2012         if (!cacheDisabled)
2013 #endif
2014         dl->m_docObjects.append( o );
2015     }
2016     return static_cast<CachedCSSStyleSheet *>(o);
2017 }
2018
2019 void Cache::preloadStyleSheet( const QString &url, const QString &stylesheet_data)
2020 {
2021     CachedObject *o = cache->find(url);
2022     if(o)
2023         removeCacheEntry(o);
2024
2025     CachedCSSStyleSheet *stylesheet = new CachedCSSStyleSheet(url, stylesheet_data);
2026     cache->insert( url, stylesheet );
2027 }
2028
2029 CachedScript *Cache::requestScript( DocLoader* dl, const DOM::DOMString &url, bool reload, time_t _expireDate, const QString& charset)
2030 {
2031     // this brings the _url to a standard form...
2032     KURL kurl;
2033     KIO::CacheControl cachePolicy;
2034     if ( dl )
2035     {
2036         kurl = dl->m_doc->completeURL( url.string() );
2037         cachePolicy = dl->cachePolicy();
2038     }
2039     else
2040     {
2041         kurl = url.string();
2042         cachePolicy = KIO::CC_Verify;
2043     }
2044
2045 #if APPLE_CHANGES
2046     // Checking if the URL is malformed is lots of extra work for little benefit.
2047 #else
2048     if( kurl.isMalformed() )
2049     {
2050       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2051       return 0;
2052     }
2053 #endif
2054
2055     CachedObject *o = cache->find(kurl.url());
2056     if(!o)
2057     {
2058 #ifdef CACHE_DEBUG
2059         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2060 #endif
2061         CachedScript *script = new CachedScript(dl, kurl.url(), cachePolicy, _expireDate, charset);
2062 #if APPLE_CHANGES
2063         if (cacheDisabled)
2064             script->setFree(true);
2065         else {
2066 #endif
2067         cache->insert( kurl.url(), script );
2068         moveToHeadOfLRUList(script);
2069 #if APPLE_CHANGES
2070         }
2071 #endif
2072         o = script;
2073     }
2074
2075 #if !APPLE_CHANGES
2076     o->setExpireDate(_expireDate, true);
2077 #endif
2078     
2079     if(!(o->type() == CachedObject::Script))
2080     {
2081 #ifdef CACHE_DEBUG
2082         kdDebug( 6060 ) << "Cache::Internal Error in requestScript url=" << kurl.url() << "!" << endl;
2083 #endif
2084         return 0;
2085     }
2086     
2087     
2088 #ifdef CACHE_DEBUG
2089     if( o->status() == CachedObject::Pending )
2090         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2091     else
2092         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2093 #endif
2094
2095     moveToHeadOfLRUList(o);
2096     if ( dl ) {
2097         dl->m_docObjects.remove( o );
2098 #if APPLE_CHANGES
2099         if (!cacheDisabled)
2100 #endif
2101         dl->m_docObjects.append( o );
2102     }
2103     return static_cast<CachedScript *>(o);
2104 }
2105
2106 void Cache::preloadScript( const QString &url, const QString &script_data)
2107 {
2108     CachedObject *o = cache->find(url);
2109     if(o)
2110         removeCacheEntry(o);
2111
2112     CachedScript *script = new CachedScript(url, script_data);
2113     cache->insert( url, script );
2114 }
2115
2116 #ifdef KHTML_XSLT
2117 CachedXSLStyleSheet* Cache::requestXSLStyleSheet(DocLoader* dl, const DOMString & url, bool reload, 
2118                                                  time_t _expireDate)
2119 {
2120     // this brings the _url to a standard form...
2121     KURL kurl;
2122     KIO::CacheControl cachePolicy;
2123     if (dl) {
2124         kurl = dl->m_doc->completeURL(url.string());
2125         cachePolicy = dl->cachePolicy();
2126     }
2127     else {
2128         kurl = url.string();
2129         cachePolicy = KIO::CC_Verify;
2130     }
2131     
2132 #if APPLE_CHANGES
2133     // Checking if the URL is malformed is lots of extra work for little benefit.
2134 #else
2135     if(kurl.isMalformed()) {
2136         kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2137         return 0;
2138     }
2139 #endif
2140     
2141     CachedObject *o = cache->find(kurl.url());
2142     if (!o) {
2143 #ifdef CACHE_DEBUG
2144         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2145 #endif
2146         CachedXSLStyleSheet* doc = new CachedXSLStyleSheet(dl, kurl.url(), cachePolicy, _expireDate);
2147 #if APPLE_CHANGES
2148         if (cacheDisabled)
2149             doc->setFree(true);
2150         else {
2151 #endif
2152             cache->insert(kurl.url(), doc);
2153             moveToHeadOfLRUList(doc);
2154 #if APPLE_CHANGES
2155         }
2156 #endif
2157         o = doc;
2158     }
2159     
2160 #if !APPLE_CHANGES
2161     o->setExpireDate(_expireDate, true);
2162 #endif
2163     
2164     if (o->type() != CachedObject::XSLStyleSheet) {
2165 #ifdef CACHE_DEBUG
2166         kdDebug( 6060 ) << "Cache::Internal Error in requestXSLStyleSheet url=" << kurl.url() << "!" << endl;
2167 #endif
2168         return 0;
2169     }
2170     
2171 #ifdef CACHE_DEBUG
2172     if (o->status() == CachedObject::Pending)
2173         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2174     else
2175         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2176 #endif
2177     
2178     moveToHeadOfLRUList(o);
2179     if (dl) {
2180         dl->m_docObjects.remove( o );
2181 #if APPLE_CHANGES
2182         if (!cacheDisabled)
2183 #endif
2184             dl->m_docObjects.append( o );
2185     }
2186     return static_cast<CachedXSLStyleSheet*>(o);
2187 }
2188 #endif
2189
2190 #ifndef KHTML_NO_XBL
2191 CachedXBLDocument* Cache::requestXBLDocument(DocLoader* dl, const DOMString & url, bool reload, 
2192                                              time_t _expireDate)
2193 {
2194     // this brings the _url to a standard form...
2195     KURL kurl;
2196     KIO::CacheControl cachePolicy;
2197     if (dl) {
2198         kurl = dl->m_doc->completeURL(url.string());
2199         cachePolicy = dl->cachePolicy();
2200     }
2201     else {
2202         kurl = url.string();
2203         cachePolicy = KIO::CC_Verify;
2204     }
2205     
2206 #if APPLE_CHANGES
2207     // Checking if the URL is malformed is lots of extra work for little benefit.
2208 #else
2209     if( kurl.isMalformed() )
2210     {
2211         kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
2212         return 0;
2213     }
2214 #endif
2215     
2216     CachedObject *o = cache->find(kurl.url());
2217     if(!o)
2218     {
2219 #ifdef CACHE_DEBUG
2220         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
2221 #endif
2222         CachedXBLDocument* doc = new CachedXBLDocument(dl, kurl.url(), cachePolicy, _expireDate);
2223 #if APPLE_CHANGES
2224         if (cacheDisabled)
2225             doc->setFree(true);
2226         else {
2227 #endif
2228             cache->insert(kurl.url(), doc);
2229             moveToHeadOfLRUList(doc);
2230 #if APPLE_CHANGES
2231         }
2232 #endif
2233         o = doc;
2234     }
2235     
2236 #if !APPLE_CHANGES
2237     o->setExpireDate(_expireDate, true);
2238 #endif
2239     
2240     if(o->type() != CachedObject::XBL)
2241     {
2242 #ifdef CACHE_DEBUG
2243         kdDebug( 6060 ) << "Cache::Internal Error in requestXBLDocument url=" << kurl.url() << "!" << endl;
2244 #endif
2245         return 0;
2246     }
2247     
2248 #ifdef CACHE_DEBUG
2249     if( o->status() == CachedObject::Pending )
2250         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
2251     else
2252         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
2253 #endif
2254     
2255     moveToHeadOfLRUList(o);
2256     if ( dl ) {
2257         dl->m_docObjects.remove( o );
2258 #if APPLE_CHANGES
2259         if (!cacheDisabled)
2260 #endif
2261             dl->m_docObjects.append( o );
2262     }
2263     return static_cast<CachedXBLDocument*>(o);
2264 }
2265 #endif
2266
2267 void Cache::flush(bool force)
2268 {
2269     if (force)
2270        flushCount = 0;
2271     // Don't flush for every image.
2272     if (m_countOfLRUAndUncacheableLists < flushCount)
2273        return;
2274
2275     init();
2276
2277 #ifdef CACHE_DEBUG
2278     statistics();
2279     kdDebug( 6060 ) << "Cache: flush()" << endl;
2280 #endif
2281
2282     while (m_headOfUncacheableList)
2283         removeCacheEntry(m_headOfUncacheableList);
2284
2285     for (int i = MAX_LRU_LISTS-1; i>=0; i--) {
2286         if (m_totalSizeOfLRULists <= maxSize)
2287             break;
2288             
2289         while (m_totalSizeOfLRULists > maxSize && m_LRULists[i].m_tail)
2290             removeCacheEntry(m_LRULists[i].m_tail);
2291     }
2292
2293     flushCount = m_countOfLRUAndUncacheableLists+10; // Flush again when the cache has grown.
2294 #ifdef CACHE_DEBUG
2295     //statistics();
2296 #endif
2297 }
2298
2299 #if 0
2300
2301 void Cache::checkLRUAndUncacheableListIntegrity()
2302 {
2303     int count = 0;
2304     
2305     {
2306         int size = 0;
2307         CachedObject *prev = 0;
2308         for (CachedObject *o = m_headOfLRUList; o; o = o->m_nextInLRUList) {
2309             ASSERT(o->allowInLRUList());
2310             ASSERT(o->status() != CachedObject::Uncacheable);
2311             ASSERT(o->m_prevInLRUList == prev);
2312             size += o->size();
2313             prev = o;
2314             ++count;
2315         }
2316         ASSERT(m_tailOfLRUList == prev);
2317         ASSERT(m_totalSizeOfLRUList == size);
2318     }
2319     
2320     {
2321         CachedObject *prev = 0;
2322         for (CachedObject *o = m_headOfUncacheableList; o; o = o->m_nextInLRUList) {
2323             ASSERT(o->allowInLRUList());
2324             ASSERT(o->status() == CachedObject::Uncacheable);
2325             ASSERT(o->m_prevInLRUList == prev);
2326             prev = o;
2327             ++count;
2328         }
2329     }
2330     
2331     ASSERT(m_countOfLRUAndUncacheableLists == count);
2332 }
2333
2334 #endif
2335
2336 void Cache::setSize( int bytes )
2337 {
2338     maxSize = bytes;
2339     maxCacheable = maxSize / 128;
2340
2341     // may be we need to clear parts of the cache
2342     flushCount = 0;
2343     flush(true);
2344 }
2345
2346 void Cache::statistics()
2347 {
2348     CachedObject *o;
2349     // this function is for debugging purposes only
2350     init();
2351
2352     int size = 0;
2353     int msize = 0;
2354     int movie = 0;
2355     int stylesheets = 0;
2356     QDictIterator<CachedObject> it(*cache);
2357     for(it.toFirst(); it.current(); ++it)
2358     {
2359         o = it.current();
2360         if(o->type() == CachedObject::Image)
2361         {
2362             CachedImage *im = static_cast<CachedImage *>(o);
2363             if(im->m != 0)
2364             {
2365                 movie++;
2366                 msize += im->size();
2367             }
2368         }
2369         else
2370         {
2371             if(o->type() == CachedObject::CSSStyleSheet)
2372                 stylesheets++;
2373
2374         }
2375         size += o->size();
2376     }
2377     size /= 1024;
2378
2379     kdDebug( 6060 ) << "------------------------- image cache statistics -------------------" << endl;
2380     kdDebug( 6060 ) << "Number of items in cache: " << cache->count() << endl;
2381     kdDebug( 6060 ) << "Number of items in lru  : " << m_countOfLRUAndUncacheableLists << endl;
2382     kdDebug( 6060 ) << "Number of cached images: " << cache->count()-movie << endl;
2383     kdDebug( 6060 ) << "Number of cached movies: " << movie << endl;
2384     kdDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets << endl;
2385     kdDebug( 6060 ) << "pixmaps:   allocated space approx. " << size << " kB" << endl;
2386     kdDebug( 6060 ) << "movies :   allocated space approx. " << msize/1024 << " kB" << endl;
2387     kdDebug( 6060 ) << "--------------------------------------------------------------------" << endl;
2388 }
2389
2390 void Cache::removeCacheEntry( CachedObject *object )
2391 {
2392   QString key = object->url().string();
2393
2394   // this indicates the deref() method of CachedObject to delete itself when the reference counter
2395   // drops down to zero
2396   object->setFree( true );
2397
2398   cache->remove( key );
2399   removeFromLRUList(object);
2400
2401   const DocLoader* dl;
2402   for ( dl=docloader->first(); dl; dl=docloader->next() )
2403       dl->removeCachedObject( object );
2404
2405   if ( object->canDelete() )
2406      delete object;
2407 }
2408
2409 #define FAST_LOG2(_log2,_n)   \
2410       unsigned int j_ = (unsigned int)(_n);   \
2411       (_log2) = 0;                    \
2412       if ((j_) & ((j_)-1))            \
2413       (_log2) += 1;               \
2414       if ((j_) >> 16)                 \
2415       (_log2) += 16, (j_) >>= 16; \
2416       if ((j_) >> 8)                  \
2417       (_log2) += 8, (j_) >>= 8;   \
2418       if ((j_) >> 4)                  \
2419       (_log2) += 4, (j_) >>= 4;   \
2420       if ((j_) >> 2)                  \
2421       (_log2) += 2, (j_) >>= 2;   \
2422       if ((j_) >> 1)                  \
2423       (_log2) += 1;
2424
2425 int FastLog2(unsigned int i) {
2426     int log2;
2427     FAST_LOG2(log2,i);
2428     return log2;
2429 }
2430
2431 LRUList* Cache::getLRUListFor(CachedObject* o)
2432 {
2433     int accessCount = o->accessCount();
2434     int queueIndex;
2435     if (accessCount == 0) {
2436         queueIndex = 0;
2437     } else {
2438         int sizeLog = FastLog2(o->size());
2439         queueIndex = sizeLog/o->accessCount() - 1;
2440         if (queueIndex < 0)
2441             queueIndex = 0;
2442         if (queueIndex >= MAX_LRU_LISTS)
2443             queueIndex = MAX_LRU_LISTS-1;
2444     }
2445     if (m_LRULists == 0) {
2446         m_LRULists = new LRUList [MAX_LRU_LISTS];
2447     }
2448     return &m_LRULists[queueIndex];
2449 }
2450
2451 void Cache::removeFromLRUList(CachedObject *object)
2452 {
2453     CachedObject *next = object->m_nextInLRUList;
2454     CachedObject *prev = object->m_prevInLRUList;
2455     bool uncacheable = object->status() == CachedObject::Uncacheable;
2456     
2457     LRUList* list = uncacheable ? 0 : getLRUListFor(object);
2458     CachedObject *&head = uncacheable ? m_headOfUncacheableList : list->m_head;
2459     
2460     if (next == 0 && prev == 0 && head != object) {
2461         return;
2462     }
2463     
2464     object->m_nextInLRUList = 0;
2465     object->m_prevInLRUList = 0;
2466     
2467     if (next)
2468         next->m_prevInLRUList = prev;
2469     else if (!uncacheable && list->m_tail == object)
2470         list->m_tail = prev;
2471
2472     if (prev)
2473         prev->m_nextInLRUList = next;
2474     else if (head == object)
2475         head = next;
2476     
2477     --m_countOfLRUAndUncacheableLists;
2478     
2479     if (!uncacheable)
2480         m_totalSizeOfLRULists -= object->size();
2481 }
2482
2483 void Cache::moveToHeadOfLRUList(CachedObject *object)
2484 {
2485     insertInLRUList(object);
2486 }
2487
2488 void Cache::insertInLRUList(CachedObject *object)
2489 {
2490     removeFromLRUList(object);
2491     
2492     if (!object->allowInLRUList())
2493         return;
2494     
2495     LRUList* list = getLRUListFor(object);
2496     
2497     bool uncacheable = object->status() == CachedObject::Uncacheable;
2498     CachedObject *&head = uncacheable ? m_headOfUncacheableList : list->m_head;
2499
2500     object->m_nextInLRUList = head;
2501     if (head)
2502         head->m_prevInLRUList = object;
2503     head = object;
2504     
2505     if (object->m_nextInLRUList == 0 && !uncacheable)
2506         list->m_tail = object;
2507     
2508     ++m_countOfLRUAndUncacheableLists;
2509     
2510     if (!uncacheable)
2511         m_totalSizeOfLRULists += object->size();
2512 }
2513
2514 bool Cache::adjustSize(CachedObject *object, int delta)
2515 {
2516     if (object->status() == CachedObject::Uncacheable)
2517         return false;
2518
2519     if (object->m_nextInLRUList == 0 && object->m_prevInLRUList == 0 &&
2520         getLRUListFor(object)->m_head != object)
2521         return false;
2522     
2523     m_totalSizeOfLRULists += delta;
2524     return delta != 0;
2525 }
2526
2527 // --------------------------------------
2528
2529 CachedObjectClient *CachedObjectClientWalker::next()
2530 {
2531     // Only advance if we already returned this item.
2532     // This handles cases where the current item is removed, and prevents us from skipping the next item.
2533     // The iterator automatically gets advanced to the next item, and we make sure we return it.
2534     if (_current == _iterator.current())
2535         ++_iterator;
2536     _current = _iterator.current();
2537     return _current;
2538 }
2539
2540 // --------------------------------------
2541
2542 void CachedObjectClient::setPixmap(const QPixmap &, const QRect&, CachedImage *) {}
2543 void CachedObjectClient::setStyleSheet(const DOM::DOMString &/*url*/, const DOM::DOMString &/*sheet*/) {}
2544 #ifndef KHTML_NO_XBL
2545 void CachedObjectClient::setXBLDocument(const DOM::DOMString& url, XBL::XBLDocumentImpl* doc) {}
2546 #endif
2547 void CachedObjectClient::notifyFinished(CachedObject * /*finishedObj*/) {}
2548
2549 #include "loader.moc"
2550
2551
2552 #if APPLE_CHANGES
2553
2554 Cache::Statistics Cache::getStatistics()
2555 {
2556     Statistics stats;
2557
2558     if (!cache)
2559         return stats;
2560
2561     QDictIterator<CachedObject> i(*cache);
2562     for (i.toFirst(); i.current(); ++i) {
2563         CachedObject *o = i.current();
2564         switch (o->type()) {
2565             case CachedObject::Image:
2566                 if (static_cast<CachedImage *>(o)->m) {
2567                     stats.movies.count++;
2568                     stats.movies.size += o->size();
2569                 } else {
2570                     stats.images.count++;
2571                     stats.images.size += o->size();
2572                 }
2573                 break;
2574
2575             case CachedObject::CSSStyleSheet:
2576                 stats.styleSheets.count++;
2577                 stats.styleSheets.size += o->size();
2578                 break;
2579
2580             case CachedObject::Script:
2581                 stats.scripts.count++;
2582                 stats.scripts.size += o->size();
2583                 break;
2584 #ifdef KHTML_XSLT
2585             case CachedObject::XSLStyleSheet:
2586                 stats.xslStyleSheets.count++;
2587                 stats.xslStyleSheets.size += o->size();
2588                 break;
2589 #endif
2590 #ifndef KHTML_NO_XBL
2591             case CachedObject::XBL:
2592                 stats.xblDocs.count++;
2593                 stats.xblDocs.size += o->size();
2594                 break;
2595 #endif
2596             default:
2597                 stats.other.count++;
2598                 stats.other.size += o->size();
2599         }
2600     }
2601     
2602     return stats;
2603 }
2604
2605 void Cache::flushAll()
2606 {
2607     if (!cache)
2608         return;
2609
2610     for (;;) {
2611         QDictIterator<CachedObject> i(*cache);
2612         CachedObject *o = i.toFirst();
2613         if (!o)
2614             break;
2615         removeCacheEntry(o);
2616     }
2617 }
2618
2619 void Cache::setCacheDisabled(bool disabled)
2620 {
2621     cacheDisabled = disabled;
2622     if (disabled)
2623         flushAll();
2624 }
2625
2626 #endif // APPLE_CHANGES