khtml Library API Documentation

loader.cpp

00001 /*
00002     This file is part of the KDE libraries
00003 
00004     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
00005     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
00006     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021     Boston, MA 02111-1307, USA.
00022 
00023     This class provides all functionality needed for loading images, style sheets and html
00024     pages from the web. It has a memory cache for these objects.
00025 */
00026 
00027 #undef CACHE_DEBUG
00028 //#define CACHE_DEBUG
00029 #include <assert.h>
00030 
00031 #include "loader.h"
00032 
00033 // up to which size is a picture for sure cacheable
00034 #define MAXCACHEABLE 40*1024
00035 // default cache size
00036 #define DEFCACHESIZE 512*1024
00037 
00038 #include <qasyncio.h>
00039 #include <qasyncimageio.h>
00040 #include <qpainter.h>
00041 #include <qbitmap.h>
00042 #include <qmovie.h>
00043 #include <qwidget.h>
00044 
00045 #include <kapplication.h>
00046 #include <kio/job.h>
00047 #include <kio/jobclasses.h>
00048 #include <kglobal.h>
00049 #include <kimageio.h>
00050 #include <kcharsets.h>
00051 #include <kiconloader.h>
00052 #include <scheduler.h>
00053 #include <kdebug.h>
00054 #include "khtml_factory.h"
00055 #include "khtml_part.h"
00056 
00057 #include "html/html_documentimpl.h"
00058 #include "css/css_stylesheetimpl.h"
00059 #include "xml/dom_docimpl.h"
00060 
00061 using namespace khtml;
00062 using namespace DOM;
00063 
00064 void CachedObject::finish()
00065 {
00066     if( m_size > MAXCACHEABLE )
00067         m_status = Uncacheable;
00068     else
00069         m_status = Cached;
00070     KURL url(m_url.string());
00071     if (m_expireDateChanged && url.protocol().startsWith("http"))
00072     {
00073         m_expireDateChanged = false;
00074         KIO::http_update_cache(url, false, m_expireDate);
00075 #ifdef CACHE_DEBUG
00076         kdDebug(6060) << " Setting expire date for image "<<m_url.string()<<" to " << m_expireDate << endl;
00077 #endif
00078     }
00079 #ifdef CACHE_DEBUG
00080     else kdDebug(6060) << " No expire date for image "<<m_url.string()<<endl;
00081 #endif
00082 }
00083 
00084 void CachedObject::setExpireDate(time_t _expireDate, bool changeHttpCache)
00085 {
00086     if ( _expireDate == m_expireDate)
00087         return;
00088 
00089     if (m_status == Uncacheable || m_status == Cached)
00090     {
00091         finish();
00092     }
00093     m_expireDate = _expireDate;
00094     if (changeHttpCache && m_expireDate)
00095        m_expireDateChanged = true;
00096 }
00097 
00098 bool CachedObject::isExpired() const
00099 {
00100     if (!m_expireDate) return false;
00101     time_t now = time(0);
00102     return (difftime(now, m_expireDate) >= 0);
00103 }
00104 
00105 void CachedObject::setRequest(Request *_request)
00106 {
00107     if ( _request && !m_request )
00108         m_status = Pending;
00109     m_request = _request;
00110 }
00111 
00112 // -------------------------------------------------------------------------------------------
00113 
00114 CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate, const QString& charset)
00115     : CachedObject(url, CSSStyleSheet, _cachePolicy, _expireDate)
00116 {
00117     // It's css we want.
00118     setAccept( QString::fromLatin1("text/css") );
00119     // load the file
00120     Cache::loader()->load(dl, this, false);
00121     m_loading = true;
00122     bool b;
00123     if(!charset.isEmpty())
00124     {
00125         m_codec = KGlobal::charsets()->codecForName(charset, b);
00126         if(m_codec->mibEnum() == 11)  {
00127             // iso8859-8 (visually ordered)
00128             m_codec = QTextCodec::codecForName("iso8859-8-i");
00129         }
00130     }
00131     else
00132         m_codec = QTextCodec::codecForMib(4); // latin-1
00133 }
00134 
00135 CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString &url, const QString &stylesheet_data)
00136     : CachedObject(url, CSSStyleSheet, KIO::CC_Verify, 0)
00137 {
00138     m_loading = false;
00139     m_status = Persistent;
00140     m_codec = 0;
00141     m_size = stylesheet_data.length();
00142     m_sheet = DOMString(stylesheet_data);
00143 }
00144 
00145 
00146 CachedCSSStyleSheet::~CachedCSSStyleSheet()
00147 {
00148 }
00149 
00150 void CachedCSSStyleSheet::ref(CachedObjectClient *c)
00151 {
00152     // make sure we don't get it twice...
00153     m_clients.remove(c);
00154     m_clients.append(c);
00155 
00156     if(!m_loading) c->setStyleSheet( m_url, m_sheet );
00157 }
00158 
00159 void CachedCSSStyleSheet::deref(CachedObjectClient *c)
00160 {
00161     Cache::flush();
00162     m_clients.remove(c);
00163 }
00164 
00165 void CachedCSSStyleSheet::data( QBuffer &buffer, bool eof )
00166 {
00167     if(!eof) return;
00168     buffer.close();
00169     m_size = buffer.buffer().size();
00170     QString data = m_codec->toUnicode( buffer.buffer().data(), m_size );
00171     m_sheet = DOMString(data);
00172     m_loading = false;
00173 
00174     checkNotify();
00175 }
00176 
00177 void CachedCSSStyleSheet::checkNotify()
00178 {
00179     if(m_loading) return;
00180 
00181 #ifdef CACHE_DEBUG
00182     kdDebug( 6060 ) << "CachedCSSStyleSheet:: finishedLoading " << m_url.string() << endl;
00183 #endif
00184 
00185     // it() first increments, then returnes the current item.
00186     // this avoids skipping an item when setStyleSheet deletes the "current" one.
00187     for (QPtrListIterator<CachedObjectClient> it( m_clients ); it.current();)
00188         it()->setStyleSheet( m_url, m_sheet );
00189 }
00190 
00191 
00192 void CachedCSSStyleSheet::error( int /*err*/, const char */*text*/ )
00193 {
00194     m_loading = false;
00195     checkNotify();
00196 }
00197 
00198 // -------------------------------------------------------------------------------------------
00199 
00200 CachedScript::CachedScript(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate, const QString& charset)
00201     : CachedObject(url, Script, _cachePolicy, _expireDate)
00202 {
00203     // It's javascript we want.
00204     // But some websites think their scripts are <some wrong mimetype here>
00205     // and refuse to serve them if we only accept application/x-javascript.
00206     setAccept( QString::fromLatin1("*/*") );
00207     // load the file
00208     Cache::loader()->load(dl, this, false);
00209     m_loading = true;
00210     bool b;
00211     if(!charset.isEmpty()) {
00212         m_codec = KGlobal::charsets()->codecForName(charset, b);
00213         if(m_codec->mibEnum() == 11)  {
00214             // iso8859-8 (visually ordered)
00215             m_codec = QTextCodec::codecForName("iso8859-8-i");
00216         }
00217     }
00218     else
00219         m_codec = QTextCodec::codecForMib(4); // latin-1
00220 }
00221 
00222 CachedScript::CachedScript(const DOMString &url, const QString &script_data)
00223     : CachedObject(url, Script, KIO::CC_Verify, 0)
00224 {
00225     m_loading = false;
00226     m_status = Persistent;
00227     m_codec = 0;
00228     m_size = script_data.length();
00229     m_script = DOMString(script_data);
00230 }
00231 
00232 CachedScript::~CachedScript()
00233 {
00234 }
00235 
00236 void CachedScript::ref(CachedObjectClient *c)
00237 {
00238     // make sure we don't get it twice...
00239     m_clients.remove(c);
00240     m_clients.append(c);
00241 
00242     if(!m_loading) c->notifyFinished(this);
00243 }
00244 
00245 void CachedScript::deref(CachedObjectClient *c)
00246 {
00247     Cache::flush();
00248     m_clients.remove(c);
00249 }
00250 
00251 void CachedScript::data( QBuffer &buffer, bool eof )
00252 {
00253     if(!eof) return;
00254     buffer.close();
00255     m_size = buffer.buffer().size();
00256     QString data = m_codec->toUnicode( buffer.buffer().data(), m_size );
00257     m_script = DOMString(data);
00258     m_loading = false;
00259     checkNotify();
00260 }
00261 
00262 void CachedScript::checkNotify()
00263 {
00264     if(m_loading) return;
00265 
00266     for (QPtrListIterator<CachedObjectClient> it( m_clients); it.current();)
00267         it()->notifyFinished(this);
00268 }
00269 
00270 
00271 void CachedScript::error( int /*err*/, const char */*text*/ )
00272 {
00273     m_loading = false;
00274     checkNotify();
00275 }
00276 
00277 // ------------------------------------------------------------------------------------------
00278 
00279 namespace khtml
00280 {
00281 
00282     class ImageSource : public QDataSource
00283     {
00284     public:
00285         ImageSource(QByteArray buf);
00286 
00291         int readyToSend();
00292 
00296         void sendTo(QDataSink*, int count);
00297 
00301         void setEOF( bool state );
00302 
00306         bool rewindable() const;
00307 
00311         void enableRewind(bool on);
00312 
00313         /*
00314           Calls reset() on the QIODevice.
00315         */
00316         void rewind();
00317 
00318         /*
00319           Indicates that the buffered data is no longer
00320           needed.
00321         */
00322         void cleanBuffer();
00323 
00324         QByteArray buffer;
00325         unsigned int pos;
00326     private:
00327         bool eof     : 1;
00328         bool rew     : 1;
00329         bool rewable : 1;
00330     };
00331 }
00332 
00333 
00334 ImageSource::ImageSource(QByteArray buf)
00335 {
00336   buffer = buf;
00337   rew = false;
00338   pos = 0;
00339   eof = false;
00340   rewable = true;
00341 }
00342 
00343 int ImageSource::readyToSend()
00344 {
00345     if(eof && pos == buffer.size())
00346         return -1;
00347 
00348     return  buffer.size() - pos;
00349 }
00350 
00351 void ImageSource::sendTo(QDataSink* sink, int n)
00352 {
00353     sink->receive((const uchar*)&buffer.at(pos), n);
00354 
00355     pos += n;
00356 
00357     // buffer is no longer needed
00358     if(eof && pos == buffer.size() && !rewable)
00359     {
00360         buffer.resize(0);
00361         pos = 0;
00362     }
00363 }
00364 
00365 void ImageSource::setEOF( bool state )
00366 {
00367     eof = state;
00368 }
00369 
00370 // ImageSource's is rewindable.
00371 bool ImageSource::rewindable() const
00372 {
00373     return rewable;
00374 }
00375 
00376 // Enables rewinding.  No special action is taken.
00377 void ImageSource::enableRewind(bool on)
00378 {
00379     rew = on;
00380 }
00381 
00382 // Calls reset() on the QIODevice.
00383 void ImageSource::rewind()
00384 {
00385     pos = 0;
00386     if (!rew) {
00387         QDataSource::rewind();
00388     } else
00389         ready();
00390 }
00391 
00392 void ImageSource::cleanBuffer()
00393 {
00394     // if we need to be able to rewind, buffer is needed
00395     if(rew)
00396         return;
00397 
00398     rewable = false;
00399 
00400     // buffer is no longer needed
00401     if(eof && pos == buffer.size())
00402     {
00403         buffer.resize(0);
00404         pos = 0;
00405     }
00406 }
00407 
00408 static QString buildAcceptHeader()
00409 {
00410     QString result = KImageIO::mimeTypes( KImageIO::Reading ).join(", ");
00411     if (result.right(2) == ", ")
00412         result = result.left(result.length()-2);
00413     return result;
00414 }
00415 
00416 static bool crossDomain(const QString &a, const QString &b)
00417 {
00418     if (a == b) return false;
00419 
00420     QStringList l1 = QStringList::split('.', a);
00421     QStringList l2 = QStringList::split('.', b);
00422 
00423     while(l1.count() > l2.count())
00424         l1.pop_front();
00425 
00426     while(l2.count() > l1.count())
00427         l2.pop_front();
00428 
00429     while(l2.count() >= 2)
00430     {
00431         if (l1 == l2)
00432            return false;
00433 
00434         l1.pop_front();
00435         l2.pop_front();
00436     }
00437     return true;
00438 }
00439 
00440 // -------------------------------------------------------------------------------------
00441 
00442 CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, time_t _expireDate)
00443     : QObject(), CachedObject(url, Image, _cachePolicy, _expireDate)
00444 {
00445     static const QString &acceptHeader = KGlobal::staticQString( buildAcceptHeader() );
00446 
00447     m = 0;
00448     p = 0;
00449     pixPart = 0;
00450     bg = 0;
00451     bgColor = qRgba( 0, 0, 0, 0xFF );
00452     typeChecked = false;
00453     isFullyTransparent = false;
00454     errorOccured = false;
00455     monochrome = false;
00456     formatType = 0;
00457     m_status = Unknown;
00458     m_size = 0;
00459     imgSource = 0;
00460     setAccept( acceptHeader );
00461     m_showAnimations = dl->showAnimations();
00462 }
00463 
00464 CachedImage::~CachedImage()
00465 {
00466     clear();
00467 }
00468 
00469 void CachedImage::ref( CachedObjectClient *c )
00470 {
00471 #ifdef CACHE_DEBUG
00472     kdDebug( 6060 ) << this << " CachedImage::ref(" << c << ") pixmap.isNull()=" << pixmap().isNull() << " valid_rect.isNull()=" << valid_rect().isNull()<< " status=" << m_status << endl;
00473 #endif
00474 
00475     // make sure we don't get it twice...
00476     m_clients.remove(c);
00477     m_clients.append(c);
00478 
00479     if( m ) {
00480         m->unpause();
00481         if( m->finished() || m_clients.count() == 1 )
00482             m->restart();
00483     }
00484 
00485     // for mouseovers, dynamic changes
00486     if ( m_status >= Persistent && !valid_rect().isNull() )
00487         c->setPixmap( pixmap(), valid_rect(), this);
00488 }
00489 
00490 void CachedImage::deref( CachedObjectClient *c )
00491 {
00492 #ifdef CACHE_DEBUG
00493     kdDebug( 6060 ) << this << " CachedImage::deref(" << c << ") " << endl;
00494 #endif
00495     Cache::flush();
00496     m_clients.remove( c );
00497     if(m && m_clients.isEmpty() && m->running())
00498         m->pause();
00499 }
00500 
00501 #define BGMINWIDTH      32
00502 #define BGMINHEIGHT     32
00503 
00504 class KHTMLPixmap : public QPixmap
00505 {
00506 public:
00507 #ifdef Q_WS_X11
00508     bool hasAlphaImage() const { return data->alphapm; }
00509 #else
00510     bool hasAlphaImage() const { return false; }
00511 #endif
00512 };
00513 
00514 const QPixmap &CachedImage::tiled_pixmap(const QColor& newc)
00515 {
00516     static QRgb bgTransparant = qRgba( 0, 0, 0, 0xFF );
00517     if ( (bgColor != bgTransparant) && (bgColor != newc.rgb()) ) {
00518         delete bg; bg = 0;
00519     }
00520 
00521     if (bg)
00522         return *bg;
00523 
00524     const QPixmap &r = pixmap();
00525 
00526     if (r.isNull()) return r;
00527 
00528     // no error indication for background images
00529     if(errorOccured) return *Cache::nullPixmap;
00530 
00531     bool isvalid = newc.isValid();
00532     QSize s(pixmap_size());
00533     int w = r.width();
00534     int h = r.height();
00535     if ( w*h < 8192 )
00536     {
00537         if ( r.width() < BGMINWIDTH )
00538             w = ((BGMINWIDTH  / s.width())+1) * s.width();
00539         if ( r.height() < BGMINHEIGHT )
00540             h = ((BGMINHEIGHT / s.height())+1) * s.height();
00541     }
00542     if ( !static_cast<const KHTMLPixmap*>( &r )->hasAlphaImage() &&
00543         ( (w != r.width()) || (h != r.height()) || (isvalid && r.mask())) )
00544     {
00545         QPixmap pix = r;
00546         if ( w != r.width() || (isvalid && pix.mask()))
00547         {
00548             bg = new QPixmap(w, r.height());
00549             QPainter p(bg);
00550             if(isvalid) p.fillRect(0, 0, w, r.height(), newc);
00551             p.drawTiledPixmap(0, 0, w, r.height(), pix);
00552             if(!isvalid && pix.mask())
00553             {
00554                 // unfortunately our anti-transparency trick doesn't work here
00555                 // we need to create a mask.
00556                 QBitmap newmask(w, r.height());
00557                 QPainter pm(&newmask);
00558                 pm.drawTiledPixmap(0, 0, w, r.height(), *pix.mask());
00559                 bg->setMask(newmask);
00560                 bgColor = bgTransparant;
00561             }
00562             else
00563                 bgColor= newc.rgb();
00564             pix = *bg;
00565         }
00566         if ( h != r.height() )
00567         {
00568             delete bg;
00569             bg = new QPixmap(w, h);
00570             QPainter p(bg);
00571             if(isvalid) p.fillRect(0, 0, w, h, newc);
00572             p.drawTiledPixmap(0, 0, w, h, pix);
00573             if(!isvalid && pix.mask())
00574             {
00575                 // unfortunately our anti-transparency trick doesn't work here
00576                 // we need to create a mask.
00577                 QBitmap newmask(w, h);
00578                 QPainter pm(&newmask);
00579                 pm.drawTiledPixmap(0, 0, w, h, *pix.mask());
00580                 bg->setMask(newmask);
00581                 bgColor = bgTransparant;
00582             }
00583             else
00584                 bgColor= newc.rgb();
00585         }
00586         return *bg;
00587     }
00588 
00589     return r;
00590 }
00591 
00592 const QPixmap &CachedImage::pixmap( ) const
00593 {
00594     if(errorOccured)
00595         return *Cache::brokenPixmap;
00596 
00597     if(m)
00598     {
00599         if(m->framePixmap().size() != m->getValidRect().size() && m->getValidRect().size().isValid())
00600         {
00601             // pixmap is not yet completely loaded, so we
00602             // return a clipped version. asserting here
00603             // that the valid rect is always from 0/0 to fullwidth/ someheight
00604             if(!pixPart) pixPart = new QPixmap(m->getValidRect().size());
00605 
00606             (*pixPart) = m->framePixmap();
00607             pixPart->resize(m->getValidRect().size());
00608             return *pixPart;
00609         }
00610         else
00611             return m->framePixmap();
00612     }
00613     else if(p)
00614         return *p;
00615 
00616     return *Cache::nullPixmap;
00617 }
00618 
00619 
00620 QSize CachedImage::pixmap_size() const
00621 {
00622     return (m ? m->framePixmap().size() : ( p ? p->size() : QSize()));
00623 }
00624 
00625 
00626 QRect CachedImage::valid_rect() const
00627 {
00628     return m ? m->getValidRect() : ( p ? p->rect() : QRect());
00629 }
00630 
00631 
00632 void CachedImage::do_notify(const QPixmap& p, const QRect& r)
00633 {
00634     CachedObjectClient *c;
00635 
00636     for ( c = m_clients.first(); c != 0; c = m_clients.next() )
00637         c->setPixmap( p, r, this);
00638 }
00639 
00640 
00641 void CachedImage::movieUpdated( const QRect& r )
00642 {
00643 #ifdef CACHE_DEBUG
00644     qDebug("movie updated %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
00645            m->framePixmap().size().width(), m->framePixmap().size().height());
00646 #endif
00647 
00648     do_notify(m->framePixmap(), r);
00649 }
00650 
00651 void CachedImage::movieStatus(int status)
00652 {
00653 #ifdef CACHE_DEBUG
00654     qDebug("movieStatus(%d)", status);
00655 #endif
00656 
00657     // ### the html image objects are supposed to send the load event after every frame (according to
00658     // netscape). We have a problem though where an image is present, and js code creates a new Image object,
00659     // which uses the same CachedImage, the one in the document is not supposed to be notified
00660 
00661     // just another Qt 2.2.0 bug. we cannot call
00662     // QMovie::frameImage if we're after QMovie::EndOfMovie
00663     if(status == QMovie::EndOfFrame)
00664     {
00665         const QImage& im = m->frameImage();
00666         monochrome = ( ( im.depth() <= 8 ) && ( im.numColors() - int( im.hasAlphaBuffer() ) <= 2 ) );
00667         for (int i = 0; monochrome && i < im.numColors(); ++i)
00668             if (im.colorTable()[i] != qRgb(0xff, 0xff, 0xff) &&
00669                 im.colorTable()[i] != qRgb(0x00, 0x00, 0x00))
00670                 monochrome = false;
00671         if( (im.width() < 5 || im.height() < 5) && im.hasAlphaBuffer()) // only evaluate for small images
00672         {
00673             QImage am = im.createAlphaMask();
00674             if(am.depth() == 1)
00675             {
00676                 bool solid = false;
00677                 for(int y = 0; y < am.height(); y++)
00678                     for(int x = 0; x < am.width(); x++)
00679                         if(am.pixelIndex(x, y)) {
00680                             solid = true;
00681                             break;
00682                         }
00683                 isFullyTransparent = (!solid);
00684             }
00685         }
00686 
00687         // we have to delete our tiled bg variant here
00688         // because the frame has changed (in order to keep it in sync)
00689         delete bg;
00690         bg = 0;
00691     }
00692 
00693     if((status == QMovie::EndOfMovie && (!m || m->frameNumber() <= 1)) ||
00694        ((status == QMovie::EndOfLoop) && (m_showAnimations == KHTMLSettings::KAnimationLoopOnce)) ||
00695        ((status == QMovie::EndOfFrame) && (m_showAnimations == KHTMLSettings::KAnimationDisabled))
00696       )
00697     {
00698         if(imgSource)
00699         {
00700             setShowAnimations( KHTMLSettings::KAnimationDisabled );
00701 
00702             // monochrome alphamasked images are usually about 10000 times
00703             // faster to draw, so this is worth the hack
00704             if (p && monochrome && p->depth() > 1 )
00705             {
00706                 QPixmap* pix = new QPixmap;
00707                 pix->convertFromImage( p->convertToImage().convertDepth( 1 ), MonoOnly|AvoidDither );
00708                 if ( p->mask() )
00709                     pix->setMask( *p->mask() );
00710                 delete p;
00711                 p = pix;
00712                 monochrome = false;
00713             }
00714         }
00715         for (QPtrListIterator<CachedObjectClient> it( m_clients ); it.current();)
00716             it()->notifyFinished(this);
00717     }
00718 
00719 #if 0
00720     if((status == QMovie::EndOfFrame) || (status == QMovie::EndOfMovie))
00721     {
00722 #ifdef CACHE_DEBUG
00723         QRect r(valid_rect());
00724         qDebug("movie Status frame update %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
00725                pixmap().size().width(), pixmap().size().height());
00726 #endif
00727             do_notify(pixmap(), valid_rect());
00728     }
00729 #endif
00730 }
00731 
00732 void CachedImage::movieResize(const QSize& /*s*/)
00733 {
00734 //    do_notify(m->framePixmap(), QRect());
00735 }
00736 
00737 void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
00738 {
00739     m_showAnimations = showAnimations;
00740     if ( (m_showAnimations == KHTMLSettings::KAnimationDisabled) && imgSource ) {
00741         imgSource->cleanBuffer();
00742         delete p;
00743         p = new QPixmap(m->framePixmap());
00744 
00745         m->disconnectUpdate( this, SLOT( movieUpdated( const QRect &) ));
00746         m->disconnectStatus( this, SLOT( movieStatus( int ) ));
00747         m->disconnectResize( this, SLOT( movieResize( const QSize& ) ) );
00748         QTimer::singleShot(0, this, SLOT( deleteMovie()));
00749         imgSource = 0;
00750     }
00751 }
00752 
00753 void CachedImage::deleteMovie()
00754 {
00755     delete m; m = 0;
00756 }
00757 
00758 void CachedImage::clear()
00759 {
00760     delete m;   m = 0;
00761     delete p;   p = 0;
00762     delete bg;  bg = 0;
00763     bgColor = qRgba( 0, 0, 0, 0xff );
00764     delete pixPart; pixPart = 0;
00765 
00766     formatType = 0;
00767 
00768     typeChecked = false;
00769     m_size = 0;
00770 
00771     // No need to delete imageSource - QMovie does it for us
00772     imgSource = 0;
00773 }
00774 
00775 void CachedImage::data ( QBuffer &_buffer, bool eof )
00776 {
00777 #ifdef CACHE_DEBUG
00778     kdDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << endl;
00779 #endif
00780     if ( !typeChecked )
00781     {
00782         formatType = QImageDecoder::formatName( (const uchar*)_buffer.buffer().data(), _buffer.size());
00783         typeChecked = true;
00784 
00785         if ( formatType )  // movie format exists
00786         {
00787             imgSource = new ImageSource( _buffer.buffer());
00788             m = new QMovie( imgSource, 8192 );
00789             m->connectUpdate( this, SLOT( movieUpdated( const QRect &) ));
00790             m->connectStatus( this, SLOT( movieStatus(int)));
00791             m->connectResize( this, SLOT( movieResize( const QSize& ) ) );
00792         }
00793     }
00794 
00795     if ( imgSource )
00796     {
00797         imgSource->setEOF(eof);
00798         imgSource->maybeReady();
00799     }
00800 
00801     if(eof)
00802     {
00803         // QMovie currently doesn't support all kinds of image formats
00804         // so we need to use a QPixmap here when we finished loading the complete
00805         // picture and display it then all at once.
00806         if(typeChecked && !formatType)
00807         {
00808 #ifdef CACHE_DEBUG
00809             kdDebug(6060) << "CachedImage::data(): reloading as pixmap:" << endl;
00810 #endif
00811             p = new QPixmap( _buffer.buffer() );
00812             // set size of image.
00813 #ifdef CACHE_DEBUG
00814             kdDebug(6060) << "CachedImage::data(): image is null: " << p->isNull() << endl;
00815 #endif
00816                 if(p->isNull())
00817                 {
00818                     errorOccured = true;
00819                     do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
00820                 }
00821                 else
00822                     do_notify(*p, p->rect());
00823         }
00824 
00825         QSize s = pixmap_size();
00826         m_size = s.width() * s.height() * 2;
00827     }
00828 }
00829 
00830 void CachedImage::finish()
00831 {
00832     Status oldStatus = m_status;
00833     CachedObject::finish();
00834     if ( oldStatus != m_status ) {
00835         const QPixmap &pm = pixmap();
00836         do_notify( pm, pm.rect() );
00837     }
00838 }
00839 
00840 
00841 void CachedImage::error( int /*err*/, const char */*text*/ )
00842 {
00843 #ifdef CACHE_DEBUG
00844     kdDebug(6060) << "CahcedImage::error" << endl;
00845 #endif
00846 
00847     clear();
00848     typeChecked = true;
00849     errorOccured = true;
00850     do_notify(pixmap(), QRect(0, 0, 16, 16));
00851 }
00852 
00853 // ------------------------------------------------------------------------------------------
00854 
00855 Request::Request(DocLoader* dl, CachedObject *_object, bool _incremental)
00856 {
00857     object = _object;
00858     object->setRequest(this);
00859     incremental = _incremental;
00860     m_docLoader = dl;
00861 }
00862 
00863 Request::~Request()
00864 {
00865     object->setRequest(0);
00866 }
00867 
00868 // ------------------------------------------------------------------------------------------
00869 
00870 DocLoader::DocLoader(KHTMLPart* part, DocumentImpl* doc)
00871 {
00872     m_cachePolicy = KIO::CC_Verify;
00873     m_expireDate = 0;
00874     m_creationDate = time(0);
00875     m_bautoloadImages = true;
00876     m_showAnimations = KHTMLSettings::KAnimationEnabled;
00877     m_part = part;
00878     m_doc = doc;
00879 
00880     Cache::docloader->append( this );
00881 }
00882 
00883 DocLoader::~DocLoader()
00884 {
00885     Cache::loader()->cancelRequests( this );
00886     Cache::docloader->remove( this );
00887 }
00888 
00889 void DocLoader::setCacheCreationDate(time_t _creationDate)
00890 {
00891     if (_creationDate)
00892        m_creationDate = _creationDate;
00893     else
00894        m_creationDate = time(0); // Now
00895 }
00896 
00897 void DocLoader::setExpireDate(time_t _expireDate, bool relative)
00898 {
00899     if (relative)
00900        m_expireDate = _expireDate + m_creationDate; // Relative date
00901     else
00902        m_expireDate = _expireDate; // Absolute date
00903 #ifdef CACHE_DEBUG
00904     kdDebug(6061) << "docLoader: " << m_expireDate - time(0) << " seconds left until reload required.\n";
00905 #endif
00906 }
00907 
00908 bool DocLoader::needReload(const KURL &fullURL)
00909 {
00910     bool reload = false;
00911     if (m_cachePolicy == KIO::CC_Verify)
00912     {
00913        if (!m_reloadedURLs.contains(fullURL.url()))
00914        {
00915           CachedObject *existing = Cache::cache->find(fullURL.url());
00916           if (existing && existing->isExpired())
00917           {
00918              Cache::removeCacheEntry(existing);
00919              m_reloadedURLs.append(fullURL.url());
00920              reload = true;
00921           }
00922        }
00923     }
00924     else if ((m_cachePolicy == KIO::CC_Reload) || (m_cachePolicy == KIO::CC_Refresh))
00925     {
00926        if (!m_reloadedURLs.contains(fullURL.url()))
00927        {
00928           CachedObject *existing = Cache::cache->find(fullURL.url());
00929           if (existing)
00930           {
00931              Cache::removeCacheEntry(existing);
00932           }
00933           m_reloadedURLs.append(fullURL.url());
00934           reload = true;
00935        }
00936     }
00937     return reload;
00938 }
00939 
00940 CachedImage *DocLoader::requestImage( const DOM::DOMString &url)
00941 {
00942     KURL fullURL = m_doc->completeURL( url.string() );
00943     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
00944     if ( kapp && m_doc && !kapp->authorizeURLAction("redirect", m_doc->URL(), fullURL.url())) return 0;
00945 
00946     bool reload = needReload(fullURL);
00947 
00948     return Cache::requestImage(this, url, reload, m_expireDate);
00949 }
00950 
00951 CachedCSSStyleSheet *DocLoader::requestStyleSheet( const DOM::DOMString &url, const QString& charset)
00952 {
00953     KURL fullURL = m_doc->completeURL( url.string() );
00954     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
00955     if ( kapp && m_doc && !kapp->authorizeURLAction("redirect", m_doc->URL(), fullURL.url())) return 0;
00956 
00957     bool reload = needReload(fullURL);
00958 
00959     return Cache::requestStyleSheet(this, url, reload, m_expireDate, charset);
00960 }
00961 
00962 CachedScript *DocLoader::requestScript( const DOM::DOMString &url, const QString& charset)
00963 {
00964     KURL fullURL = m_doc->completeURL( url.string() );
00965     if ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file") return 0;
00966     if ( kapp && m_doc && !kapp->authorizeURLAction("redirect", m_doc->URL(), fullURL.url())) return 0;
00967 
00968     bool reload = needReload(fullURL);
00969 
00970     return Cache::requestScript(this, url, reload, m_expireDate, charset);
00971 }
00972 
00973 void DocLoader::setAutoloadImages( bool enable )
00974 {
00975     if ( enable == m_bautoloadImages )
00976         return;
00977 
00978     m_bautoloadImages = enable;
00979 
00980     if ( !m_bautoloadImages ) return;
00981 
00982     for ( const CachedObject* co=m_docObjects.first(); co; co=m_docObjects.next() )
00983         if ( co->type() == CachedObject::Image )
00984         {
00985             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
00986 
00987             CachedObject::Status status = img->status();
00988             if ( status != CachedObject::Unknown )
00989                 continue;
00990 
00991             Cache::loader()->load(this, img, true);
00992         }
00993 }
00994 
00995 void DocLoader::setCachePolicy( KIO::CacheControl cachePolicy )
00996 {
00997     m_cachePolicy = cachePolicy;
00998 }
00999 
01000 void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
01001 {
01002     if ( showAnimations == m_showAnimations ) return;
01003     m_showAnimations = showAnimations;
01004 
01005     const CachedObject* co;
01006     for ( co=m_docObjects.first(); co; co=m_docObjects.next() )
01007         if ( co->type() == CachedObject::Image )
01008         {
01009             CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( co ) );
01010 
01011             img->setShowAnimations( showAnimations );
01012         }
01013 }
01014 
01015 void DocLoader::removeCachedObject( CachedObject* o ) const
01016 {
01017     m_docObjects.removeRef( o );
01018 }
01019 
01020 // ------------------------------------------------------------------------------------------
01021 
01022 Loader::Loader() : QObject()
01023 {
01024     m_requestsPending.setAutoDelete( true );
01025     m_requestsLoading.setAutoDelete( true );
01026 }
01027 
01028 Loader::~Loader()
01029 {
01030 }
01031 
01032 void Loader::load(DocLoader* dl, CachedObject *object, bool incremental)
01033 {
01034     Request *req = new Request(dl, object, incremental);
01035     m_requestsPending.append(req);
01036 
01037     emit requestStarted( req->m_docLoader, req->object );
01038 
01039     QTimer::singleShot( 0, this, SLOT( servePendingRequests() ) );
01040 }
01041 
01042 void Loader::servePendingRequests()
01043 {
01044   if ( m_requestsPending.count() == 0 )
01045       return;
01046 
01047   // get the first pending request
01048   Request *req = m_requestsPending.take(0);
01049 
01050 #ifdef CACHE_DEBUG
01051   kdDebug( 6060 ) << "starting Loader url=" << req->object->url().string() << endl;
01052 #endif
01053 
01054   KURL u(req->object->url().string());
01055   KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/);
01056 
01057   job->addMetaData("cache", KIO::getCacheControlString(req->object->cachePolicy()));
01058   if (!req->object->accept().isEmpty())
01059       job->addMetaData("accept", req->object->accept());
01060   if ( req->m_docLoader )  {
01061       KURL r = req->m_docLoader->doc()->URL();
01062       r.setRef(QString::null);
01063       r.setUser(QString::null);
01064       r.setPass(QString::null);
01065       if ( r.protocol().startsWith( "http" ) && r.path().isEmpty() )
01066           r.setPath( "/" );
01067       job->addMetaData("referrer", r.url());
01068       QString domain = r.host();
01069       if (req->m_docLoader->doc()->isHTMLDocument())
01070          domain = static_cast<HTMLDocumentImpl*>(req->m_docLoader->doc())->domain().string();
01071       if (crossDomain(u.host(), domain))
01072          job->addMetaData("cross-domain", "true");
01073 
01074       KHTMLPart *part = req->m_docLoader->part();
01075       if (part && part->widget() && part->widget()->topLevelWidget())
01076         job->setWindow (part->widget()->topLevelWidget());
01077   }
01078 
01079   connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinished( KIO::Job * ) ) );
01080   connect( job, SIGNAL( data( KIO::Job*, const QByteArray &)),
01081            SLOT( slotData( KIO::Job*, const QByteArray &)));
01082 
01083   if ( req->object->schedule() )
01084       KIO::Scheduler::scheduleJob( job );
01085 
01086   m_requestsLoading.insert(job, req);
01087 }
01088 
01089 void Loader::slotFinished( KIO::Job* job )
01090 {
01091   Request *r = m_requestsLoading.take( job );
01092   KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
01093 
01094   if ( !r )
01095     return;
01096 
01097   if (j->error() || j->isErrorPage())
01098   {
01099 #ifdef CACHE_DEBUG
01100       kdDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage() << endl;
01101 #endif
01102       r->object->error( job->error(), job->errorText().ascii() );
01103       emit requestFailed( r->m_docLoader, r->object );
01104   }
01105   else
01106   {
01107       r->object->data(r->m_buffer, true);
01108       emit requestDone( r->m_docLoader, r->object );
01109       time_t expireDate = j->queryMetaData("expire-date").toLong();
01110 #ifdef CACHE_DEBUG
01111       kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << " expires " << ctime(&expireDate) << endl;
01112 #endif
01113       r->object->setExpireDate(expireDate, false);
01114   }
01115 
01116   r->object->finish();
01117 
01118 #ifdef CACHE_DEBUG
01119   kdDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string() << endl;
01120 #endif
01121 
01122   delete r;
01123   QTimer::singleShot( 0, this, SLOT( servePendingRequests() ) );
01124 }
01125 
01126 void Loader::slotData( KIO::Job*job, const QByteArray &data )
01127 {
01128     Request *r = m_requestsLoading[job];
01129     if(!r) {
01130         kdDebug( 6060 ) << "got data for unknown request!" << endl;
01131         return;
01132     }
01133 
01134     if ( !r->m_buffer.isOpen() )
01135         r->m_buffer.open( IO_WriteOnly );
01136 
01137     r->m_buffer.writeBlock( data.data(), data.size() );
01138 
01139     if(r->incremental)
01140         r->object->data( r->m_buffer, false );
01141 }
01142 
01143 int Loader::numRequests( DocLoader* dl ) const
01144 {
01145     int res = 0;
01146 
01147     QPtrListIterator<Request> pIt( m_requestsPending );
01148     for (; pIt.current(); ++pIt )
01149         if ( pIt.current()->m_docLoader == dl )
01150             res++;
01151 
01152     QPtrDictIterator<Request> lIt( m_requestsLoading );
01153     for (; lIt.current(); ++lIt )
01154         if ( lIt.current()->m_docLoader == dl )
01155             res++;
01156 
01157     return res;
01158 }
01159 
01160 void Loader::cancelRequests( DocLoader* dl )
01161 {
01162     //kdDebug( 6060 ) << "void Loader::cancelRequests()" << endl;
01163     //kdDebug( 6060 ) << "got " << m_requestsPending.count() << " pending requests" << endl;
01164     QPtrListIterator<Request> pIt( m_requestsPending );
01165     while ( pIt.current() )
01166     {
01167         if ( pIt.current()->m_docLoader == dl )
01168         {
01169 #ifdef CACHE_DEBUG
01170             kdDebug( 6060 ) << "cancelling pending request for " << pIt.current()->object->url().string() << endl;
01171 #endif
01172             //emit requestFailed( dl, pIt.current()->object );
01173             Cache::removeCacheEntry( pIt.current()->object );
01174             m_requestsPending.remove( pIt );
01175         }
01176         else
01177             ++pIt;
01178     }
01179 
01180     //kdDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests" << endl;
01181 
01182     QPtrDictIterator<Request> lIt( m_requestsLoading );
01183     while ( lIt.current() )
01184     {
01185         if ( lIt.current()->m_docLoader == dl )
01186         {
01187             //kdDebug( 6060 ) << "cancelling loading request for " << lIt.current()->object->url().string() << endl;
01188             KIO::Job *job = static_cast<KIO::Job *>( lIt.currentKey() );
01189             Cache::removeCacheEntry( lIt.current()->object );
01190             m_requestsLoading.remove( lIt.currentKey() );
01191             job->kill();
01192             //emit requestFailed( dl, pIt.current()->object );
01193         }
01194         else
01195             ++lIt;
01196     }
01197 }
01198 
01199 KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
01200 {
01201     QPtrDictIterator<Request> it( m_requestsLoading );
01202 
01203     for (; it.current(); ++it )
01204     {
01205         CachedObject *obj = it.current()->object;
01206 
01207         if ( obj && obj->url() == url )
01208             return static_cast<KIO::Job *>( it.currentKey() );
01209     }
01210 
01211     return 0;
01212 }
01213 
01214 // ----------------------------------------------------------------------------
01215 
01216 
01217 QDict<CachedObject> *Cache::cache = 0;
01218 QPtrList<DocLoader>* Cache::docloader = 0;
01219 QPtrList<CachedObject> *Cache::freeList = 0;
01220 Cache::LRUList *Cache::lru = 0;
01221 Loader *Cache::m_loader = 0;
01222 
01223 int Cache::maxSize = DEFCACHESIZE;
01224 int Cache::flushCount = 0;
01225 int Cache::cacheSize = 0;
01226 
01227 QPixmap *Cache::nullPixmap = 0;
01228 QPixmap *Cache::brokenPixmap = 0;
01229 
01230 void Cache::init()
01231 {
01232     if ( !cache )
01233         cache = new QDict<CachedObject>(401, true);
01234 
01235     if ( !lru )
01236         lru = new LRUList;
01237 
01238     if ( !docloader )
01239         docloader = new QPtrList<DocLoader>;
01240 
01241     if ( !nullPixmap )
01242         nullPixmap = new QPixmap;
01243 
01244     if ( !brokenPixmap )
01245         brokenPixmap = new QPixmap(KHTMLFactory::instance()->iconLoader()->loadIcon("file_broken", KIcon::Desktop, 16, KIcon::DisabledState));
01246 
01247     if ( !m_loader )
01248         m_loader = new Loader();
01249 
01250     if ( !freeList ) {
01251         freeList = new QPtrList<CachedObject>;
01252         freeList->setAutoDelete(true);
01253     }
01254 }
01255 
01256 void Cache::flushFreeList()
01257 {
01258     for ( CachedObject* p = freeList->first(); p; p = freeList->next() )
01259         if (p->canDelete())
01260             freeList->remove();
01261 }
01262 
01263 void Cache::clear()
01264 {
01265     if ( !cache ) return;
01266 #ifdef CACHE_DEBUG
01267     kdDebug( 6060 ) << "Cache: CLEAR!" << endl;
01268     statistics();
01269 #endif
01270     cache->setAutoDelete( true );
01271 
01272 #if 0
01273     for (QDictIterator<CachedObject> it(*cache); it.current(); ++it)
01274         assert(it.current()->canDelete());
01275     for (QPtrListIterator<CachedObject> it(*freeList); it.current(); ++it)
01276         assert(it.current()->canDelete());
01277 #endif
01278 
01279     delete cache; cache = 0;
01280     delete lru;   lru = 0;
01281     delete nullPixmap; nullPixmap = 0;
01282     delete brokenPixmap; brokenPixmap = 0;
01283     delete m_loader;   m_loader = 0;
01284     delete docloader; docloader = 0;
01285     delete freeList; freeList = 0;
01286 }
01287 
01288 CachedImage *Cache::requestImage( DocLoader* dl, const DOMString & url, bool reload, time_t _expireDate )
01289 {
01290     // this brings the _url to a standard form...
01291     KURL kurl;
01292     KIO::CacheControl cachePolicy;
01293     if ( dl )
01294     {
01295         kurl = dl->m_doc->completeURL( url.string() );
01296         cachePolicy = dl->cachePolicy();
01297     }
01298     else
01299     {
01300         kurl = url.string();
01301         cachePolicy = KIO::CC_Verify;
01302     }
01303 
01304     if( kurl.isMalformed() )
01305     {
01306 #ifdef CACHE_DEBUG
01307       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
01308 #endif
01309       return 0;
01310     }
01311 
01312     CachedObject *o = 0;
01313     if (!reload)
01314         o = cache->find(kurl.url());
01315     if(!o)
01316     {
01317 #ifdef CACHE_DEBUG
01318         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
01319 #endif
01320         CachedImage *im = new CachedImage(dl, kurl.url(), cachePolicy, _expireDate);
01321         cache->insert( kurl.url(), im );
01322         lru->prepend( kurl.url() );
01323         o = im;
01324     }
01325 
01326     if (o->status() == CachedObject::Unknown && dl && dl->autoloadImages())
01327         Cache::loader()->load(dl, o, true);
01328 
01329     o->setExpireDate(_expireDate, true);
01330 
01331     if(!(o->type() == CachedObject::Image))
01332     {
01333 #ifdef CACHE_DEBUG
01334         kdDebug( 6060 ) << "Cache::Internal Error in requestImage url=" << kurl.url() << "!" << endl;
01335 #endif
01336         return 0;
01337     }
01338 
01339 #ifdef CACHE_DEBUG
01340     if( o->status() == CachedObject::Pending )
01341         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
01342     else
01343         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << ", status " << o->status() << endl;
01344 #endif
01345 
01346     lru->touch( kurl.url() );
01347     if ( dl ) {
01348         dl->m_docObjects.remove( o );
01349         dl->m_docObjects.append( o );
01350     }
01351     return static_cast<CachedImage *>(o);
01352 }
01353 
01354 CachedCSSStyleSheet *Cache::requestStyleSheet( DocLoader* dl, const DOMString & url, bool /*reload*/, time_t _expireDate, const QString& charset)
01355 {
01356     // this brings the _url to a standard form...
01357     KURL kurl;
01358     KIO::CacheControl cachePolicy;
01359     if ( dl )
01360     {
01361         kurl = dl->m_doc->completeURL( url.string() );
01362         cachePolicy = dl->cachePolicy();
01363     }
01364     else
01365     {
01366         kurl = url.string();
01367         cachePolicy = KIO::CC_Verify;
01368     }
01369 
01370     if( kurl.isMalformed() )
01371     {
01372       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
01373       return 0;
01374     }
01375 
01376     CachedObject *o = cache->find(kurl.url());
01377     if(!o)
01378     {
01379 #ifdef CACHE_DEBUG
01380         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
01381 #endif
01382         CachedCSSStyleSheet *sheet = new CachedCSSStyleSheet(dl, kurl.url(), cachePolicy, _expireDate, charset);
01383         cache->insert( kurl.url(), sheet );
01384         lru->prepend( kurl.url() );
01385         o = sheet;
01386     }
01387 
01388     o->setExpireDate(_expireDate, true);
01389 
01390     if(!(o->type() == CachedObject::CSSStyleSheet))
01391     {
01392 #ifdef CACHE_DEBUG
01393         kdDebug( 6060 ) << "Cache::Internal Error in requestStyleSheet url=" << kurl.url() << "!" << endl;
01394 #endif
01395         return 0;
01396     }
01397 
01398 #ifdef CACHE_DEBUG
01399     if( o->status() == CachedObject::Pending )
01400         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
01401     else
01402         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
01403 #endif
01404 
01405     lru->touch( kurl.url() );
01406     if ( dl ) {
01407         dl->m_docObjects.remove( o );
01408         dl->m_docObjects.append( o );
01409     }
01410     return static_cast<CachedCSSStyleSheet *>(o);
01411 }
01412 
01413 void Cache::preloadStyleSheet( const QString &url, const QString &stylesheet_data)
01414 {
01415     CachedObject *o = cache->find(url);
01416     if(o)
01417         removeCacheEntry(o);
01418 
01419     CachedCSSStyleSheet *stylesheet = new CachedCSSStyleSheet(url, stylesheet_data);
01420     cache->insert( url, stylesheet );
01421 }
01422 
01423 CachedScript *Cache::requestScript( DocLoader* dl, const DOM::DOMString &url, bool /*reload*/, time_t _expireDate, const QString& charset)
01424 {
01425     // this brings the _url to a standard form...
01426     KURL kurl;
01427     KIO::CacheControl cachePolicy;
01428     if ( dl )
01429     {
01430         kurl = dl->m_doc->completeURL( url.string() );
01431         cachePolicy = dl->cachePolicy();
01432     }
01433     else
01434     {
01435         kurl = url.string();
01436         cachePolicy = KIO::CC_Verify;
01437     }
01438 
01439     if( kurl.isMalformed() )
01440     {
01441       kdDebug( 6060 ) << "Cache: Malformed url: " << kurl.url() << endl;
01442       return 0;
01443     }
01444 
01445     CachedObject *o = cache->find(kurl.url());
01446     if(!o)
01447     {
01448 #ifdef CACHE_DEBUG
01449         kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
01450 #endif
01451         CachedScript *script = new CachedScript(dl, kurl.url(), cachePolicy, _expireDate, charset);
01452         cache->insert( kurl.url(), script );
01453         lru->prepend( kurl.url() );
01454         o = script;
01455     }
01456 
01457     o->setExpireDate(_expireDate, true);
01458 
01459     if(!(o->type() == CachedObject::Script))
01460     {
01461 #ifdef CACHE_DEBUG
01462         kdDebug( 6060 ) << "Cache::Internal Error in requestScript url=" << kurl.url() << "!" << endl;
01463 #endif
01464         return 0;
01465     }
01466 
01467 #ifdef CACHE_DEBUG
01468     if( o->status() == CachedObject::Pending )
01469         kdDebug( 6060 ) << "Cache: loading in progress: " << kurl.url() << endl;
01470     else
01471         kdDebug( 6060 ) << "Cache: using cached: " << kurl.url() << endl;
01472 #endif
01473 
01474     lru->touch( kurl.url() );
01475     if ( dl ) {
01476         dl->m_docObjects.remove( o );
01477         dl->m_docObjects.append( o );
01478     }
01479     return static_cast<CachedScript *>(o);
01480 }
01481 
01482 void Cache::preloadScript( const QString &url, const QString &script_data)
01483 {
01484     CachedObject *o = cache->find(url);
01485     if(o)
01486         removeCacheEntry(o);
01487 
01488     CachedScript *script = new CachedScript(url, script_data);
01489     cache->insert( url, script );
01490 }
01491 
01492 void Cache::flush(bool force)
01493 {
01494     if (force)
01495        flushCount = 0;
01496     // Don't flush for every image.
01497     if (!lru || (lru->count() < (uint) flushCount))
01498        return;
01499 
01500     init();
01501 
01502     flushFreeList();
01503 
01504 #ifdef CACHE_DEBUG
01505     statistics();
01506     kdDebug( 6060 ) << "Cache: flush()" << endl;
01507 #endif
01508 
01509     int _cacheSize = 0;
01510 
01511     for ( QStringList::Iterator it = lru->fromLast(); it != lru->end(); )
01512     {
01513         QString url = *it;
01514         --it; // Update iterator, we might delete the current entry later on.
01515         CachedObject *o = cache->find( url );
01516 
01517         if( !o->canDelete() || o->status() == CachedObject::Persistent ) {
01518                continue; // image is still used or cached permanently
01519                // in this case don't count it for the size of the cache.
01520         }
01521 
01522         if( o->status() != CachedObject::Uncacheable )
01523         {
01524            _cacheSize += o->size();
01525 
01526            if( _cacheSize < maxSize )
01527                continue;
01528         }
01529         removeCacheEntry( o );
01530     }
01531     Cache::cacheSize = _cacheSize;
01532 
01533     flushCount = lru->count()+10; // Flush again when the cache has grown.
01534 #ifdef CACHE_DEBUG
01535     //statistics();
01536 #endif
01537 }
01538 
01539 void Cache::setSize( int bytes )
01540 {
01541     maxSize = bytes;
01542     // may be we need to clear parts of the cache
01543     flushCount = 0;
01544     flush(true);
01545 }
01546 
01547 void Cache::statistics()
01548 {
01549     CachedObject *o;
01550     // this function is for debugging purposes only
01551     init();
01552 
01553     int size = 0;
01554     int msize = 0;
01555     int movie = 0;
01556     int images = 0;
01557     int scripts = 0;
01558     int stylesheets = 0;
01559     QDictIterator<CachedObject> it(*cache);
01560     for(it.toFirst(); it.current(); ++it)
01561     {
01562         o = it.current();
01563         switch(o->type()) {
01564         case CachedObject::Image:
01565         {
01566             CachedImage *im = static_cast<CachedImage *>(o);
01567             images++;
01568             if(im->m != 0)
01569             {
01570                 qDebug("found image with movie: %p", im);
01571 
01572                 movie++;
01573                 msize += im->size();
01574             }
01575             break;
01576         }
01577         case CachedObject::CSSStyleSheet:
01578             stylesheets++;
01579             break;
01580         case CachedObject::Script:
01581             scripts++;
01582             break;
01583         }
01584         size += o->size();
01585     }
01586     size /= 1024;
01587 
01588     kdDebug( 6060 ) << "------------------------- image cache statistics -------------------" << endl;
01589     kdDebug( 6060 ) << "Number of items in cache: " << cache->count() << endl;
01590     kdDebug( 6060 ) << "Number of items in lru  : " << lru->count() << endl;
01591     kdDebug( 6060 ) << "Number of cached images: " << images << endl;
01592     kdDebug( 6060 ) << "Number of cached movies: " << movie << endl;
01593     kdDebug( 6060 ) << "Number of cached scripts: " << scripts << endl;
01594     kdDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets << endl;
01595     kdDebug( 6060 ) << "pixmaps:   allocated space approx. " << size << " kB" << endl;
01596     kdDebug( 6060 ) << "movies :   allocated space approx. " << msize/1024 << " kB" << endl;
01597     kdDebug( 6060 ) << "--------------------------------------------------------------------" << endl;
01598 }
01599 
01600 void Cache::removeCacheEntry( CachedObject *object )
01601 {
01602   QString key = object->url().string();
01603 
01604   cache->remove( key );
01605   lru->remove( key );
01606 
01607   const DocLoader* dl;
01608   for ( dl=docloader->first(); dl; dl=docloader->next() )
01609       dl->removeCachedObject( object );
01610 
01611   object->setFree();
01612 }
01613 
01614 
01615 // --------------------------------------
01616 
01617 void CachedObjectClient::setPixmap(const QPixmap &, const QRect&, CachedImage *) {}
01618 void CachedObjectClient::setStyleSheet(const DOM::DOMString &/*url*/, const DOM::DOMString &/*sheet*/) {}
01619 void CachedObjectClient::notifyFinished(CachedObject * /*finishedObj*/) {}
01620 
01621 
01622 #include "loader.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:29 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001