summaryrefslogtreecommitdiffstats
path: root/khtml/misc/loader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/misc/loader.cpp')
-rw-r--r--khtml/misc/loader.cpp1679
1 files changed, 1679 insertions, 0 deletions
diff --git a/khtml/misc/loader.cpp b/khtml/misc/loader.cpp
new file mode 100644
index 000000000..8f7ae246f
--- /dev/null
+++ b/khtml/misc/loader.cpp
@@ -0,0 +1,1679 @@
+/*
+ This file is part of the KDE libraries
+
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001-2003 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2003 Apple Computer, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style sheets and html
+ pages from the web. It has a memory cache for these objects.
+
+ // regarding the LRU:
+ // http://www.is.kyusan-u.ac.jp/~chengk/pub/papers/compsac00_A07-07.pdf
+*/
+
+#undef CACHE_DEBUG
+//#define CACHE_DEBUG
+
+#ifdef CACHE_DEBUG
+#define CDEBUG kdDebug(6060)
+#else
+#define CDEBUG kndDebug()
+#endif
+
+#undef LOADER_DEBUG
+//#define LOADER_DEBUG
+
+#include <assert.h>
+
+#include "misc/loader.h"
+#include "misc/seed.h"
+
+// default cache size
+#define DEFCACHESIZE 2096*1024
+#define MAX_JOB_COUNT 32
+
+#include <qasyncio.h>
+#include <qasyncimageio.h>
+#include <qpainter.h>
+#include <qbitmap.h>
+#include <qmovie.h>
+#include <qwidget.h>
+
+#include <kapplication.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kglobal.h>
+#include <kimageio.h>
+#include <kcharsets.h>
+#include <kiconloader.h>
+#include <scheduler.h>
+#include <kdebug.h>
+#include "khtml_factory.h"
+#include "khtml_part.h"
+
+#ifdef IMAGE_TITLES
+#include <qfile.h>
+#include <kfilemetainfo.h>
+#include <ktempfile.h>
+#endif
+
+#include "html/html_documentimpl.h"
+#include "css/css_stylesheetimpl.h"
+#include "xml/dom_docimpl.h"
+
+#include "blocked_icon.cpp"
+
+using namespace khtml;
+using namespace DOM;
+
+#define MAX_LRU_LISTS 20
+struct LRUList {
+ CachedObject* m_head;
+ CachedObject* m_tail;
+
+ LRUList() : m_head(0), m_tail(0) {}
+};
+
+static LRUList m_LRULists[MAX_LRU_LISTS];
+static LRUList* getLRUListFor(CachedObject* o);
+
+CachedObjectClient::~CachedObjectClient()
+{
+}
+
+CachedObject::~CachedObject()
+{
+ Cache::removeFromLRUList(this);
+}
+
+void CachedObject::finish()
+{
+ m_status = Cached;
+}
+
+bool CachedObject::isExpired() const
+{
+ if (!m_expireDate) return false;
+ time_t now = time(0);
+ return (difftime(now, m_expireDate) >= 0);
+}
+
+void CachedObject::setRequest(Request *_request)
+{
+ if ( _request && !m_request )
+ m_status = Pending;
+
+ if ( allowInLRUList() )
+ Cache::removeFromLRUList( this );
+
+ m_request = _request;
+
+ if ( allowInLRUList() )
+ Cache::insertInLRUList( this );
+}
+
+void CachedObject::ref(CachedObjectClient *c)
+{
+ // unfortunately we can be ref'ed multiple times from the
+ // same object, because it uses e.g. the same foreground
+ // and the same background picture. so deal with it.
+ m_clients.insert(c,c);
+ Cache::removeFromLRUList(this);
+ m_accessCount++;
+}
+
+void CachedObject::deref(CachedObjectClient *c)
+{
+ assert( c );
+ assert( m_clients.count() );
+ assert( !canDelete() );
+ assert( m_clients.find( c ) );
+
+ Cache::flush();
+
+ m_clients.remove(c);
+
+ if (allowInLRUList())
+ Cache::insertInLRUList(this);
+}
+
+void CachedObject::setSize(int size)
+{
+ bool sizeChanged;
+
+ if ( !m_next && !m_prev && getLRUListFor(this)->m_head != this )
+ sizeChanged = false;
+ else
+ sizeChanged = ( size - m_size ) != 0;
+
+ // The object must now be moved to a different queue,
+ // since its size has been changed.
+ if ( sizeChanged && allowInLRUList())
+ Cache::removeFromLRUList(this);
+
+ m_size = size;
+
+ if ( sizeChanged && allowInLRUList())
+ Cache::insertInLRUList(this);
+}
+
+QTextCodec* CachedObject::codecForBuffer( const QString& charset, const QByteArray& buffer ) const
+{
+ // we don't use heuristicContentMatch here since it is a) far too slow and
+ // b) having too much functionality for our case.
+
+ uchar* d = ( uchar* ) buffer.data();
+ int s = buffer.size();
+
+ // BOM
+ if ( s >= 3 &&
+ d[0] == 0xef && d[1] == 0xbb && d[2] == 0xbf)
+ return QTextCodec::codecForMib( 106 ); // UTF-8
+
+ if ( s >= 2 && ((d[0] == 0xff && d[1] == 0xfe) ||
+ (d[0] == 0xfe && d[1] == 0xff)))
+ return QTextCodec::codecForMib( 1000 ); // UCS-2
+
+ // Link or @charset
+ if(!charset.isEmpty())
+ {
+ QTextCodec* c = KGlobal::charsets()->codecForName(charset);
+ if(c->mibEnum() == 11) {
+ // iso8859-8 (visually ordered)
+ c = QTextCodec::codecForName("iso8859-8-i");
+ }
+ return c;
+ }
+
+ // Default
+ return QTextCodec::codecForMib( 4 ); // latin 1
+}
+
+// -------------------------------------------------------------------------------------------
+
+CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy,
+ const char *accept)
+ : CachedObject(url, CSSStyleSheet, _cachePolicy, 0)
+{
+ // Set the type we want (probably css or xml)
+ QString ah = QString::fromLatin1( accept );
+ if ( !ah.isEmpty() )
+ ah += ",";
+ ah += "*/*;q=0.1";
+ setAccept( ah );
+ m_hadError = false;
+ m_wasBlocked = false;
+ m_err = 0;
+ // load the file
+ Cache::loader()->load(dl, this, false);
+ m_loading = true;
+}
+
+CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString &url, const QString &stylesheet_data)
+ : CachedObject(url, CSSStyleSheet, KIO::CC_Verify, stylesheet_data.length())
+{
+ m_loading = false;
+ m_status = Persistent;
+ m_sheet = DOMString(stylesheet_data);
+}
+
+
+void CachedCSSStyleSheet::ref(CachedObjectClient *c)
+{
+ CachedObject::ref(c);
+
+ if (!m_loading) {
+ if (m_hadError)
+ c->error( m_err, m_errText );
+ else
+ c->setStyleSheet( m_url, m_sheet, m_charset );
+ }
+}
+
+void CachedCSSStyleSheet::data( QBuffer &buffer, bool eof )
+{
+ if(!eof) return;
+ buffer.close();
+ setSize(buffer.buffer().size());
+
+// QString charset = checkCharset( buffer.buffer() );
+ QTextCodec* c = 0;
+ if (!m_charset.isEmpty()) {
+ c = KGlobal::charsets()->codecForName(m_charset);
+ if(c->mibEnum() == 11) c = QTextCodec::codecForName("iso8859-8-i");
+ }
+ else {
+ c = codecForBuffer( m_charsetHint, buffer.buffer() );
+ m_charset = c->name();
+ }
+ QString data = c->toUnicode( buffer.buffer().data(), m_size );
+ // workaround Qt bugs
+ m_sheet = static_cast<QChar>(data[0]) == QChar::byteOrderMark ? DOMString(data.mid( 1 ) ) : DOMString(data);
+ m_loading = false;
+
+ checkNotify();
+}
+
+void CachedCSSStyleSheet::checkNotify()
+{
+ if(m_loading || m_hadError) return;
+
+ CDEBUG << "CachedCSSStyleSheet:: finishedLoading " << m_url.string() << endl;
+
+ // it() first increments, then returnes the current item.
+ // this avoids skipping an item when setStyleSheet deletes the "current" one.
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients ); it.current();)
+ it()->setStyleSheet( m_url, m_sheet, m_charset );
+}
+
+
+void CachedCSSStyleSheet::error( int err, const char* text )
+{
+ m_hadError = true;
+ m_err = err;
+ m_errText = text;
+ m_loading = false;
+
+ // it() first increments, then returnes the current item.
+ // this avoids skipping an item when setStyleSheet deletes the "current" one.
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients ); it.current();)
+ it()->error( m_err, m_errText );
+}
+
+#if 0
+QString CachedCSSStyleSheet::checkCharset(const QByteArray& buffer ) const
+{
+ int s = buffer.size();
+ if (s <= 12) return m_charset;
+
+ // @charset has to be first or directly after BOM.
+ // CSS 2.1 says @charset should win over BOM, but since more browsers support BOM
+ // than @charset, we default to that.
+ const char* d = (const char*) buffer.data();
+ if (strncmp(d, "@charset \"",10) == 0)
+ {
+ // the string until "; is the charset name
+ char *p = strchr(d+10, '"');
+ if (p == 0) return m_charset;
+ QString charset = QString::fromAscii(d+10, p-(d+10));
+ return charset;
+ }
+ return m_charset;
+}
+#endif
+
+// -------------------------------------------------------------------------------------------
+
+CachedScript::CachedScript(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, const char*)
+ : CachedObject(url, Script, _cachePolicy, 0)
+{
+ // It's javascript we want.
+ // But some websites think their scripts are <some wrong mimetype here>
+ // and refuse to serve them if we only accept application/x-javascript.
+ setAccept( QString::fromLatin1("*/*") );
+ // load the file
+ Cache::loader()->load(dl, this, false);
+ m_loading = true;
+}
+
+CachedScript::CachedScript(const DOMString &url, const QString &script_data)
+ : CachedObject(url, Script, KIO::CC_Verify, script_data.length())
+{
+ m_loading = false;
+ m_status = Persistent;
+ m_script = DOMString(script_data);
+}
+
+void CachedScript::ref(CachedObjectClient *c)
+{
+ CachedObject::ref(c);
+
+ if(!m_loading) c->notifyFinished(this);
+}
+
+void CachedScript::data( QBuffer &buffer, bool eof )
+{
+ if(!eof) return;
+ buffer.close();
+ setSize(buffer.buffer().size());
+
+ QTextCodec* c = codecForBuffer( m_charset, buffer.buffer() );
+ QString data = c->toUnicode( buffer.buffer().data(), m_size );
+ m_script = static_cast<QChar>(data[0]) == QChar::byteOrderMark ? DOMString(data.mid( 1 ) ) : DOMString(data);
+ m_loading = false;
+ checkNotify();
+}
+
+void CachedScript::checkNotify()
+{
+ if(m_loading) return;
+
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients); it.current();)
+ it()->notifyFinished(this);
+}
+
+void CachedScript::error( int /*err*/, const char* /*text*/ )
+{
+ m_loading = false;
+ checkNotify();
+}
+
+// ------------------------------------------------------------------------------------------
+
+namespace khtml
+{
+
+class ImageSource : public QDataSource
+{
+public:
+ ImageSource(QByteArray buf)
+ : buffer( buf ), pos( 0 ), eof( false ), rew(false ), rewable( true )
+ {}
+
+ int readyToSend()
+ {
+ if(eof && pos == buffer.size())
+ return -1;
+
+ return buffer.size() - pos;
+ }
+
+ void sendTo(QDataSink* sink, int n)
+ {
+ sink->receive((const uchar*)&buffer.at(pos), n);
+
+ pos += n;
+
+ // buffer is no longer needed
+ if(eof && pos == buffer.size() && !rewable)
+ {
+ buffer.resize(0);
+ pos = 0;
+ }
+ }
+
+ /**
+ * Sets the EOF state.
+ */
+ void setEOF( bool state ) { eof = state; }
+
+ bool rewindable() const { return rewable; }
+ void enableRewind(bool on) { rew = on; }
+
+ /*
+ Calls reset() on the QIODevice.
+ */
+ void rewind()
+ {
+ pos = 0;
+ if (!rew) {
+ QDataSource::rewind();
+ } else
+ ready();
+ }
+
+ /*
+ Indicates that the buffered data is no longer
+ needed.
+ */
+ void cleanBuffer()
+ {
+ // if we need to be able to rewind, buffer is needed
+ if(rew)
+ return;
+
+ rewable = false;
+
+ // buffer is no longer needed
+ if(eof && pos == buffer.size())
+ {
+ buffer.resize(0);
+ pos = 0;
+ }
+ }
+
+ QByteArray buffer;
+ unsigned int pos;
+private:
+ bool eof : 1;
+ bool rew : 1;
+ bool rewable : 1;
+};
+
+} // end namespace
+
+static QString buildAcceptHeader()
+{
+ return "image/png, image/jpeg, video/x-mng, image/jp2, image/gif;q=0.5,*/*;q=0.1";
+}
+
+// -------------------------------------------------------------------------------------
+
+CachedImage::CachedImage(DocLoader* dl, const DOMString &url, KIO::CacheControl _cachePolicy, const char*)
+ : QObject(), CachedObject(url, Image, _cachePolicy, 0)
+{
+ static const QString &acceptHeader = KGlobal::staticQString( buildAcceptHeader() );
+
+ m = 0;
+ p = 0;
+ pixPart = 0;
+ bg = 0;
+ scaled = 0;
+ bgColor = qRgba( 0, 0, 0, 0xFF );
+ typeChecked = false;
+ isFullyTransparent = false;
+ monochrome = false;
+ formatType = 0;
+ m_status = Unknown;
+ imgSource = 0;
+ setAccept( acceptHeader );
+ m_showAnimations = dl->showAnimations();
+
+ if ( KHTMLFactory::defaultHTMLSettings()->isAdFiltered( url.string() ) ) {
+ m_wasBlocked = true;
+ CachedObject::finish();
+ }
+}
+
+CachedImage::~CachedImage()
+{
+ clear();
+}
+
+void CachedImage::ref( CachedObjectClient *c )
+{
+ CachedObject::ref(c);
+
+ if( m ) {
+ m->unpause();
+ if( m->finished() || m_clients.count() == 1 )
+ m->restart();
+ }
+
+ // for mouseovers, dynamic changes
+ if ( m_status >= Persistent && !valid_rect().isNull() ) {
+ c->setPixmap( pixmap(), valid_rect(), this);
+ c->notifyFinished( this );
+ }
+}
+
+void CachedImage::deref( CachedObjectClient *c )
+{
+ CachedObject::deref(c);
+ if(m && m_clients.isEmpty() && m->running())
+ m->pause();
+}
+
+#define BGMINWIDTH 32
+#define BGMINHEIGHT 32
+
+const QPixmap &CachedImage::tiled_pixmap(const QColor& newc, int xWidth, int xHeight)
+{
+ static QRgb bgTransparent = qRgba( 0, 0, 0, 0xFF );
+
+ QSize s(pixmap_size());
+ int w = xWidth;
+ int h = xHeight;
+ if (w == -1) xWidth = w = s.width();
+ if (h == -1) xHeight = h = s.height();
+
+ if ( ( (bgColor != bgTransparent) && (bgColor != newc.rgb()) ) ||
+ ( bgSize != QSize(xWidth, xHeight)) )
+ {
+ delete bg; bg = 0;
+ }
+
+ if (bg)
+ return *bg;
+
+ const QPixmap &r = pixmap();
+
+ if (r.isNull()) return r;
+
+ // no error indication for background images
+ if(m_hadError||m_wasBlocked) return *Cache::nullPixmap;
+
+ bool isvalid = newc.isValid();
+
+ const QPixmap* src; //source for pretiling, if any
+
+ //See whether we should scale
+ if (xWidth != s.width() || xHeight != s.height()) {
+ src = &scaled_pixmap(xWidth, xHeight);
+ } else {
+ src = &r;
+ }
+
+ bgSize = QSize(xWidth, xHeight);
+
+ //See whether we can - and should - pre-blend
+ if (isvalid && (r.hasAlphaChannel() || r.mask() )) {
+ bg = new QPixmap(xWidth, xHeight, r.depth());
+ bg->fill(newc);
+ bitBlt(bg, 0, 0, src);
+ bgColor = newc.rgb();
+ src = bg;
+ } else {
+ bgColor = bgTransparent;
+ }
+
+ //See whether to pre-tile.
+ if ( w*h < 8192 )
+ {
+ if ( r.width() < BGMINWIDTH )
+ w = ((BGMINWIDTH-1) / xWidth + 1) * xWidth;
+ if ( r.height() < BGMINHEIGHT )
+ h = ((BGMINHEIGHT-1) / xHeight + 1) * xHeight;
+ }
+ if ( w != xWidth || h != xHeight )
+ {
+// kdDebug() << "pre-tiling " << s.width() << "," << s.height() << " to " << w << "," << h << endl;
+ QPixmap* oldbg = bg;
+ bg = new QPixmap(w, h, r.depth());
+
+ //Tile horizontally on the first stripe
+ for (int x = 0; x < w; x += xWidth)
+ copyBlt(bg, x, 0, src, 0, 0, xWidth, xHeight);
+
+ //Copy first stripe down
+ for (int y = xHeight; y < h; y += xHeight)
+ copyBlt(bg, 0, y, bg, 0, 0, w, xHeight);
+
+ if ( src == oldbg )
+ delete oldbg;
+ }
+
+ if (bg)
+ return *bg;
+
+ return *src;
+}
+
+const QPixmap &CachedImage::scaled_pixmap( int xWidth, int xHeight )
+{
+ if (scaled) {
+ if (scaled->width() == xWidth && scaled->height() == xHeight)
+ return *scaled;
+ delete scaled;
+ }
+ const QPixmap &r = pixmap();
+ if (r.isNull()) return r;
+
+// kdDebug() << "scaling " << r.width() << "," << r.height() << " to " << xWidth << "," << xHeight << endl;
+
+ QImage image = r.convertToImage().smoothScale(xWidth, xHeight);
+
+ scaled = new QPixmap(xWidth, xHeight, r.depth());
+ scaled->convertFromImage(image);
+
+ return *scaled;
+}
+
+
+const QPixmap &CachedImage::pixmap( ) const
+{
+ if(m_hadError)
+ return *Cache::brokenPixmap;
+
+ if(m_wasBlocked)
+ return *Cache::blockedPixmap;
+
+ if(m)
+ {
+ if(m->framePixmap().size() != m->getValidRect().size())
+ {
+ // pixmap is not yet completely loaded, so we
+ // return a clipped version. asserting here
+ // that the valid rect is always from 0/0 to fullwidth/ someheight
+ if(!pixPart) pixPart = new QPixmap();
+
+ (*pixPart) = m->framePixmap();
+ if (m->getValidRect().size().isValid())
+ pixPart->resize(m->getValidRect().size());
+ else
+ pixPart->resize(0, 0);
+ return *pixPart;
+ }
+ else
+ return m->framePixmap();
+ }
+ else if(p)
+ return *p;
+
+ return *Cache::nullPixmap;
+}
+
+
+QSize CachedImage::pixmap_size() const
+{
+ if (m_wasBlocked) return Cache::blockedPixmap->size();
+ return (m_hadError ? Cache::brokenPixmap->size() : m ? m->framePixmap().size() : ( p ? p->size() : QSize()));
+}
+
+
+QRect CachedImage::valid_rect() const
+{
+ if (m_wasBlocked) return Cache::blockedPixmap->rect();
+ return (m_hadError ? Cache::brokenPixmap->rect() : m ? m->getValidRect() : ( p ? p->rect() : QRect()) );
+}
+
+
+void CachedImage::do_notify(const QPixmap& p, const QRect& r)
+{
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients ); it.current();)
+ it()->setPixmap( p, r, this);
+}
+
+
+void CachedImage::movieUpdated( const QRect& r )
+{
+#ifdef LOADER_DEBUG
+ qDebug("movie updated %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
+ m->framePixmap().size().width(), m->framePixmap().size().height());
+#endif
+
+ do_notify(m->framePixmap(), r);
+}
+
+void CachedImage::movieStatus(int status)
+{
+#ifdef LOADER_DEBUG
+ qDebug("movieStatus(%d)", status);
+#endif
+
+ // ### the html image objects are supposed to send the load event after every frame (according to
+ // netscape). We have a problem though where an image is present, and js code creates a new Image object,
+ // which uses the same CachedImage, the one in the document is not supposed to be notified
+
+ // just another Qt 2.2.0 bug. we cannot call
+ // QMovie::frameImage if we're after QMovie::EndOfMovie
+ if(status == QMovie::EndOfFrame)
+ {
+ const QImage& im = m->frameImage();
+ monochrome = ( ( im.depth() <= 8 ) && ( im.numColors() - int( im.hasAlphaBuffer() ) <= 2 ) );
+ for (int i = 0; monochrome && i < im.numColors(); ++i)
+ if (im.colorTable()[i] != qRgb(0xff, 0xff, 0xff) &&
+ im.colorTable()[i] != qRgb(0x00, 0x00, 0x00))
+ monochrome = false;
+ if( (im.width() < 5 || im.height() < 5) && im.hasAlphaBuffer()) // only evaluate for small images
+ {
+ QImage am = im.createAlphaMask();
+ if(am.depth() == 1)
+ {
+ bool solid = false;
+ for(int y = 0; y < am.height(); y++)
+ for(int x = 0; x < am.width(); x++)
+ if(am.pixelIndex(x, y)) {
+ solid = true;
+ break;
+ }
+ isFullyTransparent = (!solid);
+ }
+ }
+
+ // we have to delete our tiled bg variant here
+ // because the frame has changed (in order to keep it in sync)
+ delete bg;
+ bg = 0;
+ }
+
+ if((status == QMovie::EndOfMovie && (!m || m->frameNumber() <= 1)) ||
+ ((status == QMovie::EndOfLoop) && (m_showAnimations == KHTMLSettings::KAnimationLoopOnce)) ||
+ ((status == QMovie::EndOfFrame) && (m_showAnimations == KHTMLSettings::KAnimationDisabled))
+ )
+ {
+ if(imgSource)
+ {
+ setShowAnimations( KHTMLSettings::KAnimationDisabled );
+
+ // monochrome alphamasked images are usually about 10000 times
+ // faster to draw, so this is worth the hack
+ if (p && monochrome && p->depth() > 1)
+ {
+ QPixmap* pix = new QPixmap;
+ pix->convertFromImage( p->convertToImage().convertDepth( 1 ), MonoOnly|AvoidDither );
+ if ( p->mask() )
+ pix->setMask( *p->mask() );
+ delete p;
+ p = pix;
+ monochrome = false;
+ }
+ }
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients ); it.current();)
+ it()->notifyFinished( this );
+ m_status = Cached; //all done
+ }
+
+#if 0
+ if((status == QMovie::EndOfFrame) || (status == QMovie::EndOfMovie))
+ {
+#ifdef LOADER_DEBUG
+ QRect r(valid_rect());
+ qDebug("movie Status frame update %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
+ pixmap().size().width(), pixmap().size().height());
+#endif
+ do_notify(pixmap(), valid_rect());
+ }
+#endif
+}
+
+void CachedImage::movieResize(const QSize& /*s*/)
+{
+ do_notify(m->framePixmap(), QRect());
+}
+
+void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
+{
+ m_showAnimations = showAnimations;
+ if ( (m_showAnimations == KHTMLSettings::KAnimationDisabled) && imgSource ) {
+ imgSource->cleanBuffer();
+ delete p;
+ p = new QPixmap(m->framePixmap());
+ m->disconnectUpdate( this, SLOT( movieUpdated( const QRect &) ));
+ m->disconnectStatus( this, SLOT( movieStatus( int ) ));
+ m->disconnectResize( this, SLOT( movieResize( const QSize& ) ) );
+ QTimer::singleShot(0, this, SLOT( deleteMovie()));
+ imgSource = 0;
+ }
+}
+
+void CachedImage::pauseAnimations()
+{
+ if ( m ) m->pause();
+}
+
+void CachedImage::resumeAnimations()
+{
+ if ( m ) m->unpause();
+}
+
+
+void CachedImage::deleteMovie()
+{
+ delete m; m = 0;
+}
+
+void CachedImage::clear()
+{
+ delete m; m = 0;
+ delete p; p = 0;
+ delete bg; bg = 0;
+ delete scaled; scaled = 0;
+ bgColor = qRgba( 0, 0, 0, 0xff );
+ bgSize = QSize(-1,-1);
+ delete pixPart; pixPart = 0;
+
+ formatType = 0;
+ typeChecked = false;
+ setSize(0);
+
+ // No need to delete imageSource - QMovie does it for us
+ imgSource = 0;
+}
+
+void CachedImage::data ( QBuffer &_buffer, bool eof )
+{
+#ifdef LOADER_DEBUG
+ kdDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer.buffer().size() <<", eof=" << eof << endl;
+#endif
+ if ( !typeChecked )
+ {
+ // don't attempt incremental loading if we have all the data already
+ if (!eof)
+ {
+ formatType = QImageDecoder::formatName( (const uchar*)_buffer.buffer().data(), _buffer.size());
+ if ( formatType && strcmp( formatType, "PNG" ) == 0 )
+ formatType = 0; // Some png files contain multiple images, we want to show only the first one
+ }
+
+ typeChecked = true;
+
+ if ( formatType ) // movie format exists
+ {
+ imgSource = new ImageSource( _buffer.buffer());
+ m = new QMovie( imgSource, 8192 );
+ m->connectUpdate( this, SLOT( movieUpdated( const QRect &) ));
+ m->connectStatus( this, SLOT( movieStatus(int)));
+ m->connectResize( this, SLOT( movieResize( const QSize& ) ) );
+ }
+ }
+
+ if ( imgSource )
+ {
+ imgSource->setEOF(eof);
+ imgSource->maybeReady();
+ }
+
+ if(eof)
+ {
+ // QMovie currently doesn't support all kinds of image formats
+ // so we need to use a QPixmap here when we finished loading the complete
+ // picture and display it then all at once.
+ if(typeChecked && !formatType)
+ {
+#ifdef CACHE_DEBUG
+ kdDebug(6060) << "CachedImage::data(): reloading as pixmap:" << endl;
+#endif
+ p = new QPixmap;
+ {
+ QBuffer buffer(_buffer.buffer());
+ buffer.open(IO_ReadOnly);
+ QImageIO io( &buffer, 0 );
+ io.setGamma(2.2); // hardcoded "reasonable value"
+ bool result = io.read();
+ if (result) p->convertFromImage(io.image(), 0);
+ }
+
+ // set size of image.
+#ifdef CACHE_DEBUG
+ kdDebug(6060) << "CachedImage::data(): image is null: " << p->isNull() << endl;
+#endif
+ if(p->isNull())
+ {
+ m_hadError = true;
+ do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
+ }
+ else
+ do_notify(*p, p->rect());
+
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients ); it.current();)
+ it()->notifyFinished( this );
+ m_status = Cached; //all done
+ }
+ }
+}
+
+void CachedImage::finish()
+{
+ Status oldStatus = m_status;
+ CachedObject::finish();
+ if ( oldStatus != m_status ) {
+ const QPixmap &pm = pixmap();
+ do_notify( pm, pm.rect() );
+ }
+ QSize s = pixmap_size();
+ setSize( s.width() * s.height() * 2);
+}
+
+
+void CachedImage::error( int /*err*/, const char* /*text*/ )
+{
+ clear();
+ typeChecked = true;
+ m_hadError = true;
+ m_loading = false;
+ do_notify(pixmap(), QRect(0, 0, 16, 16));
+ for (QPtrDictIterator<CachedObjectClient> it( m_clients ); it.current();)
+ it()->notifyFinished(this);
+}
+
+// ------------------------------------------------------------------------------------------
+
+Request::Request(DocLoader* dl, CachedObject *_object, bool _incremental)
+{
+ object = _object;
+ object->setRequest(this);
+ incremental = _incremental;
+ m_docLoader = dl;
+}
+
+Request::~Request()
+{
+ object->setRequest(0);
+}
+
+// ------------------------------------------------------------------------------------------
+
+DocLoader::DocLoader(KHTMLPart* part, DocumentImpl* doc)
+{
+ m_cachePolicy = KIO::CC_Verify;
+ m_expireDate = 0;
+ m_creationDate = time(0);
+ m_bautoloadImages = true;
+ m_showAnimations = KHTMLSettings::KAnimationEnabled;
+ m_part = part;
+ m_doc = doc;
+
+ Cache::docloader->append( this );
+}
+
+DocLoader::~DocLoader()
+{
+ Cache::loader()->cancelRequests( this );
+ Cache::docloader->remove( this );
+}
+
+void DocLoader::setCacheCreationDate(time_t _creationDate)
+{
+ if (_creationDate)
+ m_creationDate = _creationDate;
+ else
+ m_creationDate = time(0); // Now
+}
+
+void DocLoader::setExpireDate(time_t _expireDate, bool relative)
+{
+ if (relative)
+ m_expireDate = _expireDate + m_creationDate; // Relative date
+ else
+ m_expireDate = _expireDate; // Absolute date
+#ifdef CACHE_DEBUG
+ kdDebug(6061) << "docLoader: " << m_expireDate - time(0) << " seconds left until reload required.\n";
+#endif
+}
+
+void DocLoader::insertCachedObject( CachedObject* o ) const
+{
+ if ( m_docObjects.find(o) )
+ return;
+ m_docObjects.insert( o, o );
+ if ( m_docObjects.count() > 3 * m_docObjects.size() )
+ m_docObjects.resize(khtml::nextSeed( m_docObjects.size() ) );
+}
+
+bool DocLoader::needReload(CachedObject *existing, const QString& fullURL)
+{
+ bool reload = false;
+ if (m_cachePolicy == KIO::CC_Verify)
+ {
+ if (!m_reloadedURLs.contains(fullURL))
+ {
+ if (existing && existing->isExpired())
+ {
+ Cache::removeCacheEntry(existing);
+ m_reloadedURLs.append(fullURL);
+ reload = true;
+ }
+ }
+ }
+ else if ((m_cachePolicy == KIO::CC_Reload) || (m_cachePolicy == KIO::CC_Refresh))
+ {
+ if (!m_reloadedURLs.contains(fullURL))
+ {
+ if (existing)
+ {
+ Cache::removeCacheEntry(existing);
+ }
+ m_reloadedURLs.append(fullURL);
+ reload = true;
+ }
+ }
+ return reload;
+}
+
+#define DOCLOADER_SECCHECK(doRedirectCheck) \
+ KURL fullURL (m_doc->completeURL( url.string() )); \
+ if ( !fullURL.isValid() || \
+ ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file" && fullURL.protocol() != "data") || \
+ doRedirectCheck && ( kapp && m_doc && !kapp->authorizeURLAction("redirect", m_doc->URL(), fullURL))) \
+ return 0L;
+
+CachedImage *DocLoader::requestImage( const DOM::DOMString &url)
+{
+ DOCLOADER_SECCHECK(true);
+
+ CachedImage* i = Cache::requestObject<CachedImage, CachedObject::Image>( this, fullURL, 0);
+
+ if (i && i->status() == CachedObject::Unknown && autoloadImages())
+ Cache::loader()->load(this, i, true);
+
+ return i;
+}
+
+CachedCSSStyleSheet *DocLoader::requestStyleSheet( const DOM::DOMString &url, const QString& charset,
+ const char *accept, bool userSheet )
+{
+ DOCLOADER_SECCHECK(!userSheet);
+
+ CachedCSSStyleSheet* s = Cache::requestObject<CachedCSSStyleSheet, CachedObject::CSSStyleSheet>( this, fullURL, accept );
+ if ( s && !charset.isEmpty() ) {
+ s->setCharsetHint( charset );
+ }
+ return s;
+}
+
+CachedScript *DocLoader::requestScript( const DOM::DOMString &url, const QString& charset)
+{
+ DOCLOADER_SECCHECK(true);
+ if ( ! KHTMLFactory::defaultHTMLSettings()->isJavaScriptEnabled(fullURL.host()) ||
+ KHTMLFactory::defaultHTMLSettings()->isAdFiltered(fullURL.url()))
+ return 0L;
+
+ CachedScript* s = Cache::requestObject<CachedScript, CachedObject::Script>( this, fullURL, 0 );
+ if ( s && !charset.isEmpty() )
+ s->setCharset( charset );
+ return s;
+}
+
+#undef DOCLOADER_SECCHECK
+
+void DocLoader::setAutoloadImages( bool enable )
+{
+ if ( enable == m_bautoloadImages )
+ return;
+
+ m_bautoloadImages = enable;
+
+ if ( !m_bautoloadImages ) return;
+
+ for ( QPtrDictIterator<CachedObject> it( m_docObjects ); it.current(); ++it )
+ if ( it.current()->type() == CachedObject::Image )
+ {
+ CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( it.current()) );
+
+ CachedObject::Status status = img->status();
+ if ( status != CachedObject::Unknown )
+ continue;
+
+ Cache::loader()->load(this, img, true);
+ }
+}
+
+void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations )
+{
+ if ( showAnimations == m_showAnimations ) return;
+ m_showAnimations = showAnimations;
+
+ for ( QPtrDictIterator<CachedObject> it( m_docObjects ); it.current(); ++it )
+ if ( it.current()->type() == CachedObject::Image )
+ {
+ CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( it.current() ) );
+
+ img->setShowAnimations( m_showAnimations );
+ }
+}
+
+void DocLoader::pauseAnimations()
+{
+ for ( QPtrDictIterator<CachedObject> it( m_docObjects ); it.current(); ++it )
+ if ( it.current()->type() == CachedObject::Image )
+ {
+ CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( it.current() ) );
+
+ img->pauseAnimations();
+ }
+}
+
+void DocLoader::resumeAnimations()
+{
+ for ( QPtrDictIterator<CachedObject> it( m_docObjects ); it.current(); ++it )
+ if ( it.current()->type() == CachedObject::Image )
+ {
+ CachedImage *img = const_cast<CachedImage*>( static_cast<const CachedImage *>( it.current() ) );
+
+ img->resumeAnimations();
+ }
+}
+
+// ------------------------------------------------------------------------------------------
+
+Loader::Loader() : QObject()
+{
+ m_requestsPending.setAutoDelete( true );
+ m_requestsLoading.setAutoDelete( true );
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT( servePendingRequests() ) );
+}
+
+void Loader::load(DocLoader* dl, CachedObject *object, bool incremental)
+{
+ Request *req = new Request(dl, object, incremental);
+ m_requestsPending.append(req);
+
+ emit requestStarted( req->m_docLoader, req->object );
+
+ m_timer.start(0, true);
+}
+
+void Loader::servePendingRequests()
+{
+ while ( (m_requestsPending.count() != 0) && (m_requestsLoading.count() < MAX_JOB_COUNT) )
+ {
+ // get the first pending request
+ Request *req = m_requestsPending.take(0);
+
+#ifdef LOADER_DEBUG
+ kdDebug( 6060 ) << "starting Loader url=" << req->object->url().string() << endl;
+#endif
+
+ KURL u(req->object->url().string());
+ KIO::TransferJob* job = KIO::get( u, false, false /*no GUI*/);
+
+ job->addMetaData("cache", KIO::getCacheControlString(req->object->cachePolicy()));
+ if (!req->object->accept().isEmpty())
+ job->addMetaData("accept", req->object->accept());
+ if ( req->m_docLoader )
+ {
+ job->addMetaData( "referrer", req->m_docLoader->doc()->URL().url() );
+
+ KHTMLPart *part = req->m_docLoader->part();
+ if (part )
+ {
+ job->addMetaData( "cross-domain", part->toplevelURL().url() );
+ if (part->widget())
+ job->setWindow (part->widget()->topLevelWidget());
+ }
+ }
+
+ connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotFinished( KIO::Job * ) ) );
+ connect( job, SIGNAL( data( KIO::Job*, const QByteArray &)),
+ SLOT( slotData( KIO::Job*, const QByteArray &)));
+
+ if ( req->object->schedule() )
+ KIO::Scheduler::scheduleJob( job );
+
+ m_requestsLoading.insert(job, req);
+ }
+}
+
+void Loader::slotFinished( KIO::Job* job )
+{
+ Request *r = m_requestsLoading.take( job );
+ KIO::TransferJob* j = static_cast<KIO::TransferJob*>(job);
+
+ if ( !r )
+ return;
+
+ if (j->error() || j->isErrorPage())
+ {
+#ifdef LOADER_DEBUG
+ kdDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j->error() << " job->isErrorPage()=" << j->isErrorPage() << endl;
+#endif
+ r->object->error( job->error(), job->errorText().ascii() );
+ emit requestFailed( r->m_docLoader, r->object );
+ }
+ else
+ {
+ QString cs = j->queryMetaData("charset");
+ if (!cs.isEmpty()) r->object->setCharset(cs);
+ r->object->data(r->m_buffer, true);
+ emit requestDone( r->m_docLoader, r->object );
+ time_t expireDate = j->queryMetaData("expire-date").toLong();
+#ifdef LOADER_DEBUG
+ kdDebug(6060) << "Loader::slotFinished, url = " << j->url().url() << endl;
+#endif
+ r->object->setExpireDate( expireDate );
+
+ if ( r->object->type() == CachedObject::Image ) {
+ QString fn = j->queryMetaData("content-disposition");
+ static_cast<CachedImage*>( r->object )->setSuggestedFilename(fn);
+#ifdef IMAGE_TITLES
+ static_cast<CachedImage*>( r->object )->setSuggestedTitle(fn);
+ KTempFile tf;
+ tf.setAutoDelete(true);
+ tf.file()->writeBlock((const char*)r->m_buffer.buffer().data(), r->m_buffer.size());
+ tf.sync();
+ KFileMetaInfo kfmi(tf.name());
+ if (!kfmi.isEmpty()) {
+ KFileMetaInfoItem i = kfmi.item("Name");
+ if (i.isValid()) {
+ static_cast<CachedImage*>(r->object)->setSuggestedTitle(i.string());
+ } else {
+ i = kfmi.item("Title");
+ if (i.isValid()) {
+ static_cast<CachedImage*>(r->object)->setSuggestedTitle(i.string());
+ }
+ }
+ }
+#endif
+ }
+ }
+
+ r->object->finish();
+
+#ifdef LOADER_DEBUG
+ kdDebug( 6060 ) << "Loader:: JOB FINISHED " << r->object << ": " << r->object->url().string() << endl;
+#endif
+
+ delete r;
+
+ if ( (m_requestsPending.count() != 0) && (m_requestsLoading.count() < MAX_JOB_COUNT / 2) )
+ m_timer.start(0, true);
+}
+
+void Loader::slotData( KIO::Job*job, const QByteArray &data )
+{
+ Request *r = m_requestsLoading[job];
+ if(!r) {
+ kdDebug( 6060 ) << "got data for unknown request!" << endl;
+ return;
+ }
+
+ if ( !r->m_buffer.isOpen() )
+ r->m_buffer.open( IO_WriteOnly );
+
+ r->m_buffer.writeBlock( data.data(), data.size() );
+
+ if(r->incremental)
+ r->object->data( r->m_buffer, false );
+}
+
+int Loader::numRequests( DocLoader* dl ) const
+{
+ int res = 0;
+
+ QPtrListIterator<Request> pIt( m_requestsPending );
+ for (; pIt.current(); ++pIt )
+ if ( pIt.current()->m_docLoader == dl )
+ res++;
+
+ QPtrDictIterator<Request> lIt( m_requestsLoading );
+ for (; lIt.current(); ++lIt )
+ if ( lIt.current()->m_docLoader == dl )
+ res++;
+
+ return res;
+}
+
+void Loader::cancelRequests( DocLoader* dl )
+{
+ QPtrListIterator<Request> pIt( m_requestsPending );
+ while ( pIt.current() ) {
+ if ( pIt.current()->m_docLoader == dl )
+ {
+ CDEBUG << "canceling pending request for " << pIt.current()->object->url().string() << endl;
+ Cache::removeCacheEntry( pIt.current()->object );
+ m_requestsPending.remove( pIt );
+ }
+ else
+ ++pIt;
+ }
+
+ //kdDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests" << endl;
+
+ QPtrDictIterator<Request> lIt( m_requestsLoading );
+ while ( lIt.current() )
+ {
+ if ( lIt.current()->m_docLoader == dl )
+ {
+ //kdDebug( 6060 ) << "canceling loading request for " << lIt.current()->object->url().string() << endl;
+ KIO::Job *job = static_cast<KIO::Job *>( lIt.currentKey() );
+ Cache::removeCacheEntry( lIt.current()->object );
+ m_requestsLoading.remove( lIt.currentKey() );
+ job->kill();
+ //emit requestFailed( dl, pIt.current()->object );
+ }
+ else
+ ++lIt;
+ }
+}
+
+KIO::Job *Loader::jobForRequest( const DOM::DOMString &url ) const
+{
+ QPtrDictIterator<Request> it( m_requestsLoading );
+
+ for (; it.current(); ++it )
+ {
+ CachedObject *obj = it.current()->object;
+
+ if ( obj && obj->url() == url )
+ return static_cast<KIO::Job *>( it.currentKey() );
+ }
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+
+
+QDict<CachedObject> *Cache::cache = 0;
+QPtrList<DocLoader>* Cache::docloader = 0;
+QPtrList<CachedObject> *Cache::freeList = 0;
+Loader *Cache::m_loader = 0;
+
+int Cache::maxSize = DEFCACHESIZE;
+int Cache::totalSizeOfLRU;
+
+QPixmap *Cache::nullPixmap = 0;
+QPixmap *Cache::brokenPixmap = 0;
+QPixmap *Cache::blockedPixmap = 0;
+
+void Cache::init()
+{
+ if ( !cache )
+ cache = new QDict<CachedObject>(401, true);
+
+ if ( !docloader )
+ docloader = new QPtrList<DocLoader>;
+
+ if ( !nullPixmap )
+ nullPixmap = new QPixmap;
+
+ if ( !brokenPixmap )
+ brokenPixmap = new QPixmap(KHTMLFactory::instance()->iconLoader()->loadIcon("file_broken", KIcon::Desktop, 16, KIcon::DisabledState));
+
+ if ( !blockedPixmap ) {
+ blockedPixmap = new QPixmap();
+ blockedPixmap->loadFromData(blocked_icon_data, blocked_icon_len);
+ }
+
+ if ( !m_loader )
+ m_loader = new Loader();
+
+ if ( !freeList ) {
+ freeList = new QPtrList<CachedObject>;
+ freeList->setAutoDelete(true);
+ }
+}
+
+void Cache::clear()
+{
+ if ( !cache ) return;
+#ifdef CACHE_DEBUG
+ kdDebug( 6060 ) << "Cache: CLEAR!" << endl;
+ statistics();
+#endif
+ cache->setAutoDelete( true );
+
+#ifndef NDEBUG
+ bool crash = false;
+ for (QDictIterator<CachedObject> it(*cache); it.current(); ++it) {
+ if (!it.current()->canDelete()) {
+ kdDebug( 6060 ) << " Object in cache still linked to" << endl;
+ kdDebug( 6060 ) << " -> URL: " << it.current()->url() << endl;
+ kdDebug( 6060 ) << " -> #clients: " << it.current()->count() << endl;
+ crash = true;
+// assert(it.current()->canDelete());
+ }
+ }
+ for (freeList->first(); freeList->current(); freeList->next()) {
+ if (!freeList->current()->canDelete()) {
+ kdDebug( 6060 ) << " Object in freelist still linked to" << endl;
+ kdDebug( 6060 ) << " -> URL: " << freeList->current()->url() << endl;
+ kdDebug( 6060 ) << " -> #clients: " << freeList->current()->count() << endl;
+ crash = true;
+ /*
+ QPtrDictIterator<CachedObjectClient> it(freeList->current()->m_clients);
+ for(;it.current(); ++it) {
+ if (dynamic_cast<RenderObject*>(it.current())) {
+ kdDebug( 6060 ) << " --> RenderObject" << endl;
+ } else
+ kdDebug( 6060 ) << " --> Something else" << endl;
+ }*/
+ }
+// assert(freeList->current()->canDelete());
+ }
+ assert(!crash);
+#endif
+
+ delete cache; cache = 0;
+ delete nullPixmap; nullPixmap = 0;
+ delete brokenPixmap; brokenPixmap = 0;
+ delete blockedPixmap; blockedPixmap = 0;
+ delete m_loader; m_loader = 0;
+ delete docloader; docloader = 0;
+ delete freeList; freeList = 0;
+}
+
+template<typename CachedObjectType, enum CachedObject::Type CachedType>
+CachedObjectType* Cache::requestObject( DocLoader* dl, const KURL& kurl, const char* accept )
+{
+ KIO::CacheControl cachePolicy = dl ? dl->cachePolicy() : KIO::CC_Verify;
+
+ QString url = kurl.url();
+ CachedObject* o = cache->find(url);
+
+ if ( o && o->type() != CachedType ) {
+ removeCacheEntry( o );
+ o = 0;
+ }
+
+ if ( o && dl->needReload( o, url ) ) {
+ o = 0;
+ assert( cache->find( url ) == 0 );
+ }
+
+ if(!o)
+ {
+#ifdef CACHE_DEBUG
+ kdDebug( 6060 ) << "Cache: new: " << kurl.url() << endl;
+#endif
+ CachedObjectType* cot = new CachedObjectType(dl, url, cachePolicy, accept);
+ cache->insert( url, cot );
+ if ( cot->allowInLRUList() )
+ insertInLRUList( cot );
+ o = cot;
+ }
+#ifdef CACHE_DEBUG
+ else {
+ kdDebug( 6060 ) << "Cache: using pending/cached: " << kurl.url() << endl;
+ }
+#endif
+
+
+ dl->insertCachedObject( o );
+
+ return static_cast<CachedObjectType *>(o);
+}
+
+void Cache::preloadStyleSheet( const QString &url, const QString &stylesheet_data)
+{
+ CachedObject *o = cache->find(url);
+ if(o)
+ removeCacheEntry(o);
+
+ CachedCSSStyleSheet *stylesheet = new CachedCSSStyleSheet(url, stylesheet_data);
+ cache->insert( url, stylesheet );
+}
+
+void Cache::preloadScript( const QString &url, const QString &script_data)
+{
+ CachedObject *o = cache->find(url);
+ if(o)
+ removeCacheEntry(o);
+
+ CachedScript *script = new CachedScript(url, script_data);
+ cache->insert( url, script );
+}
+
+void Cache::flush(bool force)
+{
+ init();
+
+ if ( force || totalSizeOfLRU > maxSize + maxSize/4) {
+ for ( int i = MAX_LRU_LISTS-1; i >= 0 && totalSizeOfLRU > maxSize; --i )
+ while ( totalSizeOfLRU > maxSize && m_LRULists[i].m_tail )
+ removeCacheEntry( m_LRULists[i].m_tail );
+
+#ifdef CACHE_DEBUG
+ statistics();
+#endif
+ }
+
+ for ( freeList->first(); freeList->current(); ) {
+ CachedObject* p = freeList->current();
+ if ( p->canDelete() )
+ freeList->remove();
+ else
+ freeList->next();
+ }
+
+}
+
+void Cache::setSize( int bytes )
+{
+ maxSize = bytes;
+ flush(true /* force */);
+}
+
+void Cache::statistics()
+{
+ CachedObject *o;
+ // this function is for debugging purposes only
+ init();
+
+ int size = 0;
+ int msize = 0;
+ int movie = 0;
+ int images = 0;
+ int scripts = 0;
+ int stylesheets = 0;
+ QDictIterator<CachedObject> it(*cache);
+ for(it.toFirst(); it.current(); ++it)
+ {
+ o = it.current();
+ switch(o->type()) {
+ case CachedObject::Image:
+ {
+ CachedImage *im = static_cast<CachedImage *>(o);
+ images++;
+ if(im->m != 0)
+ {
+ movie++;
+ msize += im->size();
+ }
+ break;
+ }
+ case CachedObject::CSSStyleSheet:
+ stylesheets++;
+ break;
+ case CachedObject::Script:
+ scripts++;
+ break;
+ }
+ size += o->size();
+ }
+ size /= 1024;
+
+ kdDebug( 6060 ) << "------------------------- image cache statistics -------------------" << endl;
+ kdDebug( 6060 ) << "Number of items in cache: " << cache->count() << endl;
+ kdDebug( 6060 ) << "Number of cached images: " << images << endl;
+ kdDebug( 6060 ) << "Number of cached movies: " << movie << endl;
+ kdDebug( 6060 ) << "Number of cached scripts: " << scripts << endl;
+ kdDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets << endl;
+ kdDebug( 6060 ) << "pixmaps: allocated space approx. " << size << " kB" << endl;
+ kdDebug( 6060 ) << "movies : allocated space approx. " << msize/1024 << " kB" << endl;
+ kdDebug( 6060 ) << "--------------------------------------------------------------------" << endl;
+}
+
+void Cache::removeCacheEntry( CachedObject *object )
+{
+ QString key = object->url().string();
+
+ cache->remove( key );
+ removeFromLRUList( object );
+
+ for (const DocLoader* dl=docloader->first(); dl; dl=docloader->next() )
+ dl->removeCachedObject( object );
+
+ if ( !object->free() ) {
+ Cache::freeList->append( object );
+ object->m_free = true;
+ }
+}
+
+static inline int FastLog2(unsigned int j)
+{
+ unsigned int log2;
+ log2 = 0;
+ if (j & (j-1))
+ log2 += 1;
+ if (j >> 16)
+ log2 += 16, j >>= 16;
+ if (j >> 8)
+ log2 += 8, j >>= 8;
+ if (j >> 4)
+ log2 += 4, j >>= 4;
+ if (j >> 2)
+ log2 += 2, j >>= 2;
+ if (j >> 1)
+ log2 += 1;
+
+ return log2;
+}
+
+static LRUList* getLRUListFor(CachedObject* o)
+{
+ int accessCount = o->accessCount();
+ int queueIndex;
+ if (accessCount == 0) {
+ queueIndex = 0;
+ } else {
+ int sizeLog = FastLog2(o->size());
+ queueIndex = sizeLog/o->accessCount() - 1;
+ if (queueIndex < 0)
+ queueIndex = 0;
+ if (queueIndex >= MAX_LRU_LISTS)
+ queueIndex = MAX_LRU_LISTS-1;
+ }
+ return &m_LRULists[queueIndex];
+}
+
+void Cache::removeFromLRUList(CachedObject *object)
+{
+ CachedObject *next = object->m_next;
+ CachedObject *prev = object->m_prev;
+
+ LRUList* list = getLRUListFor(object);
+ CachedObject *&head = getLRUListFor(object)->m_head;
+
+ if (next == 0 && prev == 0 && head != object) {
+ return;
+ }
+
+ object->m_next = 0;
+ object->m_prev = 0;
+
+ if (next)
+ next->m_prev = prev;
+ else if (list->m_tail == object)
+ list->m_tail = prev;
+
+ if (prev)
+ prev->m_next = next;
+ else if (head == object)
+ head = next;
+
+ totalSizeOfLRU -= object->size();
+}
+
+void Cache::insertInLRUList(CachedObject *object)
+{
+ removeFromLRUList(object);
+
+ assert( object );
+ assert( !object->free() );
+ assert( object->canDelete() );
+ assert( object->allowInLRUList() );
+
+ LRUList* list = getLRUListFor(object);
+
+ CachedObject *&head = list->m_head;
+
+ object->m_next = head;
+ if (head)
+ head->m_prev = object;
+ head = object;
+
+ if (object->m_next == 0)
+ list->m_tail = object;
+
+ totalSizeOfLRU += object->size();
+}
+
+// --------------------------------------
+
+void CachedObjectClient::setPixmap(const QPixmap &, const QRect&, CachedImage *) {}
+void CachedObjectClient::setStyleSheet(const DOM::DOMString &/*url*/, const DOM::DOMString &/*sheet*/, const DOM::DOMString &/*charset*/) {}
+void CachedObjectClient::notifyFinished(CachedObject * /*finishedObj*/) {}
+void CachedObjectClient::error(int /*err*/, const QString &/*text*/) {}
+
+#undef CDEBUG
+
+#include "loader.moc"