2 This file is part of the KDE libraries
4 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
5 Copyright (C) 2001-2003 Dirk Mueller (mueller@kde.org)
6 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
7 Copyright (C) 2003 Apple Computer, Inc.
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
24 This class provides all functionality needed for loading images, style sheets and html
25 pages from the web. It has a memory cache for these objects.
28 // http://www.is.kyusan-u.ac.jp/~chengk/pub/papers/compsac00_A07-07.pdf
35 #define CDEBUG kDebug(6060)
37 #define CDEBUG kDebugDevNull()
41 //#define LOADER_DEBUG
43 //#define PRELOAD_DEBUG
47 #include <imload/image.h>
48 #include <imload/imagepainter.h>
53 #define DEFCACHESIZE 2096*1024
54 #define MAX_JOB_COUNT 32
56 //#include <qasyncio.h>
57 //#include <qasyncimageio.h>
58 #include <QtGui/QApplication>
59 #include <QtGui/QDesktopWidget>
60 #include <QtGui/QPainter>
61 #include <QtGui/QBitmap>
62 #include <QtGui/QMovie>
63 #include <QtGui/QWidget>
64 #include <QtCore/QDebug>
65 #include <kauthorized.h>
67 #include <kio/jobuidelegate.h>
68 #include <kio/jobclasses.h>
70 #include <kcharsets.h>
71 #include <kiconloader.h>
72 #include <scheduler.h>
75 #include <khtml_global.h>
76 #include <khtml_part.h>
80 #include <kfilemetainfo.h>
81 #include <ktemporaryfile.h>
84 #include "html/html_documentimpl.h"
85 #include "css/css_stylesheetimpl.h"
86 #include "xml/dom_docimpl.h"
88 #include "blocked_icon.cpp"
90 #include <QPaintEngine>
92 using namespace khtml
;
94 using namespace khtmlImLoad
;
96 #define MAX_LRU_LISTS 20
101 LRUList() : m_head(0), m_tail(0) {}
104 static LRUList m_LRULists
[MAX_LRU_LISTS
];
105 static LRUList
* getLRUListFor(CachedObject
* o
);
107 CachedObjectClient::~CachedObjectClient()
111 CachedObject::~CachedObject()
113 Cache::removeFromLRUList(this);
116 void CachedObject::finish()
121 bool CachedObject::isExpired() const
123 if (!m_expireDate
) return false;
124 time_t now
= time(0);
125 return (difftime(now
, m_expireDate
) >= 0);
128 void CachedObject::setRequest(Request
*_request
)
130 if ( _request
&& !m_request
)
133 if ( allowInLRUList() )
134 Cache::removeFromLRUList( this );
136 m_request
= _request
;
138 if ( allowInLRUList() )
139 Cache::insertInLRUList( this );
142 void CachedObject::ref(CachedObjectClient
*c
)
144 if (m_preloadResult
== PreloadNotReferenced
) {
146 m_preloadResult
= PreloadReferencedWhileComplete
;
147 else if (m_prospectiveRequest
)
148 m_preloadResult
= PreloadReferencedWhileLoading
;
150 m_preloadResult
= PreloadReferenced
;
152 // unfortunately we can be ref'ed multiple times from the
153 // same object, because it uses e.g. the same foreground
154 // and the same background picture. so deal with it.
155 // Hence the use of a QHash rather than of a QSet.
156 m_clients
.insertMulti(c
,c
);
157 Cache::removeFromLRUList(this);
161 void CachedObject::deref(CachedObjectClient
*c
)
164 assert( m_clients
.count() );
165 assert( !canDelete() );
166 assert( m_clients
.contains( c
) );
172 if (allowInLRUList())
173 Cache::insertInLRUList(this);
176 void CachedObject::setSize(int size
)
180 if ( !m_next
&& !m_prev
&& getLRUListFor(this)->m_head
!= this )
183 sizeChanged
= ( size
- m_size
) != 0;
185 // The object must now be moved to a different queue,
186 // since its size has been changed.
187 if ( sizeChanged
&& allowInLRUList())
188 Cache::removeFromLRUList(this);
192 if ( sizeChanged
&& allowInLRUList())
193 Cache::insertInLRUList(this);
196 QTextCodec
* CachedObject::codecForBuffer( const QString
& charset
, const QByteArray
& buffer
) const
198 // we don't use heuristicContentMatch here since it is a) far too slow and
199 // b) having too much functionality for our case.
201 uchar
* d
= ( uchar
* ) buffer
.data();
202 int s
= buffer
.size();
206 d
[0] == 0xef && d
[1] == 0xbb && d
[2] == 0xbf)
207 return QTextCodec::codecForMib( 106 ); // UTF-8
209 if ( s
>= 2 && ((d
[0] == 0xff && d
[1] == 0xfe) ||
210 (d
[0] == 0xfe && d
[1] == 0xff)))
211 return QTextCodec::codecForMib( 1000 ); // UCS-2
214 if(!charset
.isEmpty())
216 QTextCodec
* c
= KGlobal::charsets()->codecForName(charset
);
217 if(c
->mibEnum() == 11) {
218 // iso8859-8 (visually ordered)
219 c
= QTextCodec::codecForName("iso8859-8-i");
225 return QTextCodec::codecForMib( 4 ); // latin 1
228 // -------------------------------------------------------------------------------------------
230 CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
,
232 : CachedObject(url
, CSSStyleSheet
, _cachePolicy
, 0)
234 // Set the type we want (probably css or xml)
235 QString ah
= QLatin1String( accept
);
241 m_wasBlocked
= false;
244 Cache::loader()->load(dl
, this, false, true /*highPriority*/);
248 CachedCSSStyleSheet::CachedCSSStyleSheet(const DOMString
&url
, const QString
&stylesheet_data
)
249 : CachedObject(url
, CSSStyleSheet
, KIO::CC_Verify
, stylesheet_data
.length())
252 m_status
= Persistent
;
253 m_sheet
= DOMString(stylesheet_data
);
256 bool khtml::isAcceptableCSSMimetype( const DOM::DOMString
& mimetype
)
258 // matches Mozilla's check (cf. nsCSSLoader.cpp)
259 return mimetype
.isEmpty() || mimetype
== "text/css" || mimetype
== "application/x-unknown-content-type";
262 void CachedCSSStyleSheet::ref(CachedObjectClient
*c
)
264 CachedObject::ref(c
);
268 c
->error( m_err
, m_errText
);
270 c
->setStyleSheet( m_url
, m_sheet
, m_charset
, m_mimetype
);
274 void CachedCSSStyleSheet::data( QBuffer
&buffer
, bool eof
)
278 setSize(buffer
.buffer().size());
280 // QString charset = checkCharset( buffer.buffer() );
282 if (!m_charset
.isEmpty()) {
283 c
= KGlobal::charsets()->codecForName(m_charset
);
284 if(c
->mibEnum() == 11) c
= QTextCodec::codecForName("iso8859-8-i");
287 c
= codecForBuffer( m_charsetHint
, buffer
.buffer() );
288 m_charset
= c
->name();
290 QString data
= c
->toUnicode( buffer
.buffer().data(), m_size
);
291 // workaround Qt bugs
292 m_sheet
= static_cast<QChar
>(data
[0]) == QChar::ByteOrderMark
? DOMString(data
.mid( 1 ) ) : DOMString(data
);
298 void CachedCSSStyleSheet::checkNotify()
300 if(m_loading
|| m_hadError
) return;
302 CDEBUG
<< "CachedCSSStyleSheet:: finishedLoading " << m_url
.string() << endl
;
304 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
305 it
.next().value()->setStyleSheet( m_url
, m_sheet
, m_charset
, m_mimetype
);
309 void CachedCSSStyleSheet::error( int err
, const char* text
)
316 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
317 it
.next().value()->error( m_err
, m_errText
);
321 QString
CachedCSSStyleSheet::checkCharset(const QByteArray
& buffer
) const
323 int s
= buffer
.size();
324 if (s
<= 12) return m_charset
;
326 // @charset has to be first or directly after BOM.
327 // CSS 2.1 says @charset should win over BOM, but since more browsers support BOM
328 // than @charset, we default to that.
329 const char* d
= (const char*) buffer
.data();
330 if (strncmp(d
, "@charset \"",10) == 0)
332 // the string until "; is the charset name
333 char *p
= strchr(d
+10, '"');
334 if (p
== 0) return m_charset
;
335 QString charset
= QString::fromAscii(d
+10, p
-(d
+10));
342 // -------------------------------------------------------------------------------------------
344 CachedScript::CachedScript(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
, const char*)
345 : CachedObject(url
, Script
, _cachePolicy
, 0)
347 // It's javascript we want.
348 // But some websites think their scripts are <some wrong mimetype here>
349 // and refuse to serve them if we only accept application/x-javascript.
350 setAccept( QLatin1String("*/*") );
352 Cache::loader()->load(dl
, this, false);
357 CachedScript::CachedScript(const DOMString
&url
, const QString
&script_data
)
358 : CachedObject(url
, Script
, KIO::CC_Verify
, script_data
.length())
362 m_status
= Persistent
;
363 m_script
= DOMString(script_data
);
366 void CachedScript::ref(CachedObjectClient
*c
)
368 CachedObject::ref(c
);
370 if(!m_loading
) c
->notifyFinished(this);
373 void CachedScript::data( QBuffer
&buffer
, bool eof
)
377 setSize(buffer
.buffer().size());
379 QTextCodec
* c
= codecForBuffer( m_charset
, buffer
.buffer() );
380 QString data
= c
->toUnicode( buffer
.buffer().data(), m_size
);
381 m_script
= static_cast<QChar
>(data
[0]) == QChar::ByteOrderMark
? DOMString(data
.mid( 1 ) ) : DOMString(data
);
386 void CachedScript::checkNotify()
388 if(m_loading
) return;
390 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
391 it
.next().value()->notifyFinished(this);
394 void CachedScript::error( int /*err*/, const char* /*text*/ )
401 // ------------------------------------------------------------------------------------------
403 static QString
buildAcceptHeader()
405 return "image/png, image/jpeg, video/x-mng, image/jp2, image/gif;q=0.5,*/*;q=0.1";
408 // -------------------------------------------------------------------------------------
410 CachedImage::CachedImage(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
, const char*)
411 : QObject(), CachedObject(url
, Image
, _cachePolicy
, 0)
413 static const QString
&acceptHeader
= KGlobal::staticQString( buildAcceptHeader() );
415 i
= new khtmlImLoad::Image(this);
420 bgColor
= qRgba( 0, 0, 0, 0 );
422 isFullyTransparent
= false;
427 setAccept( acceptHeader
);
428 i
->setShowAnimations(dl
->showAnimations());
431 if ( KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( url
.string() ) ) {
433 CachedObject::finish();
437 CachedImage::~CachedImage()
443 void CachedImage::ref( CachedObjectClient
*c
)
445 CachedObject::ref(c
);
448 kDebug(6060) << " image "<<this<<" ref'd by client " << c
<< "\n";
451 // for mouseovers, dynamic changes
452 //### having both makes no sense
453 if ( m_status
>= Persistent
&& !pixmap_size().isNull() ) {
455 kDebug(6060) << "Notifying finished size:" <<
456 i
->size().width() << ", " << i
->size().height() << endl
;
458 c
->updatePixmap( QRect(QPoint(0, 0), pixmap_size()),
460 c
->notifyFinished( this );
464 void CachedImage::deref( CachedObjectClient
*c
)
466 CachedObject::deref(c
);
467 /* if(m && m_clients.isEmpty() && m->running())
471 #define BGMINWIDTH 32
472 #define BGMINHEIGHT 32
474 QPixmap
CachedImage::tiled_pixmap(const QColor
& newc
, int xWidth
, int xHeight
)
477 // no error indication for background images
478 if(m_hadError
||m_wasBlocked
) return *Cache::nullPixmap
;
480 // If we don't have size yet, nothing to draw yet
481 if (i
->size().width() == 0 || i
->size().height() == 0)
482 return *Cache::nullPixmap
;
485 #warning "Needs some additional performance work"
488 static QRgb bgTransparent
= qRgba( 0, 0, 0, 0 );
490 QSize
s(pixmap_size());
494 if (w
== -1) xWidth
= w
= s
.width();
495 if (h
== -1) xHeight
= h
= s
.height();
497 if ( ( (bgColor
!= bgTransparent
) && (bgColor
!= newc
.rgba()) ) ||
498 ( bgSize
!= QSize(xWidth
, xHeight
)) )
506 const QPixmap
* src
; //source for pretiling, if any
508 const QPixmap
&r
= pixmap(); //this is expensive
509 if (r
.isNull()) return r
;
511 //See whether we should scale
512 if (xWidth
!= s
.width() || xHeight
!= s
.height()) {
513 src
= scaled_pixmap(xWidth
, xHeight
);
518 bgSize
= QSize(xWidth
, xHeight
);
520 //See whether we can - and should - pre-blend
521 // ### this needs serious investigations. Not likely to help with transparent bgColor,
522 // won't work with CSS3 multiple backgrounds. Does it help at all in Qt4? (ref: #114938)
523 if (newc
.isValid() && (r
.hasAlpha() || r
.hasAlphaChannel())) {
524 bg
= new QPixmap(xWidth
, xHeight
);
527 p
.drawPixmap(0, 0, *src
);
528 bgColor
= newc
.rgba();
531 bgColor
= bgTransparent
;
534 //See whether to pre-tile.
537 if ( r
.width() < BGMINWIDTH
)
538 w
= ((BGMINWIDTH
-1) / xWidth
+ 1) * xWidth
;
539 if ( r
.height() < BGMINHEIGHT
)
540 h
= ((BGMINHEIGHT
-1) / xHeight
+ 1) * xHeight
;
543 if ( w
!= xWidth
|| h
!= xHeight
)
545 // kDebug() << "pre-tiling " << s.width() << "," << s.height() << " to " << w << "," << h;
547 bg
= new QPixmap(w
, h
);
548 if (src
->hasAlpha() || src
->hasAlphaChannel()) {
549 if (newc
.isValid() && (bgColor
!= bgTransparent
))
552 bg
->fill( Qt::transparent
);
556 p
.drawTiledPixmap(0, 0, w
, h
, *src
);
561 } else if (src
&& !bg
) {
562 // we were asked for the entire pixmap. Cache it.
563 // ### goes against imload stuff, but it's far too expensive
564 // to recreate the full pixmap each time it's requested as
565 // we don't know what portion of it will be used eventually
566 // (by e.g. paintBackgroundExtended). It could be a few pixels of
567 // a huge image. See #140248/#1 for an obvious example.
568 // Imload probably needs to handle all painting in paintBackgroundExtended.
569 bg
= new QPixmap(*src
);
579 QPixmap
* CachedImage::scaled_pixmap( int xWidth
, int xHeight
)
581 // no error indication for background images
582 if(m_hadError
||m_wasBlocked
) return Cache::nullPixmap
;
584 // If we don't have size yet, nothing to draw yet
585 if (i
->size().width() == 0 || i
->size().height() == 0)
586 return Cache::nullPixmap
;
589 if (scaled
->width() == xWidth
&& scaled
->height() == xHeight
)
594 //### this is quite awful performance-wise. It should avoid
595 // alpha if not needed, and go to pixmap, etc.
596 QImage
im(xWidth
, xHeight
, QImage::Format_ARGB32_Premultiplied
);
599 paint
.setCompositionMode(QPainter::CompositionMode_Source
);
600 ImagePainter
pi(i
, QSize(xWidth
, xHeight
));
601 pi
.paint(0, 0, &paint
);
604 scaled
= new QPixmap(QPixmap::fromImage(im
));
609 QPixmap
CachedImage::pixmap( ) const
612 return *Cache::brokenPixmap
;
615 return *Cache::blockedPixmap
;
617 int w
= i
->size().width();
618 int h
= i
->size().height();
620 if (i
->hasAlpha() && !QApplication::desktop()->paintEngine()->hasFeature(QPaintEngine::PorterDuff
)) {
621 QImage
im(w
, h
, QImage::Format_ARGB32_Premultiplied
);
623 paint
.setCompositionMode(QPainter::CompositionMode_Source
);
625 pi
.paint(0, 0, &paint
);
627 return QPixmap::fromImage( im
, Qt::NoOpaqueDetection
);
631 pm
.fill(Qt::transparent
);
633 paint
.setCompositionMode(QPainter::CompositionMode_Source
);
635 pi
.paint(0, 0, &paint
);
642 QSize
CachedImage::pixmap_size() const
644 if (m_wasBlocked
) return Cache::blockedPixmap
->size();
645 if (m_hadError
) return Cache::brokenPixmap
->size();
646 if (i
) return i
->size();
651 void CachedImage::imageHasGeometry(khtmlImLoad::Image
* /*img*/, int width
, int height
)
654 kDebug(6060) << this << " got geometry "<< width
<< "x" << height
;
656 do_notify(QRect(0, 0, width
, height
));
659 void CachedImage::imageChange (khtmlImLoad::Image
* /*img*/, QRect region
)
662 kDebug(6060) << "Image " << this << " change " <<
663 region
.x() << "," << region
.y() << ":" << region
.width() << "x" << region
.height() << endl
;
665 //### this is overly conservative -- I guess we need to also specify reason,
666 //e.g. repaint vs. changed !!!
673 void CachedImage::doNotifyFinished()
675 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
677 it
.next().value()->notifyFinished(this);
681 void CachedImage::imageError(khtmlImLoad::Image
* /*img*/)
687 void CachedImage::imageDone(khtmlImLoad::Image
* /*img*/)
690 kDebug(6060)<<"Image is done:" << this;
692 m_status
= Persistent
;
697 // QRect CachedImage::valid_rect() const
699 // if (m_wasBlocked) return Cache::blockedPixmap->rect();
700 // return (m_hadError ? Cache::brokenPixmap->rect() : m ? m->frameRect() : ( p ? p->rect() : QRect()) );
704 void CachedImage::do_notify(const QRect
& r
)
706 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
709 kDebug(6060) << " image "<<this<<" notify of geom client " << it
.peekNext() << "\n";
711 it
.next().value()->updatePixmap( r
, this);
716 // void CachedImage::movieUpdated( const QRect& r )
718 // #ifdef LOADER_DEBUG
719 // qDebug("movie updated %d/%d/%d/%d, pixmap size %d/%d", r.x(), r.y(), r.right(), r.bottom(),
720 // m->framePixmap().size().width(), m->framePixmap().size().height());
723 // do_notify(m->framePixmap(), r);
727 void CachedImage::movieStatus(int status
)
729 #warning QMovie emits different signals now and requires different ways to call
731 qDebug("movieStatus(%d)", status
);
734 // ### the html image objects are supposed to send the load event after every frame (according to
735 // netscape). We have a problem though where an image is present, and js code creates a new Image object,
736 // which uses the same CachedImage, the one in the document is not supposed to be notified
738 // just another Qt 2.2.0 bug. we cannot call
739 // QMovie::frameImage if we're after QMovie::EndOfMovie
740 if(status
== QMovie::EndOfFrame
)
742 const QImage
& im
= m
->frameImage();
743 monochrome
= ( ( im
.depth() <= 8 ) && ( im
.numColors() - int( im
.hasAlphaBuffer() ) <= 2 ) );
744 for (int i
= 0; monochrome
&& i
< im
.numColors(); ++i
)
745 if (im
.colorTable()[i
] != qRgb(0xff, 0xff, 0xff) &&
746 im
.colorTable()[i
] != qRgb(0x00, 0x00, 0x00))
748 if( (im
.width() < 5 || im
.height() < 5) && im
.hasAlphaBuffer()) // only evaluate for small images
750 QImage am
= im
.createAlphaMask();
754 for(int y
= 0; y
< am
.height(); y
++)
755 for(int x
= 0; x
< am
.width(); x
++)
756 if(am
.pixelIndex(x
, y
)) {
760 isFullyTransparent
= (!solid
);
764 // we have to delete our tiled bg variant here
765 // because the frame has changed (in order to keep it in sync)
770 if((status
== QMovie::EndOfMovie
&& (!m
|| m
->frameNumber() <= 1)) ||
771 ((status
== QMovie::EndOfLoop
) && (m_showAnimations
== KHTMLSettings::KAnimationLoopOnce
)) ||
772 ((status
== QMovie::EndOfFrame
) && (m_showAnimations
== KHTMLSettings::KAnimationDisabled
))
777 setShowAnimations( KHTMLSettings::KAnimationDisabled
);
779 // monochrome alphamasked images are usually about 10000 times
780 // faster to draw, so this is worth the hack
781 if (p
&& monochrome
&& p
->depth() > 1)
783 QPixmap
* pix
= new QPixmap
;
784 pix
->convertFromImage( p
->convertToImage().convertDepth( 1 ), Qt::MonoOnly
|Qt::AvoidDither
);
785 pix
->setMask( p
->mask() );
791 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
792 it
.next().value()->notifyFinished( this );
793 m_status
= Cached
; //all done
796 if((status
== QMovie::EndOfFrame
) || (status
== QMovie::EndOfMovie
))
799 QRect
r(valid_rect());
800 qDebug("movie Status frame update %d/%d/%d/%d, pixmap size %d/%d", r
.x(), r
.y(), r
.right(), r
.bottom(),
801 pixmap().size().width(), pixmap().size().height());
803 do_notify(pixmap(), valid_rect());
808 // void CachedImage::movieResize(const QSize& /*s*/)
810 // do_notify(m->framePixmap(), QRect());
813 void CachedImage::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations
)
816 i
->setShowAnimations(showAnimations
);
819 // void CachedImage::deleteMovie()
824 void CachedImage::pauseAnimations()
826 // if ( m ) m->setPaused( true );
829 void CachedImage::resumeAnimations()
831 // if ( m ) m->setPaused( false );
834 void CachedImage::clear()
836 delete i
; i
= new khtmlImLoad::Image(this);
837 delete scaled
; scaled
= 0;
838 bgColor
= qRgba( 0, 0, 0, 0xff );
840 bgSize
= QSize(-1,-1);
846 // No need to delete imageSource - QMovie does it for us
850 void CachedImage::data ( QBuffer
&_buffer
, bool eof
)
853 kDebug( 6060 ) << this << "in CachedImage::data(buffersize " << _buffer
.buffer().size() <<", eof=" << eof
<< " pos:" << _buffer
.pos();
855 i
->processData((uchar
*)_buffer
.data().data(), _buffer
.pos());
862 //if ( !typeChecked )
864 // don't attempt incremental loading if we have all the data already
868 #warning QImage* requires heavy porting
871 formatType
= QImageDecoder::formatName( (const uchar
*)_buffer
.buffer().data(), _buffer
.size());
872 if ( formatType
&& strcmp( formatType
, "PNG" ) == 0 )
873 formatType
= 0; // Some png files contain multiple images, we want to show only the first one
877 if ( formatType
) // movie format exists
879 // imgSource = new ImageSource( _buffer.buffer());
880 // m = new QMovie( imgSource, 8192 );
881 m
= new QMovie( _buffer
);
882 m
->connectUpdate( this, SLOT( movieUpdated( const QRect
&) ));
883 m
->connectStatus( this, SLOT( movieStatus(int)));
884 m
->connectResize( this, SLOT( movieResize( const QSize
& ) ) );
890 imgSource
->setEOF(eof
);
891 imgSource
->maybeReady();
896 // QMovie currently doesn't support all kinds of image formats
897 // so we need to use a QPixmap here when we finished loading the complete
898 // picture and display it then all at once.
899 if(typeChecked
&& !formatType
)
902 kDebug(6060) << "CachedImage::data(): reloading as pixmap:";
906 QBuffer
buffer(_buffer
.buffer());
907 buffer
.open(IO_ReadOnly
);
908 QImageIO
io( &buffer
, 0 );
909 io
.setGamma(2.2); // hardcoded "reasonable value"
910 bool result
= io
.read();
911 if (result
) p
->convertFromImage(io
.image(), 0);
914 // set size of image.
916 kDebug(6060) << "CachedImage::data(): image is null: " << p
->isNull();
921 do_notify(pixmap(), QRect(0, 0, 16, 16)); // load "broken image" icon
924 do_notify(*p
, p
->rect());
926 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
927 it
.next().value()->notifyFinished( this );
928 m_status
= Cached
; //all done
934 void CachedImage::finish()
936 //Status oldStatus = m_status;
937 CachedObject::finish();
938 /* if ( oldStatus != m_status ) {
939 const QPixmap &pm = pixmap();
940 do_notify( pm, pm.rect() );
943 QSize s
= pixmap_size();
944 setSize( s
.width() * s
.height() * 2);
948 void CachedImage::error( int /*err*/, const char* /*text*/ )
954 do_notify(QRect(0, 0, 16, 16));
955 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
956 it
.next().value()->notifyFinished(this);
959 // -------------------------------------------------------------------------------------------
961 CachedSound::CachedSound(DocLoader
* dl
, const DOMString
&url
, KIO::CacheControl _cachePolicy
, const char*)
962 : CachedObject(url
, Sound
, _cachePolicy
, 0)
964 setAccept( QLatin1String("*/*") ); // should be whatever phonon would accept...
965 Cache::loader()->load(dl
, this, false);
969 void CachedSound::ref(CachedObjectClient
*c
)
971 CachedObject::ref(c
);
973 if(!m_loading
) c
->notifyFinished(this);
976 void CachedSound::data( QBuffer
&buffer
, bool eof
)
980 setSize(buffer
.buffer().size());
982 m_sound
= buffer
.buffer();
987 void CachedSound::checkNotify()
989 if(m_loading
) return;
991 for (QHashIterator
<CachedObjectClient
*,CachedObjectClient
*> it( m_clients
); it
.hasNext();)
992 it
.next().value()->notifyFinished(this);
995 void CachedSound::error( int /*err*/, const char* /*text*/ )
1001 // ------------------------------------------------------------------------------------------
1003 Request::Request(DocLoader
* dl
, CachedObject
*_object
, bool _incremental
)
1006 object
->setRequest(this);
1007 incremental
= _incremental
;
1013 object
->setRequest(0);
1016 // ------------------------------------------------------------------------------------------
1018 DocLoader::DocLoader(KHTMLPart
* part
, DocumentImpl
* doc
)
1020 m_cachePolicy
= KIO::CC_Verify
;
1022 m_creationDate
= time(0);
1023 m_bautoloadImages
= true;
1024 m_showAnimations
= KHTMLSettings::KAnimationEnabled
;
1028 Cache::docloader
->append( this );
1031 DocLoader::~DocLoader()
1034 Cache::loader()->cancelRequests( this );
1035 Cache::docloader
->removeAll( this );
1038 void DocLoader::setCacheCreationDate(time_t _creationDate
)
1041 m_creationDate
= _creationDate
;
1043 m_creationDate
= time(0); // Now
1046 void DocLoader::setExpireDate(time_t _expireDate
, bool relative
)
1049 m_expireDate
= _expireDate
+ m_creationDate
; // Relative date
1051 m_expireDate
= _expireDate
; // Absolute date
1053 kDebug(6061) << "docLoader: " << m_expireDate
- time(0) << " seconds left until reload required.\n";
1057 void DocLoader::insertCachedObject( CachedObject
* o
) const
1059 m_docObjects
.insert( o
);
1062 bool DocLoader::needReload(CachedObject
*existing
, const QString
& fullURL
)
1064 bool reload
= false;
1065 if (m_cachePolicy
== KIO::CC_Verify
||
1066 // During a softReload, we favour using cached images
1067 // over forcibly re-downloading them.
1068 (existing
->type() == CachedObject::Image
&& m_part
->browserExtension()->browserArguments().softReload
))
1070 if (!m_reloadedURLs
.contains(fullURL
))
1072 if (existing
&& existing
->isExpired() && !existing
->isPreloaded())
1074 Cache::removeCacheEntry(existing
);
1075 m_reloadedURLs
.append(fullURL
);
1080 else if ((m_cachePolicy
== KIO::CC_Reload
) || (m_cachePolicy
== KIO::CC_Refresh
))
1082 if (!m_reloadedURLs
.contains(fullURL
))
1084 if (existing
&& !existing
->isPreloaded())
1086 Cache::removeCacheEntry(existing
);
1088 if (!existing
|| !existing
->isPreloaded()) {
1089 m_reloadedURLs
.append(fullURL
);
1097 void DocLoader::registerPreload(CachedObject
* resource
)
1099 if (!resource
|| resource
->isLoaded() || m_preloads
.contains(resource
))
1101 resource
->increasePreloadCount();
1102 m_preloads
.insert(resource
);
1103 resource
->setProspectiveRequest();
1104 #ifdef PRELOAD_DEBUG
1105 fprintf(stderr
, "PRELOADING %s\n", resource
->url().string().toLatin1().data());
1109 void DocLoader::clearPreloads()
1111 printPreloadStats();
1112 QSet
<CachedObject
*>::iterator end
= m_preloads
.end();
1113 for (QSet
<CachedObject
*>::iterator it
= m_preloads
.begin(); it
!= end
; ++it
) {
1114 CachedObject
* res
= *it
;
1115 res
->decreasePreloadCount();
1116 if (res
->preloadResult() == CachedObject::PreloadNotReferenced
|| res
->hadError())
1117 Cache::removeCacheEntry(res
);
1122 void DocLoader::printPreloadStats()
1124 #ifdef PRELOAD_DEBUG
1125 unsigned scripts
= 0;
1126 unsigned scriptMisses
= 0;
1127 unsigned stylesheets
= 0;
1128 unsigned stylesheetMisses
= 0;
1129 unsigned images
= 0;
1130 unsigned imageMisses
= 0;
1131 QSet
<CachedObject
*>::iterator end
= m_preloads
.end();
1132 for (QSet
<CachedObject
*>::iterator it
= m_preloads
.begin(); it
!= end
; ++it
) {
1133 CachedObject
* res
= *it
;
1134 if (res
->preloadResult() == CachedObject::PreloadNotReferenced
)
1135 fprintf(stderr
,"!! UNREFERENCED PRELOAD %s\n", res
->url().string().toLatin1().data());
1136 else if (res
->preloadResult() == CachedObject::PreloadReferencedWhileComplete
)
1137 fprintf(stderr
,"HIT COMPLETE PRELOAD %s\n", res
->url().string().toLatin1().data());
1138 else if (res
->preloadResult() == CachedObject::PreloadReferencedWhileLoading
)
1139 fprintf(stderr
,"HIT LOADING PRELOAD %s\n", res
->url().string().toLatin1().data());
1141 if (res
->type() == CachedObject::Script
) {
1143 if (res
->preloadResult() < CachedObject::PreloadReferencedWhileLoading
)
1145 } else if (res
->type() == CachedObject::CSSStyleSheet
) {
1147 if (res
->preloadResult() < CachedObject::PreloadReferencedWhileLoading
)
1151 if (res
->preloadResult() < CachedObject::PreloadReferencedWhileLoading
)
1156 fprintf(stderr
, "SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts
, scripts
- scriptMisses
, (scripts
- scriptMisses
) * 100 / scripts
);
1158 fprintf(stderr
, "STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets
, stylesheets
- stylesheetMisses
, (stylesheets
- stylesheetMisses
) * 100 / stylesheets
);
1160 fprintf(stderr
, "IMAGES: %d (%d hits, hit rate %d%%)\n", images
, images
- imageMisses
, (images
- imageMisses
) * 100 / images
);
1164 #define DOCLOADER_SECCHECK(doRedirectCheck) \
1165 KUrl fullURL (m_doc->completeURL( url.string() )); \
1166 if ( !fullURL.isValid() || \
1167 ( m_part && m_part->onlyLocalReferences() && fullURL.protocol() != "file" && fullURL.protocol() != "data") || \
1168 doRedirectCheck && (m_doc && !KAuthorized::authorizeUrlAction("redirect", m_doc->URL(), fullURL))) \
1171 CachedImage
*DocLoader::requestImage( const DOM::DOMString
&url
)
1173 DOCLOADER_SECCHECK(true);
1175 CachedImage
* i
= Cache::requestObject
<CachedImage
, CachedObject::Image
>( this, fullURL
, 0);
1177 if (i
&& i
->status() == CachedObject::Unknown
&& autoloadImages())
1178 Cache::loader()->load(this, i
, true);
1183 CachedCSSStyleSheet
*DocLoader::requestStyleSheet( const DOM::DOMString
&url
, const QString
& charset
,
1184 const char *accept
, bool userSheet
)
1186 DOCLOADER_SECCHECK(!userSheet
);
1188 CachedCSSStyleSheet
* s
= Cache::requestObject
<CachedCSSStyleSheet
, CachedObject::CSSStyleSheet
>( this, fullURL
, accept
);
1189 if ( s
&& !charset
.isEmpty() ) {
1190 s
->setCharsetHint( charset
);
1195 CachedScript
*DocLoader::requestScript( const DOM::DOMString
&url
, const QString
& charset
)
1197 DOCLOADER_SECCHECK(true);
1198 if ( ! KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(fullURL
.host()) ||
1199 KHTMLGlobal::defaultHTMLSettings()->isAdFiltered(fullURL
.url()))
1202 CachedScript
* s
= Cache::requestObject
<CachedScript
, CachedObject::Script
>( this, fullURL
, 0 );
1203 if ( s
&& !charset
.isEmpty() )
1204 s
->setCharset( charset
);
1208 CachedSound
*DocLoader::requestSound( const DOM::DOMString
&url
)
1210 DOCLOADER_SECCHECK(true);
1211 CachedSound
* s
= Cache::requestObject
<CachedSound
, CachedObject::Sound
>( this, fullURL
, 0 );
1215 #undef DOCLOADER_SECCHECK
1217 void DocLoader::setAutoloadImages( bool enable
)
1219 if ( enable
== m_bautoloadImages
)
1222 m_bautoloadImages
= enable
;
1224 if ( !m_bautoloadImages
) return;
1226 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1228 CachedObject
* cur
= it
.next();
1229 if ( cur
->type() == CachedObject::Image
)
1231 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>(cur
) );
1233 CachedObject::Status status
= img
->status();
1234 if ( status
!= CachedObject::Unknown
)
1237 Cache::loader()->load(this, img
, true);
1242 void DocLoader::setShowAnimations( KHTMLSettings::KAnimationAdvice showAnimations
)
1244 if ( showAnimations
== m_showAnimations
) return;
1245 m_showAnimations
= showAnimations
;
1247 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1249 CachedObject
* cur
= it
.next();
1250 if ( cur
->type() == CachedObject::Image
)
1252 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>( cur
) );
1254 img
->setShowAnimations( m_showAnimations
);
1259 void DocLoader::pauseAnimations()
1261 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1263 CachedObject
* cur
= it
.next();
1264 if ( cur
->type() == CachedObject::Image
)
1266 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>( cur
) );
1268 img
->pauseAnimations();
1273 void DocLoader::resumeAnimations()
1275 for ( QSetIterator
<CachedObject
*> it( m_docObjects
); it
.hasNext(); )
1277 CachedObject
* cur
= it
.next();
1278 if ( cur
->type() == CachedObject::Image
)
1280 CachedImage
*img
= const_cast<CachedImage
*>( static_cast<const CachedImage
*>( cur
) );
1282 img
->resumeAnimations();
1287 // ------------------------------------------------------------------------------------------
1289 Loader::Loader() : QObject()
1291 connect(&m_timer
, SIGNAL(timeout()), this, SLOT( servePendingRequests() ) );
1292 m_highPriorityRequestPending
= 0;
1297 delete m_highPriorityRequestPending
;
1298 qDeleteAll(m_requestsPending
);
1299 qDeleteAll(m_requestsLoading
);
1302 void Loader::load(DocLoader
* dl
, CachedObject
*object
, bool incremental
, bool highPriority
)
1304 Request
*req
= new Request(dl
, object
, incremental
);
1305 if (highPriority
&& !m_highPriorityRequestPending
) {
1306 m_highPriorityRequestPending
= req
;
1309 m_requestsPending
.prepend(req
);
1311 m_requestsPending
.append(req
);
1313 highPriority
= false;
1316 emit
requestStarted( req
->m_docLoader
, req
->object
);
1319 servePendingRequests();
1321 m_timer
.setSingleShot(true);
1326 void Loader::servePendingRequests()
1328 while ( (m_highPriorityRequestPending
!= 0 || m_requestsPending
.count() != 0) && (m_requestsLoading
.count() < MAX_JOB_COUNT
) )
1330 // get the first pending request
1331 Request
*req
= m_highPriorityRequestPending
? m_highPriorityRequestPending
: m_requestsPending
.takeFirst();
1334 kDebug( 6060 ) << "starting Loader url=" << req
->object
->url().string();
1337 KUrl
u(req
->object
->url().string());
1338 KIO::TransferJob
* job
= KIO::get( u
, KIO::NoReload
, KIO::HideProgressInfo
/*no GUI*/);
1340 job
->addMetaData("cache", KIO::getCacheControlString(req
->object
->cachePolicy()));
1341 if (!req
->object
->accept().isEmpty())
1342 job
->addMetaData("accept", req
->object
->accept());
1343 if ( req
->m_docLoader
)
1345 job
->addMetaData( "referrer", req
->m_docLoader
->doc()->URL().url() );
1347 KHTMLPart
*part
= req
->m_docLoader
->part();
1350 job
->addMetaData( "cross-domain", part
->toplevelURL().url() );
1352 job
->ui()->setWindow (part
->widget()->topLevelWidget());
1356 connect( job
, SIGNAL( result( KJob
* ) ), this, SLOT( slotFinished( KJob
* ) ) );
1357 connect( job
, SIGNAL( mimetype( KIO::Job
*, const QString
& ) ), this, SLOT( slotMimetype( KIO::Job
*, const QString
& ) ) );
1358 connect( job
, SIGNAL( data( KIO::Job
*, const QByteArray
&)),
1359 SLOT( slotData( KIO::Job
*, const QByteArray
&)));
1361 if ( req
->object
->schedule() )
1362 KIO::Scheduler::scheduleJob( job
);
1364 m_requestsLoading
.insertMulti(job
, req
);
1365 if (m_highPriorityRequestPending
) {
1366 m_highPriorityRequestPending
= 0;
1372 void Loader::slotMimetype( KIO::Job
*j
, const QString
& s
)
1374 KIO::TransferJob
* job
= static_cast<KIO::TransferJob
*>(j
);
1375 Request
*r
= m_requestsLoading
.value( j
);
1378 CachedObject
*o
= r
->object
;
1380 // Mozilla plain ignores any mimetype that doesn't have / in it, and handles it as "",
1381 // including when being picky about mimetypes. Match that for better compatibility with broken servers.
1382 if (s
.contains('/'))
1388 void Loader::slotFinished( KJob
* job
)
1390 KIO::TransferJob
* j
= static_cast<KIO::TransferJob
*>(job
);
1391 Request
*r
= m_requestsLoading
.take( j
);
1396 if (j
->error() || j
->isErrorPage())
1399 kDebug(6060) << "Loader::slotFinished, with error. job->error()= " << j
->error() << " job->isErrorPage()=" << j
->isErrorPage();
1401 r
->object
->error( job
->error(), job
->errorText().toAscii().constData() );
1402 emit
requestFailed( r
->m_docLoader
, r
->object
);
1406 QString cs
= j
->queryMetaData("charset");
1407 if (!cs
.isEmpty()) r
->object
->setCharset(cs
);
1408 r
->object
->data(r
->m_buffer
, true);
1409 emit
requestDone( r
->m_docLoader
, r
->object
);
1410 time_t expireDate
= j
->queryMetaData("expire-date").toLong();
1412 kDebug(6060) << "Loader::slotFinished, url = " << j
->url().url();
1414 r
->object
->setExpireDate( expireDate
);
1416 if ( r
->object
->type() == CachedObject::Image
) {
1417 QString fn
= j
->queryMetaData("content-disposition");
1418 static_cast<CachedImage
*>( r
->object
)->setSuggestedFilename(fn
);
1420 static_cast<CachedImage
*>( r
->object
)->setSuggestedTitle(fn
);
1423 tf
.write((const char*)r
->m_buffer
.buffer().data(), r
->m_buffer
.size());
1425 KFileMetaInfo
kfmi(tf
.fileName());
1426 if (!kfmi
.isEmpty()) {
1427 KFileMetaInfoItem i
= kfmi
.item("Name");
1429 static_cast<CachedImage
*>(r
->object
)->setSuggestedTitle(i
.string());
1431 i
= kfmi
.item("Title");
1433 static_cast<CachedImage
*>(r
->object
)->setSuggestedTitle(i
.string());
1441 r
->object
->finish();
1444 kDebug( 6060 ) << "Loader:: JOB FINISHED " << r
->object
<< ": " << r
->object
->url().string();
1449 if ( (m_highPriorityRequestPending
!= 0 || m_requestsPending
.count() != 0) && (m_requestsLoading
.count() < MAX_JOB_COUNT
/ 2) ) {
1450 m_timer
.setSingleShot(true);
1455 void Loader::slotData( KIO::Job
*job
, const QByteArray
&data
)
1457 Request
*r
= m_requestsLoading
[job
];
1459 kDebug( 6060 ) << "got data for unknown request!";
1463 if ( !r
->m_buffer
.isOpen() )
1464 r
->m_buffer
.open( QIODevice::WriteOnly
);
1466 r
->m_buffer
.write( data
.data(), data
.size() );
1469 r
->object
->data( r
->m_buffer
, false );
1472 int Loader::numRequests( DocLoader
* dl
) const
1475 if (m_highPriorityRequestPending
&& m_highPriorityRequestPending
->m_docLoader
== dl
)
1478 foreach( Request
* req
, m_requestsPending
)
1479 if ( req
->m_docLoader
== dl
)
1482 foreach( Request
* req
, m_requestsLoading
)
1483 if ( req
->m_docLoader
== dl
)
1489 void Loader::cancelRequests( DocLoader
* dl
)
1491 if (m_highPriorityRequestPending
&& m_highPriorityRequestPending
->m_docLoader
== dl
) {
1492 CDEBUG
<< "canceling high priority pending request for " << m_highPriorityRequestPending
->object
->url().string() << endl
;
1493 Cache::removeCacheEntry( m_highPriorityRequestPending
->object
);
1494 delete m_highPriorityRequestPending
;
1495 m_highPriorityRequestPending
= 0;
1498 QMutableLinkedListIterator
<Request
*> pIt( m_requestsPending
);
1499 while ( pIt
.hasNext() ) {
1500 Request
* cur
= pIt
.next();
1501 if ( cur
->m_docLoader
== dl
)
1503 CDEBUG
<< "canceling pending request for " << cur
->object
->url().string() << endl
;
1504 Cache::removeCacheEntry( cur
->object
);
1510 //kDebug( 6060 ) << "got " << m_requestsLoading.count() << "loading requests";
1512 QMutableHashIterator
<KIO::Job
*,Request
*> lIt( m_requestsLoading
);
1513 while ( lIt
.hasNext() )
1516 if ( lIt
.value()->m_docLoader
== dl
)
1518 //kDebug( 6060 ) << "canceling loading request for " << lIt.current()->object->url().string();
1519 KIO::Job
*job
= static_cast<KIO::Job
*>( lIt
.key() );
1520 Cache::removeCacheEntry( lIt
.value()->object
);
1528 KIO::Job
*Loader::jobForRequest( const DOM::DOMString
&url
) const
1530 QHashIterator
<KIO::Job
*,Request
*> it( m_requestsLoading
);
1531 while (it
.hasNext())
1534 if ( it
.value()->object
&& it
.value()->object
->url() == url
)
1535 return static_cast<KIO::Job
*>( it
.key() );
1541 // ----------------------------------------------------------------------------
1544 QHash
<QString
,CachedObject
*> *Cache::cache
;
1545 QLinkedList
<DocLoader
*> *Cache::docloader
;
1546 QLinkedList
<CachedObject
*> *Cache::freeList
;
1547 Loader
*Cache::m_loader
;
1549 int Cache::maxSize
= DEFCACHESIZE
;
1550 int Cache::totalSizeOfLRU
;
1552 QPixmap
*Cache::nullPixmap
;
1553 QPixmap
*Cache::brokenPixmap
;
1554 QPixmap
*Cache::blockedPixmap
;
1559 cache
= new QHash
<QString
,CachedObject
*>();
1562 docloader
= new QLinkedList
<DocLoader
*>;
1565 nullPixmap
= new QPixmap
;
1567 if ( !brokenPixmap
)
1568 brokenPixmap
= new QPixmap(KHTMLGlobal::iconLoader()->loadIcon("image-missing", KIconLoader::Desktop
, 16, KIconLoader::DisabledState
));
1570 if ( !blockedPixmap
) {
1571 blockedPixmap
= new QPixmap();
1572 blockedPixmap
->loadFromData(blocked_icon_data
, blocked_icon_len
);
1576 m_loader
= new Loader();
1579 freeList
= new QLinkedList
<CachedObject
*>;
1584 if ( !cache
) return;
1586 kDebug( 6060 ) << "Cache: CLEAR!";
1592 foreach (CachedObject
* co
, *cache
) {
1593 if (!co
->canDelete()) {
1594 kDebug( 6060 ) << " Object in cache still linked to";
1595 kDebug( 6060 ) << " -> URL: " << co
->url();
1596 kDebug( 6060 ) << " -> #clients: " << co
->count();
1598 // assert(co->canDelete());
1601 foreach (CachedObject
* co
, *freeList
) {
1602 if (!co
->canDelete()) {
1603 kDebug( 6060 ) << " Object in freelist still linked to";
1604 kDebug( 6060 ) << " -> URL: " << co
->url();
1605 kDebug( 6060 ) << " -> #clients: " << co
->count();
1608 foreach (CachedObjectClient* cur, (*co->m_clients)))
1610 if (dynamic_cast<RenderObject*>(cur)) {
1611 kDebug( 6060 ) << " --> RenderObject";
1613 kDebug( 6060 ) << " --> Something else";
1616 // assert(freeList->current()->canDelete());
1621 delete cache
; cache
= 0;
1622 delete nullPixmap
; nullPixmap
= 0;
1623 delete brokenPixmap
; brokenPixmap
= 0;
1624 delete blockedPixmap
; blockedPixmap
= 0;
1625 delete m_loader
; m_loader
= 0;
1626 delete docloader
; docloader
= 0;
1627 qDeleteAll(*freeList
);
1628 delete freeList
; freeList
= 0;
1631 template<typename CachedObjectType
, enum CachedObject::Type CachedType
>
1632 CachedObjectType
* Cache::requestObject( DocLoader
* dl
, const KUrl
& kurl
, const char* accept
)
1634 KIO::CacheControl cachePolicy
= dl
->cachePolicy();
1636 QString url
= kurl
.url();
1637 CachedObject
* o
= cache
->value(url
);
1639 if ( o
&& o
->type() != CachedType
) {
1640 removeCacheEntry( o
);
1644 if ( o
&& dl
->needReload( o
, url
) ) {
1646 assert( !cache
->contains( url
) );
1652 kDebug( 6060 ) << "Cache: new: " << kurl
.url();
1654 CachedObjectType
* cot
= new CachedObjectType(dl
, url
, cachePolicy
, accept
);
1655 cache
->insert( url
, cot
);
1656 if ( cot
->allowInLRUList() )
1657 insertInLRUList( cot
);
1662 kDebug( 6060 ) << "Cache: using pending/cached: " << kurl
.url();
1667 dl
->insertCachedObject( o
);
1669 return static_cast<CachedObjectType
*>(o
);
1672 void Cache::preloadStyleSheet( const QString
&url
, const QString
&stylesheet_data
)
1674 if (cache
->contains(url
))
1675 removeCacheEntry(cache
->value(url
));
1677 CachedCSSStyleSheet
*stylesheet
= new CachedCSSStyleSheet(url
, stylesheet_data
);
1678 cache
->insert( url
, stylesheet
);
1681 void Cache::preloadScript( const QString
&url
, const QString
&script_data
)
1683 if (cache
->contains(url
))
1684 removeCacheEntry(cache
->value(url
));
1686 CachedScript
*script
= new CachedScript(url
, script_data
);
1687 cache
->insert( url
, script
);
1690 void Cache::flush(bool force
)
1694 if ( force
|| totalSizeOfLRU
> maxSize
+ maxSize
/4) {
1695 for ( int i
= MAX_LRU_LISTS
-1; i
>= 0 && totalSizeOfLRU
> maxSize
; --i
)
1696 while ( totalSizeOfLRU
> maxSize
&& m_LRULists
[i
].m_tail
)
1697 removeCacheEntry( m_LRULists
[i
].m_tail
);
1704 QMutableLinkedListIterator
<CachedObject
*> it(*freeList
);
1705 while ( it
.hasNext() ) {
1706 CachedObject
* p
= it
.next();
1707 if ( p
->canDelete() ) {
1714 void Cache::setSize( int bytes
)
1717 flush(true /* force */);
1720 void Cache::statistics()
1722 // this function is for debugging purposes only
1730 int stylesheets
= 0;
1732 foreach (CachedObject
* o
, *cache
)
1735 case CachedObject::Image
:
1737 //CachedImage *im = static_cast<CachedImage *>(o);
1742 msize += im->size();
1746 case CachedObject::CSSStyleSheet
:
1749 case CachedObject::Script
:
1752 case CachedObject::Sound
:
1760 kDebug( 6060 ) << "------------------------- image cache statistics -------------------";
1761 kDebug( 6060 ) << "Number of items in cache: " << cache
->count();
1762 kDebug( 6060 ) << "Number of cached images: " << images
;
1763 kDebug( 6060 ) << "Number of cached movies: " << movie
;
1764 kDebug( 6060 ) << "Number of cached scripts: " << scripts
;
1765 kDebug( 6060 ) << "Number of cached stylesheets: " << stylesheets
;
1766 kDebug( 6060 ) << "Number of cached sounds: " << sound
;
1767 kDebug( 6060 ) << "pixmaps: allocated space approx. " << size
<< " kB";
1768 kDebug( 6060 ) << "movies : allocated space approx. " << msize
/1024 << " kB";
1769 kDebug( 6060 ) << "--------------------------------------------------------------------";
1772 void Cache::removeCacheEntry( CachedObject
*object
)
1774 QString key
= object
->url().string();
1776 cache
->remove( key
);
1777 removeFromLRUList( object
);
1779 foreach( DocLoader
* dl
, *docloader
)
1780 dl
->removeCachedObject( object
);
1782 if ( !object
->free() ) {
1783 Cache::freeList
->append( object
);
1784 object
->m_free
= true;
1788 static inline int FastLog2(unsigned int j
)
1795 log2
+= 16, j
>>= 16;
1808 static LRUList
* getLRUListFor(CachedObject
* o
)
1810 int accessCount
= o
->accessCount();
1812 if (accessCount
== 0) {
1815 int sizeLog
= FastLog2(o
->size());
1816 queueIndex
= sizeLog
/o
->accessCount() - 1;
1819 if (queueIndex
>= MAX_LRU_LISTS
)
1820 queueIndex
= MAX_LRU_LISTS
-1;
1822 return &m_LRULists
[queueIndex
];
1825 void Cache::removeFromLRUList(CachedObject
*object
)
1827 CachedObject
*next
= object
->m_next
;
1828 CachedObject
*prev
= object
->m_prev
;
1830 LRUList
* list
= getLRUListFor(object
);
1831 CachedObject
*&head
= getLRUListFor(object
)->m_head
;
1833 if (next
== 0 && prev
== 0 && head
!= object
) {
1841 next
->m_prev
= prev
;
1842 else if (list
->m_tail
== object
)
1843 list
->m_tail
= prev
;
1846 prev
->m_next
= next
;
1847 else if (head
== object
)
1850 totalSizeOfLRU
-= object
->size();
1853 void Cache::insertInLRUList(CachedObject
*object
)
1855 removeFromLRUList(object
);
1858 assert( !object
->free() );
1859 assert( object
->canDelete() );
1860 assert( object
->allowInLRUList() );
1862 LRUList
* list
= getLRUListFor(object
);
1864 CachedObject
*&head
= list
->m_head
;
1866 object
->m_next
= head
;
1868 head
->m_prev
= object
;
1871 if (object
->m_next
== 0)
1872 list
->m_tail
= object
;
1874 totalSizeOfLRU
+= object
->size();
1877 // --------------------------------------
1879 void CachedObjectClient::updatePixmap(const QRect
&, CachedImage
*) {}
1880 void CachedObjectClient::setStyleSheet(const DOM::DOMString
&/*url*/, const DOM::DOMString
&/*sheet*/, const DOM::DOMString
&/*charset*/, const DOM::DOMString
&/*mimetype*/) {}
1881 void CachedObjectClient::notifyFinished(CachedObject
* /*finishedObj*/) {}
1882 void CachedObjectClient::error(int /*err*/, const QString
&/*text*/) {}
1886 #include "loader.moc"