loader.cpp Source File (khtml Library)

| # khtml Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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"

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