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